|
68 | 68 | #include "clang/AST/DeclObjC.h"
|
69 | 69 | #include "clang/AST/DeclTemplate.h"
|
70 | 70 | #include "clang/AST/ExprCXX.h"
|
| 71 | +#include "clang/AST/ExprConcepts.h" |
| 72 | +#include "clang/AST/ExprObjC.h" |
| 73 | +#include "clang/AST/ExprOpenMP.h" |
71 | 74 | #include "clang/AST/NestedNameSpecifier.h"
|
| 75 | +#include "clang/AST/StmtObjC.h" |
| 76 | +#include "clang/AST/StmtOpenMP.h" |
72 | 77 | #include "clang/AST/TemplateBase.h"
|
73 | 78 | #include "clang/AST/TemplateName.h"
|
74 | 79 | #include "clang/AST/Type.h"
|
@@ -149,32 +154,230 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
149 | 154 | return true;
|
150 | 155 | }
|
151 | 156 |
|
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 | + } |
157 | 177 |
|
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)) |
161 | 185 | 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) { |
162 | 215 | if (!IsStructurallyEquivalent(Context, DE1->getDeclName(),
|
163 | 216 | DE2->getDeclName()))
|
164 | 217 | return false;
|
165 | 218 | return IsStructurallyEquivalent(Context, DE1->getQualifier(),
|
166 | 219 | 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) |
170 | 377 | return false;
|
171 |
| - if (!IsStructurallyEquivalent(Context, CastE1->getType(), |
172 |
| - CastE2->getType())) |
| 378 | + if (!IsStructurallyEquivalent(Context, *Child1, *Child2)) |
173 | 379 | return false;
|
174 |
| - return IsStructurallyEquivalent(Context, CastE1->getSubExpr(), |
175 |
| - CastE2->getSubExpr()); |
176 | 380 | }
|
177 |
| - // FIXME: Handle other kind of expressions! |
178 | 381 | return true;
|
179 | 382 | }
|
180 | 383 |
|
@@ -1790,6 +1993,15 @@ bool StructuralEquivalenceContext::IsEquivalent(QualType T1, QualType T2) {
|
1790 | 1993 | return !Finish();
|
1791 | 1994 | }
|
1792 | 1995 |
|
| 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 | + |
1793 | 2005 | bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
|
1794 | 2006 | // Check for equivalent described template.
|
1795 | 2007 | TemplateDecl *Template1 = D1->getDescribedTemplate();
|
|
0 commit comments