Skip to content

Commit 6d54d9f

Browse files
authored
Reland [clang][dataflow] Transfer more cast expressions. (#157535)
Reverts #157148 Adds fixes to `TransferVisitor::VisitCXXConstructExpr` and `copyRecord` to avoid crashing on base class initialization from sibling-derived class instances. I believe this is the only use of copyRecord where we need this special handling for a shared base class.
1 parent 5c17af4 commit 6d54d9f

File tree

6 files changed

+492
-22
lines changed

6 files changed

+492
-22
lines changed

clang/include/clang/Analysis/FlowSensitive/RecordOps.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_RECORDOPS_H
1414
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_RECORDOPS_H
1515

16+
#include "clang/AST/Type.h"
1617
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
1718
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
1819

@@ -36,8 +37,11 @@ namespace dataflow {
3637
/// - The type of `Src` must be derived from `Dest`, or
3738
/// - The type of `Dest` must be derived from `Src` (in this case, any fields
3839
/// that are only present in `Dest` are not overwritten).
40+
/// - The types of `Dest` and `Src` are both derived from a non-null
41+
/// `TypeToCopy` (in this case, only fields present in `TypeToCopy` are
42+
/// overwritten).
3943
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
40-
Environment &Env);
44+
Environment &Env, QualType TypeToCopy = QualType());
4145

4246
/// Returns whether the records `Loc1` and `Loc2` are equal.
4347
///

clang/include/clang/Analysis/FlowSensitive/StorageLocation.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "clang/AST/Decl.h"
1818
#include "clang/AST/Type.h"
1919
#include "llvm/ADT/DenseMap.h"
20+
#include "llvm/ADT/StringRef.h"
2021
#include "llvm/Support/Debug.h"
2122
#include <cassert>
2223

@@ -152,6 +153,11 @@ class RecordStorageLocation final : public StorageLocation {
152153
return {SyntheticFields.begin(), SyntheticFields.end()};
153154
}
154155

156+
/// Add a synthetic field, if none by that name is already present.
157+
void addSyntheticField(llvm::StringRef Name, StorageLocation &Loc) {
158+
SyntheticFields.insert({Name, &Loc});
159+
}
160+
155161
/// Changes the child storage location for a field `D` of reference type.
156162
/// All other fields cannot change their storage location and always retain
157163
/// the storage location passed to the `RecordStorageLocation` constructor.
@@ -164,6 +170,11 @@ class RecordStorageLocation final : public StorageLocation {
164170
Children[&D] = Loc;
165171
}
166172

173+
/// Add a child storage location for a field `D`, if not already present.
174+
void addChild(const ValueDecl &D, StorageLocation *Loc) {
175+
Children.insert({&D, Loc});
176+
}
177+
167178
llvm::iterator_range<FieldToLoc::const_iterator> children() const {
168179
return {Children.begin(), Children.end()};
169180
}

clang/lib/Analysis/FlowSensitive/RecordOps.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/Analysis/FlowSensitive/RecordOps.h"
14+
#include "clang/AST/Decl.h"
15+
#include "clang/AST/DeclCXX.h"
16+
#include "clang/AST/Type.h"
1417

1518
#define DEBUG_TYPE "dataflow"
1619

@@ -49,25 +52,30 @@ static void copySyntheticField(QualType FieldType, StorageLocation &SrcFieldLoc,
4952
}
5053

5154
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
52-
Environment &Env) {
55+
Environment &Env, const QualType TypeToCopy) {
5356
auto SrcType = Src.getType().getCanonicalType().getUnqualifiedType();
5457
auto DstType = Dst.getType().getCanonicalType().getUnqualifiedType();
5558

5659
auto SrcDecl = SrcType->getAsCXXRecordDecl();
5760
auto DstDecl = DstType->getAsCXXRecordDecl();
5861

59-
[[maybe_unused]] bool compatibleTypes =
62+
const CXXRecordDecl *DeclToCopy =
63+
TypeToCopy.isNull() ? nullptr : TypeToCopy->getAsCXXRecordDecl();
64+
65+
[[maybe_unused]] bool CompatibleTypes =
6066
SrcType == DstType ||
6167
(SrcDecl != nullptr && DstDecl != nullptr &&
62-
(SrcDecl->isDerivedFrom(DstDecl) || DstDecl->isDerivedFrom(SrcDecl)));
68+
(SrcDecl->isDerivedFrom(DstDecl) || DstDecl->isDerivedFrom(SrcDecl) ||
69+
(DeclToCopy != nullptr && SrcDecl->isDerivedFrom(DeclToCopy) &&
70+
DstDecl->isDerivedFrom(DeclToCopy))));
6371

