llvm-project/clang/lib/AST/ASTStructuralEquivalence.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2087 lines
78 KiB
C++
Raw Normal View History

//===- ASTStructuralEquivalence.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implement StructuralEquivalenceContext class and helper functions
// for layout matching.
//
// The structural equivalence check could have been implemented as a parallel
// BFS on a pair of graphs. That must have been the original approach at the
// beginning.
// Let's consider this simple BFS algorithm from the `s` source:
// ```
// void bfs(Graph G, int s)
// {
// Queue<Integer> queue = new Queue<Integer>();
// marked[s] = true; // Mark the source
// queue.enqueue(s); // and put it on the queue.
// while (!q.isEmpty()) {
// int v = queue.dequeue(); // Remove next vertex from the queue.
// for (int w : G.adj(v))
// if (!marked[w]) // For every unmarked adjacent vertex,
// {
// marked[w] = true;
// queue.enqueue(w);
// }
// }
// }
// ```
// Indeed, it has it's queue, which holds pairs of nodes, one from each graph,
// this is the `DeclsToCheck` member. `VisitedDecls` plays the role of the
// marking (`marked`) functionality above, we use it to check whether we've
// already seen a pair of nodes.
//
// We put in the elements into the queue only in the toplevel decl check
// function:
// ```
// static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
// Decl *D1, Decl *D2);
// ```
// The `while` loop where we iterate over the children is implemented in
// `Finish()`. And `Finish` is called only from the two **member** functions
// which check the equivalency of two Decls or two Types. ASTImporter (and
// other clients) call only these functions.
//
// The `static` implementation functions are called from `Finish`, these push
// the children nodes to the queue via `static bool
// IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1,
// Decl *D2)`. So far so good, this is almost like the BFS. However, if we
// let a static implementation function to call `Finish` via another **member**
// function that means we end up with two nested while loops each of them
// working on the same queue. This is wrong and nobody can reason about it's
// doing. Thus, static implementation functions must not call the **member**
// functions.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
#include "clang/AST/DeclOpenMP.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
#include <utility>
using namespace clang;
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
QualType T1, QualType T2);
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2);
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const TemplateArgument &Arg1,
const TemplateArgument &Arg2);
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
NestedNameSpecifier *NNS1,
NestedNameSpecifier *NNS2);
static bool IsStructurallyEquivalent(const IdentifierInfo *Name1,
const IdentifierInfo *Name2);
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const DeclarationName Name1,
const DeclarationName Name2) {
if (Name1.getNameKind() != Name2.getNameKind())
return false;
switch (Name1.getNameKind()) {
case DeclarationName::Identifier:
return IsStructurallyEquivalent(Name1.getAsIdentifierInfo(),
Name2.getAsIdentifierInfo());
case DeclarationName::CXXConstructorName:
case DeclarationName::CXXDestructorName:
case DeclarationName::CXXConversionFunctionName:
return IsStructurallyEquivalent(Context, Name1.getCXXNameType(),
Name2.getCXXNameType());
case DeclarationName::CXXDeductionGuideName: {
if (!IsStructurallyEquivalent(
Context, Name1.getCXXDeductionGuideTemplate()->getDeclName(),
Name2.getCXXDeductionGuideTemplate()->getDeclName()))
return false;
return IsStructurallyEquivalent(Context,
Name1.getCXXDeductionGuideTemplate(),
Name2.getCXXDeductionGuideTemplate());
}
case DeclarationName::CXXOperatorName:
return Name1.getCXXOverloadedOperator() == Name2.getCXXOverloadedOperator();
case DeclarationName::CXXLiteralOperatorName:
return IsStructurallyEquivalent(Name1.getCXXLiteralIdentifier(),
Name2.getCXXLiteralIdentifier());
case DeclarationName::CXXUsingDirective:
return true; // FIXME When do we consider two using directives equal?
case DeclarationName::ObjCZeroArgSelector:
case DeclarationName::ObjCOneArgSelector:
case DeclarationName::ObjCMultiArgSelector:
return true; // FIXME
}
llvm_unreachable("Unhandled kind of DeclarationName");
return true;
}
namespace {
/// Encapsulates Stmt comparison logic.
class StmtComparer {
StructuralEquivalenceContext &Context;
// IsStmtEquivalent overloads. Each overload compares a specific statement
// and only has to compare the data that is specific to the specific statement
// class. Should only be called from TraverseStmt.
bool IsStmtEquivalent(const AddrLabelExpr *E1, const AddrLabelExpr *E2) {
return IsStructurallyEquivalent(Context, E1->getLabel(), E2->getLabel());
}
bool IsStmtEquivalent(const AtomicExpr *E1, const AtomicExpr *E2) {
return E1->getOp() == E2->getOp();
}
bool IsStmtEquivalent(const BinaryOperator *E1, const BinaryOperator *E2) {
return E1->getOpcode() == E2->getOpcode();
}
bool IsStmtEquivalent(const CallExpr *E1, const CallExpr *E2) {
// FIXME: IsStructurallyEquivalent requires non-const Decls.
Decl *Callee1 = const_cast<Decl *>(E1->getCalleeDecl());
Decl *Callee2 = const_cast<Decl *>(E2->getCalleeDecl());
// Compare whether both calls know their callee.
if (static_cast<bool>(Callee1) != static_cast<bool>(Callee2))
return false;
// Both calls have no callee, so nothing to do.
if (!static_cast<bool>(Callee1))
return true;
assert(Callee2);
return IsStructurallyEquivalent(Context, Callee1, Callee2);
}
bool IsStmtEquivalent(const CharacterLiteral *E1,
const CharacterLiteral *E2) {
return E1->getValue() == E2->getValue() && E1->getKind() == E2->getKind();
}
bool IsStmtEquivalent(const ChooseExpr *E1, const ChooseExpr *E2) {
return true; // Semantics only depend on children.
}
bool IsStmtEquivalent(const CompoundStmt *E1, const CompoundStmt *E2) {
// Number of children is actually checked by the generic children comparison
// code, but a CompoundStmt is one of the few statements where the number of
// children frequently differs and the number of statements is also always
// precomputed. Directly comparing the number of children here is thus
// just an optimization.
return E1->size() == E2->size();
}
bool IsStmtEquivalent(const DependentScopeDeclRefExpr *DE1,
const DependentScopeDeclRefExpr *DE2) {
if (!IsStructurallyEquivalent(Context, DE1->getDeclName(),
DE2->getDeclName()))
return false;
return IsStructurallyEquivalent(Context, DE1->getQualifier(),
DE2->getQualifier());
}
bool IsStmtEquivalent(const Expr *E1, const Expr *E2) {
return IsStructurallyEquivalent(Context, E1->getType(), E2->getType());
}
bool IsStmtEquivalent(const ExpressionTraitExpr *E1,
const ExpressionTraitExpr *E2) {
return E1->getTrait() == E2->getTrait() && E1->getValue() == E2->getValue();
}
bool IsStmtEquivalent(const FloatingLiteral *E1, const FloatingLiteral *E2) {
return E1->isExact() == E2->isExact() && E1->getValue() == E2->getValue();
}
bool IsStmtEquivalent(const GenericSelectionExpr *E1,
const GenericSelectionExpr *E2) {
for (auto Pair : zip_longest(E1->getAssocTypeSourceInfos(),
E2->getAssocTypeSourceInfos())) {
Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair);
Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair);
// Skip this case if there are a different number of associated types.
if (!Child1 || !Child2)
return false;
if (!IsStructurallyEquivalent(Context, (*Child1)->getType(),
(*Child2)->getType()))
return false;
}
return true;
}
bool IsStmtEquivalent(const ImplicitCastExpr *CastE1,
const ImplicitCastExpr *CastE2) {
return IsStructurallyEquivalent(Context, CastE1->getType(),
CastE2->getType());
}
bool IsStmtEquivalent(const IntegerLiteral *E1, const IntegerLiteral *E2) {
return E1->getValue() == E2->getValue();
}
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
bool IsStmtEquivalent(const MemberExpr *E1, const MemberExpr *E2) {
return IsStructurallyEquivalent(Context, E1->getFoundDecl(),
E2->getFoundDecl());
}
bool IsStmtEquivalent(const ObjCStringLiteral *E1,
const ObjCStringLiteral *E2) {
// Just wraps a StringLiteral child.
return true;
}
bool IsStmtEquivalent(const Stmt *S1, const Stmt *S2) { return true; }
bool IsStmtEquivalent(const SourceLocExpr *E1, const SourceLocExpr *E2) {
return E1->getIdentKind() == E2->getIdentKind();
}
bool IsStmtEquivalent(const StmtExpr *E1, const StmtExpr *E2) {
return E1->getTemplateDepth() == E2->getTemplateDepth();
}
bool IsStmtEquivalent(const StringLiteral *E1, const StringLiteral *E2) {
return E1->getBytes() == E2->getBytes();
}
bool IsStmtEquivalent(const SubstNonTypeTemplateParmExpr *E1,
const SubstNonTypeTemplateParmExpr *E2) {
return IsStructurallyEquivalent(Context, E1->getParameter(),
E2->getParameter());
}
bool IsStmtEquivalent(const SubstNonTypeTemplateParmPackExpr *E1,
const SubstNonTypeTemplateParmPackExpr *E2) {
return IsStructurallyEquivalent(Context, E1->getArgumentPack(),
E2->getArgumentPack());
}
bool IsStmtEquivalent(const TypeTraitExpr *E1, const TypeTraitExpr *E2) {
if (E1->getTrait() != E2->getTrait())
return false;
for (auto Pair : zip_longest(E1->getArgs(), E2->getArgs())) {
Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair);
Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair);
// Different number of args.
if (!Child1 || !Child2)
return false;
if (!IsStructurallyEquivalent(Context, (*Child1)->getType(),
(*Child2)->getType()))
return false;
}
return true;
}
bool IsStmtEquivalent(const UnaryExprOrTypeTraitExpr *E1,
const UnaryExprOrTypeTraitExpr *E2) {
if (E1->getKind() != E2->getKind())
return false;
return IsStructurallyEquivalent(Context, E1->getTypeOfArgument(),
E2->getTypeOfArgument());
}
bool IsStmtEquivalent(const UnaryOperator *E1, const UnaryOperator *E2) {
return E1->getOpcode() == E2->getOpcode();
}
bool IsStmtEquivalent(const VAArgExpr *E1, const VAArgExpr *E2) {
// Semantics only depend on children.
return true;
}
/// End point of the traversal chain.
bool TraverseStmt(const Stmt *S1, const Stmt *S2) { return true; }
// Create traversal methods that traverse the class hierarchy and return
// the accumulated result of the comparison. Each TraverseStmt overload
// calls the TraverseStmt overload of the parent class. For example,
// the TraverseStmt overload for 'BinaryOperator' calls the TraverseStmt
// overload of 'Expr' which then calls the overload for 'Stmt'.
#define STMT(CLASS, PARENT) \
bool TraverseStmt(const CLASS *S1, const CLASS *S2) { \
if (!TraverseStmt(static_cast<const PARENT *>(S1), \
static_cast<const PARENT *>(S2))) \
return false; \
return IsStmtEquivalent(S1, S2); \
}
#include "clang/AST/StmtNodes.inc"
public:
StmtComparer(StructuralEquivalenceContext &C) : Context(C) {}
/// Determine whether two statements are equivalent. The statements have to
/// be of the same kind. The children of the statements and their properties
/// are not compared by this function.
bool IsEquivalent(const Stmt *S1, const Stmt *S2) {
if (S1->getStmtClass() != S2->getStmtClass())
return false;
// Each TraverseStmt walks the class hierarchy from the leaf class to
// the root class 'Stmt' (e.g. 'BinaryOperator' -> 'Expr' -> 'Stmt'). Cast
// the Stmt we have here to its specific subclass so that we call the
// overload that walks the whole class hierarchy from leaf to root (e.g.,
// cast to 'BinaryOperator' so that 'Expr' and 'Stmt' is traversed).
switch (S1->getStmtClass()) {
case Stmt::NoStmtClass:
llvm_unreachable("Can't traverse NoStmtClass");
#define STMT(CLASS, PARENT) \
case Stmt::StmtClass::CLASS##Class: \
return TraverseStmt(static_cast<const CLASS *>(S1), \
static_cast<const CLASS *>(S2));
#define ABSTRACT_STMT(S)
#include "clang/AST/StmtNodes.inc"
}
llvm_unreachable("Invalid statement kind");
}
};
} // namespace
/// Determine structural equivalence of two statements.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const Stmt *S1, const Stmt *S2) {
if (!S1 || !S2)
return S1 == S2;
// Compare the statements itself.
StmtComparer Comparer(Context);
if (!Comparer.IsEquivalent(S1, S2))
return false;
// Iterate over the children of both statements and also compare them.
for (auto Pair : zip_longest(S1->children(), S2->children())) {
Optional<const Stmt *> Child1 = std::get<0>(Pair);
Optional<const Stmt *> Child2 = std::get<1>(Pair);
// One of the statements has a different amount of children than the other,
// so the statements can't be equivalent.
if (!Child1 || !Child2)
return false;
if (!IsStructurallyEquivalent(Context, *Child1, *Child2))
return false;
}
return true;
}
/// Determine whether two identifiers are equivalent.
static bool IsStructurallyEquivalent(const IdentifierInfo *Name1,
const IdentifierInfo *Name2) {
if (!Name1 || !Name2)
return Name1 == Name2;
return Name1->getName() == Name2->getName();
}
/// Determine whether two nested-name-specifiers are equivalent.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
NestedNameSpecifier *NNS1,
NestedNameSpecifier *NNS2) {
if (NNS1->getKind() != NNS2->getKind())
return false;
NestedNameSpecifier *Prefix1 = NNS1->getPrefix(),
*Prefix2 = NNS2->getPrefix();
if ((bool)Prefix1 != (bool)Prefix2)
return false;
if (Prefix1)
if (!IsStructurallyEquivalent(Context, Prefix1, Prefix2))
return false;
switch (NNS1->getKind()) {
case NestedNameSpecifier::Identifier:
return IsStructurallyEquivalent(NNS1->getAsIdentifier(),
NNS2->getAsIdentifier());
case NestedNameSpecifier::Namespace:
return IsStructurallyEquivalent(Context, NNS1->getAsNamespace(),
NNS2->getAsNamespace());
case NestedNameSpecifier::NamespaceAlias:
return IsStructurallyEquivalent(Context, NNS1->getAsNamespaceAlias(),
NNS2->getAsNamespaceAlias());
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
return IsStructurallyEquivalent(Context, QualType(NNS1->getAsType(), 0),
QualType(NNS2->getAsType(), 0));
case NestedNameSpecifier::Global:
return true;
case NestedNameSpecifier::Super:
return IsStructurallyEquivalent(Context, NNS1->getAsRecordDecl(),
NNS2->getAsRecordDecl());
}
return false;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const TemplateName &N1,
const TemplateName &N2) {
TemplateDecl *TemplateDeclN1 = N1.getAsTemplateDecl();
TemplateDecl *TemplateDeclN2 = N2.getAsTemplateDecl();
if (TemplateDeclN1 && TemplateDeclN2) {
if (!IsStructurallyEquivalent(Context, TemplateDeclN1, TemplateDeclN2))
return false;
// If the kind is different we compare only the template decl.
if (N1.getKind() != N2.getKind())
return true;
} else if (TemplateDeclN1 || TemplateDeclN2)
return false;
else if (N1.getKind() != N2.getKind())
return false;
// Check for special case incompatibilities.
switch (N1.getKind()) {
case TemplateName::OverloadedTemplate: {
OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(),
*OS2 = N2.getAsOverloadedTemplate();
OverloadedTemplateStorage::iterator I1 = OS1->begin(), I2 = OS2->begin(),
E1 = OS1->end(), E2 = OS2->end();
for (; I1 != E1 && I2 != E2; ++I1, ++I2)
if (!IsStructurallyEquivalent(Context, *I1, *I2))
return false;
return I1 == E1 && I2 == E2;
}
case TemplateName::AssumedTemplate: {
AssumedTemplateStorage *TN1 = N1.getAsAssumedTemplateName(),
*TN2 = N1.getAsAssumedTemplateName();
return TN1->getDeclName() == TN2->getDeclName();
}
case TemplateName::DependentTemplate: {
DependentTemplateName *DN1 = N1.getAsDependentTemplateName(),
*DN2 = N2.getAsDependentTemplateName();
if (!IsStructurallyEquivalent(Context, DN1->getQualifier(),
DN2->getQualifier()))
return false;
if (DN1->isIdentifier() && DN2->isIdentifier())
return IsStructurallyEquivalent(DN1->getIdentifier(),
DN2->getIdentifier());
else if (DN1->isOverloadedOperator() && DN2->isOverloadedOperator())
return DN1->getOperator() == DN2->getOperator();
return false;
}
case TemplateName::SubstTemplateTemplateParmPack: {
SubstTemplateTemplateParmPackStorage
*P1 = N1.getAsSubstTemplateTemplateParmPack(),
*P2 = N2.getAsSubstTemplateTemplateParmPack();
return IsStructurallyEquivalent(Context, P1->getArgumentPack(),
P2->getArgumentPack()) &&
IsStructurallyEquivalent(Context, P1->getParameterPack(),
P2->getParameterPack());
}
case TemplateName::Template:
case TemplateName::QualifiedTemplate:
case TemplateName::SubstTemplateTemplateParm:
// It is sufficient to check value of getAsTemplateDecl.
break;
}
return true;
}
/// Determine whether two template arguments are equivalent.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const TemplateArgument &Arg1,
const TemplateArgument &Arg2) {
if (Arg1.getKind() != Arg2.getKind())
return false;
switch (Arg1.getKind()) {
case TemplateArgument::Null:
return true;
case TemplateArgument::Type:
return IsStructurallyEquivalent(Context, Arg1.getAsType(), Arg2.getAsType());
case TemplateArgument::Integral:
if (!IsStructurallyEquivalent(Context, Arg1.getIntegralType(),
Arg2.getIntegralType()))
return false;
return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
Arg2.getAsIntegral());
case TemplateArgument::Declaration:
return IsStructurallyEquivalent(Context, Arg1.getAsDecl(), Arg2.getAsDecl());
case TemplateArgument::NullPtr:
return true; // FIXME: Is this correct?
case TemplateArgument::Template:
return IsStructurallyEquivalent(Context, Arg1.getAsTemplate(),
Arg2.getAsTemplate());
case TemplateArgument::TemplateExpansion:
return IsStructurallyEquivalent(Context,
Arg1.getAsTemplateOrTemplatePattern(),
Arg2.getAsTemplateOrTemplatePattern());
case TemplateArgument::Expression:
return IsStructurallyEquivalent(Context, Arg1.getAsExpr(),
Arg2.getAsExpr());
case TemplateArgument::Pack:
if (Arg1.pack_size() != Arg2.pack_size())
return false;
for (unsigned I = 0, N = Arg1.pack_size(); I != N; ++I)
if (!IsStructurallyEquivalent(Context, Arg1.pack_begin()[I],
Arg2.pack_begin()[I]))
return false;
return true;
}
llvm_unreachable("Invalid template argument kind");
}
/// Determine structural equivalence for the common part of array
/// types.
static bool IsArrayStructurallyEquivalent(StructuralEquivalenceContext &Context,
const ArrayType *Array1,
const ArrayType *Array2) {
if (!IsStructurallyEquivalent(Context, Array1->getElementType(),
Array2->getElementType()))
return false;
if (Array1->getSizeModifier() != Array2->getSizeModifier())
return false;
if (Array1->getIndexTypeQualifiers() != Array2->getIndexTypeQualifiers())
return false;
return true;
}
/// Determine structural equivalence based on the ExtInfo of functions. This
/// is inspired by ASTContext::mergeFunctionTypes(), we compare calling
/// conventions bits but must not compare some other bits.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FunctionType::ExtInfo EI1,
FunctionType::ExtInfo EI2) {
// Compatible functions must have compatible calling conventions.
if (EI1.getCC() != EI2.getCC())
return false;
// Regparm is part of the calling convention.
if (EI1.getHasRegParm() != EI2.getHasRegParm())
return false;
if (EI1.getRegParm() != EI2.getRegParm())
return false;
if (EI1.getProducesResult() != EI2.getProducesResult())
return false;
if (EI1.getNoCallerSavedRegs() != EI2.getNoCallerSavedRegs())
return false;
if (EI1.getNoCfCheck() != EI2.getNoCfCheck())
return false;
return true;
}
/// Check the equivalence of exception specifications.
static bool IsEquivalentExceptionSpec(StructuralEquivalenceContext &Context,
const FunctionProtoType *Proto1,
const FunctionProtoType *Proto2) {
auto Spec1 = Proto1->getExceptionSpecType();
auto Spec2 = Proto2->getExceptionSpecType();
if (isUnresolvedExceptionSpec(Spec1) || isUnresolvedExceptionSpec(Spec2))
return true;
if (Spec1 != Spec2)
return false;
if (Spec1 == EST_Dynamic) {
if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
return false;
for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Proto1->getExceptionType(I),
Proto2->getExceptionType(I)))
return false;
}
} else if (isComputedNoexcept(Spec1)) {
if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(),
Proto2->getNoexceptExpr()))
return false;
}
return true;
}
/// Determine structural equivalence of two types.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
QualType T1, QualType T2) {
if (T1.isNull() || T2.isNull())
return T1.isNull() && T2.isNull();
QualType OrigT1 = T1;
QualType OrigT2 = T2;
if (!Context.StrictTypeSpelling) {
// We aren't being strict about token-to-token equivalence of types,
// so map down to the canonical type.
T1 = Context.FromCtx.getCanonicalType(T1);
T2 = Context.ToCtx.getCanonicalType(T2);
}
if (T1.getQualifiers() != T2.getQualifiers())
return false;
Type::TypeClass TC = T1->getTypeClass();
if (T1->getTypeClass() != T2->getTypeClass()) {
// Compare function types with prototypes vs. without prototypes as if
// both did not have prototypes.
if (T1->getTypeClass() == Type::FunctionProto &&
T2->getTypeClass() == Type::FunctionNoProto)
TC = Type::FunctionNoProto;
else if (T1->getTypeClass() == Type::FunctionNoProto &&
T2->getTypeClass() == Type::FunctionProto)
TC = Type::FunctionNoProto;
else
return false;
}
switch (TC) {
case Type::Builtin:
// FIXME: Deal with Char_S/Char_U.
if (cast<BuiltinType>(T1)->getKind() != cast<BuiltinType>(T2)->getKind())
return false;
break;
case Type::Complex:
if (!IsStructurallyEquivalent(Context,
cast<ComplexType>(T1)->getElementType(),
cast<ComplexType>(T2)->getElementType()))
return false;
break;
case Type::Adjusted:
case Type::Decayed:
if (!IsStructurallyEquivalent(Context,
cast<AdjustedType>(T1)->getOriginalType(),
cast<AdjustedType>(T2)->getOriginalType()))
return false;
break;
case Type::Pointer:
if (!IsStructurallyEquivalent(Context,
cast<PointerType>(T1)->getPointeeType(),
cast<PointerType>(T2)->getPointeeType()))
return false;
break;
case Type::BlockPointer:
if (!IsStructurallyEquivalent(Context,
cast<BlockPointerType>(T1)->getPointeeType(),
cast<BlockPointerType>(T2)->getPointeeType()))
return false;
break;
case Type::LValueReference:
case Type::RValueReference: {
const auto *Ref1 = cast<ReferenceType>(T1);
const auto *Ref2 = cast<ReferenceType>(T2);
if (Ref1->isSpelledAsLValue() != Ref2->isSpelledAsLValue())
return false;
if (Ref1->isInnerRef() != Ref2->isInnerRef())
return false;
if (!IsStructurallyEquivalent(Context, Ref1->getPointeeTypeAsWritten(),
Ref2->getPointeeTypeAsWritten()))
return false;
break;
}
case Type::MemberPointer: {
const auto *MemPtr1 = cast<MemberPointerType>(T1);
const auto *MemPtr2 = cast<MemberPointerType>(T2);
if (!IsStructurallyEquivalent(Context, MemPtr1->getPointeeType(),
MemPtr2->getPointeeType()))
return false;
if (!IsStructurallyEquivalent(Context, QualType(MemPtr1->getClass(), 0),
QualType(MemPtr2->getClass(), 0)))
return false;
break;
}
case Type::ConstantArray: {
const auto *Array1 = cast<ConstantArrayType>(T1);
const auto *Array2 = cast<ConstantArrayType>(T2);
if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize()))
return false;
if (!IsArrayStructurallyEquivalent(Context, Array1, Array2))
return false;
break;
}
case Type::IncompleteArray:
if (!IsArrayStructurallyEquivalent(Context, cast<ArrayType>(T1),
cast<ArrayType>(T2)))
return false;
break;
case Type::VariableArray: {
const auto *Array1 = cast<VariableArrayType>(T1);
const auto *Array2 = cast<VariableArrayType>(T2);
if (!IsStructurallyEquivalent(Context, Array1->getSizeExpr(),
Array2->getSizeExpr()))
return false;
if (!IsArrayStructurallyEquivalent(Context, Array1, Array2))
return false;
break;
}
case Type::DependentSizedArray: {
const auto *Array1 = cast<DependentSizedArrayType>(T1);
const auto *Array2 = cast<DependentSizedArrayType>(T2);
if (!IsStructurallyEquivalent(Context, Array1->getSizeExpr(),
Array2->getSizeExpr()))
return false;
if (!IsArrayStructurallyEquivalent(Context, Array1, Array2))
return false;
break;
}
case Type::DependentAddressSpace: {
const auto *DepAddressSpace1 = cast<DependentAddressSpaceType>(T1);
const auto *DepAddressSpace2 = cast<DependentAddressSpaceType>(T2);
if (!IsStructurallyEquivalent(Context, DepAddressSpace1->getAddrSpaceExpr(),
DepAddressSpace2->getAddrSpaceExpr()))
return false;
if (!IsStructurallyEquivalent(Context, DepAddressSpace1->getPointeeType(),
DepAddressSpace2->getPointeeType()))
return false;
break;
}
case Type::DependentSizedExtVector: {
const auto *Vec1 = cast<DependentSizedExtVectorType>(T1);
const auto *Vec2 = cast<DependentSizedExtVectorType>(T2);
if (!IsStructurallyEquivalent(Context, Vec1->getSizeExpr(),
Vec2->getSizeExpr()))
return false;
if (!IsStructurallyEquivalent(Context, Vec1->getElementType(),
Vec2->getElementType()))
return false;
break;
}
case Type::DependentVector: {
const auto *Vec1 = cast<DependentVectorType>(T1);
const auto *Vec2 = cast<DependentVectorType>(T2);
if (Vec1->getVectorKind() != Vec2->getVectorKind())
return false;
if (!IsStructurallyEquivalent(Context, Vec1->getSizeExpr(),
Vec2->getSizeExpr()))
return false;
if (!IsStructurallyEquivalent(Context, Vec1->getElementType(),
Vec2->getElementType()))
return false;
break;
}
case Type::Vector:
case Type::ExtVector: {
const auto *Vec1 = cast<VectorType>(T1);
const auto *Vec2 = cast<VectorType>(T2);
if (!IsStructurallyEquivalent(Context, Vec1->getElementType(),
Vec2->getElementType()))
return false;
if (Vec1->getNumElements() != Vec2->getNumElements())
return false;
if (Vec1->getVectorKind() != Vec2->getVectorKind())
return false;
break;
}
case Type::DependentSizedMatrix: {
const DependentSizedMatrixType *Mat1 = cast<DependentSizedMatrixType>(T1);
const DependentSizedMatrixType *Mat2 = cast<DependentSizedMatrixType>(T2);
// The element types, row and column expressions must be structurally
// equivalent.
if (!IsStructurallyEquivalent(Context, Mat1->getRowExpr(),
Mat2->getRowExpr()) ||
!IsStructurallyEquivalent(Context, Mat1->getColumnExpr(),
Mat2->getColumnExpr()) ||
!IsStructurallyEquivalent(Context, Mat1->getElementType(),
Mat2->getElementType()))
return false;
break;
}
case Type::ConstantMatrix: {
const ConstantMatrixType *Mat1 = cast<ConstantMatrixType>(T1);
const ConstantMatrixType *Mat2 = cast<ConstantMatrixType>(T2);
// The element types must be structurally equivalent and the number of rows
// and columns must match.
if (!IsStructurallyEquivalent(Context, Mat1->getElementType(),
Mat2->getElementType()) ||
Mat1->getNumRows() != Mat2->getNumRows() ||
Mat1->getNumColumns() != Mat2->getNumColumns())
return false;
break;
}
case Type::FunctionProto: {
const auto *Proto1 = cast<FunctionProtoType>(T1);
const auto *Proto2 = cast<FunctionProtoType>(T2);
if (Proto1->getNumParams() != Proto2->getNumParams())
return false;
for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Proto1->getParamType(I),
Proto2->getParamType(I)))
return false;
}
if (Proto1->isVariadic() != Proto2->isVariadic())
return false;
if (Proto1->getMethodQuals() != Proto2->getMethodQuals())
return false;
// Check exceptions, this information is lost in canonical type.
const auto *OrigProto1 =
cast<FunctionProtoType>(OrigT1.getDesugaredType(Context.FromCtx));
const auto *OrigProto2 =
cast<FunctionProtoType>(OrigT2.getDesugaredType(Context.ToCtx));
if (!IsEquivalentExceptionSpec(Context, OrigProto1, OrigProto2))
return false;
// Fall through to check the bits common with FunctionNoProtoType.
LLVM_FALLTHROUGH;
}
case Type::FunctionNoProto: {
const auto *Function1 = cast<FunctionType>(T1);
const auto *Function2 = cast<FunctionType>(T2);
if (!IsStructurallyEquivalent(Context, Function1->getReturnType(),
Function2->getReturnType()))
return false;
if (!IsStructurallyEquivalent(Context, Function1->getExtInfo(),
Function2->getExtInfo()))
return false;
break;
}
case Type::UnresolvedUsing:
if (!IsStructurallyEquivalent(Context,
cast<UnresolvedUsingType>(T1)->getDecl(),
cast<UnresolvedUsingType>(T2)->getDecl()))
return false;
break;
case Type::Attributed:
if (!IsStructurallyEquivalent(Context,
cast<AttributedType>(T1)->getModifiedType(),
cast<AttributedType>(T2)->getModifiedType()))
return false;
if (!IsStructurallyEquivalent(
Context, cast<AttributedType>(T1)->getEquivalentType(),
cast<AttributedType>(T2)->getEquivalentType()))
return false;
break;
case Type::Paren:
if (!IsStructurallyEquivalent(Context, cast<ParenType>(T1)->getInnerType(),
cast<ParenType>(T2)->getInnerType()))
return false;
break;
case Type::MacroQualified:
if (!IsStructurallyEquivalent(
Context, cast<MacroQualifiedType>(T1)->getUnderlyingType(),
cast<MacroQualifiedType>(T2)->getUnderlyingType()))
return false;
break;
case Type::Typedef:
if (!IsStructurallyEquivalent(Context, cast<TypedefType>(T1)->getDecl(),
cast<TypedefType>(T2)->getDecl()))
return false;
break;
case Type::TypeOfExpr:
if (!IsStructurallyEquivalent(
Context, cast<TypeOfExprType>(T1)->getUnderlyingExpr(),
cast<TypeOfExprType>(T2)->getUnderlyingExpr()))
return false;
break;
case Type::TypeOf:
if (!IsStructurallyEquivalent(Context,
cast<TypeOfType>(T1)->getUnderlyingType(),
cast<TypeOfType>(T2)->getUnderlyingType()))
return false;
break;
case Type::UnaryTransform:
if (!IsStructurallyEquivalent(
Context, cast<UnaryTransformType>(T1)->getUnderlyingType(),
cast<UnaryTransformType>(T2)->getUnderlyingType()))
return false;
break;
case Type::Decltype:
if (!IsStructurallyEquivalent(Context,
cast<DecltypeType>(T1)->getUnderlyingExpr(),
cast<DecltypeType>(T2)->getUnderlyingExpr()))
return false;
break;
case Type::Auto: {
auto *Auto1 = cast<AutoType>(T1);
auto *Auto2 = cast<AutoType>(T2);
if (!IsStructurallyEquivalent(Context, Auto1->getDeducedType(),
Auto2->getDeducedType()))
return false;
if (Auto1->isConstrained() != Auto2->isConstrained())
return false;
if (Auto1->isConstrained()) {
if (Auto1->getTypeConstraintConcept() !=
Auto2->getTypeConstraintConcept())
return false;
ArrayRef<TemplateArgument> Auto1Args =
Auto1->getTypeConstraintArguments();
ArrayRef<TemplateArgument> Auto2Args =
Auto2->getTypeConstraintArguments();
if (Auto1Args.size() != Auto2Args.size())
return false;
for (unsigned I = 0, N = Auto1Args.size(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Auto1Args[I], Auto2Args[I]))
return false;
}
}
break;
}
case Type::DeducedTemplateSpecialization: {
const auto *DT1 = cast<DeducedTemplateSpecializationType>(T1);
const auto *DT2 = cast<DeducedTemplateSpecializationType>(T2);
if (!IsStructurallyEquivalent(Context, DT1->getTemplateName(),
DT2->getTemplateName()))
return false;
if (!IsStructurallyEquivalent(Context, DT1->getDeducedType(),
DT2->getDeducedType()))
return false;
break;
}
case Type::Record:
case Type::Enum:
if (!IsStructurallyEquivalent(Context, cast<TagType>(T1)->getDecl(),
cast<TagType>(T2)->getDecl()))
return false;
break;
case Type::TemplateTypeParm: {
const auto *Parm1 = cast<TemplateTypeParmType>(T1);
const auto *Parm2 = cast<TemplateTypeParmType>(T2);
if (Parm1->getDepth() != Parm2->getDepth())
return false;
if (Parm1->getIndex() != Parm2->getIndex())
return false;
if (Parm1->isParameterPack() != Parm2->isParameterPack())
return false;
// Names of template type parameters are never significant.
break;
}
case Type::SubstTemplateTypeParm: {
const auto *Subst1 = cast<SubstTemplateTypeParmType>(T1);
const auto *Subst2 = cast<SubstTemplateTypeParmType>(T2);
if (!IsStructurallyEquivalent(Context,
QualType(Subst1->getReplacedParameter(), 0),
QualType(Subst2->getReplacedParameter(), 0)))
return false;
if (!IsStructurallyEquivalent(Context, Subst1->getReplacementType(),
Subst2->getReplacementType()))
return false;
break;
}
case Type::SubstTemplateTypeParmPack: {
const auto *Subst1 = cast<SubstTemplateTypeParmPackType>(T1);
const auto *Subst2 = cast<SubstTemplateTypeParmPackType>(T2);
if (!IsStructurallyEquivalent(Context,
QualType(Subst1->getReplacedParameter(), 0),
QualType(Subst2->getReplacedParameter(), 0)))
return false;
if (!IsStructurallyEquivalent(Context, Subst1->getArgumentPack(),
Subst2->getArgumentPack()))
return false;
break;
}
case Type::TemplateSpecialization: {
const auto *Spec1 = cast<TemplateSpecializationType>(T1);
const auto *Spec2 = cast<TemplateSpecializationType>(T2);
if (!IsStructurallyEquivalent(Context, Spec1->getTemplateName(),
Spec2->getTemplateName()))
return false;
if (Spec1->getNumArgs() != Spec2->getNumArgs())
return false;
for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Spec1->getArg(I),
Spec2->getArg(I)))
return false;
}
break;
}
case Type::Elaborated: {
const auto *Elab1 = cast<ElaboratedType>(T1);
const auto *Elab2 = cast<ElaboratedType>(T2);
// CHECKME: what if a keyword is ETK_None or ETK_typename ?
if (Elab1->getKeyword() != Elab2->getKeyword())
return false;
if (!IsStructurallyEquivalent(Context, Elab1->getQualifier(),
Elab2->getQualifier()))
return false;
if (!IsStructurallyEquivalent(Context, Elab1->getNamedType(),
Elab2->getNamedType()))
return false;
break;
}
case Type::InjectedClassName: {
const auto *Inj1 = cast<InjectedClassNameType>(T1);
const auto *Inj2 = cast<InjectedClassNameType>(T2);
if (!IsStructurallyEquivalent(Context,
Inj1->getInjectedSpecializationType(),
Inj2->getInjectedSpecializationType()))
return false;
break;
}
case Type::DependentName: {
const auto *Typename1 = cast<DependentNameType>(T1);
const auto *Typename2 = cast<DependentNameType>(T2);
if (!IsStructurallyEquivalent(Context, Typename1->getQualifier(),
Typename2->getQualifier()))
return false;
if (!IsStructurallyEquivalent(Typename1->getIdentifier(),
Typename2->getIdentifier()))
return false;
break;
}
case Type::DependentTemplateSpecialization: {
const auto *Spec1 = cast<DependentTemplateSpecializationType>(T1);
const auto *Spec2 = cast<DependentTemplateSpecializationType>(T2);
if (!IsStructurallyEquivalent(Context, Spec1->getQualifier(),
Spec2->getQualifier()))
return false;
if (!IsStructurallyEquivalent(Spec1->getIdentifier(),
Spec2->getIdentifier()))
return false;
if (Spec1->getNumArgs() != Spec2->getNumArgs())
return false;
for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Spec1->getArg(I),
Spec2->getArg(I)))
return false;
}
break;
}
case Type::PackExpansion:
if (!IsStructurallyEquivalent(Context,
cast<PackExpansionType>(T1)->getPattern(),
cast<PackExpansionType>(T2)->getPattern()))
return false;
break;
case Type::ObjCInterface: {
const auto *Iface1 = cast<ObjCInterfaceType>(T1);
const auto *Iface2 = cast<ObjCInterfaceType>(T2);
if (!IsStructurallyEquivalent(Context, Iface1->getDecl(),
Iface2->getDecl()))
return false;
break;
}
case Type::ObjCTypeParam: {
const auto *Obj1 = cast<ObjCTypeParamType>(T1);
const auto *Obj2 = cast<ObjCTypeParamType>(T2);
if (!IsStructurallyEquivalent(Context, Obj1->getDecl(), Obj2->getDecl()))
return false;
if (Obj1->getNumProtocols() != Obj2->getNumProtocols())
return false;
for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Obj1->getProtocol(I),
Obj2->getProtocol(I)))
return false;
}
break;
}
case Type::ObjCObject: {
const auto *Obj1 = cast<ObjCObjectType>(T1);
const auto *Obj2 = cast<ObjCObjectType>(T2);
if (!IsStructurallyEquivalent(Context, Obj1->getBaseType(),
Obj2->getBaseType()))
return false;
if (Obj1->getNumProtocols() != Obj2->getNumProtocols())
return false;
for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) {
if (!IsStructurallyEquivalent(Context, Obj1->getProtocol(I),
Obj2->getProtocol(I)))
return false;
}
break;
}
case Type::ObjCObjectPointer: {
const auto *Ptr1 = cast<ObjCObjectPointerType>(T1);
const auto *Ptr2 = cast<ObjCObjectPointerType>(T2);
if (!IsStructurallyEquivalent(Context, Ptr1->getPointeeType(),
Ptr2->getPointeeType()))
return false;
break;
}
case Type::Atomic:
if (!IsStructurallyEquivalent(Context, cast<AtomicType>(T1)->getValueType(),
cast<AtomicType>(T2)->getValueType()))
return false;
break;
case Type::Pipe:
if (!IsStructurallyEquivalent(Context, cast<PipeType>(T1)->getElementType(),
cast<PipeType>(T2)->getElementType()))
return false;
break;
case Type::ExtInt: {
const auto *Int1 = cast<ExtIntType>(T1);
const auto *Int2 = cast<ExtIntType>(T2);
if (Int1->isUnsigned() != Int2->isUnsigned() ||
Int1->getNumBits() != Int2->getNumBits())
return false;
break;
}
case Type::DependentExtInt: {
const auto *Int1 = cast<DependentExtIntType>(T1);
const auto *Int2 = cast<DependentExtIntType>(T2);
if (Int1->isUnsigned() != Int2->isUnsigned() ||
!IsStructurallyEquivalent(Context, Int1->getNumBitsExpr(),
Int2->getNumBitsExpr()))
return false;
}
} // end switch
return true;
}
/// Determine structural equivalence of two fields.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FieldDecl *Field1, FieldDecl *Field2) {
const auto *Owner2 = cast<RecordDecl>(Field2->getDeclContext());
// For anonymous structs/unions, match up the anonymous struct/union type
// declarations directly, so that we don't go off searching for anonymous
// types
if (Field1->isAnonymousStructOrUnion() &&
Field2->isAnonymousStructOrUnion()) {
RecordDecl *D1 = Field1->getType()->castAs<RecordType>()->getDecl();
RecordDecl *D2 = Field2->getType()->castAs<RecordType>()->getDecl();
return IsStructurallyEquivalent(Context, D1, D2);
}
// Check for equivalent field names.
IdentifierInfo *Name1 = Field1->getIdentifier();
IdentifierInfo *Name2 = Field2->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2)) {
if (Context.Complain) {
Context.Diag2(
Owner2->getLocation(),
Context.getApplicableDiagnostic(diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(Owner2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field_name)
<< Field2->getDeclName();
Context.Diag1(Field1->getLocation(), diag::note_odr_field_name)
<< Field1->getDeclName();
}
return false;
}
if (!IsStructurallyEquivalent(Context, Field1->getType(),
Field2->getType())) {
if (Context.Complain) {
Context.Diag2(
Owner2->getLocation(),
Context.getApplicableDiagnostic(diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(Owner2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field)
<< Field2->getDeclName() << Field2->getType();
Context.Diag1(Field1->getLocation(), diag::note_odr_field)
<< Field1->getDeclName() << Field1->getType();
}
return false;
}
if (Field1->isBitField())
return IsStructurallyEquivalent(Context, Field1->getBitWidth(),
Field2->getBitWidth());
return true;
}
/// Determine structural equivalence of two methods.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
CXXMethodDecl *Method1,
CXXMethodDecl *Method2) {
bool PropertiesEqual =
Method1->getDeclKind() == Method2->getDeclKind() &&
Method1->getRefQualifier() == Method2->getRefQualifier() &&
Method1->getAccess() == Method2->getAccess() &&
Method1->getOverloadedOperator() == Method2->getOverloadedOperator() &&
Method1->isStatic() == Method2->isStatic() &&
Method1->isConst() == Method2->isConst() &&
Method1->isVolatile() == Method2->isVolatile() &&
Method1->isVirtual() == Method2->isVirtual() &&
Method1->isPure() == Method2->isPure() &&
Method1->isDefaulted() == Method2->isDefaulted() &&
Method1->isDeleted() == Method2->isDeleted();
if (!PropertiesEqual)
return false;
// FIXME: Check for 'final'.
if (auto *Constructor1 = dyn_cast<CXXConstructorDecl>(Method1)) {
auto *Constructor2 = cast<CXXConstructorDecl>(Method2);
if (!Constructor1->getExplicitSpecifier().isEquivalent(
Constructor2->getExplicitSpecifier()))
return false;
}
if (auto *Conversion1 = dyn_cast<CXXConversionDecl>(Method1)) {
auto *Conversion2 = cast<CXXConversionDecl>(Method2);
if (!Conversion1->getExplicitSpecifier().isEquivalent(
Conversion2->getExplicitSpecifier()))
return false;
if (!IsStructurallyEquivalent(Context, Conversion1->getConversionType(),
Conversion2->getConversionType()))
return false;
}
const IdentifierInfo *Name1 = Method1->getIdentifier();
const IdentifierInfo *Name2 = Method2->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2)) {
return false;
// TODO: Names do not match, add warning like at check for FieldDecl.
}
// Check the prototypes.
if (!::IsStructurallyEquivalent(Context,
Method1->getType(), Method2->getType()))
return false;
return true;
}
/// Determine structural equivalence of two lambda classes.
static bool
IsStructurallyEquivalentLambdas(StructuralEquivalenceContext &Context,
CXXRecordDecl *D1, CXXRecordDecl *D2) {
assert(D1->isLambda() && D2->isLambda() &&
"Must be called on lambda classes");
if (!IsStructurallyEquivalent(Context, D1->getLambdaCallOperator(),
D2->getLambdaCallOperator()))
return false;
return true;
}
/// Determine structural equivalence of two records.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
RecordDecl *D1, RecordDecl *D2) {
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
// Check for equivalent structure names.
IdentifierInfo *Name1 = D1->getIdentifier();
if (!Name1 && D1->getTypedefNameForAnonDecl())
Name1 = D1->getTypedefNameForAnonDecl()->getIdentifier();
IdentifierInfo *Name2 = D2->getIdentifier();
if (!Name2 && D2->getTypedefNameForAnonDecl())
Name2 = D2->getTypedefNameForAnonDecl()->getIdentifier();
if (!IsStructurallyEquivalent(Name1, Name2))
return false;
if (D1->isUnion() != D2->isUnion()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here)
<< D1->getDeclName() << (unsigned)D1->getTagKind();
}
return false;
}
if (!D1->getDeclName() && !D2->getDeclName()) {
// If both anonymous structs/unions are in a record context, make sure
// they occur in the same location in the context records.
if (Optional<unsigned> Index1 =
StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(D1)) {
if (Optional<unsigned> Index2 =
StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(
D2)) {
if (*Index1 != *Index2)
return false;
}
}
}
// If both declarations are class template specializations, we know
// the ODR applies, so check the template and template arguments.
const auto *Spec1 = dyn_cast<ClassTemplateSpecializationDecl>(D1);
const auto *Spec2 = dyn_cast<ClassTemplateSpecializationDecl>(D2);
if (Spec1 && Spec2) {
// Check that the specialized templates are the same.
if (!IsStructurallyEquivalent(Context, Spec1->getSpecializedTemplate(),
Spec2->getSpecializedTemplate()))
return false;
// Check that the template arguments are the same.
if (Spec1->getTemplateArgs().size() != Spec2->getTemplateArgs().size())
return false;
for (unsigned I = 0, N = Spec1->getTemplateArgs().size(); I != N; ++I)
if (!IsStructurallyEquivalent(Context, Spec1->getTemplateArgs().get(I),
Spec2->getTemplateArgs().get(I)))
return false;
}
// If one is a class template specialization and the other is not, these
// structures are different.
else if (Spec1 || Spec2)
return false;
// Compare the definitions of these two records. If either or both are
// incomplete (i.e. it is a forward decl), we assume that they are
// equivalent.
D1 = D1->getDefinition();
D2 = D2->getDefinition();
if (!D1 || !D2)
return true;
[ASTImporter] Refactor Decl creation Summary: Generalize the creation of Decl nodes during Import. With this patch we do the same things after and before a new AST node is created (::Create) The import logic should be really simple, we create the node, then we mark that as imported, then we recursively import the parts for that node and then set them on that node. However, the AST is actually a graph, so we have to handle circles. If we mark something as imported (`MapImported()`) then we return with the corresponding `To` decl whenever we want to import that node again, this way circles are handled. In order to make this algorithm work we must ensure things, which are handled in the generic CreateDecl<> template: * There are no `Import()` calls in between any node creation (::Create) and the `MapImported()` call. * Before actually creating an AST node (::Create), we must check if the Node had been imported already, if yes then return with that one. One very important case for this is connected to templates: we may start an import both from the templated decl of a template and from the template itself. Now, the virtual `Imported` function is called in `ASTImporter::Impor(Decl *)`, but only once, when the `Decl` is imported. One point of this refactor is to separate responsibilities. The original `Imported()` had 3 responsibilities: - notify subclasses when an import happened - register the decl into `ImportedDecls` - initialise the Decl (set attributes, etc) Now all of these are in separate functions: - `Imported` - `MapImported` - `InitializeImportedDecl` I tried to check all the clients, I executed tests for `ExternalASTMerger.cpp` and some unittests for lldb. Reviewers: a.sidorin, balazske, xazax.hun, r.stahl Subscribers: rnkovacs, dkrupp, cfe-commits Differential Revision: https://reviews.llvm.org/D47632 llvm-svn: 336896
2018-07-12 17:42:05 +08:00
// If any of the records has external storage and we do a minimal check (or
// AST import) we assume they are equivalent. (If we didn't have this
[ASTImporter] Refactor Decl creation Summary: Generalize the creation of Decl nodes during Import. With this patch we do the same things after and before a new AST node is created (::Create) The import logic should be really simple, we create the node, then we mark that as imported, then we recursively import the parts for that node and then set them on that node. However, the AST is actually a graph, so we have to handle circles. If we mark something as imported (`MapImported()`) then we return with the corresponding `To` decl whenever we want to import that node again, this way circles are handled. In order to make this algorithm work we must ensure things, which are handled in the generic CreateDecl<> template: * There are no `Import()` calls in between any node creation (::Create) and the `MapImported()` call. * Before actually creating an AST node (::Create), we must check if the Node had been imported already, if yes then return with that one. One very important case for this is connected to templates: we may start an import both from the templated decl of a template and from the template itself. Now, the virtual `Imported` function is called in `ASTImporter::Impor(Decl *)`, but only once, when the `Decl` is imported. One point of this refactor is to separate responsibilities. The original `Imported()` had 3 responsibilities: - notify subclasses when an import happened - register the decl into `ImportedDecls` - initialise the Decl (set attributes, etc) Now all of these are in separate functions: - `Imported` - `MapImported` - `InitializeImportedDecl` I tried to check all the clients, I executed tests for `ExternalASTMerger.cpp` and some unittests for lldb. Reviewers: a.sidorin, balazske, xazax.hun, r.stahl Subscribers: rnkovacs, dkrupp, cfe-commits Differential Revision: https://reviews.llvm.org/D47632 llvm-svn: 336896
2018-07-12 17:42:05 +08:00
// assumption then `RecordDecl::LoadFieldsFromExternalStorage` could trigger
// another AST import which in turn would call the structural equivalency
// check again and finally we'd have an improper result.)
if (Context.EqKind == StructuralEquivalenceKind::Minimal)
if (D1->hasExternalLexicalStorage() || D2->hasExternalLexicalStorage())
return true;
// If one definition is currently being defined, we do not compare for
// equality and we assume that the decls are equal.
if (D1->isBeingDefined() || D2->isBeingDefined())
return true;
if (auto *D1CXX = dyn_cast<CXXRecordDecl>(D1)) {
if (auto *D2CXX = dyn_cast<CXXRecordDecl>(D2)) {
if (D1CXX->hasExternalLexicalStorage() &&
!D1CXX->isCompleteDefinition()) {
D1CXX->getASTContext().getExternalSource()->CompleteType(D1CXX);
}
if (D1CXX->isLambda() != D2CXX->isLambda())
return false;
if (D1CXX->isLambda()) {
if (!IsStructurallyEquivalentLambdas(Context, D1CXX, D2CXX))
return false;
}
if (D1CXX->getNumBases() != D2CXX->getNumBases()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(D2->getLocation(), diag::note_odr_number_of_bases)
<< D2CXX->getNumBases();
Context.Diag1(D1->getLocation(), diag::note_odr_number_of_bases)
<< D1CXX->getNumBases();
}
return false;
}
// Check the base classes.
for (CXXRecordDecl::base_class_iterator Base1 = D1CXX->bases_begin(),
BaseEnd1 = D1CXX->bases_end(),
Base2 = D2CXX->bases_begin();
Base1 != BaseEnd1; ++Base1, ++Base2) {
if (!IsStructurallyEquivalent(Context, Base1->getType(),
Base2->getType())) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(Base2->getBeginLoc(), diag::note_odr_base)
<< Base2->getType() << Base2->getSourceRange();
Context.Diag1(Base1->getBeginLoc(), diag::note_odr_base)
<< Base1->getType() << Base1->getSourceRange();
}
return false;
}
// Check virtual vs. non-virtual inheritance mismatch.
if (Base1->isVirtual() != Base2->isVirtual()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(Base2->getBeginLoc(), diag::note_odr_virtual_base)
<< Base2->isVirtual() << Base2->getSourceRange();
Context.Diag1(Base1->getBeginLoc(), diag::note_odr_base)
<< Base1->isVirtual() << Base1->getSourceRange();
}
return false;
}
}
// Check the friends for consistency.
CXXRecordDecl::friend_iterator Friend2 = D2CXX->friend_begin(),
Friend2End = D2CXX->friend_end();
for (CXXRecordDecl::friend_iterator Friend1 = D1CXX->friend_begin(),
Friend1End = D1CXX->friend_end();
Friend1 != Friend1End; ++Friend1, ++Friend2) {
if (Friend2 == Friend2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2CXX);
Context.Diag1((*Friend1)->getFriendLoc(), diag::note_odr_friend);
Context.Diag2(D2->getLocation(), diag::note_odr_missing_friend);
}
return false;
}
if (!IsStructurallyEquivalent(Context, *Friend1, *Friend2)) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2CXX);
Context.Diag1((*Friend1)->getFriendLoc(), diag::note_odr_friend);
Context.Diag2((*Friend2)->getFriendLoc(), diag::note_odr_friend);
}
return false;
}
}
if (Friend2 != Friend2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2((*Friend2)->getFriendLoc(), diag::note_odr_friend);
Context.Diag1(D1->getLocation(), diag::note_odr_missing_friend);
}
return false;
}
} else if (D1CXX->getNumBases() > 0) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
const CXXBaseSpecifier *Base1 = D1CXX->bases_begin();
Context.Diag1(Base1->getBeginLoc(), diag::note_odr_base)
<< Base1->getType() << Base1->getSourceRange();
Context.Diag2(D2->getLocation(), diag::note_odr_missing_base);
}
return false;
}
}
// Check the fields for consistency.
RecordDecl::field_iterator Field2 = D2->field_begin(),
Field2End = D2->field_end();
for (RecordDecl::field_iterator Field1 = D1->field_begin(),
Field1End = D1->field_end();
Field1 != Field1End; ++Field1, ++Field2) {
if (Field2 == Field2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(Field1->getLocation(), diag::note_odr_field)
<< Field1->getDeclName() << Field1->getType();
Context.Diag2(D2->getLocation(), diag::note_odr_missing_field);
}
return false;
}
if (!IsStructurallyEquivalent(Context, *Field1, *Field2))
return false;
}
if (Field2 != Field2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field)
<< Field2->getDeclName() << Field2->getType();
Context.Diag1(D1->getLocation(), diag::note_odr_missing_field);
}
return false;
}
return true;
}
/// Determine structural equivalence of two enums.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
EnumDecl *D1, EnumDecl *D2) {
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
// Check for equivalent enum names.
IdentifierInfo *Name1 = D1->getIdentifier();
if (!Name1 && D1->getTypedefNameForAnonDecl())
Name1 = D1->getTypedefNameForAnonDecl()->getIdentifier();
IdentifierInfo *Name2 = D2->getIdentifier();
if (!Name2 && D2->getTypedefNameForAnonDecl())
Name2 = D2->getTypedefNameForAnonDecl()->getIdentifier();
if (!IsStructurallyEquivalent(Name1, Name2))
return false;
// Compare the definitions of these two enums. If either or both are
// incomplete (i.e. forward declared), we assume that they are equivalent.
D1 = D1->getDefinition();
D2 = D2->getDefinition();
if (!D1 || !D2)
return true;
EnumDecl::enumerator_iterator EC2 = D2->enumerator_begin(),
EC2End = D2->enumerator_end();
for (EnumDecl::enumerator_iterator EC1 = D1->enumerator_begin(),
EC1End = D1->enumerator_end();
EC1 != EC1End; ++EC1, ++EC2) {
if (EC2 == EC2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator)
<< EC1->getDeclName() << EC1->getInitVal().toString(10);
Context.Diag2(D2->getLocation(), diag::note_odr_missing_enumerator);
}
return false;
}
llvm::APSInt Val1 = EC1->getInitVal();
llvm::APSInt Val2 = EC2->getInitVal();
if (!llvm::APSInt::isSameValue(Val1, Val2) ||
!IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator)
<< EC2->getDeclName() << EC2->getInitVal().toString(10);
Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator)
<< EC1->getDeclName() << EC1->getInitVal().toString(10);
}
return false;
}
}
if (EC2 != EC2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator)
<< EC2->getDeclName() << EC2->getInitVal().toString(10);
Context.Diag1(D1->getLocation(), diag::note_odr_missing_enumerator);
}
return false;
}
return true;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TemplateParameterList *Params1,
TemplateParameterList *Params2) {
if (Params1->size() != Params2->size()) {
if (Context.Complain) {
Context.Diag2(Params2->getTemplateLoc(),
Context.getApplicableDiagnostic(
diag::err_odr_different_num_template_parameters))
<< Params1->size() << Params2->size();
Context.Diag1(Params1->getTemplateLoc(),
diag::note_odr_template_parameter_list);
}
return false;
}
for (unsigned I = 0, N = Params1->size(); I != N; ++I) {
if (Params1->getParam(I)->getKind() != Params2->getParam(I)->getKind()) {
if (Context.Complain) {
Context.Diag2(Params2->getParam(I)->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_different_template_parameter_kind));
Context.Diag1(Params1->getParam(I)->getLocation(),
diag::note_odr_template_parameter_here);
}
return false;
}
if (!IsStructurallyEquivalent(Context, Params1->getParam(I),
Params2->getParam(I)))
return false;
}
return true;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TemplateTypeParmDecl *D1,
TemplateTypeParmDecl *D2) {
if (D1->isParameterPack() != D2->isParameterPack()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_parameter_pack_non_pack))
<< D2->isParameterPack();
Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack)
<< D1->isParameterPack();
}
return false;
}
return true;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
NonTypeTemplateParmDecl *D1,
NonTypeTemplateParmDecl *D2) {
if (D1->isParameterPack() != D2->isParameterPack()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_parameter_pack_non_pack))
<< D2->isParameterPack();
Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack)
<< D1->isParameterPack();
}
return false;
}
// Check types.
if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType())) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_non_type_parameter_type_inconsistent))
<< D2->getType() << D1->getType();
Context.Diag1(D1->getLocation(), diag::note_odr_value_here)
<< D1->getType();
}
return false;
}
return true;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TemplateTemplateParmDecl *D1,
TemplateTemplateParmDecl *D2) {
if (D1->isParameterPack() != D2->isParameterPack()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_parameter_pack_non_pack))
<< D2->isParameterPack();
Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack)
<< D1->isParameterPack();
}
return false;
}
// Check template parameter lists.
return IsStructurallyEquivalent(Context, D1->getTemplateParameters(),
D2->getTemplateParameters());
}
static bool IsTemplateDeclCommonStructurallyEquivalent(
StructuralEquivalenceContext &Ctx, TemplateDecl *D1, TemplateDecl *D2) {
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
return false;
if (!D1->getIdentifier()) // Special name
if (D1->getNameAsString() != D2->getNameAsString())
return false;
return IsStructurallyEquivalent(Ctx, D1->getTemplateParameters(),
D2->getTemplateParameters());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
ClassTemplateDecl *D1,
ClassTemplateDecl *D2) {
// Check template parameters.
if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2))
return false;
// Check the templated declaration.
return IsStructurallyEquivalent(Context, D1->getTemplatedDecl(),
D2->getTemplatedDecl());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FunctionTemplateDecl *D1,
FunctionTemplateDecl *D2) {
// Check template parameters.
if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2))
return false;
// Check the templated declaration.
return IsStructurallyEquivalent(Context, D1->getTemplatedDecl()->getType(),
D2->getTemplatedDecl()->getType());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
ConceptDecl *D1,
ConceptDecl *D2) {
// Check template parameters.
if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2))
return false;
// Check the constraint expression.
return IsStructurallyEquivalent(Context, D1->getConstraintExpr(),
D2->getConstraintExpr());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FriendDecl *D1, FriendDecl *D2) {
if ((D1->getFriendType() && D2->getFriendDecl()) ||
(D1->getFriendDecl() && D2->getFriendType())) {
return false;
}
if (D1->getFriendType() && D2->getFriendType())
return IsStructurallyEquivalent(Context,
D1->getFriendType()->getType(),
D2->getFriendType()->getType());
if (D1->getFriendDecl() && D2->getFriendDecl())
return IsStructurallyEquivalent(Context, D1->getFriendDecl(),
D2->getFriendDecl());
return false;
}
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TypedefNameDecl *D1, TypedefNameDecl *D2) {
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
return false;
return IsStructurallyEquivalent(Context, D1->getUnderlyingType(),
D2->getUnderlyingType());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FunctionDecl *D1, FunctionDecl *D2) {
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
return false;
if (D1->isOverloadedOperator()) {
if (!D2->isOverloadedOperator())
return false;
if (D1->getOverloadedOperator() != D2->getOverloadedOperator())
return false;
}
// FIXME: Consider checking for function attributes as well.
if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType()))
return false;
return true;
}
/// Determine structural equivalence of two declarations.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2) {
// FIXME: Check for known structural equivalences via a callback of some sort.
D1 = D1->getCanonicalDecl();
D2 = D2->getCanonicalDecl();
std::pair<Decl *, Decl *> P{D1, D2};
// Check whether we already know that these two declarations are not
// structurally equivalent.
if (Context.NonEquivalentDecls.count(P))
return false;
// Check if a check for these declarations is already pending.
// If yes D1 and D2 will be checked later (from DeclsToCheck),
// or these are already checked (and equivalent).
bool Inserted = Context.VisitedDecls.insert(P).second;
if (!Inserted)
return true;
Context.DeclsToCheck.push(P);
return true;
}
DiagnosticBuilder StructuralEquivalenceContext::Diag1(SourceLocation Loc,
unsigned DiagID) {
assert(Complain && "Not allowed to complain");
if (LastDiagFromC2)
FromCtx.getDiagnostics().notePriorDiagnosticFrom(ToCtx.getDiagnostics());
LastDiagFromC2 = false;
return FromCtx.getDiagnostics().Report(Loc, DiagID);
}
DiagnosticBuilder StructuralEquivalenceContext::Diag2(SourceLocation Loc,
unsigned DiagID) {
assert(Complain && "Not allowed to complain");
if (!LastDiagFromC2)
ToCtx.getDiagnostics().notePriorDiagnosticFrom(FromCtx.getDiagnostics());
LastDiagFromC2 = true;
return ToCtx.getDiagnostics().Report(Loc, DiagID);
}
Optional<unsigned>
StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(RecordDecl *Anon) {
ASTContext &Context = Anon->getASTContext();
QualType AnonTy = Context.getRecordType(Anon);
const auto *Owner = dyn_cast<RecordDecl>(Anon->getDeclContext());
if (!Owner)
return None;
unsigned Index = 0;
for (const auto *D : Owner->noload_decls()) {
const auto *F = dyn_cast<FieldDecl>(D);
if (!F)
continue;
if (F->isAnonymousStructOrUnion()) {
if (Context.hasSameType(F->getType(), AnonTy))
break;
++Index;
continue;
}
// If the field looks like this:
// struct { ... } A;
QualType FieldType = F->getType();
// In case of nested structs.
while (const auto *ElabType = dyn_cast<ElaboratedType>(FieldType))
FieldType = ElabType->getNamedType();
if (const auto *RecType = dyn_cast<RecordType>(FieldType)) {
const RecordDecl *RecDecl = RecType->getDecl();
if (RecDecl->getDeclContext() == Owner && !RecDecl->getIdentifier()) {
if (Context.hasSameType(FieldType, AnonTy))
break;
++Index;
continue;
}
}
}
return Index;
}
unsigned StructuralEquivalenceContext::getApplicableDiagnostic(
unsigned ErrorDiagnostic) {
if (ErrorOnTagTypeMismatch)
return ErrorDiagnostic;
switch (ErrorDiagnostic) {
case diag::err_odr_variable_type_inconsistent:
return diag::warn_odr_variable_type_inconsistent;
case diag::err_odr_variable_multiple_def:
return diag::warn_odr_variable_multiple_def;
case diag::err_odr_function_type_inconsistent:
return diag::warn_odr_function_type_inconsistent;
case diag::err_odr_tag_type_inconsistent:
return diag::warn_odr_tag_type_inconsistent;
case diag::err_odr_field_type_inconsistent:
return diag::warn_odr_field_type_inconsistent;
case diag::err_odr_ivar_type_inconsistent:
return diag::warn_odr_ivar_type_inconsistent;
case diag::err_odr_objc_superclass_inconsistent:
return diag::warn_odr_objc_superclass_inconsistent;
case diag::err_odr_objc_method_result_type_inconsistent:
return diag::warn_odr_objc_method_result_type_inconsistent;
case diag::err_odr_objc_method_num_params_inconsistent:
return diag::warn_odr_objc_method_num_params_inconsistent;
case diag::err_odr_objc_method_param_type_inconsistent:
return diag::warn_odr_objc_method_param_type_inconsistent;
case diag::err_odr_objc_method_variadic_inconsistent:
return diag::warn_odr_objc_method_variadic_inconsistent;
case diag::err_odr_objc_property_type_inconsistent:
return diag::warn_odr_objc_property_type_inconsistent;
case diag::err_odr_objc_property_impl_kind_inconsistent:
return diag::warn_odr_objc_property_impl_kind_inconsistent;
case diag::err_odr_objc_synthesize_ivar_inconsistent:
return diag::warn_odr_objc_synthesize_ivar_inconsistent;
case diag::err_odr_different_num_template_parameters:
return diag::warn_odr_different_num_template_parameters;
case diag::err_odr_different_template_parameter_kind:
return diag::warn_odr_different_template_parameter_kind;
case diag::err_odr_parameter_pack_non_pack:
return diag::warn_odr_parameter_pack_non_pack;
case diag::err_odr_non_type_parameter_type_inconsistent:
return diag::warn_odr_non_type_parameter_type_inconsistent;
}
llvm_unreachable("Diagnostic kind not handled in preceding switch");
}
bool StructuralEquivalenceContext::IsEquivalent(Decl *D1, Decl *D2) {
// Ensure that the implementation functions (all static functions in this TU)
// never call the public ASTStructuralEquivalence::IsEquivalent() functions,
// because that will wreak havoc the internal state (DeclsToCheck and
// VisitedDecls members) and can cause faulty behaviour.
// In other words: Do not start a graph search from a new node with the
// internal data of another search in progress.
// FIXME: Better encapsulation and separation of internal and public
// functionality.
assert(DeclsToCheck.empty());
assert(VisitedDecls.empty());
if (!::IsStructurallyEquivalent(*this, D1, D2))
return false;
return !Finish();
}
bool StructuralEquivalenceContext::IsEquivalent(QualType T1, QualType T2) {
assert(DeclsToCheck.empty());
assert(VisitedDecls.empty());
if (!::IsStructurallyEquivalent(*this, T1, T2))
return false;
return !Finish();
}
bool StructuralEquivalenceContext::IsEquivalent(Stmt *S1, Stmt *S2) {
assert(DeclsToCheck.empty());
assert(VisitedDecls.empty());
if (!::IsStructurallyEquivalent(*this, S1, S2))
return false;
return !Finish();
}
bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
// Check for equivalent described template.
TemplateDecl *Template1 = D1->getDescribedTemplate();
TemplateDecl *Template2 = D2->getDescribedTemplate();
if ((Template1 != nullptr) != (Template2 != nullptr))
return false;
if (Template1 && !IsStructurallyEquivalent(*this, Template1, Template2))
return false;
// FIXME: Move check for identifier names into this function.
return true;
}
bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
Decl *D1, Decl *D2) {
[ASTImporter] Refactor IsStructurallyEquivalent's Decl overloads to be more consistent There are several `::IsStructurallyEquivalent` overloads for Decl subclasses that are used for comparing declarations. There is also one overload that takes just two Decl pointers which ends up queuing the passed Decls to be later compared in `CheckKindSpecificEquivalence`. `CheckKindSpecificEquivalence` implements the dispatch logic for the different Decl subclasses. It is supposed to hand over the queued Decls to the subclass-specific `::IsStructurallyEquivalent` overload that will actually compare the Decl instance. It also seems to implement a few pieces of actual node comparison logic inbetween the dispatch code. This implementation causes that the different overloads of `::IsStructurallyEquivalent` do different (and sometimes no) comparisons depending on which overload of `::IsStructurallyEquivalent` ends up being called. For example, if I want to compare two FieldDecl instances, then I could either call the `::IsStructurallyEquivalent` with `Decl *` or with `FieldDecl *` parameters. The overload that takes FieldDecls is doing a correct comparison. However, the `Decl *` overload just queues the Decl pair. `CheckKindSpecificEquivalence` has no dispatch logic for `FieldDecl`, so it always returns true and never does any actual comparison. On the other hand, if I try to compare two FunctionDecl instances the two possible overloads of `::IsStructurallyEquivalent` have the opposite behaviour: The overload that takes `FunctionDecl` pointers isn't comparing the names of the FunctionDecls while the overload taking a plain `Decl` ends up comparing the function names (as the comparison logic for that is implemented in `CheckKindSpecificEquivalence`). This patch tries to make this set of functions more consistent by making `CheckKindSpecificEquivalence` a pure dispatch function without any subclass-specific comparison logic. Also the dispatch logic is now autogenerated so it can no longer miss certain subclasses. The comparison code from `CheckKindSpecificEquivalence` is moved to the respective `::IsStructurallyEquivalent` overload so that the comparison result no longer depends if one calls the `Decl *` overload or the overload for the specific subclass. The only difference is now that the `Decl *` overload is queuing the parameter while the subclass-specific overload is directly doing the comparison. `::IsStructurallyEquivalent` is an implementation detail and I don't think the behaviour causes any bugs in the current implementation (as carefully calling the right overload for the different classes works around the issue), so the test for this change is that I added some new code for comparing `MemberExpr`. The new comparison code always calls the dispatching overload and it previously failed as the dispatch didn't support FieldDecls. Reviewed By: martong, a_sidorin Differential Revision: https://reviews.llvm.org/D87619
2020-09-21 20:53:35 +08:00
// Kind mismatch.
if (D1->getKind() != D2->getKind())
return false;
// Cast the Decls to their actual subclass so that the right overload of
// IsStructurallyEquivalent is called.
switch (D1->getKind()) {
#define ABSTRACT_DECL(DECL)
#define DECL(DERIVED, BASE) \
case Decl::Kind::DERIVED: \
return ::IsStructurallyEquivalent(*this, static_cast<DERIVED##Decl *>(D1), \
static_cast<DERIVED##Decl *>(D2));
#include "clang/AST/DeclNodes.inc"
}
return true;
}
bool StructuralEquivalenceContext::Finish() {
while (!DeclsToCheck.empty()) {
// Check the next declaration.
std::pair<Decl *, Decl *> P = DeclsToCheck.front();
DeclsToCheck.pop();
Decl *D1 = P.first;
Decl *D2 = P.second;
bool Equivalent =
CheckCommonEquivalence(D1, D2) && CheckKindSpecificEquivalence(D1, D2);
if (!Equivalent) {
// Note that these two declarations are not equivalent (and we already
// know about it).
NonEquivalentDecls.insert(P);
return true;
}
}
return false;
}