Skip to content

Commit d835dd4

Browse files
committed
[analyzer] Produce SymbolCast symbols for integral types in SValBuilder::evalCast
Summary: Produce SymbolCast for integral types in `evalCast` function. Apply several simplification techniques while producing the symbols. Added a boolean option `handle-integral-cast-for-ranges` under `-analyzer-config` flag. Disabled the feature by default. Differential Revision: https://reviews.llvm.org/D105340
1 parent 7f4d66f commit d835dd4

File tree

7 files changed

+3386
-22
lines changed

7 files changed

+3386
-22
lines changed

clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ ANALYZER_OPTION(bool, ShouldDisplayCheckerNameForText, "display-checker-name",
320320
"Display the checker name for textual outputs",
321321
true)
322322

323+
ANALYZER_OPTION(bool, ShouldSupportSymbolicIntegerCasts,
324+
"support-symbolic-integer-casts",
325+
"Produce cast symbols for integral types.",
326+
false)
327+
323328
ANALYZER_OPTION(
324329
bool, ShouldConsiderSingleElementArraysAsFlexibleArrayMembers,
325330
"consider-single-element-arrays-as-flexible-array-members",

clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,25 +96,24 @@ class SValBuilder {
9696
QualType OriginalTy);
9797
SVal evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
9898
QualType OriginalTy);
99+
/// Reduce cast expression by removing redundant intermediate casts.
100+
/// E.g.
101+
/// - (char)(short)(int x) -> (char)(int x)
102+
/// - (int)(int x) -> int x
103+
///
104+
/// \param V -- SymbolVal, which pressumably contains SymbolCast or any symbol
105+
/// that is applicable for cast operation.
106+
/// \param CastTy -- QualType, which `V` shall be cast to.
107+
/// \return SVal with simplified cast expression.
108+
/// \note: Currently only support integral casts.
109+
SVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy);
99110

100111
public:
101112
SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
102113
ProgramStateManager &stateMgr);
103114

104115
virtual ~SValBuilder() = default;
105116

106-
bool haveSameType(const SymExpr *Sym1, const SymExpr *Sym2) {
107-
return haveSameType(Sym1->getType(), Sym2->getType());
108-
}
109-
110-
bool haveSameType(QualType Ty1, QualType Ty2) {
111-
// FIXME: Remove the second disjunct when we support symbolic
112-
// truncation/extension.
113-
return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) ||
114-
(Ty1->isIntegralOrEnumerationType() &&
115-
Ty2->isIntegralOrEnumerationType()));
116-
}
117-
118117
SVal evalCast(SVal V, QualType CastTy, QualType OriginalTy);
119118

120119
// Handles casts of type CK_IntegralCast.

clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
416416
case CK_IntegralCast: {
417417
// Delegate to SValBuilder to process.
418418
SVal V = state->getSVal(Ex, LCtx);
419-
V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
419+
if (AMgr.options.ShouldSupportSymbolicIntegerCasts)
420+
V = svalBuilder.evalCast(V, T, ExTy);
421+
else
422+
V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
420423
state = state->BindExpr(CastE, LCtx, V);
421424
Bldr.generateNode(CastE, Pred, state);
422425
continue;

clang/lib/StaticAnalyzer/Core/SValBuilder.cpp

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -980,15 +980,19 @@ SVal SValBuilder::evalCastSubKind(nonloc::SymbolVal V, QualType CastTy,
980980
} else {
981981
// Symbol to integer, float.
982982
QualType T = Context.getCanonicalType(SE->getType());
983-
// If types are the same or both are integers, ignore the cast.
984-
// FIXME: Remove this hack when we support symbolic truncation/extension.
985-
// HACK: If both castTy and T are integers, ignore the cast. This is
986-
// not a permanent solution. Eventually we want to precisely handle
987-
// extension/truncation of symbolic integers. This prevents us from losing
988-
// precision when we assign 'x = y' and 'y' is symbolic and x and y are
989-
// different integer types.
990-
if (haveSameType(T, CastTy))
991-
return V;
983+
984+
// Produce SymbolCast if CastTy and T are different integers.
985+
// NOTE: In the end the type of SymbolCast shall be equal to CastTy.
986+
if (T->isIntegralOrEnumerationType() &&
987+
CastTy->isIntegralOrEnumerationType()) {
988+
AnalyzerOptions &Opts =
989+
StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
990+
// If appropriate option is disabled, ignore the cast.
991+
// NOTE: ShouldSupportSymbolicIntegerCasts is `false` by default.
992+
if (!Opts.ShouldSupportSymbolicIntegerCasts)
993+
return V;
994+
return simplifySymbolCast(V, CastTy);
995+
}
992996
if (!Loc::isLocType(CastTy))
993997
if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
994998
T->isFloatingType())
@@ -1004,3 +1008,75 @@ SVal SValBuilder::evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
10041008
// Member pointer to whatever.
10051009
return V;
10061010
}
1011+
1012+
SVal clang::ento::SValBuilder::simplifySymbolCast(nonloc::SymbolVal V,
1013+
QualType CastTy) {
1014+
// We use seven conditions to recognize a simplification case.
1015+
// For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type - `R`,
1016+
// prefix `u` for unsigned, `s` for signed, no prefix - any sign:
1017+
// E.g. (char)(short)(uint x)
1018+
// ( sC )( sT )( uR x)
1019+
//
1020+
// C === R (the same type)
1021+
// (char)(char x) -> (char x)
1022+
// (long)(long x) -> (long x)
1023+
// Note: Comparisons operators below are for bit width.
1024+
// C == T
1025+
// (short)(short)(int x) -> (short)(int x)
1026+
// (int)(long)(char x) -> (int)(char x) (sizeof(long) == sizeof(int))
1027+
// (long)(ullong)(char x) -> (long)(char x) (sizeof(long) == sizeof(ullong))
1028+
// C < T
1029+
// (short)(int)(char x) -> (short)(char x)
1030+
// (char)(int)(short x) -> (char)(short x)
1031+
// (short)(int)(short x) -> (short x)
1032+
// C > T > uR
1033+
// (int)(short)(uchar x) -> (int)(uchar x)
1034+
// (uint)(short)(uchar x) -> (uint)(uchar x)
1035+
// (int)(ushort)(uchar x) -> (int)(uchar x)
1036+
// C > sT > sR
1037+
// (int)(short)(char x) -> (int)(char x)
1038+
// (uint)(short)(char x) -> (uint)(char x)
1039+
// C > sT == sR
1040+
// (int)(char)(char x) -> (int)(char x)
1041+
// (uint)(short)(short x) -> (uint)(short x)
1042+
// C > uT == uR
1043+
// (int)(uchar)(uchar x) -> (int)(uchar x)
1044+
// (uint)(ushort)(ushort x) -> (uint)(ushort x)
1045+
// (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) == sizeof(uint))
1046+
1047+
SymbolRef SE = V.getSymbol();
1048+
QualType T = Context.getCanonicalType(SE->getType());
1049+
1050+
if (T == CastTy)
1051+
return V;
1052+
1053+
if (!isa<SymbolCast>(SE))
1054+
return makeNonLoc(SE, T, CastTy);
1055+
1056+
SymbolRef RootSym = cast<SymbolCast>(SE)->getOperand();
1057+
QualType RT = RootSym->getType().getCanonicalType();
1058+
1059+
BasicValueFactory &BVF = getBasicValueFactory();
1060+
APSIntType CTy = BVF.getAPSIntType(CastTy);
1061+
APSIntType TTy = BVF.getAPSIntType(T);
1062+
1063+
const auto WC = CTy.getBitWidth();
1064+
const auto WT = TTy.getBitWidth();
1065+
1066+
if (WC <= WT) {
1067+
const bool isSameType = (RT == CastTy);
1068+
if (isSameType)
1069+
return nonloc::SymbolVal(RootSym);
1070+
return makeNonLoc(RootSym, RT, CastTy);
1071+
}
1072+
1073+
APSIntType RTy = BVF.getAPSIntType(RT);
1074+
const auto WR = RTy.getBitWidth();
1075+
const bool UT = TTy.isUnsigned();
1076+
const bool UR = RTy.isUnsigned();
1077+
1078+
if (((WT > WR) && (UR || !UT)) || ((WT == WR) && (UT == UR)))
1079+
return makeNonLoc(RootSym, RT, CastTy);
1080+
1081+
return makeNonLoc(SE, T, CastTy);
1082+
}

clang/test/Analysis/analyzer-config.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
// CHECK-NEXT: serialize-stats = false
116116
// CHECK-NEXT: silence-checkers = ""
117117
// CHECK-NEXT: stable-report-filename = false
118+
// CHECK-NEXT: support-symbolic-integer-casts = false
118119
// CHECK-NEXT: suppress-c++-stdlib = true
119120
// CHECK-NEXT: suppress-inlined-defensive-checks = true
120121
// CHECK-NEXT: suppress-null-return-paths = true

0 commit comments

Comments
 (0)