6472
LLVM_DEBUG({
65-
if (!compatibleTypes) {
73+
if (!CompatibleTypes) {
6674
llvm::dbgs() << "Source type " << Src.getType() << "\n";
6775
llvm::dbgs() << "Destination type " << Dst.getType() << "\n";
6876
}
6977
});
70-
assert(compatibleTypes);
78+
assert(CompatibleTypes);
7179

7280
if (SrcType == DstType || (SrcDecl != nullptr && DstDecl != nullptr &&
7381
SrcDecl->isDerivedFrom(DstDecl))) {
@@ -76,12 +84,24 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
7684
for (const auto &[Name, DstFieldLoc] : Dst.synthetic_fields())
7785
copySyntheticField(DstFieldLoc->getType(), Src.getSyntheticField(Name),
7886
*DstFieldLoc, Env);
79-
} else {
87+
} else if (SrcDecl != nullptr && DstDecl != nullptr &&
88+
DstDecl->isDerivedFrom(SrcDecl)) {
8089
for (auto [Field, SrcFieldLoc] : Src.children())
8190
copyField(*Field, SrcFieldLoc, Dst.getChild(*Field), Dst, Env);
8291
for (const auto &[Name, SrcFieldLoc] : Src.synthetic_fields())
8392
copySyntheticField(SrcFieldLoc->getType(), *SrcFieldLoc,
8493
Dst.getSyntheticField(Name), Env);
94+
} else {
95+
for (const FieldDecl *Field :
96+
Env.getDataflowAnalysisContext().getModeledFields(TypeToCopy)) {
97+
copyField(*Field, Src.getChild(*Field), Dst.getChild(*Field), Dst, Env);
98+
}
99+
for (const auto &[SyntheticFieldName, SyntheticFieldType] :
100+
Env.getDataflowAnalysisContext().getSyntheticFields(TypeToCopy)) {
101+
copySyntheticField(SyntheticFieldType,
102+
Src.getSyntheticField(SyntheticFieldName),
103+
Dst.getSyntheticField(SyntheticFieldName), Env);
104+
}
85105
}
86106
}
87107

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020
#include "clang/AST/OperationKinds.h"
2121
#include "clang/AST/Stmt.h"
2222
#include "clang/AST/StmtVisitor.h"
23+
#include "clang/AST/Type.h"
2324
#include "clang/Analysis/FlowSensitive/ASTOps.h"
2425
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
2526
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
2627
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
2728
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
2829
#include "clang/Analysis/FlowSensitive/RecordOps.h"
30+
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
2931
#include "clang/Analysis/FlowSensitive/Value.h"
3032
#include "clang/Basic/Builtins.h"
33+
#include "clang/Basic/LLVM.h"
3134
#include "clang/Basic/OperatorKinds.h"
3235
#include "llvm/Support/Casting.h"
3336
#include <assert.h>
@@ -287,7 +290,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
287290
}
288291
}
289292

290-
void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
293+
void VisitCastExpr(const CastExpr *S) {
291294
const Expr *SubExpr = S->getSubExpr();
292295
assert(SubExpr != nullptr);
293296

@@ -317,17 +320,70 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
317320
break;
318321
}
319322

