Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cpp/ql/lib/change-notes/2026-03-24-field-init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: feature
---
* Added a class `ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
* Added a class `ConstructorDefaultFieldInit` to represent default field initializations.
29 changes: 27 additions & 2 deletions cpp/ql/lib/semmle/code/cpp/exprs/Call.qll
Original file line number Diff line number Diff line change
Expand Up @@ -585,12 +585,15 @@ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit

/**
* An initialization of a member variable performed as part of a
* constructor's explicit initializer list or implicit actions.
* constructor's initializer list or by default initialization.
*
* In the example below, member variable `b` is being initialized by
* constructor parameter `a`:
* constructor parameter `a`, and `c` is initialized by default
* initialization:
* ```
* struct S {
* int b;
* int c = 3;
* S(int a): b(a) {}
* } s(2);
* ```
Expand All @@ -616,6 +619,28 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
override predicate mayBeGloballyImpure() { this.getExpr().mayBeGloballyImpure() }
}

/**
* An initialization of a member variable performed as part of a
* constructor's explicit initializer list.
*/
class ConstructorDirectFieldInit extends ConstructorFieldInit {
ConstructorDirectFieldInit() { exists(this.getChild(0)) }

override string getAPrimaryQlClass() { result = "ConstructorDirectFieldInit" }
}

/**
* An initialization of a member variable performed by default
* initialization.
*/
class ConstructorDefaultFieldInit extends ConstructorFieldInit {
ConstructorDefaultFieldInit() {
not exists(this.getChild(0)) and exists(this.getTarget().getInitializer())
}

override string getAPrimaryQlClass() { result = "ConstructorDefaultFieldInit" }
}

/**
* A call to a destructor of a base class or field as part of a destructor's
* compiler-generated actions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,11 @@ module Public {

/** Gets the parameter through which this value is assigned. */
Parameter getParameter() {
result = this.getCallInstruction().getStaticCallTarget().getParameter(this.getArgumentIndex())
result =
this.getCallInstruction()
.getStaticCallTarget()
.(Function)
.getParameter(this.getArgumentIndex())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ private class PointerWrapperTypeIndirection extends Indirection instanceof Point
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
this = call.getStaticCallTarget().getClassAndName(["operator*", "operator->", "get"]) and
this =
call.getStaticCallTarget().(Function).getClassAndName(["operator*", "operator->", "get"]) and
address = call.getThisArgumentOperand()
)
}
Expand All @@ -194,7 +195,7 @@ private module IteratorIndirections {

override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
this = call.getStaticCallTarget().getClassAndName("operator=") and
this = call.getStaticCallTarget().(Function).getClassAndName("operator=") and
address = call.getThisArgumentOperand() and
certain = false
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class FieldInstruction extends Instruction {
* `FunctionAddress` instruction.
*/
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
Language::Declaration funcSymbol;

FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }

Expand All @@ -504,7 +504,7 @@ class FunctionInstruction extends Instruction {
/**
* Gets the function that this instruction references.
*/
final Language::Function getFunctionSymbol() { result = funcSymbol }
final Language::Declaration getFunctionSymbol() { result = funcSymbol }
}

/**
Expand Down Expand Up @@ -1678,7 +1678,7 @@ class CallInstruction extends Instruction {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Function getStaticCallTarget() {
final Language::Declaration getStaticCallTarget() {
result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class FieldInstruction extends Instruction {
* `FunctionAddress` instruction.
*/
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
Language::Declaration funcSymbol;

FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }

Expand All @@ -504,7 +504,7 @@ class FunctionInstruction extends Instruction {
/**
* Gets the function that this instruction references.
*/
final Language::Function getFunctionSymbol() { result = funcSymbol }
final Language::Declaration getFunctionSymbol() { result = funcSymbol }
}

/**
Expand Down Expand Up @@ -1678,7 +1678,7 @@ class CallInstruction extends Instruction {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Function getStaticCallTarget() {
final Language::Declaration getStaticCallTarget() {
result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
private import TranslatedElement

Check warning

Code scanning / CodeQL

Redundant import Warning

Redundant import, the module is already imported inside
TranslatedGlobalVar
.
private import TranslatedExpr
private import TranslatedCall
private import TranslatedStmt
private import TranslatedFunction
private import TranslatedGlobalVar
private import TranslatedNonStaticDataMember
private import TranslatedInitialization

TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
Expand Down Expand Up @@ -45,6 +46,8 @@
or
not var.isFromUninstantiatedTemplate(_) and
var instanceof StaticInitializedStaticLocalVariable
or
var instanceof Field
) and
var.hasInitializer() and
(
Expand All @@ -64,6 +67,8 @@
getTranslatedFunction(decl).hasUserVariable(var, type)
or
getTranslatedVarInit(decl).hasUserVariable(var, type)
or
getTranslatedFieldInit(decl).hasUserVariable(var, type)
}

cached
Expand Down Expand Up @@ -110,7 +115,7 @@
}

cached
Function getInstructionFunction(Instruction instruction) {
Declaration getInstructionFunction(Instruction instruction) {
result =
getInstructionTranslatedElement(instruction)
.getInstructionFunction(getInstructionTag(instruction))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,35 +130,39 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
}

/**
* A `Call` or `NewOrNewArrayExpr` or `DeleteOrDeleteArrayExpr`.
* An expression that can have call side effects.
*
* All kinds of expression invoke a function as part of their evaluation. This class provides a
* way to treat both kinds of function similarly, and to get the invoked `Function`.
* All kinds of expressions invoke a function as part of their evaluation. This class provides a
* way to treat those expressions similarly, and to get the invoked `Declaration`.
*/
class CallOrAllocationExpr extends Expr {
CallOrAllocationExpr() {
class ExprWithCallSideEffects extends Expr {
ExprWithCallSideEffects() {
this instanceof Call
or
this instanceof NewOrNewArrayExpr
or
this instanceof DeleteOrDeleteArrayExpr
or
this instanceof ConstructorDefaultFieldInit
}

/** Gets the `Function` invoked by this expression, if known. */
final Function getTarget() {
/** Gets the `Declaration` invoked by this expression, if known. */
final Declaration getTarget() {
result = this.(Call).getTarget()
or
result = this.(NewOrNewArrayExpr).getAllocator()
or
result = this.(DeleteOrDeleteArrayExpr).getDeallocator()
or
result = this.(ConstructorDefaultFieldInit).getTarget()
}
}

/**
* Returns the side effect opcode, if any, that represents any side effects not specifically modeled
* by an argument side effect.
*/
Opcode getCallSideEffectOpcode(CallOrAllocationExpr expr) {
Opcode getCallSideEffectOpcode(ExprWithCallSideEffects expr) {
not exists(expr.getTarget().(SideEffectFunction)) and result instanceof Opcode::CallSideEffect
or
exists(SideEffectFunction sideEffectFunction |
Expand All @@ -175,7 +179,7 @@ Opcode getCallSideEffectOpcode(CallOrAllocationExpr expr) {
/**
* Returns a side effect opcode for parameter index `i` of the specified call.
*
* This predicate will return at most two results: one read side effect, and one write side effect.
* This predicate will yield at most two results: one read side effect, and one write side effect.
*/
Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
exists(boolean buffer |
Expand Down Expand Up @@ -228,3 +232,14 @@ Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
)
)
}

/**
* Returns a side effect opcode for a default field initialization.
*
* This predicate will yield two results: one read side effect, and one write side effect.
*/
Opcode getDefaultFieldInitSideEffectOpcode() {
result instanceof Opcode::IndirectReadSideEffect
or
result instanceof Opcode::IndirectMayWriteSideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ private import SideEffects
private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
private import TranslatedInitialization
private import DefaultOptions as DefaultOptions

/**
Expand Down Expand Up @@ -348,7 +349,7 @@ class TranslatedExprCall extends TranslatedCallExpr {
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
override FunctionCall expr;

override Function getInstructionFunction(InstructionTag tag) {
override Declaration getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getTarget()
}

Expand Down Expand Up @@ -429,6 +430,9 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
or
expr instanceof DeleteOrDeleteArrayExpr and
result = getTranslatedDeleteOrDeleteArray(expr).getInstruction(CallTag())
or
expr instanceof ConstructorDefaultFieldInit and
result = getTranslatedConstructorFieldInitialization(expr).getInstruction(CallTag())
}
}

Expand Down Expand Up @@ -504,11 +508,25 @@ abstract class TranslatedSideEffect extends TranslatedElement {
abstract predicate sideEffectInstruction(Opcode opcode, CppType type);
}

private class CallOrDefaultFieldInit extends Expr {
CallOrDefaultFieldInit() {
this instanceof Call
or
this instanceof ConstructorDefaultFieldInit
}

Declaration getTarget() {
result = this.(Call).getTarget()
or
result = this.(ConstructorDefaultFieldInit).getTarget()
}
}

/**
* The IR translation of a single argument side effect for a call.
*/
abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
Call call;
CallOrDefaultFieldInit callOrInit;
int index;
SideEffectOpcode sideEffectOpcode;

Expand All @@ -524,7 +542,7 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
result = "(read side effect for " + this.getArgString() + ")"
}

override Call getPrimaryExpr() { result = call }
override Expr getPrimaryExpr() { result = callOrInit }

override predicate sortOrder(int group, int indexInGroup) {
indexInGroup = index and
Expand Down Expand Up @@ -586,9 +604,10 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
tag instanceof OnlyInstructionTag and
operandTag instanceof BufferSizeOperandTag and
result =
getTranslatedExpr(call.getArgument(call.getTarget()
.(SideEffectFunction)
.getParameterSizeIndex(index)).getFullyConverted()).getResult()
getTranslatedExpr(callOrInit
.(Call)
.getArgument(callOrInit.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
.getFullyConverted()).getResult()
}

/** Holds if this side effect is a write side effect, rather than a read side effect. */
Expand Down Expand Up @@ -616,7 +635,7 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
Expr arg;

TranslatedArgumentExprSideEffect() {
this = TTranslatedArgumentExprSideEffect(call, arg, index, sideEffectOpcode)
this = TTranslatedArgumentExprSideEffect(callOrInit, arg, index, sideEffectOpcode)
}

final override Locatable getAst() { result = arg }
Expand All @@ -640,28 +659,31 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
* The IR translation of an argument side effect for `*this` on a call, where there is no `Expr`
* object that represents the `this` argument.
*
* The applies only to constructor calls, as the AST has exploit qualifier `Expr`s for all other
* calls to non-static member functions.
* This applies to constructor calls and default field initializations, as the AST has explicit
* qualifier `Expr`s for all other calls to non-static member functions.
*/
class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect,
TTranslatedStructorQualifierSideEffect
class TranslatedImplicitThisQualifierSideEffect extends TranslatedArgumentSideEffect,
TTranslatedImplicitThisQualifierSideEffect
{
TranslatedStructorQualifierSideEffect() {
this = TTranslatedStructorQualifierSideEffect(call, sideEffectOpcode) and
TranslatedImplicitThisQualifierSideEffect() {
this = TTranslatedImplicitThisQualifierSideEffect(callOrInit, sideEffectOpcode) and
index = -1
}

final override Locatable getAst() { result = call }
final override Locatable getAst() { result = callOrInit }

final override Type getIndirectionType() { result = call.getTarget().getDeclaringType() }
final override Type getIndirectionType() { result = callOrInit.getTarget().getDeclaringType() }

final override string getArgString() { result = "this" }

final override Instruction getArgInstruction() {
exists(TranslatedStructorCall structorCall |
structorCall.getExpr() = call and
structorCall.getExpr() = callOrInit and
result = structorCall.getQualifierResult()
)
or
callOrInit instanceof ConstructorDefaultFieldInit and
result = getTranslatedFunction(callOrInit.getEnclosingFunction()).getLoadThisInstruction()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ abstract class TranslatedCondition extends TranslatedElement {
final override Declaration getFunction() {
result = getEnclosingFunction(expr) or
result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable)
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) or
result = getEnclosingVariable(expr).(Field)
}

final Type getResultType() { result = expr.getUnspecifiedType() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ abstract class TranslatedDeclarationEntry extends TranslatedElement, TTranslated
or
not entry.getDeclaration() instanceof StaticInitializedStaticLocalVariable and
not entry.getDeclaration() instanceof GlobalOrNamespaceVariable and
not entry.getDeclaration() instanceof Field and
result = stmt.getEnclosingFunction()
)
}
Expand Down
Loading
Loading