Files
codeql/cpp/ql/lib/semmle/code/cpp/valuenumbering/HashCons.qll
2023-05-09 15:35:54 +02:00

1114 lines
38 KiB
Plaintext

/**
* Provides an implementation of Hash consing.
* See https://en.wikipedia.org/wiki/Hash_consing
*
* The predicate `hashCons` converts an expression into a `HashCons`, which is an
* abstract type presenting the hash-cons of the expression. If two
* expressions have the same `HashCons` then they are structurally equal.
*
* Important note: this library ignores the possibility that the value of
* an expression might change between one occurrence and the next. For
* example:
*
* ```
* x = a+b;
* a++;
* y = a+b;
* ```
*
* In this example, both copies of the expression `a+b` will hash-cons to
* the same value, even though the value of `a` has changed. This is the
* intended behavior of this library. If you care about the value of the
* expression being the same, then you should use the GlobalValueNumbering
* library instead.
*
* To determine if the expression `x` is structurally equal to the
* expression `y`, use the library like this:
*
* ```
* hashCons(x) = hashCons(y)
* ```
*/
/*
* Note to developers: the correctness of this module depends on the
* definitions of HC, hashCons, and analyzableExpr being kept in sync with
* each other. If you change this module then make sure that the change is
* symmetric across all three.
*/
import cpp
/** Used to represent the hash-cons of an expression. */
cached
private newtype HCBase =
HC_IntLiteral(int val, Type t) { mk_IntLiteral(val, t, _) } or
HC_EnumConstantAccess(EnumConstant val, Type t) { mk_EnumConstantAccess(val, t, _) } or
HC_FloatLiteral(float val, Type t) { mk_FloatLiteral(val, t, _) } or
HC_StringLiteral(string val, Type t) { mk_StringLiteral(val, t, _) } or
HC_Nullptr() { mk_Nullptr(_) } or
HC_Variable(Variable x) { mk_Variable(x, _) } or
HC_FieldAccess(HashCons s, Field f) { mk_DotFieldAccess(s, f, _) } or
HC_Deref(HashCons p) { mk_Deref(p, _) } or
HC_PointerFieldAccess(HashCons qual, Field target) { mk_PointerFieldAccess(qual, target, _) } or
HC_ThisExpr(Function fcn) { mk_ThisExpr(fcn, _) } or
HC_ImplicitThisFieldAccess(Function fcn, Field target) {
mk_ImplicitThisFieldAccess(fcn, target, _)
} or
HC_Conversion(Type t, HashCons child) { mk_Conversion(t, child, _) } or
HC_BinaryOp(HashCons lhs, HashCons rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
HC_UnaryOp(HashCons child, string opname) { mk_UnaryOp(child, opname, _) } or
HC_ArrayAccess(HashCons x, HashCons i) { mk_ArrayAccess(x, i, _) } or
HC_NonmemberFunctionCall(Function fcn, HC_Args args) { mk_NonmemberFunctionCall(fcn, args, _) } or
HC_ExprCall(HashCons hc, HC_Args args) { mk_ExprCall(hc, args, _) } or
HC_MemberFunctionCall(Function trg, HashCons qual, HC_Args args) {
mk_MemberFunctionCall(trg, qual, args, _)
} or
// Hack to get around argument 0 of allocator calls being an error expression
HC_AllocatorArgZero(Type t) { mk_AllocatorArgZero(t, _) } or
HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init) { mk_NewExpr(t, alloc, init, _) } or
HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init) {
mk_NewArrayExpr(t, alloc, extent, init, _)
} or
HC_SizeofType(Type t) { mk_SizeofType(t, _) } or
HC_SizeofExpr(HashCons child) { mk_SizeofExpr(child, _) } or
HC_AlignofType(Type t) { mk_AlignofType(t, _) } or
HC_AlignofExpr(HashCons child) { mk_AlignofExpr(child, _) } or
HC_UuidofOperator(Type t) { mk_UuidofOperator(t, _) } or
HC_TypeidType(Type t) { mk_TypeidType(t, _) } or
HC_TypeidExpr(HashCons child) { mk_TypeidExpr(child, _) } or
HC_ClassAggregateLiteral(Class c, HC_Fields hcf) { mk_ClassAggregateLiteral(c, hcf, _) } or
HC_ArrayAggregateLiteral(Type t, HC_Array hca) { mk_ArrayAggregateLiteral(t, hca, _) } or
HC_DeleteExpr(HashCons child) { mk_DeleteExpr(child, _) } or
HC_DeleteArrayExpr(HashCons child) { mk_DeleteArrayExpr(child, _) } or
HC_ThrowExpr(HashCons child) { mk_ThrowExpr(child, _) } or
HC_ReThrowExpr() or
HC_ConditionalExpr(HashCons cond, HashCons trueHC, HashCons falseHC) {
mk_ConditionalExpr(cond, trueHC, falseHC, _)
} or
HC_NoExceptExpr(HashCons child) { mk_NoExceptExpr(child, _) } or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
HC_Unanalyzable(Expr e) { not analyzableExpr(e, _) }
/** Used to implement optional init on `new` expressions */
private newtype HC_Init =
HC_NoInit() or
HC_HasInit(HashCons hc) { mk_HasInit(hc, _) }
/**
* Used to implement optional allocator call on `new` expressions
*/
private newtype HC_Alloc =
HC_NoAlloc() or
HC_HasAlloc(HashCons hc) { mk_HasAlloc(hc, _) }
/**
* Used to implement optional extent expression on `new[]` expressions
*/
private newtype HC_Extent =
HC_NoExtent() or
HC_HasExtent(HashCons hc) { mk_HasExtent(hc, _) }
/** Used to implement hash-consing of argument lists */
private newtype HC_Args =
HC_EmptyArgs() { any() } or
HC_ArgCons(HashCons hc, int i, HC_Args list) { mk_ArgCons(hc, i, list, _) }
/**
* Used to implement hash-consing of struct initializers.
*/
private newtype HC_Fields =
HC_EmptyFields(Class c) { exists(ClassAggregateLiteral cal | c = cal.getUnspecifiedType()) } or
HC_FieldCons(Class c, int i, Field f, HashCons hc, HC_Fields hcf) {
mk_FieldCons(c, i, f, hc, hcf, _)
}
private newtype HC_Array =
HC_EmptyArray(Type t) { exists(ArrayAggregateLiteral aal | aal.getUnspecifiedType() = t) } or
HC_ArrayCons(Type t, int i, HashCons hc, HC_Array hca) { mk_ArrayCons(t, i, hc, hca, _) }
/**
* HashCons is the hash-cons of an expression. The relationship between `Expr`
* and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple
* expressions can have the same `HC`. If two expressions have the same
* `HC`, it means that they are structurally equal. The `HC` is an opaque
* value. The only use for the `HC` of an expression is to find other
* expressions that are structurally equal to it. Use the predicate
* `hashCons` to get the `HC` for an `Expr`.
*
* Note: `HC` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `HC` and using its `toString` and `getLocation`
* methods.
*/
class HashCons extends HCBase {
/** Gets an expression that has this HC. */
Expr getAnExpr() { this = hashCons(result) }
/** Gets the kind of the HC. This can be useful for debugging. */
string getKind() {
if this instanceof HC_IntLiteral
then result = "IntLiteral"
else
if this instanceof HC_EnumConstantAccess
then result = "EnumConstantAccess"
else
if this instanceof HC_FloatLiteral
then result = "FloatLiteral"
else
if this instanceof HC_StringLiteral
then result = "StringLiteral"
else
if this instanceof HC_Nullptr
then result = "Nullptr"
else
if this instanceof HC_Variable
then result = "Variable"
else
if this instanceof HC_FieldAccess
then result = "FieldAccess"
else
if this instanceof HC_Deref
then result = "Deref"
else
if this instanceof HC_ThisExpr
then result = "ThisExpr"
else
if this instanceof HC_Conversion
then result = "Conversion"
else
if this instanceof HC_BinaryOp
then result = "BinaryOp"
else
if this instanceof HC_UnaryOp
then result = "UnaryOp"
else
if this instanceof HC_ArrayAccess
then result = "ArrayAccess"
else
if this instanceof HC_Unanalyzable
then result = "Unanalyzable"
else
if this instanceof HC_NonmemberFunctionCall
then result = "NonmemberFunctionCall"
else
if this instanceof HC_MemberFunctionCall
then result = "MemberFunctionCall"
else
if this instanceof HC_NewExpr
then result = "NewExpr"
else
if this instanceof HC_NewArrayExpr
then result = "NewArrayExpr"
else
if this instanceof HC_SizeofType
then result = "SizeofTypeOperator"
else
if this instanceof HC_SizeofExpr
then result = "SizeofExprOperator"
else
if this instanceof HC_AlignofType
then result = "AlignofTypeOperator"
else
if this instanceof HC_AlignofExpr
then result = "AlignofExprOperator"
else
if this instanceof HC_UuidofOperator
then result = "UuidofOperator"
else
if this instanceof HC_TypeidType
then result = "TypeidType"
else
if this instanceof HC_TypeidExpr
then result = "TypeidExpr"
else
if this instanceof HC_ArrayAggregateLiteral
then result = "ArrayAggregateLiteral"
else
if this instanceof HC_ClassAggregateLiteral
then result = "ClassAggregateLiteral"
else
if this instanceof HC_DeleteExpr
then result = "DeleteExpr"
else
if this instanceof HC_DeleteArrayExpr
then result = "DeleteArrayExpr"
else
if this instanceof HC_ThrowExpr
then result = "ThrowExpr"
else
if this instanceof HC_ReThrowExpr
then result = "ReThrowExpr"
else
if this instanceof HC_ExprCall
then result = "ExprCall"
else
if
this instanceof
HC_ConditionalExpr
then result = "ConditionalExpr"
else
if
this instanceof
HC_NoExceptExpr
then result = "NoExceptExpr"
else
if
this instanceof
HC_AllocatorArgZero
then
result =
"AllocatorArgZero"
else result = "error"
}
/**
* Gets an example of an expression with this HC.
* This is useful for things like implementing toString().
*/
private Expr exampleExpr() {
// Pick the expression with the minimum source location string. This is
// just an arbitrary way to pick an expression with this `HC`.
result =
min(Expr e |
this = hashCons(e)
|
e
order by
exampleLocationString(e.getLocation()), e.getLocation().getStartColumn(),
e.getLocation().getEndLine(), e.getLocation().getEndColumn()
)
}
/** Gets a textual representation of this element. */
string toString() { result = this.exampleExpr().toString() }
/** Gets the primary location of this element. */
Location getLocation() { result = this.exampleExpr().getLocation() }
}
/**
* Gets the absolute path of a known location or "~" for an unknown location. This ensures that
* expressions with unknown locations are ordered after expressions with known locations when
* selecting an example expression for a HashCons value.
*/
private string exampleLocationString(Location l) {
if l instanceof UnknownLocation then result = "~" else result = l.getFile().getAbsolutePath()
}
private predicate analyzableIntLiteral(Literal e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
e.getUnspecifiedType() instanceof IntegralType
}
private predicate mk_IntLiteral(int val, Type t, Expr e) {
analyzableIntLiteral(e) and
val = e.getValue().toInt() and
t = e.getUnspecifiedType()
}
private predicate analyzableEnumConstantAccess(EnumConstantAccess e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
e.getUnspecifiedType() instanceof Enum
}
private predicate mk_EnumConstantAccess(EnumConstant val, Type t, Expr e) {
analyzableEnumConstantAccess(e) and
val = e.(EnumConstantAccess).getTarget() and
t = e.getUnspecifiedType()
}
private predicate analyzableFloatLiteral(Literal e) {
strictcount(e.getValue().toFloat()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
e.getUnspecifiedType() instanceof FloatingPointType
}
private predicate mk_FloatLiteral(float val, Type t, Expr e) {
analyzableFloatLiteral(e) and
val = e.getValue().toFloat() and
t = e.getUnspecifiedType()
}
private predicate analyzableNullptr(NullValue e) {
strictcount(e.getUnspecifiedType()) = 1 and
e.getType() instanceof NullPointerType
}
private predicate mk_Nullptr(Expr e) { analyzableNullptr(e) }
private predicate analyzableStringLiteral(Literal e) {
strictcount(e.getValue()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
e.getUnspecifiedType().(ArrayType).getBaseType() instanceof CharType
}
private predicate mk_StringLiteral(string val, Type t, Expr e) {
analyzableStringLiteral(e) and
val = e.getValue() and
t = e.getUnspecifiedType() and
t.(ArrayType).getBaseType() instanceof CharType
}
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1
}
private predicate mk_DotFieldAccess(HashCons qualifier, Field target, DotFieldAccess access) {
analyzableDotFieldAccess(access) and
target = access.getTarget() and
qualifier = hashCons(access.getQualifier().getFullyConverted())
}
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1
}
private predicate mk_PointerFieldAccess(HashCons qualifier, Field target, PointerFieldAccess access) {
analyzablePointerFieldAccess(access) and
target = access.getTarget() and
qualifier = hashCons(access.getQualifier().getFullyConverted())
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getEnclosingFunction()) = 1
}
private predicate mk_ImplicitThisFieldAccess(
Function fcn, Field target, ImplicitThisFieldAccess access
) {
analyzableImplicitThisFieldAccess(access) and
target = access.getTarget() and
fcn = access.getEnclosingFunction()
}
private predicate analyzableVariable(VariableAccess access) {
not access instanceof FieldAccess and
strictcount(access.getTarget()) = 1
}
private predicate mk_Variable(Variable x, VariableAccess access) {
analyzableVariable(access) and
x = access.getTarget()
}
private predicate analyzableConversion(Conversion conv) {
strictcount(conv.getUnspecifiedType()) = 1 and
strictcount(conv.getExpr()) = 1
}
private predicate mk_Conversion(Type t, HashCons child, Conversion conv) {
analyzableConversion(conv) and
t = conv.getUnspecifiedType() and
child = hashCons(conv.getExpr())
}
private predicate analyzableBinaryOp(BinaryOperation op) {
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1
}
private predicate mk_BinaryOp(HashCons lhs, HashCons rhs, string opname, BinaryOperation op) {
analyzableBinaryOp(op) and
lhs = hashCons(op.getLeftOperand().getFullyConverted()) and
rhs = hashCons(op.getRightOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableUnaryOp(UnaryOperation op) {
not op instanceof PointerDereferenceExpr and
strictcount(op.getOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1
}
private predicate mk_UnaryOp(HashCons child, string opname, UnaryOperation op) {
analyzableUnaryOp(op) and
child = hashCons(op.getOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableThisExpr(ThisExpr thisExpr) {
strictcount(thisExpr.getEnclosingFunction()) = 1
}
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
analyzableThisExpr(thisExpr) and
fcn = thisExpr.getEnclosingFunction()
}
private predicate analyzableArrayAccess(ArrayExpr ae) {
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
strictcount(ae.getArrayOffset().getFullyConverted()) = 1
}
private predicate mk_ArrayAccess(HashCons base, HashCons offset, ArrayExpr ae) {
analyzableArrayAccess(ae) and
base = hashCons(ae.getArrayBase().getFullyConverted()) and
offset = hashCons(ae.getArrayOffset().getFullyConverted())
}
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
strictcount(deref.getOperand().getFullyConverted()) = 1
}
private predicate mk_Deref(HashCons p, PointerDereferenceExpr deref) {
analyzablePointerDereferenceExpr(deref) and
p = hashCons(deref.getOperand().getFullyConverted())
}
private predicate analyzableNonmemberFunctionCall(FunctionCall fc) {
forall(int i | i in [0 .. fc.getNumberOfArguments() - 1] |
strictcount(fc.getArgument(i).getFullyConverted()) = 1
) and
strictcount(fc.getTarget()) = 1 and
not exists(fc.getQualifier())
}
private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionCall fc) {
fc.getTarget() = fcn and
analyzableNonmemberFunctionCall(fc) and
(
mk_ArgConsInner(_, _, fc.getNumberOfArguments() - 1, args, fc)
or
fc.getNumberOfArguments() = 0 and
args = HC_EmptyArgs()
)
}
private predicate analyzableExprCall(ExprCall ec) {
forall(int i | i in [0 .. ec.getNumberOfArguments() - 1] |
strictcount(ec.getArgument(i).getFullyConverted()) = 1
) and
strictcount(ec.getExpr().getFullyConverted()) = 1
}
private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) {
hc.getAnExpr() = ec.getExpr() and
(
mk_ArgConsInner(_, _, ec.getNumberOfArguments() - 1, args, ec)
or
ec.getNumberOfArguments() = 0 and
args = HC_EmptyArgs()
)
}
private predicate analyzableMemberFunctionCall(FunctionCall fc) {
forall(int i | i in [0 .. fc.getNumberOfArguments() - 1] |
strictcount(fc.getArgument(i).getFullyConverted()) = 1
) and
strictcount(fc.getTarget()) = 1 and
strictcount(fc.getQualifier().getFullyConverted()) = 1
}
private predicate mk_MemberFunctionCall(Function fcn, HashCons qual, HC_Args args, FunctionCall fc) {
fc.getTarget() = fcn and
analyzableMemberFunctionCall(fc) and
hashCons(fc.getQualifier().getFullyConverted()) = qual and
(
mk_ArgConsInner(_, _, fc.getNumberOfArguments() - 1, args, fc)
or
fc.getNumberOfArguments() = 0 and
args = HC_EmptyArgs()
)
}
private predicate analyzableCall(Call c) {
analyzableNonmemberFunctionCall(c)
or
analyzableMemberFunctionCall(c)
or
analyzableExprCall(c)
}
/**
* Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons
* `list`, and `fc`'s argument at index `i` has hash-cons `hc`.
*/
private predicate mk_ArgCons(HashCons hc, int i, HC_Args list, Call c) {
analyzableCall(c) and
hc = hashCons(c.getArgument(i).getFullyConverted()) and
(
mk_ArgConsInner(_, _, i - 1, list, c) and
i > 0
or
i = 0 and
list = HC_EmptyArgs()
)
}
// avoid a join ordering issue
pragma[noopt]
private predicate mk_ArgConsInner(HashCons head, HC_Args tail, int i, HC_Args list, Call c) {
list = HC_ArgCons(head, i, tail) and
mk_ArgCons(head, i, tail, c)
}
/**
* The 0th argument of an allocator call in a new expression is always an error expression;
* this works around it
*/
private predicate analyzableAllocatorArgZero(ErrorExpr e) {
exists(NewOrNewArrayExpr new |
new.getAllocatorCall().getChild(0) = e and
strictcount(new.getUnspecifiedType()) = 1
) and
strictcount(NewOrNewArrayExpr new | new.getAllocatorCall().getChild(0) = e) = 1
}
private predicate mk_AllocatorArgZero(Type t, ErrorExpr e) {
analyzableAllocatorArgZero(e) and
exists(NewOrNewArrayExpr new |
new.getAllocatorCall().getChild(0) = e and
t = new.getUnspecifiedType()
)
}
private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) {
hc = hashCons(new.(NewExpr).getInitializer().getFullyConverted()) or
hc = hashCons(new.(NewArrayExpr).getInitializer().getFullyConverted())
}
private predicate mk_HasAlloc(HashCons hc, NewOrNewArrayExpr new) {
hc = hashCons(new.(NewExpr).getAllocatorCall().getFullyConverted()) or
hc = hashCons(new.(NewArrayExpr).getAllocatorCall().getFullyConverted())
}
private predicate mk_HasExtent(HashCons hc, NewArrayExpr new) {
hc = hashCons(new.getExtent().getFullyConverted())
}
private predicate analyzableNewExpr(NewExpr new) {
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
count(new.getAllocatorCall().getFullyConverted()) <= 1 and
count(new.getInitializer().getFullyConverted()) <= 1
}
private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, NewExpr new) {
analyzableNewExpr(new) and
t = new.getAllocatedType().getUnspecifiedType() and
(
alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted()))
or
not exists(new.getAllocatorCall().getFullyConverted()) and
alloc = HC_NoAlloc()
) and
(
init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted()))
or
not exists(new.getInitializer().getFullyConverted()) and
init = HC_NoInit()
)
}
private predicate analyzableNewArrayExpr(NewArrayExpr new) {
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
count(new.getAllocatorCall().getFullyConverted()) <= 1 and
count(new.getInitializer().getFullyConverted()) <= 1 and
count(new.getExtent().getFullyConverted()) <= 1
}
private predicate mk_NewArrayExpr(
Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init, NewArrayExpr new
) {
analyzableNewArrayExpr(new) and
t = new.getAllocatedType() and
(
alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted()))
or
not exists(new.getAllocatorCall().getFullyConverted()) and
alloc = HC_NoAlloc()
) and
(
init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted()))
or
not exists(new.getInitializer().getFullyConverted()) and
init = HC_NoInit()
) and
(
extent = HC_HasExtent(hashCons(new.getExtent().getFullyConverted()))
or
not exists(new.getExtent().getFullyConverted()) and
extent = HC_NoExtent()
)
}
private predicate analyzableDeleteExpr(DeleteExpr e) {
strictcount(e.getAChild().getFullyConverted()) = 1
}
private predicate mk_DeleteExpr(HashCons hc, DeleteExpr e) {
analyzableDeleteExpr(e) and
hc = hashCons(e.getAChild().getFullyConverted())
}
private predicate analyzableDeleteArrayExpr(DeleteArrayExpr e) {
strictcount(e.getAChild().getFullyConverted()) = 1
}
private predicate mk_DeleteArrayExpr(HashCons hc, DeleteArrayExpr e) {
analyzableDeleteArrayExpr(e) and
hc = hashCons(e.getAChild().getFullyConverted())
}
private predicate analyzableSizeofType(SizeofTypeOperator e) {
strictcount(e.getUnspecifiedType()) = 1 and
strictcount(e.getTypeOperand()) = 1
}
private predicate mk_SizeofType(Type t, SizeofTypeOperator e) {
analyzableSizeofType(e) and
t = e.getTypeOperand()
}
private predicate analyzableSizeofExpr(Expr e) {
e instanceof SizeofExprOperator and
strictcount(e.getAChild().getFullyConverted()) = 1
}
private predicate mk_SizeofExpr(HashCons child, SizeofExprOperator e) {
analyzableSizeofExpr(e) and
child = hashCons(e.getAChild())
}
private predicate analyzableUuidofOperator(UuidofOperator e) { strictcount(e.getTypeOperand()) = 1 }
private predicate mk_UuidofOperator(Type t, UuidofOperator e) {
analyzableUuidofOperator(e) and
t = e.getTypeOperand()
}
private predicate analyzableTypeidType(TypeidOperator e) {
count(e.getAChild()) = 0 and
strictcount(e.getResultType()) = 1
}
private predicate mk_TypeidType(Type t, TypeidOperator e) {
analyzableTypeidType(e) and
t = e.getResultType()
}
private predicate analyzableTypeidExpr(Expr e) {
e instanceof TypeidOperator and
strictcount(e.getAChild().getFullyConverted()) = 1
}
private predicate mk_TypeidExpr(HashCons child, TypeidOperator e) {
analyzableTypeidExpr(e) and
child = hashCons(e.getAChild())
}
private predicate analyzableAlignofType(AlignofTypeOperator e) {
strictcount(e.getUnspecifiedType()) = 1 and
strictcount(e.getTypeOperand()) = 1
}
private predicate mk_AlignofType(Type t, AlignofTypeOperator e) {
analyzableAlignofType(e) and
t = e.getTypeOperand()
}
private predicate analyzableAlignofExpr(AlignofExprOperator e) {
strictcount(e.getExprOperand()) = 1
}
private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) {
analyzableAlignofExpr(e) and
child = hashCons(e.getAChild())
}
/**
* Gets the hash cons of field initializer expressions [0..i), where i > 0, for
* the class aggregate literal `cal` of type `c`, where `head` is the hash cons
* of the i'th initializer expression.
*/
HC_Fields aggInitExprsUpTo(ClassAggregateLiteral cal, Class c, int i) {
exists(Field f, HashCons head, HC_Fields tail |
result = HC_FieldCons(c, i - 1, f, head, tail) and
mk_FieldCons(c, i - 1, f, head, tail, cal)
)
}
private predicate mk_FieldCons(
Class c, int i, Field f, HashCons hc, HC_Fields hcf, ClassAggregateLiteral cal
) {
analyzableClassAggregateLiteral(cal) and
cal.getUnspecifiedType() = c and
exists(Expr e |
e = cal.getAFieldExpr(f).getFullyConverted() and
f.getInitializationOrder() = i and
(
hc = hashCons(e) and
hcf = aggInitExprsUpTo(cal, c, i)
or
hc = hashCons(e) and
i = 0 and
hcf = HC_EmptyFields(c)
)
)
}
private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) {
forall(int i | exists(cal.getChild(i)) |
strictcount(cal.getChild(i).getFullyConverted()) = 1 and
strictcount(Field f | cal.getChild(i) = cal.getAFieldExpr(f)) = 1 and
strictcount(Field f, int j |
cal.getAFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder()
) = 1
)
}
private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregateLiteral cal) {
analyzableClassAggregateLiteral(cal) and
c = cal.getUnspecifiedType() and
(
hcf = aggInitExprsUpTo(cal, c, cal.getNumChild())
or
cal.getNumChild() = 0 and
hcf = HC_EmptyFields(c)
)
}
private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) {
forall(int i | exists(aal.getChild(i)) | strictcount(aal.getChild(i).getFullyConverted()) = 1) and
strictcount(aal.getUnspecifiedType()) = 1
}
/**
* Gets the hash cons of array elements in [0..i), where i > 0, for
* the array aggregate literal `aal` of type `t`.
*/
private HC_Array arrayElemsUpTo(ArrayAggregateLiteral aal, Type t, int i) {
exists(HC_Array tail, HashCons head |
result = HC_ArrayCons(t, i - 1, head, tail) and
mk_ArrayCons(t, i - 1, head, tail, aal)
)
}
private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) {
analyzableArrayAggregateLiteral(aal) and
t = aal.getUnspecifiedType() and
hc = hashCons(aal.getChild(i)) and
(
hca = arrayElemsUpTo(aal, t, i)
or
i = 0 and
hca = HC_EmptyArray(t)
)
}
private predicate mk_ArrayAggregateLiteral(Type t, HC_Array hca, ArrayAggregateLiteral aal) {
t = aal.getUnspecifiedType() and
(
exists(HashCons head, HC_Array tail, int numElements |
numElements = aal.getNumChild() and
hca = HC_ArrayCons(t, numElements - 1, head, tail) and
mk_ArrayCons(t, numElements - 1, head, tail, aal)
)
or
aal.getNumChild() = 0 and
hca = HC_EmptyArray(t)
)
}
private predicate analyzableThrowExpr(ThrowExpr te) {
strictcount(te.getExpr().getFullyConverted()) = 1
}
private predicate mk_ThrowExpr(HashCons hc, ThrowExpr te) {
analyzableThrowExpr(te) and
hc.getAnExpr() = te.getExpr().getFullyConverted()
}
private predicate analyzableReThrowExpr(ReThrowExpr rte) { any() }
private predicate mk_ReThrowExpr(ReThrowExpr te) { any() }
private predicate analyzableConditionalExpr(ConditionalExpr ce) {
strictcount(ce.getCondition().getFullyConverted()) = 1 and
strictcount(ce.getThen().getFullyConverted()) = 1 and
strictcount(ce.getElse().getFullyConverted()) = 1
}
private predicate mk_ConditionalExpr(
HashCons cond, HashCons trueHc, HashCons falseHc, ConditionalExpr ce
) {
analyzableConditionalExpr(ce) and
cond.getAnExpr() = ce.getCondition() and
trueHc.getAnExpr() = ce.getThen() and
falseHc.getAnExpr() = ce.getElse()
}
private predicate analyzableNoExceptExpr(NoExceptExpr nee) {
strictcount(nee.getAChild().getFullyConverted()) = 1
}
private predicate mk_NoExceptExpr(HashCons child, NoExceptExpr nee) {
analyzableNoExceptExpr(nee) and
nee.getExpr().getFullyConverted() = child.getAnExpr()
}
/** Gets the hash-cons of expression `e`. */
cached
HashCons hashCons(Expr e) {
exists(int val, Type t |
mk_IntLiteral(val, t, e) and
result = HC_IntLiteral(val, t)
)
or
exists(EnumConstant val, Type t |
mk_EnumConstantAccess(val, t, e) and
result = HC_EnumConstantAccess(val, t)
)
or
exists(float val, Type t |
mk_FloatLiteral(val, t, e) and
result = HC_FloatLiteral(val, t)
)
or
exists(string val, Type t |
mk_StringLiteral(val, t, e) and
result = HC_StringLiteral(val, t)
)
or
exists(Variable x |
mk_Variable(x, e) and
result = HC_Variable(x)
)
or
exists(HashCons qualifier, Field target |
mk_DotFieldAccess(qualifier, target, e) and
result = HC_FieldAccess(qualifier, target)
)
or
exists(HashCons qualifier, Field target |
mk_PointerFieldAccess(qualifier, target, e) and
result = HC_PointerFieldAccess(qualifier, target)
)
or
exists(Function fcn, Field target |
mk_ImplicitThisFieldAccess(fcn, target, e) and
result = HC_ImplicitThisFieldAccess(fcn, target)
)
or
exists(Function fcn |
mk_ThisExpr(fcn, e) and
result = HC_ThisExpr(fcn)
)
or
exists(Type t, HashCons child |
mk_Conversion(t, child, e) and
result = HC_Conversion(t, child)
)
or
exists(HashCons lhs, HashCons rhs, string opname |
mk_BinaryOp(lhs, rhs, opname, e) and
result = HC_BinaryOp(lhs, rhs, opname)
)
or
exists(HashCons child, string opname |
mk_UnaryOp(child, opname, e) and
result = HC_UnaryOp(child, opname)
)
or
exists(HashCons x, HashCons i |
mk_ArrayAccess(x, i, e) and
result = HC_ArrayAccess(x, i)
)
or
exists(HashCons p |
mk_Deref(p, e) and
result = HC_Deref(p)
)
or
exists(Function fcn, HC_Args args |
mk_NonmemberFunctionCall(fcn, args, e) and
result = HC_NonmemberFunctionCall(fcn, args)
)
or
exists(HashCons hc, HC_Args args |
mk_ExprCall(hc, args, e) and
result = HC_ExprCall(hc, args)
)
or
exists(Function fcn, HashCons qual, HC_Args args |
mk_MemberFunctionCall(fcn, qual, args, e) and
result = HC_MemberFunctionCall(fcn, qual, args)
)
or
// works around an extractor issue
exists(Type t |
mk_AllocatorArgZero(t, e) and
result = HC_AllocatorArgZero(t)
)
or
exists(Type t, HC_Alloc alloc, HC_Init init |
mk_NewExpr(t, alloc, init, e) and
result = HC_NewExpr(t, alloc, init)
)
or
exists(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init |
mk_NewArrayExpr(t, alloc, extent, init, e) and
result = HC_NewArrayExpr(t, alloc, extent, init)
)
or
exists(Type t |
mk_SizeofType(t, e) and
result = HC_SizeofType(t)
)
or
exists(HashCons child |
mk_SizeofExpr(child, e) and
result = HC_SizeofExpr(child)
)
or
exists(Type t |
mk_TypeidType(t, e) and
result = HC_TypeidType(t)
)
or
exists(HashCons child |
mk_TypeidExpr(child, e) and
result = HC_TypeidExpr(child)
)
or
exists(Type t |
mk_UuidofOperator(t, e) and
result = HC_UuidofOperator(t)
)
or
exists(Type t |
mk_AlignofType(t, e) and
result = HC_AlignofType(t)
)
or
exists(HashCons child |
mk_AlignofExpr(child, e) and
result = HC_AlignofExpr(child)
)
or
exists(Class c, HC_Fields hfc |
mk_ClassAggregateLiteral(c, hfc, e) and
result = HC_ClassAggregateLiteral(c, hfc)
)
or
exists(Type t, HC_Array hca |
mk_ArrayAggregateLiteral(t, hca, e) and
result = HC_ArrayAggregateLiteral(t, hca)
)
or
exists(HashCons child |
mk_DeleteExpr(child, e) and
result = HC_DeleteExpr(child)
)
or
exists(HashCons child |
mk_DeleteArrayExpr(child, e) and
result = HC_DeleteArrayExpr(child)
)
or
exists(HashCons child |
mk_ThrowExpr(child, e) and
result = HC_ThrowExpr(child)
)
or
mk_ReThrowExpr(e) and
result = HC_ReThrowExpr()
or
exists(HashCons cond, HashCons thenHC, HashCons elseHC |
mk_ConditionalExpr(cond, thenHC, elseHC, e) and
result = HC_ConditionalExpr(cond, thenHC, elseHC)
)
or
mk_Nullptr(e) and
result = HC_Nullptr()
or
not analyzableExpr(e, _) and result = HC_Unanalyzable(e)
}
/**
* Holds if the expression is explicitly handled by `hashCons`.
* Unanalyzable expressions still need to be given a hash-cons,
* but it will be a unique number that is not shared with any other
* expression.
*/
predicate analyzableExpr(Expr e, string kind) {
analyzableIntLiteral(e) and kind = "IntLiteral"
or
analyzableEnumConstantAccess(e) and kind = "EnumConstantAccess"
or
analyzableFloatLiteral(e) and kind = "FloatLiteral"
or
analyzableStringLiteral(e) and kind = "StringLiteral"
or
analyzableNullptr(e) and kind = "Nullptr"
or
analyzableDotFieldAccess(e) and kind = "DotFieldAccess"
or
analyzablePointerFieldAccess(e) and kind = "PointerFieldAccess"
or
analyzableImplicitThisFieldAccess(e) and kind = "ImplicitThisFieldAccess"
or
analyzableVariable(e) and kind = "Variable"
or
analyzableConversion(e) and kind = "Conversion"
or
analyzableBinaryOp(e) and kind = "BinaryOp"
or
analyzableUnaryOp(e) and kind = "UnaryOp"
or
analyzableThisExpr(e) and kind = "ThisExpr"
or
analyzableArrayAccess(e) and kind = "ArrayAccess"
or
analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr"
or
analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall"
or
analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall"
or
analyzableExprCall(e) and kind = "ExprCall"
or
analyzableNewExpr(e) and kind = "NewExpr"
or
analyzableNewArrayExpr(e) and kind = "NewArrayExpr"
or
analyzableSizeofType(e) and kind = "SizeofTypeOperator"
or
analyzableSizeofExpr(e) and kind = "SizeofExprOperator"
or
analyzableAlignofType(e) and kind = "AlignofTypeOperator"
or
analyzableAlignofExpr(e) and kind = "AlignofExprOperator"
or
analyzableUuidofOperator(e) and kind = "UuidofOperator"
or
analyzableTypeidType(e) and kind = "TypeidType"
or
analyzableTypeidExpr(e) and kind = "TypeidExpr"
or
analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral"
or
analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral"
or
analyzableDeleteExpr(e) and kind = "DeleteExpr"
or
analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr"
or
analyzableThrowExpr(e) and kind = "ThrowExpr"
or
analyzableReThrowExpr(e) and kind = "ReThrowExpr"
or
analyzableConditionalExpr(e) and kind = "ConditionalExpr"
or
analyzableNoExceptExpr(e) and kind = "NoExceptExpr"
or
analyzableAllocatorArgZero(e) and kind = "AllocatorArgZero"
}