323+
case CK_BaseToDerived: {
324+
// This is a cast of (single-layer) pointer or reference to a record type.
325+
// We should now model the fields for the derived type.
326+
327+
// Get the RecordStorageLocation for the record object underneath.
328+
RecordStorageLocation *Loc = nullptr;
329+
if (S->getType()->isPointerType()) {
330+
auto *PV = Env.get<PointerValue>(*SubExpr);
331+
assert(PV != nullptr);
332+
if (PV == nullptr)
333+
break;
334+
Loc = cast<RecordStorageLocation>(&PV->getPointeeLoc());
335+
} else {
336+
assert(S->getType()->isRecordType());
337+
if (SubExpr->isGLValue()) {
338+
Loc = Env.get<RecordStorageLocation>(*SubExpr);
339+
} else {
340+
Loc = &Env.getResultObjectLocation(*SubExpr);
341+
}
342+
}
343+
if (!Loc) {
344+
// Nowhere to add children or propagate from, so we're done.
345+
break;
346+
}
347+
348+
// Get the derived record type underneath the reference or pointer.
349+
QualType Derived = S->getType().getNonReferenceType();
350+
if (Derived->isPointerType()) {
351+
Derived = Derived->getPointeeType();
352+
}
353+
354+
// Add children to the storage location for fields (including synthetic
355+
// fields) of the derived type and initialize their values.
356+
for (const FieldDecl *Field :
357+
Env.getDataflowAnalysisContext().getModeledFields(Derived)) {
358+
assert(Field != nullptr);
359+
QualType FieldType = Field->getType();
360+
if (FieldType->isReferenceType()) {
361+
Loc->addChild(*Field, nullptr);
362+
} else {
363+
Loc->addChild(*Field, &Env.createStorageLocation(FieldType));
364+
}
365+
366+
for (const auto &Entry :
367+
Env.getDataflowAnalysisContext().getSyntheticFields(Derived)) {
368+
Loc->addSyntheticField(Entry.getKey(),
369+
Env.createStorageLocation(Entry.getValue()));
370+
}
371+
}
372+
Env.initializeFieldsWithValues(*Loc, Derived);
373+
374+
// Fall through to propagate SubExpr's StorageLocation to the CastExpr.
375+
[[fallthrough]];
376+
}
320377
case CK_IntegralCast:
321378
// FIXME: This cast creates a new integral value from the
322379
// subexpression. But, because we don't model integers, we don't
323380
// distinguish between this new value and the underlying one. If integer
324381
// modeling is added, then update this code to create a fresh location and
325382
// value.
326383
case CK_UncheckedDerivedToBase:
384+
case CK_DerivedToBase:
327385
case CK_ConstructorConversion:
328386
case CK_UserDefinedConversion:
329-
// FIXME: Add tests that excercise CK_UncheckedDerivedToBase,
330-
// CK_ConstructorConversion, and CK_UserDefinedConversion.
331387
case CK_NoOp: {
332388
// FIXME: Consider making `Environment::getStorageLocation` skip noop
333389
// expressions (this and other similar expressions in the file) instead
@@ -554,7 +610,15 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
554610
// Even if the copy/move constructor call is elidable, we choose to copy
555611
// the record in all cases (which isn't wrong, just potentially not
556612
// optimal).
557-
copyRecord(*ArgLoc, Loc, Env);
613+
//
614+
// To handle cases of base class initializers in constructors, where a
615+
// sibling derived class can be used to initialize a shared-base-class
616+
// subobject through a DerivedToBase cast, intentionally copy only the
617+
// parts of `ArgLoc` that are part of the base class being initialized.
618+
// This is necessary because the type of `Loc` in these cases is the
619+
// derived type ultimately being constructed, not the type of the base
620+
// class subobject.
621+
copyRecord(*ArgLoc, Loc, Env, S->getType());
558622
return;
559623
}
560624

@@ -684,15 +748,6 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
684748
propagateValue(*SubExpr, *S, Env);
685749
}
686750

