]> granicus.if.org Git - clang/commitdiff
Implement basic importing and merging of variable declarations within
authorDouglas Gregor <dgregor@apple.com>
Mon, 8 Feb 2010 21:09:39 +0000 (21:09 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 8 Feb 2010 21:09:39 +0000 (21:09 +0000)
the AST importer. This doesn't actually do anything (yet), because we
don't have driver logic for merging ASTs.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95570 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTImporter.h
include/clang/Basic/DiagnosticASTKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Frontend/FrontendAction.h
lib/AST/ASTImporter.cpp

index 01ec13470e654f2ab21c9053a1bcde50618c1bd4..71b6443f50220c544e6738a4e5b9c0323e7350cb 100644 (file)
@@ -44,11 +44,15 @@ namespace clang {
     /// to the corresponding types in the "to" context.
     llvm::DenseMap<Type *, Type *> ImportedTypes;
     
+    /// \brief Mapping from the already-imported declarations in the "from"
+    /// context to the corresponding declarations in the "to" context.
+    llvm::DenseMap<Decl *, Decl *> ImportedDecls;
+    
   public:
     ASTImporter(ASTContext &ToContext, Diagnostic &ToDiags,
                 ASTContext &FromContext, Diagnostic &FromDiags);
     
-    ~ASTImporter();
+    virtual ~ASTImporter();
     
     /// \brief Import the given type from the "from" context into the "to"
     /// context.
@@ -126,6 +130,38 @@ namespace clang {
     /// \returns the equivalent identifier in the "to" context.
     IdentifierInfo *Import(IdentifierInfo *FromId);
 
+    /// \brief Cope with a name conflict when importing a declaration into the
+    /// given context.
+    ///
+    /// This routine is invoked whenever there is a name conflict while 
+    /// importing a declaration. The returned name will become the name of the
+    /// imported declaration. By default, the returned name is the same as the
+    /// original name, leaving the conflict unresolve such that name lookup
+    /// for this name is likely to find an ambiguity later.
+    ///
+    /// Subclasses may override this routine to resolve the conflict, e.g., by
+    /// renaming the declaration being imported.
+    ///
+    /// \param Name the name of the declaration being imported, which conflicts
+    /// with other declarations.
+    ///
+    /// \param DC the declaration context (in the "to" AST context) in which 
+    /// the name is being imported.
+    ///
+    /// \param IDNS the identifier namespace in which the name will be found.
+    ///
+    /// \param Decls the set of declarations with the same name as the
+    /// declaration being imported.
+    ///
+    /// \param NumDecls the number of conflicting declarations in \p Decls.
+    ///
+    /// \returns the name that the newly-imported declaration should have.
+    virtual DeclarationName HandleNameConflict(DeclarationName Name,
+                                               DeclContext *DC,
+                                               unsigned IDNS,
+                                               NamedDecl **Decls,
+                                               unsigned NumDecls);
+    
     /// \brief Retrieve the context that AST nodes are being imported into.
     ASTContext &getToContext() const { return ToContext; }
     
@@ -139,6 +175,16 @@ namespace clang {
     /// \brief Retrieve the diagnostics object to use to report errors within
     /// the context we're importing from.
     Diagnostic &getFromDiags() const { return FromDiags; }
+    
+    /// \brief Retrieve the mapping from declarations in the "from" context
+    /// to the already-imported declarations in the "to" context.
+    llvm::DenseMap<Decl *, Decl *> &getImportedDecls() { return ImportedDecls; }
+    
+    /// \brief Report a diagnostic in the "to" context.
+    DiagnosticBuilder ToDiag(SourceLocation Loc, unsigned DiagID);
+    
+    /// \brief Report a diagnostic in the "from" context.
+    DiagnosticBuilder FromDiag(SourceLocation Loc, unsigned DiagID);
   };
 }
 
index f075aaaf422f19180786c1dc8ff2d21c46ca4e29..d6921df0a34b9d228db2e19ce275f55e547744ff 100644 (file)
@@ -26,4 +26,12 @@ def err_asm_empty_symbolic_operand_name : Error<
 def err_asm_invalid_operand_number : Error<
   "invalid operand number in inline asm string">;
 
+// Importing ASTs
+def err_odr_variable_type_inconsistent : Error<
+  "external variable %0 declared with incompatible types in different "
+  "translation units (%1 vs. %2)">;
+def err_odr_variable_multiple_def : Error<
+  "external variable %0 defined in multiple translation units">;
+def note_odr_value_here : Note<"declared here with type %0">;
+def note_odr_defined_here : Note<"also defined here">;
 }
index d31dbd531af2b60d899538199f782643c0f88dff..496e0202eb8663124b1b25a8a5c82f41dddd31f6 100644 (file)
@@ -2724,6 +2724,7 @@ def err_undeclared_protocol_suggest : Error<
   "cannot find protocol declaration for %0; did you mean %1?">;
 def note_base_class_specified_here : Note<
   "base class %0 specified here">;
+
 }
 
 
