Skip to content

Commit c0bcd11

Browse files
committed
[ASTImporter] Add basic support for comparing Stmts and compare function bodies
Right now the ASTImporter assumes for most Expr nodes that they are always equal which leads to non-compatible declarations ending up being merged. This patch adds the basic framework for comparing Stmts (and with that also Exprs) and implements the custom checks for a few Stmt subclasses. I'll implement the remaining subclasses in follow up patches (mostly because there are a lot of subclasses and some of them require further changes like having GNU language in the testing framework) The motivation for this is that in LLDB we try to import libc++ source code and some of the types we are importing there contain expressions (e.g. because they use `enable_if<expr>`), so those declarations are currently merged even if they are completely different (e.g. `enable_if<value> ...` and `enable_if<!value> ...` are currently considered equal which is clearly not true). Reviewed By: martong, balazske Differential Revision: https://reviews.llvm.org/D87444
1 parent a4c5351 commit c0bcd11

File tree

3 files changed

+541
-32
lines changed

3 files changed

+541
-32
lines changed

clang/include/clang/AST/ASTStructuralEquivalence.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ struct StructuralEquivalenceContext {
9797
/// \c VisitedDecls members) and can cause faulty equivalent results.
9898
bool IsEquivalent(QualType T1, QualType T2);
9999

100+
/// Determine whether the two statements are structurally equivalent.
101+
/// Implementation functions (all static functions in
102+
/// ASTStructuralEquivalence.cpp) must never call this function because that
103+
/// will wreak havoc the internal state (\c DeclsToCheck and
104+
/// \c VisitedDecls members) and can cause faulty equivalent results.
105+
bool IsEquivalent(Stmt *S1, Stmt *S2);
106+
100107
/// Find the index of the given anonymous struct/union within its
101108
/// context.
102109
///

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 228 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@
6868
#include "clang/AST/DeclObjC.h"
6969
#include "clang/AST/DeclTemplate.h"
7070
#include "clang/AST/ExprCXX.h"
71+
#include "clang/AST/ExprConcepts.h"
72+
#include "clang/AST/ExprObjC.h"
73+
#include "clang/AST/ExprOpenMP.h"
7174
#include "clang/AST/NestedNameSpecifier.h"
75+
#include "clang/AST/StmtObjC.h"
76+
#include "clang/AST/StmtOpenMP.h"
7277
#include "clang/AST/TemplateBase.h"
7378
#include "clang/AST/TemplateName.h"
7479
#include "clang/AST/Type.h"
@@ -149,32 +154,230 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
149154
return true;
150155
}
151156