687-
void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
688-
if (S->getCastKind() == CK_NoOp) {
689-
const Expr *SubExpr = S->getSubExpr();
690-
assert(SubExpr != nullptr);
691-
692-
propagateValueOrStorageLocation(*SubExpr, *S, Env);
693-
}
694-
}
695-
696751
void VisitConditionalOperator(const ConditionalOperator *S) {
697752
const Environment *TrueEnv = StmtToEnv.getEnvironment(*S->getTrueExpr());
698753
const Environment *FalseEnv = StmtToEnv.getEnvironment(*S->getFalseExpr());

clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88

99
#include "clang/Analysis/FlowSensitive/RecordOps.h"
1010
#include "TestingSupport.h"
11+
#include "clang/AST/Type.h"
12+
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
13+
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
14+
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
15+
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
16+
#include "llvm/ADT/StringMap.h"
1117
#include "llvm/Testing/Support/Error.h"
1218
#include "gtest/gtest.h"
19+
#include <string>
1320

1421
namespace clang {
1522
namespace dataflow {
@@ -190,7 +197,7 @@ TEST(RecordOpsTest, RecordsEqual) {
190197
});
191198
}
192199

193-
TEST(TransferTest, CopyRecordBetweenDerivedAndBase) {
200+
TEST(RecordOpsTest, CopyRecordBetweenDerivedAndBase) {
194201
std::string Code = R"(
195202
struct A {
196203
int i;
@@ -266,6 +273,67 @@ TEST(TransferTest, CopyRecordBetweenDerivedAndBase) {
266273
});
267274
}
268275

276+
TEST(RecordOpsTest, CopyRecordWithExplicitSharedBaseTypeToCopy) {
277+
std::string Code = R"(
278+
struct Base {
279+
bool BaseField;
280+
char UnmodeledField;
281+
};
282+
283+
struct DerivedOne : public Base {
284+
int DerivedOneField;
285+
};
286+
287+
struct DerivedTwo : public Base {
288+
int DerivedTwoField;
289+
};
290+
291+
void target(Base B, DerivedOne D1, DerivedTwo D2) {
292+
(void) B.BaseField;
293+
// [[p]]
294+
}
295+
)";
296+
auto SyntheticFieldCallback = [](QualType Ty) -> llvm::StringMap<QualType> {
297+
CXXRecordDecl *BaseDecl = nullptr;
298+
std::string TypeAsString = Ty.getAsString();
299+
if (TypeAsString == "Base")
300+
BaseDecl = Ty->getAsCXXRecordDecl();
301+
else if (TypeAsString == "DerivedOne" || TypeAsString == "DerivedTwo")
302+
BaseDecl = Ty->getAsCXXRecordDecl()
303+
->bases_begin()
304+
->getType()
305+
->getAsCXXRecordDecl();
306+
else
307+
return {};
308+
QualType FieldType = getFieldNamed(BaseDecl, "BaseField")->getType();
309+
return {{"synth_field", FieldType}};
310+
};
311+
// Test copying derived to base class.
312+
runDataflow(
313+
Code, SyntheticFieldCallback,
314+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
315+
ASTContext &ASTCtx) {
316+
Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
317+
318+
const ValueDecl *BaseFieldDecl = findValueDecl(ASTCtx, "BaseField");
319+
auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "B");
320+
auto &D1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "D1");
321+
auto &D2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "D2");
322+
323+
EXPECT_NE(Env.getValue(*D1.getChild(*BaseFieldDecl)),
324+
Env.getValue(*D2.getChild(*BaseFieldDecl)));
325+
EXPECT_NE(Env.getValue(D1.getSyntheticField("synth_field")),
326+
Env.getValue(D2.getSyntheticField("synth_field")));
327+
328+
copyRecord(D1, D2, Env, B.getType());
329+
330+
EXPECT_EQ(Env.getValue(*D1.getChild(*BaseFieldDecl)),
331+
Env.getValue(*D2.getChild(*BaseFieldDecl)));
332+
EXPECT_EQ(Env.getValue(D1.getSyntheticField("synth_field")),
333+
Env.getValue(D2.getSyntheticField("synth_field")));
334+
});
335+
}
336+
269337
} // namespace
270338
} // namespace test
271339
} // namespace dataflow

0 commit comments

Comments
 (0)