Skip to content

Commit de02994

Browse files
committed
[clang][Interp] Handle negative shift amounts correctly
We need to invert them and use the opposite shift.
1 parent 7645823 commit de02994

File tree

3 files changed

+56
-38
lines changed

3 files changed

+56
-38
lines changed

clang/lib/AST/Interp/Interp.h

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,66 +2209,76 @@ inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
22092209
//===----------------------------------------------------------------------===//
22102210
// Shr, Shl
22112211
//===----------------------------------------------------------------------===//
2212+
enum class ShiftDir { Left, Right };
22122213

2213-
template <PrimType NameL, PrimType NameR>
2214-
inline bool Shr(InterpState &S, CodePtr OpPC) {
2215-
using LT = typename PrimConv<NameL>::T;
2216-
using RT = typename PrimConv<NameR>::T;
2217-
auto RHS = S.Stk.pop<RT>();
2218-
const auto &LHS = S.Stk.pop<LT>();
2214+
template <class LT, class RT, ShiftDir Dir>
2215+
inline bool DoShift(InterpState &S, CodePtr OpPC, LT &LHS, RT &RHS) {
22192216
const unsigned Bits = LHS.bitWidth();
22202217

22212218
// OpenCL 6.3j: shift values are effectively % word size of LHS.
22222219
if (S.getLangOpts().OpenCL)
22232220
RT::bitAnd(RHS, RT::from(LHS.bitWidth() - 1, RHS.bitWidth()),
22242221
RHS.bitWidth(), &RHS);
22252222

2223+
if (RHS.isNegative()) {
2224+
// During constant-folding, a negative shift is an opposite shift. Such a
2225+
// shift is not a constant expression.
2226+
const SourceInfo &Loc = S.Current->getSource(OpPC);
2227+
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
2228+
if (S.getLangOpts().CPlusPlus11 && S.getEvalStatus().Diag &&
2229+
!S.getEvalStatus().Diag->empty())
2230+
return false;
2231+
RHS = -RHS;
2232+
return DoShift < LT, RT,
2233+
Dir == ShiftDir::Left ? ShiftDir::Right
2234+
: ShiftDir::Left > (S, OpPC, LHS, RHS);
2235+
}
2236+
22262237
if (!CheckShift(S, OpPC, LHS, RHS, Bits))
22272238
return false;
22282239

22292240
// Limit the shift amount to Bits - 1. If this happened,
22302241
// it has already been diagnosed by CheckShift() above,
22312242
// but we still need to handle it.
22322243
typename LT::AsUnsigned R;
2233-
if (RHS > RT::from(Bits - 1, RHS.bitWidth()))
2234-
LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS),
2235-
LT::AsUnsigned::from(Bits - 1), Bits, &R);
2236-
else
2237-
LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS),
2238-
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2244+
if constexpr (Dir == ShiftDir::Left) {
2245+
if (RHS > RT::from(Bits - 1, RHS.bitWidth()))
2246+
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2247+
LT::AsUnsigned::from(Bits - 1), Bits, &R);
2248+
else
2249+
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2250+
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2251+
} else {
2252+
if (RHS > RT::from(Bits - 1, RHS.bitWidth()))
2253+
LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS),
2254+
LT::AsUnsigned::from(Bits - 1), Bits, &R);
2255+
else
2256+
LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS),
2257+
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2258+
}
2259+
22392260
S.Stk.push<LT>(LT::from(R));
22402261
return true;
22412262
}
22422263

22432264
template <PrimType NameL, PrimType NameR>
2244-
inline bool Shl(InterpState &S, CodePtr OpPC) {
2265+
inline bool Shr(InterpState &S, CodePtr OpPC) {
22452266
using LT = typename PrimConv<NameL>::T;
22462267
using RT = typename PrimConv<NameR>::T;
22472268
auto RHS = S.Stk.pop<RT>();
2248-
const auto &LHS = S.Stk.pop<LT>();
2249-
const unsigned Bits = LHS.bitWidth();
2250-
2251-
// OpenCL 6.3j: shift values are effectively % word size of LHS.
2252-
if (S.getLangOpts().OpenCL)
2253-
RT::bitAnd(RHS, RT::from(LHS.bitWidth() - 1, RHS.bitWidth()),
2254-
RHS.bitWidth(), &RHS);
2269+
auto LHS = S.Stk.pop<LT>();
22552270

2256-
if (!CheckShift(S, OpPC, LHS, RHS, Bits))
2257-
return false;
2271+
return DoShift<LT, RT, ShiftDir::Right>(S, OpPC, LHS, RHS);
2272+
}
22582273

2259-
// Limit the shift amount to Bits - 1. If this happened,
2260-
// it has already been diagnosed by CheckShift() above,
2261-
// but we still need to handle it.
2262-
typename LT::AsUnsigned R;
2263-
if (RHS > RT::from(Bits - 1, RHS.bitWidth()))
2264-
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2265-
LT::AsUnsigned::from(Bits - 1), Bits, &R);
2266-
else
2267-
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2268-
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2274+
template <PrimType NameL, PrimType NameR>
2275+
inline bool Shl(InterpState &S, CodePtr OpPC) {
2276+
using LT = typename PrimConv<NameL>::T;
2277+
using RT = typename PrimConv<NameR>::T;
2278+
auto RHS = S.Stk.pop<RT>();
2279+
auto LHS = S.Stk.pop<LT>();
22692280

2270-
S.Stk.push<LT>(LT::from(R));
2271-
return true;
2281+
return DoShift<LT, RT, ShiftDir::Left>(S, OpPC, LHS, RHS);
22722282
}
22732283

22742284
//===----------------------------------------------------------------------===//

clang/test/AST/Interp/shifts.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s
2-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=cxx17 %s
3-
// RUN: %clang_cc1 -std=c++20 -verify=ref %s
4-
// RUN: %clang_cc1 -std=c++17 -verify=ref-cxx17 %s
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected,all %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=cxx17,all %s
3+
// RUN: %clang_cc1 -std=c++20 -verify=ref,all %s
4+
// RUN: %clang_cc1 -std=c++17 -verify=ref-cxx17,all %s
55

66
#define INT_MIN (~__INT_MAX__)
77

@@ -198,3 +198,8 @@ namespace LongInt {
198198
}
199199
static_assert(f() == 1, "");
200200
};
201+
202+
enum shiftof {
203+
X = (1<<-29) // all-error {{expression is not an integral constant expression}} \
204+
// all-note {{negative shift count -29}}
205+
};

clang/test/Sema/shift-count-negative.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// RUN: %clang_cc1 -x c -fsyntax-only -verify=expected,c -pedantic %s
22
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify=expected,cpp %s
33

4+
// RUN: %clang_cc1 -x c -fsyntax-only -verify=expected,c -pedantic %s -fexperimental-new-constant-interpreter
5+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify=expected,cpp %s -fexperimental-new-constant-interpreter
6+
47
enum shiftof {
58
X = (1<<-29) // c-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
69
// cpp-error@-1 {{expression is not an integral constant expression}}

0 commit comments

Comments
 (0)