r192092 - When merging class definitions across modules in C++, merge together fields.

Richard Smith richard-llvm at metafoo.co.uk
Mon Oct 7 01:02:11 PDT 2013


Author: rsmith
Date: Mon Oct  7 03:02:11 2013
New Revision: 192092

URL: http://llvm.org/viewvc/llvm-project?rev=192092&view=rev
Log:
When merging class definitions across modules in C++, merge together fields.
This change doesn't go all the way to making fields redeclarable; instead, it
makes them 'mergeable', which means we can find the canonical declaration, but
not much else (and for a declaration that's not from a module, the canonical
declaration is always that declaration).

Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/Redeclarable.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/test/Modules/Inputs/templates-left.h
    cfe/trunk/test/Modules/Inputs/templates-right.h
    cfe/trunk/test/Modules/Inputs/templates-top.h
    cfe/trunk/test/Modules/templates.mm

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Mon Oct  7 03:02:11 2013
@@ -271,6 +271,11 @@ class ASTContext : public RefCountedBase
   /// wasting space in the Decl class.
   llvm::DenseMap<const Decl*, AttrVec*> DeclAttrs;
 
+  /// \brief A mapping from non-redeclarable declarations in modules that were
+  /// merged with other declarations to the canonical declaration that they were
+  /// merged into.
+  llvm::DenseMap<Decl*, Decl*> MergedDecls;
+
 public:
   /// \brief A type synonym for the TemplateOrInstantiation mapping.
   typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>
@@ -746,7 +751,15 @@ public:
     return import_iterator(FirstLocalImport); 
   }
   import_iterator local_import_end() const { return import_iterator(); }
-  
+
+  Decl *getPrimaryMergedDecl(Decl *D) {
+    Decl *Result = MergedDecls.lookup(D);
+    return Result ? Result : D;
+  }
+  void setPrimaryMergedDecl(Decl *D, Decl *Primary) {
+    MergedDecls[D] = Primary;
+  }
+
   TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
 
 

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Mon Oct  7 03:02:11 2013
@@ -2108,7 +2108,7 @@ public:
 
 /// FieldDecl - An instance of this class is created by Sema::ActOnField to
 /// represent a member of a struct/union/class.
-class FieldDecl : public DeclaratorDecl {
+class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
   // FIXME: This can be packed into the bitfields in Decl.
   bool Mutable : 1;
   mutable unsigned CachedFieldIndex : 31;
@@ -2222,6 +2222,14 @@ public:
 
   SourceRange getSourceRange() const LLVM_READONLY;
 
+  /// Retrieves the canonical declaration of this field.
+  FieldDecl *getCanonicalDecl() {
+    return getFirstDeclaration();
+  }
+  const FieldDecl *getCanonicalDecl() const {
+    return getFirstDeclaration();
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K >= firstField && K <= lastField; }

Modified: cfe/trunk/include/clang/AST/Redeclarable.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Redeclarable.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Redeclarable.h (original)
+++ cfe/trunk/include/clang/AST/Redeclarable.h Mon Oct  7 03:02:11 2013
@@ -175,6 +175,42 @@ public:
   friend class ASTDeclWriter;
 };
 
+/// \brief Get the primary declaration for a declaration from an AST file. That
+/// will be the first-loaded declaration.
+Decl *getPrimaryMergedDecl(Decl *D);
+
+/// \brief Provides common interface for the Decls that cannot be redeclared,
+/// but can be merged if the same declaration is brought in from multiple
+/// modules.
+template<typename decl_type>
+class Mergeable {
+public:
+  Mergeable() {}
+
+  /// \brief Return the first declaration of this declaration or itself if this
+  /// is the only declaration.
+  decl_type *getFirstDeclaration() {
+    decl_type *D = static_cast<decl_type*>(this);
+    if (!D->isFromASTFile())
+      return D;
+    return cast<decl_type>(getPrimaryMergedDecl(const_cast<decl_type*>(D)));
+  }
+
+  /// \brief Return the first declaration of this declaration or itself if this
+  /// is the only declaration.
+  const decl_type *getFirstDeclaration() const {
+    const decl_type *D = static_cast<const decl_type*>(this);
+    if (!D->isFromASTFile())
+      return D;
+    return cast<decl_type>(getPrimaryMergedDecl(const_cast<decl_type*>(D)));
+  }
+
+  /// \brief Returns true if this is the first declaration.
+  bool isFirstDeclaration() const {
+    return getFirstDeclaration() == this;
+  }
+};
+
 }
 
 #endif

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Mon Oct  7 03:02:11 2013
@@ -34,6 +34,10 @@
 
 using namespace clang;
 
