[clang] 8e8658b - [clang][AST] Check context of record in structural equivalence.

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 25 23:39:16 PST 2021


Author: Balázs Kéri
Date: 2021-11-26T08:54:34+01:00
New Revision: 8e8658b19c057a48264514483f188bf7b6c2db7d

URL: https://github.com/llvm/llvm-project/commit/8e8658b19c057a48264514483f188bf7b6c2db7d
DIFF: https://github.com/llvm/llvm-project/commit/8e8658b19c057a48264514483f188bf7b6c2db7d.diff

LOG: [clang][AST] Check context of record in structural equivalence.

The AST structural equivalence check did not differentiate between
a struct and a struct with same name in different namespace. When
type of a member is checked it is possible to encounter such a case
and wrongly decide that the types are similar. This problem is fixed
by check for the namespaces of a record declaration.

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D113118

Added: 
    

Modified: 
    clang/lib/AST/ASTStructuralEquivalence.cpp
    clang/unittests/AST/StructuralEquivalenceTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index e85feb779190f..7fd24e2aa9ad2 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1347,6 +1347,42 @@ IsStructurallyEquivalentLambdas(StructuralEquivalenceContext &Context,
   return true;
 }
 
+/// Determine if context of a class is equivalent.
+static bool IsRecordContextStructurallyEquivalent(RecordDecl *D1,
+                                                  RecordDecl *D2) {
+  // The context should be completely equal, including anonymous and inline
+  // namespaces.
+  // We compare objects as part of full translation units, not subtrees of
+  // translation units.
+  DeclContext *DC1 = D1->getDeclContext()->getNonTransparentContext();
+  DeclContext *DC2 = D2->getDeclContext()->getNonTransparentContext();
+  while (true) {
+    // Special case: We allow a struct defined in a function to be equivalent
+    // with a similar struct defined outside of a function.
+    if ((DC1->isFunctionOrMethod() && DC2->isTranslationUnit()) ||
+        (DC2->isFunctionOrMethod() && DC1->isTranslationUnit()))
+      return true;
+
+    if (DC1->getDeclKind() != DC2->getDeclKind())
+      return false;
+    if (DC1->isTranslationUnit())
+      break;
+    if (DC1->isInlineNamespace() != DC2->isInlineNamespace())
+      return false;
+    if (const auto *ND1 = dyn_cast<NamedDecl>(DC1)) {
+      const auto *ND2 = cast<NamedDecl>(DC2);
+      if (!DC1->isInlineNamespace() &&
+          !IsStructurallyEquivalent(ND1->getIdentifier(), ND2->getIdentifier()))
+        return false;
+    }
+
+    DC1 = DC1->getParent()->getNonTransparentContext();
+    DC2 = DC2->getParent()->getNonTransparentContext();
+  }
+
+  return true;
+}
+
 /// Determine structural equivalence of two records.
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      RecordDecl *D1, RecordDecl *D2) {
@@ -1386,6 +1422,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
     }
   }
 
+  // If the records occur in 
diff erent context (namespace), these should be
+  // 
diff erent. This is specially important if the definition of one or both
+  // records is missing.
+  if (!IsRecordContextStructurallyEquivalent(D1, D2))
+    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);

diff  --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index 9ae0da8b9dd2c..a1b506c3c28d9 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -929,6 +929,128 @@ TEST_F(StructuralEquivalenceTest, ExplicitBoolSame) {
   EXPECT_TRUE(testStructuralMatch(First, Second));
 }
 
+struct StructuralEquivalenceRecordContextTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsNamed) {
+  auto Decls =
+      makeNamedDecls("class X;", "namespace N { class X; }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsNamed) {
+  auto Decls = makeNamedDecls("namespace A { class X; }",
+                              "namespace B { class X; }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsNamed) {
+  auto Decls = makeNamedDecls("namespace { class X; }",
+                              "namespace N { class X; }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsAnon) {
+  auto Decls =
+      makeNamedDecls("class X;", "namespace { class X; }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnon) {
+  auto Decls = makeNamedDecls("namespace { class X; }",
+                              "namespace { class X; }", Lang_CXX03, "X");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnonAnon) {
+  auto Decls =
+      makeNamedDecls("namespace { class X; }",
+                     "namespace { namespace { class X; } }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest,
+       NamespaceNamedNamedVsNamedNamed) {
+  auto Decls = makeNamedDecls("namespace A { namespace N { class X; } }",
+                              "namespace B { namespace N { class X; } }",
+                              Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsInline) {
+  auto Decls = makeNamedDecls("namespace A { namespace A { class X; } }",
+                              "namespace A { inline namespace A { class X; } }",
+                              Lang_CXX17, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineVsInline) {
+  auto Decls = makeNamedDecls("namespace A { inline namespace A { class X; } }",
+                              "namespace A { inline namespace B { class X; } }",
+                              Lang_CXX17, "X");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineTopLevel) {
+  auto Decls =
+      makeNamedDecls("inline namespace A { class X; } }",
+                     "inline namespace B { class X; } }", Lang_CXX17, "X");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContext) {
+  auto Decls =
+      makeNamedDecls("extern \"C\" { class X; }", "class X;", Lang_CXX03, "X");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextNE) {
+  auto Decls = makeNamedDecls("extern \"C\" { class X; }",
+                              "namespace { class X; }", Lang_CXX03, "X");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextInNamespace) {
+  auto Decls = makeNamedDecls("extern \"C\" { namespace N { class X; } }",
+                              "namespace N { extern \"C\" { class X; } }",
+                              Lang_CXX03, "X");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, NamespaceOfRecordMember) {
+  auto Decls = makeNamedDecls(
+      R"(
+      class X;
+      class Y { X* x; };
+      )",
+      R"(
+      namespace N { class X; }
+      class Y { N::X* x; };
+      )",
+      Lang_CXX03, "Y");
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototype) {
+  auto Decls =
+      makeNamedDecls("struct Param { int a; }; void foo(struct Param *p);",
+                     "void foo(struct Param { int a; } *p);", Lang_C89);
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototypeDifferentName) {
+  auto Decls =
+      makeNamedDecls("struct Param1 { int a; }; void foo(struct Param1 *p);",
+                     "void foo(struct Param2 { int a; } *p);", Lang_C89);
+  EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, RecordInsideFunction) {
+  auto Decls = makeNamedDecls("struct Param { int a; };",
+                              "void f() { struct Param { int a; }; }", Lang_C89,
+                              "Param");
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
 struct StructuralEquivalenceEnumTest : StructuralEquivalenceTest {};
 
 TEST_F(StructuralEquivalenceEnumTest, FwdDeclEnumShouldBeEqualWithFwdDeclEnum) {


        


More information about the cfe-commits mailing list