152-
/// Determine structural equivalence of two expressions.
153-
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
154-
const Expr *E1, const Expr *E2) {
155-
if (!E1 || !E2)
156-
return E1 == E2;
157+
namespace {
158+
/// Encapsulates Stmt comparison logic.
159+
class StmtComparer {
160+
StructuralEquivalenceContext &Context;
161+
162+
// IsStmtEquivalent overloads. Each overload compares a specific statement
163+
// and only has to compare the data that is specific to the specific statement
164+
// class. Should only be called from TraverseStmt.
165+
166+
bool IsStmtEquivalent(const AddrLabelExpr *E1, const AddrLabelExpr *E2) {
167+
return IsStructurallyEquivalent(Context, E1->getLabel(), E2->getLabel());
168+
}
169+
170+
bool IsStmtEquivalent(const AtomicExpr *E1, const AtomicExpr *E2) {
171+
return E1->getOp() == E2->getOp();
172+
}
173+
174+
bool IsStmtEquivalent(const BinaryOperator *E1, const BinaryOperator *E2) {
175+
return E1->getOpcode() == E2->getOpcode();
176+
}
157177

158-
if (auto *DE1 = dyn_cast<DependentScopeDeclRefExpr>(E1)) {
159-
auto *DE2 = dyn_cast<DependentScopeDeclRefExpr>(E2);
160-
if (!DE2)
178+
bool IsStmtEquivalent(const CallExpr *E1, const CallExpr *E2) {
179+
// FIXME: IsStructurallyEquivalent requires non-const Decls.
180+
Decl *Callee1 = const_cast<Decl *>(E1->getCalleeDecl());
181+
Decl *Callee2 = const_cast<Decl *>(E2->getCalleeDecl());
182+
183+
// Compare whether both calls know their callee.
184+
if (static_cast<bool>(Callee1) != static_cast<bool>(Callee2))
161185
return false;
186+
187+
// Both calls have no callee, so nothing to do.
188+
if (!static_cast<bool>(Callee1))
189+
return true;
190+
191+
assert(Callee2);
192+
return IsStructurallyEquivalent(Context, Callee1, Callee2);
193+
}
194+
195+
bool IsStmtEquivalent(const CharacterLiteral *E1,
196+
const CharacterLiteral *E2) {
197+
return E1->getValue() == E2->getValue() && E1->getKind() == E2->getKind();
198+
}
199+
200+
bool IsStmtEquivalent(const ChooseExpr *E1, const ChooseExpr *E2) {
201+
return true; // Semantics only depend on children.
202+
}
203+
204+
bool IsStmtEquivalent(const CompoundStmt *E1, const CompoundStmt *E2) {
205+
// Number of children is actually checked by the generic children comparison
206+
// code, but a CompoundStmt is one of the few statements where the number of
207+
// children frequently differs and the number of statements is also always
208+
// precomputed. Directly comparing the number of children here is thus
209+
// just an optimization.
210+
return E1->size() == E2->size();
211+
}
212+
213+
bool IsStmtEquivalent(const DependentScopeDeclRefExpr *DE1,
214+
const DependentScopeDeclRefExpr *DE2) {
162215
if (!IsStructurallyEquivalent(Context, DE1->getDeclName(),
163216
DE2->getDeclName()))
164217
return false;
165218
return IsStructurallyEquivalent(Context, DE1->getQualifier(),
166219
DE2->getQualifier());
167-
} else if (auto CastE1 = dyn_cast<ImplicitCastExpr>(E1)) {
168-
auto *CastE2 = dyn_cast<ImplicitCastExpr>(E2);
169-
if (!CastE2)
220+
}
221+
222+
bool IsStmtEquivalent(const Expr *E1, const Expr *E2) {
223+
return IsStructurallyEquivalent(Context, E1->getType(), E2->getType());
224+
}
225+
226+
bool IsStmtEquivalent(const ExpressionTraitExpr *E1,
227+
const ExpressionTraitExpr *E2) {
228+
return E1->getTrait() == E2->getTrait() && E1->getValue() == E2->getValue();
229+
}
230+
231+
bool IsStmtEquivalent(const FloatingLiteral *E1, const FloatingLiteral *E2) {
232+
return E1->isExact() == E2->isExact() && E1->getValue() == E2->getValue();
233+
}
234+
235+
bool IsStmtEquivalent(const ImplicitCastExpr *CastE1,
236+
const ImplicitCastExpr *CastE2) {
237+
return IsStructurallyEquivalent(Context, CastE1->getType(),
238+
CastE2->getType());
239+
}
240+
241+
bool IsStmtEquivalent(const IntegerLiteral *E1, const IntegerLiteral *E2) {
242+
return E1->getValue() == E2->getValue();
243+
}
244+
245+
bool IsStmtEquivalent(const ObjCStringLiteral *E1,
246+
const ObjCStringLiteral *E2) {
247+
// Just wraps a StringLiteral child.
248+
return true;
249+
}
250+
251+
bool IsStmtEquivalent(const Stmt *S1, const Stmt *S2) { return true; }
252+
253+
bool IsStmtEquivalent(const SourceLocExpr *E1, const SourceLocExpr *E2) {
254+
return E1->getIdentKind() == E2->getIdentKind();
255+
}
256+
257+
bool IsStmtEquivalent(const StmtExpr *E1, const StmtExpr *E2) {
258+
return E1->getTemplateDepth() == E2->getTemplateDepth();
259+
}
260+
261+
bool IsStmtEquivalent(const StringLiteral *E1, const StringLiteral *E2) {
262+
return E1->getBytes() == E2->getBytes();
263+
}
264+
265+
bool IsStmtEquivalent(const SubstNonTypeTemplateParmExpr *E1,
266+
const SubstNonTypeTemplateParmExpr *E2) {
267+
return IsStructurallyEquivalent(Context, E1->getParameter(),
268+
E2->getParameter());
269+
}
270+
271+
bool IsStmtEquivalent(const SubstNonTypeTemplateParmPackExpr *E1,
272+
const SubstNonTypeTemplateParmPackExpr *E2) {
273+
return IsStructurallyEquivalent(Context, E1->getArgumentPack(),
274+
E2->getArgumentPack());
275+
}
276+
277+
bool IsStmtEquivalent(const TypeTraitExpr *E1, const TypeTraitExpr *E2) {
278+
if (E1->getTrait() != E2->getTrait())
279+
return false;
280+
281+
for (auto Pair : zip_longest(E1->getArgs(), E2->getArgs())) {
282+
Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair);
283+
Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair);
284+
// Different number of args.
285+
if (!Child1 || !Child2)
286+
return false;
287+
288+
if (!IsStructurallyEquivalent(Context, (*Child1)->getType(),
289+
(*Child2)->getType()))
290+
return false;
291+
}
292+
return true;
293+
}
294+
295+
bool IsStmtEquivalent(const UnaryExprOrTypeTraitExpr *E1,
296+
const UnaryExprOrTypeTraitExpr *E2) {
297+
if (E1->getKind() != E2->getKind())
298+
return false;
299+
return IsStructurallyEquivalent(Context, E1->getTypeOfArgument(),
300+
E2->getTypeOfArgument());
301+
}
302+
303+
bool IsStmtEquivalent(const UnaryOperator *E1, const UnaryOperator *E2) {
304+
return E1->getOpcode() == E2->getOpcode();
305+
}
306+
307+
bool IsStmtEquivalent(const VAArgExpr *E1, const VAArgExpr *E2) {
308+
// Semantics only depend on children.
309+
return true;
310+
}
311+
312+
/// End point of the traversal chain.
313+
bool TraverseStmt(const Stmt *S1, const Stmt *S2) { return true; }
314+
315+
// Create traversal methods that traverse the class hierarchy and return
316+
// the accumulated result of the comparison. Each TraverseStmt overload
317+
// calls the TraverseStmt overload of the parent class. For example,
318+
// the TraverseStmt overload for 'BinaryOperator' calls the TraverseStmt
319+
// overload of 'Expr' which then calls the overload for 'Stmt'.
320+
#define STMT(CLASS, PARENT) \
321+
bool TraverseStmt(const CLASS *S1, const CLASS *S2) { \
322+
if (!TraverseStmt(static_cast<const PARENT *>(S1), \
323+
static_cast<const PARENT *>(S2))) \
324+
return false; \
325+
return IsStmtEquivalent(S1, S2); \
326+
}
327+
#include "clang/AST/StmtNodes.inc"
328+
329+
public:
330+
StmtComparer(StructuralEquivalenceContext &C) : Context(C) {}
331+
332+
/// Determine whether two statements are equivalent. The statements have to
333+
/// be of the same kind. The children of the statements and their properties
334+
/// are not compared by this function.
335+
bool IsEquivalent(const Stmt *S1, const Stmt *S2) {
336+
if (S1->getStmtClass() != S2->getStmtClass())
337+
return false;
338+
339+
// Each TraverseStmt walks the class hierarchy from the leaf class to
340+
// the root class 'Stmt' (e.g. 'BinaryOperator' -> 'Expr' -> 'Stmt'). Cast
341+
// the Stmt we have here to its specific subclass so that we call the
342+
// overload that walks the whole class hierarchy from leaf to root (e.g.,
343+
// cast to 'BinaryOperator' so that 'Expr' and 'Stmt' is traversed).
344+
switch (S1->getStmtClass()) {
345+
case Stmt::NoStmtClass:
346+
llvm_unreachable("Can't traverse NoStmtClass");
347+
#define STMT(CLASS, PARENT) \
348+
case Stmt::StmtClass::CLASS##Class: \
349+
return TraverseStmt(static_cast<const CLASS *>(S1), \
350+
static_cast<const CLASS *>(S2));
351+
#define ABSTRACT_STMT(S)
352+
#include "clang/AST/StmtNodes.inc"
353+
}
354+
llvm_unreachable("Invalid statement kind");
355+
}
356+
};
357+
} // namespace
358+
359+
/// Determine structural equivalence of two statements.
360+
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
361+
const Stmt *S1, const Stmt *S2) {
362+
if (!S1 || !S2)
363+
return S1 == S2;
364+
365+
// Compare the statements itself.
366+
StmtComparer Comparer(Context);
367+
if (!Comparer.IsEquivalent(S1, S2))
368+
return false;
369+
370+
// Iterate over the children of both statements and also compare them.
371+
for (auto Pair : zip_longest(S1->children(), S2->children())) {
372+
Optional<const Stmt *> Child1 = std::get<0>(Pair);
373+
Optional<const Stmt *> Child2 = std::get<1>(Pair);
374+
// One of the statements has a different amount of children than the other,
375+
// so the statements can't be equivalent.
376+
if (!Child1 || !Child2)
170377
return false;
171-
if (!IsStructurallyEquivalent(Context, CastE1->getType(),
172-
CastE2->getType()))
378+
if (!IsStructurallyEquivalent(Context, *Child1, *Child2))
173379
return false;
174-
return IsStructurallyEquivalent(Context, CastE1->getSubExpr(),
175-
CastE2->getSubExpr());
176380
}
177-
// FIXME: Handle other kind of expressions!
178381
return true;
179382
}
180383

@@ -1790,6 +1993,15 @@ bool StructuralEquivalenceContext::IsEquivalent(QualType T1, QualType T2) {
17901993
return !Finish();
17911994
}
17921995

1996+
bool StructuralEquivalenceContext::IsEquivalent(Stmt *S1, Stmt *S2) {
1997+
assert(DeclsToCheck.empty());
1998+
assert(VisitedDecls.empty());
1999+
if (!::IsStructurallyEquivalent(*this, S1, S2))
2000+
return false;
2001+
2002+
return !Finish();
2003+
}
2004+
17932005
bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
17942006
// Check for equivalent described template.
17952007
TemplateDecl *Template1 = D1->getDescribedTemplate();

0 commit comments

Comments
 (0)