+Decl *clang::getPrimaryMergedDecl(Decl *D) {
+  return D->getASTContext().getPrimaryMergedDecl(D);
+}
+
 //===----------------------------------------------------------------------===//
 // NamedDecl Implementation
 //===----------------------------------------------------------------------===//
@@ -3094,6 +3098,10 @@ unsigned FieldDecl::getBitWidthValue(con
 }
 
 unsigned FieldDecl::getFieldIndex() const {
+  const FieldDecl *Canonical = getCanonicalDecl();
+  if (Canonical != this)
+    return Canonical->getFieldIndex();
+
   if (CachedFieldIndex) return CachedFieldIndex - 1;
 
   unsigned Index = 0;
@@ -3101,7 +3109,7 @@ unsigned FieldDecl::getFieldIndex() cons
 
   for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
        I != E; ++I, ++Index)
-    I->CachedFieldIndex = Index + 1;
+    I->getCanonicalDecl()->CachedFieldIndex = Index + 1;
 
   assert(CachedFieldIndex && "failed to find field in parent");
   return CachedFieldIndex - 1;

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Mon Oct  7 03:02:11 2013
@@ -296,6 +296,9 @@ namespace clang {
     void mergeRedeclarable(Redeclarable<T> *D, T *Existing,
                            RedeclarableResult &Redecl);
 
+    template<typename T>
+    void mergeMergeable(Mergeable<T> *D);
+
     // FIXME: Reorder according to DeclNodes.td?
     void VisitObjCMethodDecl(ObjCMethodDecl *D);
     void VisitObjCContainerDecl(ObjCContainerDecl *D);
@@ -914,6 +917,7 @@ void ASTDeclReader::VisitFieldDecl(Field
     if (FieldDecl *Tmpl = ReadDeclAs<FieldDecl>(Record, Idx))
       Reader.getContext().setInstantiatedFromUnnamedFieldDecl(FD, Tmpl);
   }
+  mergeMergeable(FD);
 }
 
 void ASTDeclReader::VisitMSPropertyDecl(MSPropertyDecl *PD) {
@@ -1874,6 +1878,22 @@ void ASTDeclReader::mergeRedeclarable(Re
   }
 }
 
+/// \brief Attempts to merge the given declaration (D) with another declaration
+/// of the same entity, for the case where the entity is not actually
+/// redeclarable. This happens, for instance, when merging the fields of
+/// identical class definitions from two different modules.
+template<typename T>
+void ASTDeclReader::mergeMergeable(Mergeable<T> *D) {
+  // If modules are not available, there is no reason to perform this merge.
+  if (!Reader.getContext().getLangOpts().Modules)
+    return;
+
+  if (FindExistingResult ExistingRes = findExisting(static_cast<T*>(D)))
+    if (T *Existing = ExistingRes)
+      Reader.Context.setPrimaryMergedDecl(static_cast<T*>(D),
+                                          Existing->getCanonicalDecl());
+}
+
 void ASTDeclReader::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) {
   VisitDecl(D);
   unsigned NumVars = D->varlist_size();
@@ -2091,6 +2111,15 @@ static bool isSameEntity(NamedDecl *X, N
                                        TemplateY->getTemplateParameters());
   }
 
+  // Fields with the same name and the same type match.
+  if (FieldDecl *FDX = dyn_cast<FieldDecl>(X)) {
+    FieldDecl *FDY = cast<FieldDecl>(Y);
+    // FIXME: Diagnose if the types don't match. More generally, diagnose if we
+    // get a declaration in a class definition that isn't in the canonical class
+    // definition.
+    return X->getASTContext().hasSameType(FDX->getType(), FDY->getType());
+  }
+
   // FIXME: Many other cases to implement.
   return false;
 }

Modified: cfe/trunk/test/Modules/Inputs/templates-left.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/templates-left.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/templates-left.h (original)
+++ cfe/trunk/test/Modules/Inputs/templates-left.h Mon Oct  7 03:02:11 2013
@@ -19,6 +19,10 @@ namespace N {
   };
 }
 
+constexpr unsigned List<int>::*size_left = &List<int>::size;
+List<int> list_left = { 0, 8 };
+typedef List<int> ListInt_left;
+
 template <typename T>
 void pendingInstantiationEmit(T) {}
 void triggerPendingInstantiation() {

Modified: cfe/trunk/test/Modules/Inputs/templates-right.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/templates-right.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/templates-right.h (original)
+++ cfe/trunk/test/Modules/Inputs/templates-right.h Mon Oct  7 03:02:11 2013
@@ -18,6 +18,10 @@ namespace N {
   };
 }
 
