From 770013d315e9a905e92794876b604568d2fb568e Mon Sep 17 00:00:00 2001 From: EliseZeroTwo Date: Wed, 13 Dec 2023 18:46:03 -0700 Subject: [PATCH 1/4] fix: Overlapping spans in delimited meta-vars --- compiler/rustc_expand/src/mbe/macro_rules.rs | 7 +++ tests/ui/macros/issue-118786.rs | 16 +++++++ tests/ui/macros/issue-118786.stderr | 47 ++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/ui/macros/issue-118786.rs create mode 100644 tests/ui/macros/issue-118786.stderr diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 19734394382ca..0b3ef8292194f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -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); diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs new file mode 100644 index 0000000000000..84af3a651137e --- /dev/null +++ b/tests/ui/macros/issue-118786.rs @@ -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)); diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr new file mode 100644 index 0000000000000..ca3a40f31c1f5 --- /dev/null +++ b/tests/ui/macros/issue-118786.stderr @@ -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 + From 1fc6dbc32b0aba985af20e8eddfb05337f691dc5 Mon Sep 17 00:00:00 2001 From: GearsDatapacks Date: Tue, 12 Dec 2023 15:20:29 +0000 Subject: [PATCH 2/4] Change expr_trailing_brace to an exhaustive match to force new expression kinds to specify whether they contain a brace Add inline const and other possible curly brace expressions to expr_trailing_brace Add tests for `}` before `else` in `let...else` error Change to explicit cases for expressions with optional values when being checked for trailing braces Add tests for more complex cases of `}` before `else` in `let..else` statement Move other possible `}` cases into separate arm and add FIXME for future reference --- compiler/rustc_ast/src/util/classify.rs | 35 ++- tests/ui/parser/bad-let-else-statement.rs | 164 +++++++++++++ tests/ui/parser/bad-let-else-statement.stderr | 232 ++++++++++++++++++ 3 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 tests/ui/parser/bad-let-else-statement.rs create mode 100644 tests/ui/parser/bad-let-else-statement.stderr diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 821fca6656cdd..4dece07978376 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -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, } } } diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs new file mode 100644 index 0000000000000..7b927c89ba074 --- /dev/null +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -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() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr new file mode 100644 index 0000000000000..7cbda25e417f3 --- /dev/null +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -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 + From 20927d39562415387e25655da0bc5f52bdc0f662 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 16 Dec 2023 17:21:20 +0000 Subject: [PATCH 3/4] Remove unnecessary constness from ProjectionCandidate --- compiler/rustc_middle/src/traits/select.rs | 6 ++---- .../src/traits/select/candidate_assembly.rs | 7 ++----- .../src/traits/select/confirmation.rs | 4 +--- .../rustc_trait_selection/src/traits/select/mod.rs | 14 +++++++------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index e8e2907eb33a9..c52103eb247e1 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -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. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c7d0ab7164411..cd7fd028a46aa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -154,10 +154,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .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 ``, searches the obligations that the caller @@ -585,7 +582,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } 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. // diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7e3d304d1cdf0..bcaf6aff66b50 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -71,7 +71,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } - ProjectionCandidate(idx, _) => { + ProjectionCandidate(idx) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; ImplSource::Param(obligations) } @@ -1313,7 +1313,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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(), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7f31a2529f5c2..9886e33ca3b5a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1883,7 +1883,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | 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 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // 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 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ) } - (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 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate, - ObjectCandidate(_) | ProjectionCandidate(..), + ObjectCandidate(_) | ProjectionCandidate(_), ) => DropVictim::No, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { From 8022057ebbcc2a6a811d2c6ffbca4391ebfe0778 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 17 Dec 2023 19:49:48 +0000 Subject: [PATCH 4/4] Avoid overflow in GVN constant indexing. --- compiler/rustc_mir_transform/src/gvn.rs | 8 +- ...nstant_index_overflow.GVN.panic-abort.diff | 104 ++++++++++++++++++ ...stant_index_overflow.GVN.panic-unwind.diff | 104 ++++++++++++++++++ tests/mir-opt/gvn.rs | 18 +++ 4 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b21988e179d0f..3b8adf7e86b71 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -644,12 +644,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { 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); diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff new file mode 100644 index 0000000000000..4b1e337911468 --- /dev/null +++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff @@ -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::::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::(move _14) -> [return: bb7, unwind unreachable]; + } + + bb7: { + StorageDead(_14); + StorageDead(_3); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..8abcd7e9387fe --- /dev/null +++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff @@ -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::::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::(move _14) -> [return: bb7, unwind continue]; + } + + bb7: { + StorageDead(_14); + StorageDead(_3); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 6e082acdbd393..db131f7f97d8b 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -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(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(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