Skip to content

Commit 257a0d5

Browse files
committed
[clang][Interp] Diagnose out-of-range casts to enum types
1 parent c0c4ad5 commit 257a0d5

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,16 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
466466
if (!this->visit(SubExpr))
467467
return false;
468468

469+
// Possibly diagnose casts to enum types if the target type does not
470+
// have a fixed size.
471+
if (CE->getType()->isEnumeralType()) {
472+
if (const auto *ET = CE->getType().getCanonicalType()->getAs<EnumType>();
473+
ET && !ET->getDecl()->isFixed()) {
474+
if (!this->emitCheckEnumValue(*FromT, ET->getDecl(), CE))
475+
return false;
476+
}
477+
}
478+
469479
if (ToT == PT_IntAP)
470480
return this->emitCastAP(*FromT, Ctx.getBitWidth(CE->getType()), CE);
471481
if (ToT == PT_IntAPS)

clang/lib/AST/Interp/Interp.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/AST/Expr.h"
2323
#include "clang/AST/ExprCXX.h"
2424
#include "llvm/ADT/APSInt.h"
25+
#include "llvm/ADT/StringExtras.h"
2526
#include <limits>
2627
#include <vector>
2728

@@ -899,6 +900,31 @@ bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
899900
return runRecordDestructor(S, OpPC, Pointer(const_cast<Block *>(B)), Desc);
900901
}
901902

903+
void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
904+
const APSInt &Value) {
905+
llvm::APInt Min;
906+
llvm::APInt Max;
907+
908+
if (S.EvaluatingDecl && !S.EvaluatingDecl->isConstexpr())
909+
return;
910+
911+
ED->getValueRange(Max, Min);
912+
--Max;
913+
914+
if (ED->getNumNegativeBits() &&
915+
(Max.slt(Value.getSExtValue()) || Min.sgt(Value.getSExtValue()))) {
916+
const SourceLocation &Loc = S.Current->getLocation(OpPC);
917+
S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range)
918+
<< llvm::toString(Value, 10) << Min.getSExtValue() << Max.getSExtValue()
919+
<< ED;
920+
} else if (!ED->getNumNegativeBits() && Max.ult(Value.getZExtValue())) {
921+
const SourceLocation &Loc = S.Current->getLocation(OpPC);
922+
S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range)
923+
<< llvm::toString(Value, 10) << Min.getZExtValue() << Max.getZExtValue()
924+
<< ED;
925+
}
926+
}
927+
902928
bool Interpret(InterpState &S, APValue &Result) {
903929
// The current stack frame when we started Interpret().
904930
// This is being used by the ops to determine wheter

clang/lib/AST/Interp/Interp.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,6 +2774,20 @@ inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
27742774
return false;
27752775
}
27762776