+constexpr unsigned List<int>::*size_right = &List<int>::size;
+List<int> list_right = { 0, 12 };
+typedef List<int> ListInt_right;
+
 template <typename T>
 void pendingInstantiationEmit(T) {}
 void triggerPendingInstantiationToo() {

Modified: cfe/trunk/test/Modules/Inputs/templates-top.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/templates-top.h?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/templates-top.h (original)
+++ cfe/trunk/test/Modules/Inputs/templates-top.h Mon Oct  7 03:02:11 2013
@@ -3,6 +3,10 @@ template<typename T> class Vector;
 template<typename T> class List {
 public:
   void push_back(T);
+
+  struct node {};
+  node *head;
+  unsigned size;
 };
 
 namespace A {

Modified: cfe/trunk/test/Modules/templates.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/templates.mm?rev=192092&r1=192091&r2=192092&view=diff
==============================================================================
--- cfe/trunk/test/Modules/templates.mm (original)
+++ cfe/trunk/test/Modules/templates.mm Mon Oct  7 03:02:11 2013
@@ -1,11 +1,15 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -x objective-c++ -fmodules -fmodules-cache-path=%t -I %S/Inputs -verify %s -Wno-objc-root-class
-// RUN: %clang_cc1 -x objective-c++ -fmodules -fmodules-cache-path=%t -I %S/Inputs -emit-llvm %s -o - -Wno-objc-root-class | grep Emit | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++11 -x objective-c++ -fmodules -fmodules-cache-path=%t -I %S/Inputs -verify %s -Wno-objc-root-class
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++11 -x objective-c++ -fmodules -fmodules-cache-path=%t -I %S/Inputs -emit-llvm %s -o - -Wno-objc-root-class | FileCheck %s
 // expected-no-diagnostics
 
 @import templates_left;
 @import templates_right;
 
+// CHECK: @list_left = global { %{{.*}}*, i32, [4 x i8] } { %{{.*}}* null, i32 8,
+// CHECK: @list_right = global { %{{.*}}*, i32, [4 x i8] } { %{{.*}}* null, i32 12,
+// CHECK: @_ZZ15testMixedStructvE1l = {{.*}} constant { %{{.*}}*, i32, [4 x i8] } { %{{.*}}* null, i32 1,
+// CHECK: @_ZZ15testMixedStructvE1r = {{.*}} constant { %{{.*}}*, i32, [4 x i8] } { %{{.*}}* null, i32 2,
 
 void testTemplateClasses() {
   Vector<int> vec_int;
@@ -39,3 +43,27 @@ typedef Outer<int>::Inner OuterIntInner;
 
 // CHECK: call {{.*pendingInstantiation}}
 // CHECK: call {{.*redeclDefinitionEmit}}
+
+static_assert(size_left == size_right, "same field both ways");
+void useListInt(List<int> &);
+
+// CHECK-LABEL: define i32 @_Z15testMixedStructv(
+unsigned testMixedStruct() {
+  // CHECK: %[[l:.*]] = alloca %[[ListInt:[^ ]*]], align 8
+  // CHECK: %[[r:.*]] = alloca %[[ListInt]], align 8
+
+  // CHECK: call {{.*}}memcpy{{.*}}(i8* %{{.*}}, i8* bitcast ({{.*}}* @_ZZ15testMixedStructvE1l to i8*), i64 16,
+  ListInt_left l{0, 1};
+
+  // CHECK: call {{.*}}memcpy{{.*}}(i8* %{{.*}}, i8* bitcast ({{.*}}* @_ZZ15testMixedStructvE1r to i8*), i64 16,
+  ListInt_right r{0, 2};
+
+  // CHECK: call void @_Z10useListIntR4ListIiE(%[[ListInt]]* %[[l]])
+  useListInt(l);
+  // CHECK: call void @_Z10useListIntR4ListIiE(%[[ListInt]]* %[[r]])
+  useListInt(r);
+
+  // CHECK: load i32* bitcast (i8* getelementptr inbounds (i8* bitcast ({{.*}}* @list_left to i8*), i64 8) to i32*)
+  // CHECK: load i32* bitcast (i8* getelementptr inbounds (i8* bitcast ({{.*}}* @list_right to i8*), i64 8) to i32*)
+  return list_left.*size_right + list_right.*size_left;
+}





More information about the cfe-commits mailing list