index 3042767af8747aee7caded917de55931fa95bea7..29a9302c5cbcf2d5f19bfdb29fdea82f93d7a544 100644 (file)
@@ -167,7 +167,7 @@ public:
 };
 
 /// ASTFrontendAction - Abstract base class to use for AST consumer based
-/// frontend actios.
+/// frontend actions.
 class ASTFrontendAction : public FrontendAction {
   /// ExecuteAction - Implement the ExecuteAction interface by running Sema on
   /// the already initialized AST consumer.
index 16fd7e6184e8121ae8a894ddd1120f5e79d59984..7668b7ba6733f54423888474ddb9fad634f85d8e 100644 (file)
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclVisitor.h"
 #include "clang/AST/TypeVisitor.h"
+#include "clang/AST/ASTDiagnostic.h"
 
 using namespace clang;
 
 namespace {
-  class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType> {
+  class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
+                          public DeclVisitor<ASTNodeImporter, Decl *> {
     ASTImporter &Importer;
     
   public:
@@ -62,6 +65,9 @@ namespace {
     // FIXME: TypenameType
     QualType VisitObjCInterfaceType(ObjCInterfaceType *T);
     QualType VisitObjCObjectPointerType(ObjCObjectPointerType *T);
+                            
+    // Importing declarations
+    Decl *VisitVarDecl(VarDecl *D);
   };
 }
 
@@ -425,6 +431,114 @@ QualType ASTNodeImporter::VisitObjCObjectPointerType(ObjCObjectPointerType *T) {
                                                           Protocols.size());
 }
 
+//----------------------------------------------------------------------------
+// Import Declarations
+//----------------------------------------------------------------------------
+Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
+  // Import the context of this declaration.
+  DeclContext *DC = Importer.ImportContext(D->getDeclContext());
+  if (!DC)
+    return 0;
+    
+  // Import the name of this declaration.
+  DeclarationName Name = Importer.Import(D->getDeclName());
+  if (D->getDeclName() && !Name)
+    return 0;
+  
+  // Import the type of this declaration.
+  QualType T = Importer.Import(D->getType());
+  if (T.isNull())
+    return 0;
+  
+  // Import the location of this declaration.
+  SourceLocation Loc = Importer.Import(D->getLocation());
+  
+  // Try to find a variable in our own ("to") context with the same name and
+  // in the same context as the variable we're importing.
+  if (!D->isFileVarDecl()) {
+    VarDecl *MergeWithVar = 0;
+    llvm::SmallVector<NamedDecl *, 4> ConflictingDecls;
+    unsigned IDNS = Decl::IDNS_Ordinary;
+    for (DeclContext::lookup_result Lookup = DC->lookup(D->getDeclName());
+         Lookup.first != Lookup.second; 
+         ++Lookup.first) {
+      if (!(*Lookup.first)->isInIdentifierNamespace(IDNS))
+        continue;
+      
+      if (VarDecl *FoundVar = dyn_cast<VarDecl>(*Lookup.first)) {
+        // We have found a variable that we may need to merge with. Check it.
+        if (isExternalLinkage(FoundVar->getLinkage()) &&
+            isExternalLinkage(D->getLinkage())) {
+          if (Importer.getToContext().typesAreCompatible(T, 
+                                                         FoundVar->getType())) {
+            MergeWithVar = FoundVar;
+            break;
+          }
+
+          Importer.ToDiag(Loc, diag::err_odr_variable_type_inconsistent)
+            << Name << T << FoundVar->getType();
+          Importer.ToDiag(FoundVar->getLocation(), diag::note_odr_value_here)
+            << FoundVar->getType();
+        }
+      }
+      
+      ConflictingDecls.push_back(*Lookup.first);
+    }
+
+    if (MergeWithVar) {
+      // An equivalent variable with external linkage has been found. Link 
+      // the two declarations, then merge them.
+      Importer.getImportedDecls()[D] = MergeWithVar;
+      
+      if (VarDecl *DDef = D->getDefinition()) {
+        if (VarDecl *ExistingDef = MergeWithVar->getDefinition()) {
+          Importer.ToDiag(ExistingDef->getLocation(), 
+                          diag::err_odr_variable_multiple_def)
+            << Name;
+          Importer.FromDiag(DDef->getLocation(), diag::note_odr_defined_here);
+        } else {
+          Expr *Init = Importer.Import(DDef->getInit());
+          MergeWithVar->setInit(Importer.getToContext(), Init);
+        }
+      }
+      
+      return MergeWithVar;
+    }
+    
+    if (!ConflictingDecls.empty()) {
+      Name = Importer.HandleNameConflict(Name, DC, IDNS,
+                                         ConflictingDecls.data(), 
+                                         ConflictingDecls.size());
+      if (!Name)
+        return 0;
+    }
+  }
+  
+  TypeSourceInfo *TInfo = 0;
+  if (TypeSourceInfo *FromTInfo = D->getTypeSourceInfo()) {
+    TInfo = Importer.Import(FromTInfo);
+    if (!TInfo)
+      return 0;
+  }
+  
+  // Create the imported variable.
+  VarDecl *ToVar = VarDecl::Create(Importer.getToContext(), DC, Loc, 
+                                   Name.getAsIdentifierInfo(), T, TInfo,
+                                   D->getStorageClass());
+  Importer.getImportedDecls()[D] = ToVar;
+  
+  // Merge the initializer.
+  // FIXME: Can we really import any initializer? Alternatively, we could force
+  // ourselves to import every declaration of a variable and then only use
+  // getInit() here.
+  ToVar->setInit(Importer.getToContext(),
+                 Importer.Import(const_cast<Expr *>(D->getAnyInitializer())));
+
+  // FIXME: Other bits to merge?
+  
+  return ToVar;
+}
+
 ASTImporter::ASTImporter(ASTContext &ToContext, Diagnostic &ToDiags,
                          ASTContext &FromContext, Diagnostic &FromDiags)
   : ToContext(ToContext), FromContext(FromContext),
@@ -515,3 +629,21 @@ IdentifierInfo *ASTImporter::Import(IdentifierInfo *FromId) {
 
   return &ToContext.Idents.get(FromId->getName());
 }
+
+DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name,
+                                                DeclContext *DC,
+                                                unsigned IDNS,
+                                                NamedDecl **Decls,
+                                                unsigned NumDecls) {
+  return Name;
+}
+
+DiagnosticBuilder ASTImporter::ToDiag(SourceLocation Loc, unsigned DiagID) {
+  return ToDiags.Report(FullSourceLoc(Loc, ToContext.getSourceManager()), 
+                        DiagID);
+}
+
+DiagnosticBuilder ASTImporter::FromDiag(SourceLocation Loc, unsigned DiagID) {
+  return FromDiags.Report(FullSourceLoc(Loc, FromContext.getSourceManager()), 
+                          DiagID);
+}