2777+
void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
2778+
const APSInt &Value);
2779+
2780+
template <PrimType Name, class T = typename PrimConv<Name>::T>
2781+
inline bool CheckEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED) {
2782+
assert(ED);
2783+
assert(!ED->isFixed());
2784+
const APSInt Val = S.Stk.peek<T>().toAPSInt();
2785+
2786+
if (S.inConstantContext())
2787+
diagnoseEnumValue(S, OpPC, ED, Val);
2788+
return true;
2789+
}
2790+
27772791
/// OldPtr -> Integer -> NewPtr.
27782792
template <PrimType TIn, PrimType TOut>
27792793
inline bool DecayPtr(InterpState &S, CodePtr OpPC) {

clang/lib/AST/Interp/Opcodes.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def ArgDecl : ArgType { let Name = "const Decl*"; }
6666
def ArgVarDecl : ArgType { let Name = "const VarDecl*"; }
6767
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
6868
def ArgPrimType : ArgType { let Name = "PrimType"; }
69+
def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
6970

7071
//===----------------------------------------------------------------------===//
7172
// Classes of types instructions operate on.
@@ -389,6 +390,12 @@ def CheckDecl : Opcode {
389390
let Args = [ArgVarDecl];
390391
}
391392

393+
def CheckEnumValue : Opcode {
394+
let Args = [ArgEnumDecl];
395+
let Types = [FixedSizeIntegralTypeClass];
396+
let HasGroup = 1;
397+
}
398+
392399
// [] -> [Value]
393400
def GetGlobal : AccessOpcode;
394401
def GetGlobalUnchecked : AccessOpcode;

clang/test/AST/Interp/cxx11.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,93 @@ namespace ReferenceToConst {
6262
}
6363
};
6464
}
65+
66+
67+
68+
namespace GH50055 {
69+
// Enums without fixed underlying type
70+
enum E1 {e11=-4, e12=4};
71+
enum E2 {e21=0, e22=4};
72+
enum E3 {e31=-4, e32=1024};
73+
enum E4 {e41=0};
74+
// Empty but as-if it had a single enumerator with value 0
75+
enum EEmpty {};
76+
77+
// Enum with fixed underlying type because the underlying type is explicitly specified
78+
enum EFixed : int {efixed1=-4, efixed2=4};
79+
// Enum with fixed underlying type because it is scoped
80+
enum class EScoped {escoped1=-4, escoped2=4};
81+
82+
enum EMaxInt {emaxint1=-1, emaxint2=__INT_MAX__};
83+
84+
enum NumberType {};
85+
86+
E2 testDefaultArgForParam(E2 e2Param = (E2)-1) { // ok, not a constant expression context
87+
E2 e2LocalInit = e2Param; // ok, not a constant expression context
88+
return e2LocalInit;
89+
}
90+
91+
// #include <enum-constexpr-conversion-system-header.h>
92+
93+
void testValueInRangeOfEnumerationValues() {
94+
constexpr E1 x1 = static_cast<E1>(-8);
95+
constexpr E1 x2 = static_cast<E1>(8);
96+
// both-error@-1 {{integer value 8 is outside the valid range of values [-8, 7] for the enumeration type 'E1'}}
97+
E1 x2b = static_cast<E1>(8); // ok, not a constant expression context
98+
99+
constexpr E2 x3 = static_cast<E2>(-8);
100+
// both-error@-1 {{integer value -8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
101+
constexpr E2 x4 = static_cast<E2>(0);
102+
constexpr E2 x5 = static_cast<E2>(8);
103+
// both-error@-1 {{integer value 8 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
104+
105+
constexpr E3 x6 = static_cast<E3>(-2048);
106+
constexpr E3 x7 = static_cast<E3>(-8);
107+
constexpr E3 x8 = static_cast<E3>(0);
108+
constexpr E3 x9 = static_cast<E3>(8);
109+
constexpr E3 x10 = static_cast<E3>(2048);
110+
// both-error@-1 {{integer value 2048 is outside the valid range of values [-2048, 2047] for the enumeration type 'E3'}}
111+
112+
constexpr E4 x11 = static_cast<E4>(0);
113+
constexpr E4 x12 = static_cast<E4>(1);
114+
constexpr E4 x13 = static_cast<E4>(2);
115+
// both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'E4'}}
116+
117+
constexpr EEmpty x14 = static_cast<EEmpty>(0);
118+
constexpr EEmpty x15 = static_cast<EEmpty>(1);
119+
constexpr EEmpty x16 = static_cast<EEmpty>(2);
120+
// both-error@-1 {{integer value 2 is outside the valid range of values [0, 1] for the enumeration type 'EEmpty'}}
121+
122+
constexpr EFixed x17 = static_cast<EFixed>(100);
123+
constexpr EScoped x18 = static_cast<EScoped>(100);
124+
125+
constexpr EMaxInt x19 = static_cast<EMaxInt>(__INT_MAX__-1);
126+
constexpr EMaxInt x20 = static_cast<EMaxInt>((long)__INT_MAX__+1);
127+
// both-error@-1 {{integer value 2147483648 is outside the valid range of values [-2147483648, 2147483647] for the enumeration type 'EMaxInt'}}
128+
129+
const NumberType neg_one = (NumberType) ((NumberType) 0 - (NumberType) 1); // ok, not a constant expression context
130+
}
131+
132+
template<class T, unsigned size> struct Bitfield {
133+
static constexpr T max = static_cast<T>((1 << size) - 1); // #enum
134+
};
135+
136+
void testValueInRangeOfEnumerationValuesViaTemplate() {
137+
Bitfield<E2, 3> good;
138+
Bitfield<E2, 4> bad; // both-error@#enum {{integer value 15 is outside the valid range of values [0, 7] for the enumeration type 'E2'}}
139+
}
140+
141+
enum SortOrder {
142+
AscendingOrder,
143+
DescendingOrder
144+
};
145+
146+
class A {
147+
static void f(SortOrder order);
148+
};
149+
150+
void A::f(SortOrder order) {
151+
if (order == SortOrder(-1)) // ok, not a constant expression context
152+
return;
153+
}
154+
}

0 commit comments

Comments
 (0)