blob: 2d0f169260a457b9950e148fe68098d918d2c57a [file] [log] [blame]
//== SMTConv.h --------------------------------------------------*- C++ -*--==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a set of functions to create SMT expressions
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
#include "clang/AST/Expr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/Support/SMTAPI.h"
namespace clang {
namespace ento {
class SMTConv {
public:
// Returns an appropriate sort, given a QualType and it's bit width.
static inline llvm::SMTSortRef mkSort(llvm::SMTSolverRef &Solver,
const QualType &Ty, unsigned BitWidth) {
if (Ty->isBooleanType())
return Solver->getBoolSort();
if (Ty->isRealFloatingType())
return Solver->getFloatSort(BitWidth);
return Solver->getBitvectorSort(BitWidth);
}
/// Constructs an SMTSolverRef from an unary operator.
static inline llvm::SMTExprRef fromUnOp(llvm::SMTSolverRef &Solver,
const UnaryOperator::Opcode Op,
const llvm::SMTExprRef &Exp) {
switch (Op) {
case UO_Minus:
return Solver->mkBVNeg(Exp);
case UO_Not:
return Solver->mkBVNot(Exp);
case UO_LNot:
return Solver->mkNot(Exp);
default:;
}
llvm_unreachable("Unimplemented opcode");
}
/// Constructs an SMTSolverRef from a floating-point unary operator.
static inline llvm::SMTExprRef fromFloatUnOp(llvm::SMTSolverRef &Solver,
const UnaryOperator::Opcode Op,
const llvm::SMTExprRef &Exp) {
switch (Op) {
case UO_Minus:
return Solver->mkFPNeg(Exp);
case UO_LNot:
return fromUnOp(Solver, Op, Exp);
default:;
}
llvm_unreachable("Unimplemented opcode");
}
/// Construct an SMTSolverRef from a n-ary binary operator.
static inline llvm::SMTExprRef
fromNBinOp(llvm::SMTSolverRef &Solver, const BinaryOperator::Opcode Op,
const std::vector<llvm::SMTExprRef> &ASTs) {
assert(!ASTs.empty());
if (Op != BO_LAnd && Op != BO_LOr)
llvm_unreachable("Unimplemented opcode");
llvm::SMTExprRef res = ASTs.front();
for (std::size_t i = 1; i < ASTs.size(); ++i)
res = (Op == BO_LAnd) ? Solver->mkAnd(res, ASTs[i])
: Solver->mkOr(res, ASTs[i]);
return res;
}
/// Construct an SMTSolverRef from a binary operator.
static inline llvm::SMTExprRef fromBinOp(llvm::SMTSolverRef &Solver,
const llvm::SMTExprRef &LHS,
const BinaryOperator::Opcode Op,
const llvm::SMTExprRef &RHS,
bool isSigned) {
assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) &&
"AST's must have the same sort!");
switch (Op) {
// Multiplicative operators
case BO_Mul:
return Solver->mkBVMul(LHS, RHS);
case BO_Div:
return isSigned ? Solver->mkBVSDiv(LHS, RHS) : Solver->mkBVUDiv(LHS, RHS);
case BO_Rem:
return isSigned ? Solver->mkBVSRem(LHS, RHS) : Solver->mkBVURem(LHS, RHS);
// Additive operators
case BO_Add:
return Solver->mkBVAdd(LHS, RHS);
case BO_Sub:
return Solver->mkBVSub(LHS, RHS);
// Bitwise shift operators
case BO_Shl:
return Solver->mkBVShl(LHS, RHS);
case BO_Shr:
return isSigned ? Solver->mkBVAshr(LHS, RHS) : Solver->mkBVLshr(LHS, RHS);
// Relational operators
case BO_LT:
return isSigned ? Solver->mkBVSlt(LHS, RHS) : Solver->mkBVUlt(LHS, RHS);
case BO_GT:
return isSigned ? Solver->mkBVSgt(LHS, RHS) : Solver->mkBVUgt(LHS, RHS);
case BO_LE:
return isSigned ? Solver->mkBVSle(LHS, RHS) : Solver->mkBVUle(LHS, RHS);
case BO_GE:
return isSigned ? Solver->mkBVSge(LHS, RHS) : Solver->mkBVUge(LHS, RHS);
// Equality operators
case BO_EQ:
return Solver->mkEqual(LHS, RHS);
case BO_NE:
return fromUnOp(Solver, UO_LNot,
fromBinOp(Solver, LHS, BO_EQ, RHS, isSigned));
// Bitwise operators
case BO_And:
return Solver->mkBVAnd(LHS, RHS);
case BO_Xor:
return Solver->mkBVXor(LHS, RHS);
case BO_Or:
return Solver->mkBVOr(LHS, RHS);
// Logical operators
case BO_LAnd:
return Solver->mkAnd(LHS, RHS);
case BO_LOr:
return Solver->mkOr(LHS, RHS);
default:;
}
llvm_unreachable("Unimplemented opcode");
}
/// Construct an SMTSolverRef from a special floating-point binary
/// operator.
static inline llvm::SMTExprRef
fromFloatSpecialBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS,
const BinaryOperator::Opcode Op,
const llvm::APFloat::fltCategory &RHS) {
switch (Op) {
// Equality operators
case BO_EQ:
switch (RHS) {
case llvm::APFloat::fcInfinity:
return Solver->mkFPIsInfinite(LHS);
case llvm::APFloat::fcNaN:
return Solver->mkFPIsNaN(LHS);
case llvm::APFloat::fcNormal:
return Solver->mkFPIsNormal(LHS);
case llvm::APFloat::fcZero:
return Solver->mkFPIsZero(LHS);
}
break;
case BO_NE:
return fromFloatUnOp(Solver, UO_LNot,
fromFloatSpecialBinOp(Solver, LHS, BO_EQ, RHS));
default:;
}
llvm_unreachable("Unimplemented opcode");
}
/// Construct an SMTSolverRef from a floating-point binary operator.
static inline llvm::SMTExprRef fromFloatBinOp(llvm::SMTSolverRef &Solver,
const llvm::SMTExprRef &LHS,
const BinaryOperator::Opcode Op,
const llvm::SMTExprRef &RHS) {
assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) &&
"AST's must have the same sort!");
switch (Op) {
// Multiplicative operators
case BO_Mul:
return Solver->mkFPMul(LHS, RHS);
case BO_Div:
return Solver->mkFPDiv(LHS, RHS);
case BO_Rem:
return Solver->mkFPRem(LHS, RHS);
// Additive operators
case BO_Add:
return Solver->mkFPAdd(LHS, RHS);
case BO_Sub:
return Solver->mkFPSub(LHS, RHS);
// Relational operators
case BO_LT:
return Solver->mkFPLt(LHS, RHS);
case BO_GT:
return Solver->mkFPGt(LHS, RHS);
case BO_LE:
return Solver->mkFPLe(LHS, RHS);
case BO_GE:
return Solver->mkFPGe(LHS, RHS);
// Equality operators
case BO_EQ:
return Solver->mkFPEqual(LHS, RHS);
case BO_NE:
return fromFloatUnOp(Solver, UO_LNot,
fromFloatBinOp(Solver, LHS, BO_EQ, RHS));
// Logical operators
case BO_LAnd:
case BO_LOr:
return fromBinOp(Solver, LHS, Op, RHS, /*isSigned=*/false);
default:;
}
llvm_unreachable("Unimplemented opcode");
}
/// Construct an SMTSolverRef from a QualType FromTy to a QualType ToTy,
/// and their bit widths.
static inline llvm::SMTExprRef fromCast(llvm::SMTSolverRef &Solver,
const llvm::SMTExprRef &Exp,
QualType ToTy, uint64_t ToBitWidth,
QualType FromTy,
uint64_t FromBitWidth) {
if ((FromTy->isIntegralOrEnumerationType() &&
ToTy->isIntegralOrEnumerationType()) ||
(FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) ||
(FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) ||
(FromTy->isReferenceType() ^ ToTy->isReferenceType())) {
if (FromTy->isBooleanType()) {
assert(ToBitWidth > 0 && "BitWidth must be positive!");
return Solver->mkIte(
Exp, Solver->mkBitvector(llvm::APSInt("1"), ToBitWidth),
Solver->mkBitvector(llvm::APSInt("0"), ToBitWidth));
}
if (ToBitWidth > FromBitWidth)
return FromTy->isSignedIntegerOrEnumerationType()
? Solver->mkBVSignExt(ToBitWidth - FromBitWidth, Exp)
: Solver->mkBVZeroExt(ToBitWidth - FromBitWidth, Exp);
if (ToBitWidth < FromBitWidth)
return Solver->mkBVExtract(ToBitWidth - 1, 0, Exp);
// Both are bitvectors with the same width, ignore the type cast
return Exp;
}
if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) {
if (ToBitWidth != FromBitWidth)
return Solver->mkFPtoFP(Exp, Solver->getFloatSort(ToBitWidth));
return Exp;
}
if (FromTy->isIntegralOrEnumerationType() && ToTy->isRealFloatingType()) {
llvm::SMTSortRef Sort = Solver->getFloatSort(ToBitWidth);
return FromTy->isSignedIntegerOrEnumerationType()
? Solver->mkSBVtoFP(Exp, Sort)
: Solver->mkUBVtoFP(Exp, Sort);
}
if (FromTy->isRealFloatingType() && ToTy->isIntegralOrEnumerationType())
return ToTy->isSignedIntegerOrEnumerationType()
? Solver->mkFPtoSBV(Exp, ToBitWidth)
: Solver->mkFPtoUBV(Exp, ToBitWidth);
llvm_unreachable("Unsupported explicit type cast!");
}
// Callback function for doCast parameter on APSInt type.
static inline llvm::APSInt castAPSInt(llvm::SMTSolverRef &Solver,
const llvm::APSInt &V, QualType ToTy,
uint64_t ToWidth, QualType FromTy,
uint64_t FromWidth) {
APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType());
return TargetType.convert(V);
}
/// Construct an SMTSolverRef from a SymbolData.
static inline llvm::SMTExprRef
fromData(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const SymbolData *Sym) {
const SymbolID ID = Sym->getSymbolID();
const QualType Ty = Sym->getType();
const uint64_t BitWidth = Ctx.getTypeSize(Ty);
llvm::SmallString<16> Str;
llvm::raw_svector_ostream OS(Str);
OS << Sym->getKindStr() << ID;
return Solver->mkSymbol(Str.c_str(), mkSort(Solver, Ty, BitWidth));
}
// Wrapper to generate SMTSolverRef from SymbolCast data.
static inline llvm::SMTExprRef getCastExpr(llvm::SMTSolverRef &Solver,
ASTContext &Ctx,
const llvm::SMTExprRef &Exp,
QualType FromTy, QualType ToTy) {
return fromCast(Solver, Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy,
Ctx.getTypeSize(FromTy));
}
// Wrapper to generate SMTSolverRef from unpacked binary symbolic
// expression. Sets the RetTy parameter. See getSMTSolverRef().
static inline llvm::SMTExprRef
getBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx,
const llvm::SMTExprRef &LHS, QualType LTy,
BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS,
QualType RTy, QualType *RetTy) {
llvm::SMTExprRef NewLHS = LHS;
llvm::SMTExprRef NewRHS = RHS;
doTypeConversion(Solver, Ctx, NewLHS, NewRHS, LTy, RTy);
// Update the return type parameter if the output type has changed.
if (RetTy) {
// A boolean result can be represented as an integer type in C/C++, but at
// this point we only care about the SMT sorts. Set it as a boolean type
// to avoid subsequent SMT errors.
if (BinaryOperator::isComparisonOp(Op) ||
BinaryOperator::isLogicalOp(Op)) {
*RetTy = Ctx.BoolTy;
} else {
*RetTy = LTy;
}
// If the two operands are pointers and the operation is a subtraction,
// the result is of type ptrdiff_t, which is signed
if (LTy->isAnyPointerType() && RTy->isAnyPointerType() && Op == BO_Sub) {
*RetTy = Ctx.getPointerDiffType();
}
}
return LTy->isRealFloatingType()
? fromFloatBinOp(Solver, NewLHS, Op, NewRHS)
: fromBinOp(Solver, NewLHS, Op, NewRHS,
LTy->isSignedIntegerOrEnumerationType());
}
// Wrapper to generate SMTSolverRef from BinarySymExpr.
// Sets the hasComparison and RetTy parameters. See getSMTSolverRef().
static inline llvm::SMTExprRef getSymBinExpr(llvm::SMTSolverRef &Solver,
ASTContext &Ctx,
const BinarySymExpr *BSE,
bool *hasComparison,
QualType *RetTy) {
QualType LTy, RTy;
BinaryOperator::Opcode Op = BSE->getOpcode();
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
llvm::SMTExprRef LHS =
getSymExpr(Solver, Ctx, SIE->getLHS(), &LTy, hasComparison);
llvm::APSInt NewRInt;
std::tie(NewRInt, RTy) = fixAPSInt(Ctx, SIE->getRHS());
llvm::SMTExprRef RHS =
Solver->mkBitvector(NewRInt, NewRInt.getBitWidth());
return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy);
}
if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
llvm::APSInt NewLInt;
std::tie(NewLInt, LTy) = fixAPSInt(Ctx, ISE->getLHS());
llvm::SMTExprRef LHS =
Solver->mkBitvector(NewLInt, NewLInt.getBitWidth());
llvm::SMTExprRef RHS =
getSymExpr(Solver, Ctx, ISE->getRHS(), &RTy, hasComparison);
return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy);
}
if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
llvm::SMTExprRef LHS =
getSymExpr(Solver, Ctx, SSM->getLHS(), &LTy, hasComparison);
llvm::SMTExprRef RHS =
getSymExpr(Solver, Ctx, SSM->getRHS(), &RTy, hasComparison);
return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy);
}
llvm_unreachable("Unsupported BinarySymExpr type!");
}
// Recursive implementation to unpack and generate symbolic expression.
// Sets the hasComparison and RetTy parameters. See getExpr().
static inline llvm::SMTExprRef getSymExpr(llvm::SMTSolverRef &Solver,
ASTContext &Ctx, SymbolRef Sym,
QualType *RetTy,
bool *hasComparison) {
if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
if (RetTy)
*RetTy = Sym->getType();
return fromData(Solver, Ctx, SD);
}
if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
if (RetTy)
*RetTy = Sym->getType();
QualType FromTy;
llvm::SMTExprRef Exp =
getSymExpr(Solver, Ctx, SC->getOperand(), &FromTy, hasComparison);
// Casting an expression with a comparison invalidates it. Note that this
// must occur after the recursive call above.
// e.g. (signed char) (x > 0)
if (hasComparison)
*hasComparison = false;
return getCastExpr(Solver, Ctx, Exp, FromTy, Sym->getType());
}
if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
llvm::SMTExprRef Exp =
getSymBinExpr(Solver, Ctx, BSE, hasComparison, RetTy);
// Set the hasComparison parameter, in post-order traversal order.
if (hasComparison)
*hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode());
return Exp;
}
llvm_unreachable("Unsupported SymbolRef type!");
}
// Generate an SMTSolverRef that represents the given symbolic expression.
// Sets the hasComparison parameter if the expression has a comparison
// operator. Sets the RetTy parameter to the final return type after
// promotions and casts.
static inline llvm::SMTExprRef getExpr(llvm::SMTSolverRef &Solver,
ASTContext &Ctx, SymbolRef Sym,
QualType *RetTy = nullptr,
bool *hasComparison = nullptr) {
if (hasComparison) {
*hasComparison = false;
}
return getSymExpr(Solver, Ctx, Sym, RetTy, hasComparison);
}
// Generate an SMTSolverRef that compares the expression to zero.
static inline llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver,
ASTContext &Ctx,
const llvm::SMTExprRef &Exp,
QualType Ty, bool Assumption) {
if (Ty->isRealFloatingType()) {
llvm::APFloat Zero =
llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
return fromFloatBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE,
Solver->mkFloat(Zero));
}
if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() ||
Ty->isBlockPointerType() || Ty->isReferenceType()) {
// Skip explicit comparison for boolean types
bool isSigned = Ty->isSignedIntegerOrEnumerationType();
if (Ty->isBooleanType())
return Assumption ? fromUnOp(Solver, UO_LNot, Exp) : Exp;
return fromBinOp(
Solver, Exp, Assumption ? BO_EQ : BO_NE,
Solver->mkBitvector(llvm::APSInt("0"), Ctx.getTypeSize(Ty)),
isSigned);
}
llvm_unreachable("Unsupported type for zero value!");
}
// Wrapper to generate SMTSolverRef from a range. If From == To, an
// equality will be created instead.
static inline llvm::SMTExprRef
getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) {
// Convert lower bound
QualType FromTy;
llvm::APSInt NewFromInt;
std::tie(NewFromInt, FromTy) = fixAPSInt(Ctx, From);
llvm::SMTExprRef FromExp =
Solver->mkBitvector(NewFromInt, NewFromInt.getBitWidth());
// Convert symbol
QualType SymTy;
llvm::SMTExprRef Exp = getExpr(Solver, Ctx, Sym, &SymTy);
// Construct single (in)equality
if (From == To)
return getBinExpr(Solver, Ctx, Exp, SymTy, InRange ? BO_EQ : BO_NE,
FromExp, FromTy, /*RetTy=*/nullptr);
QualType ToTy;
llvm::APSInt NewToInt;
std::tie(NewToInt, ToTy) = fixAPSInt(Ctx, To);
llvm::SMTExprRef ToExp =
Solver->mkBitvector(NewToInt, NewToInt.getBitWidth());
assert(FromTy == ToTy && "Range values have different types!");
// Construct two (in)equalities, and a logical and/or
llvm::SMTExprRef LHS =
getBinExpr(Solver, Ctx, Exp, SymTy, InRange ? BO_GE : BO_LT, FromExp,
FromTy, /*RetTy=*/nullptr);
llvm::SMTExprRef RHS = getBinExpr(Solver, Ctx, Exp, SymTy,
InRange ? BO_LE : BO_GT, ToExp, ToTy,
/*RetTy=*/nullptr);
return fromBinOp(Solver, LHS, InRange ? BO_LAnd : BO_LOr, RHS,
SymTy->isSignedIntegerOrEnumerationType());
}
// Recover the QualType of an APSInt.
// TODO: Refactor to put elsewhere
static inline QualType getAPSIntType(ASTContext &Ctx,
const llvm::APSInt &Int) {
return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
}
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
static inline std::pair<llvm::APSInt, QualType>
fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) {
llvm::APSInt NewInt;
// FIXME: This should be a cast from a 1-bit integer type to a boolean type,
// but the former is not available in Clang. Instead, extend the APSInt
// directly.
if (Int.getBitWidth() == 1 && getAPSIntType(Ctx, Int).isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy));
} else
NewInt = Int;
return std::make_pair(NewInt, getAPSIntType(Ctx, NewInt));
}
// Perform implicit type conversion on binary symbolic expressions.
// May modify all input parameters.
// TODO: Refactor to use built-in conversion functions
static inline void doTypeConversion(llvm::SMTSolverRef &Solver,
ASTContext &Ctx, llvm::SMTExprRef &LHS,
llvm::SMTExprRef &RHS, QualType &LTy,
QualType &RTy) {
assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
// Perform type conversion
if ((LTy->isIntegralOrEnumerationType() &&
RTy->isIntegralOrEnumerationType()) &&
(LTy->isArithmeticType() && RTy->isArithmeticType())) {
SMTConv::doIntTypeConversion<llvm::SMTExprRef, &fromCast>(
Solver, Ctx, LHS, LTy, RHS, RTy);
return;
}
if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) {
SMTConv::doFloatTypeConversion<llvm::SMTExprRef, &fromCast>(
Solver, Ctx, LHS, LTy, RHS, RTy);
return;
}
if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) ||
(LTy->isBlockPointerType() || RTy->isBlockPointerType()) ||
(LTy->isReferenceType() || RTy->isReferenceType())) {
// TODO: Refactor to Sema::FindCompositePointerType(), and
// Sema::CheckCompareOperands().
uint64_t LBitWidth = Ctx.getTypeSize(LTy);
uint64_t RBitWidth = Ctx.getTypeSize(RTy);
// Cast the non-pointer type to the pointer type.
// TODO: Be more strict about this.
if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) ||
(LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) ||
(LTy->isReferenceType() ^ RTy->isReferenceType())) {
if (LTy->isNullPtrType() || LTy->isBlockPointerType() ||
LTy->isReferenceType()) {
LHS = fromCast(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
} else {
RHS = fromCast(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
}
}
// Cast the void pointer type to the non-void pointer type.
// For void types, this assumes that the casted value is equal to the
// value of the original pointer, and does not account for alignment
// requirements.
if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) {
assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) &&
"Pointer types have different bitwidths!");
if (RTy->isVoidPointerType())
RTy = LTy;
else
LTy = RTy;
}
if (LTy == RTy)
return;
}
// Fallback: for the solver, assume that these types don't really matter
if ((LTy.getCanonicalType() == RTy.getCanonicalType()) ||
(LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) {
LTy = RTy;
return;
}
// TODO: Refine behavior for invalid type casts
}
// Perform implicit integer type conversion.
// May modify all input parameters.
// TODO: Refactor to use Sema::handleIntegerConversion()
template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
QualType, uint64_t, QualType, uint64_t)>
static inline void doIntTypeConversion(llvm::SMTSolverRef &Solver,
ASTContext &Ctx, T &LHS, QualType &LTy,
T &RHS, QualType &RTy) {
uint64_t LBitWidth = Ctx.getTypeSize(LTy);
uint64_t RBitWidth = Ctx.getTypeSize(RTy);
assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
// Always perform integer promotion before checking type equality.
// Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion
if (LTy->isPromotableIntegerType()) {
QualType NewTy = Ctx.getPromotedIntegerType(LTy);
uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
LHS = (*doCast)(Solver, LHS, NewTy, NewBitWidth, LTy, LBitWidth);
LTy = NewTy;
LBitWidth = NewBitWidth;
}
if (RTy->isPromotableIntegerType()) {
QualType NewTy = Ctx.getPromotedIntegerType(RTy);
uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
RHS = (*doCast)(Solver, RHS, NewTy, NewBitWidth, RTy, RBitWidth);
RTy = NewTy;
RBitWidth = NewBitWidth;
}
if (LTy == RTy)
return;
// Perform integer type conversion
// Note: Safe to skip updating bitwidth because this must terminate
bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType();
bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType();
int order = Ctx.getIntegerTypeOrder(LTy, RTy);
if (isLSignedTy == isRSignedTy) {
// Same signedness; use the higher-ranked type
if (order == 1) {
RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
} else {
LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
}
} else if (order != (isLSignedTy ? 1 : -1)) {
// The unsigned type has greater than or equal rank to the
// signed type, so use the unsigned type
if (isRSignedTy) {
RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
} else {
LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
}
} else if (LBitWidth != RBitWidth) {
// The two types are different widths; if we are here, that
// means the signed type is larger than the unsigned type, so
// use the signed type.
if (isLSignedTy) {
RHS = (doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
} else {
LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
}
} else {
// The signed type is higher-ranked than the unsigned type,
// but isn't actually any bigger (like unsigned int and long
// on most 32-bit systems). Use the unsigned type corresponding
// to the signed type.
QualType NewTy =
Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy);
RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = NewTy;
LHS = (doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = NewTy;
}
}
// Perform implicit floating-point type conversion.
// May modify all input parameters.
// TODO: Refactor to use Sema::handleFloatConversion()
template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
QualType, uint64_t, QualType, uint64_t)>
static inline void
doFloatTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS,
QualType &LTy, T &RHS, QualType &RTy) {
uint64_t LBitWidth = Ctx.getTypeSize(LTy);
uint64_t RBitWidth = Ctx.getTypeSize(RTy);
// Perform float-point type promotion
if (!LTy->isRealFloatingType()) {
LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
LBitWidth = RBitWidth;
}
if (!RTy->isRealFloatingType()) {
RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
RBitWidth = LBitWidth;
}
if (LTy == RTy)
return;
// If we have two real floating types, convert the smaller operand to the
// bigger result
// Note: Safe to skip updating bitwidth because this must terminate
int order = Ctx.getFloatingTypeOrder(LTy, RTy);
if (order > 0) {
RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
RTy = LTy;
} else if (order == 0) {
LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
LTy = RTy;
} else {
llvm_unreachable("Unsupported floating-point type cast!");
}
}
};
} // namespace ento
} // namespace clang
#endif