From: Douglas Gregor Date: Mon, 8 Feb 2010 21:09:39 +0000 (+0000) Subject: Implement basic importing and merging of variable declarations within X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=089459a16bf7e9cd10617d1fac5ec0240a0a1ee6;p=clang Implement basic importing and merging of variable declarations within 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 --- diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h index 01ec13470e..71b6443f50 100644 --- a/include/clang/AST/ASTImporter.h +++ b/include/clang/AST/ASTImporter.h @@ -44,11 +44,15 @@ namespace clang { /// to the corresponding types in the "to" context. llvm::DenseMap ImportedTypes; + /// \brief Mapping from the already-imported declarations in the "from" + /// context to the corresponding declarations in the "to" context. + llvm::DenseMap 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 &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); }; } diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index f075aaaf42..d6921df0a3 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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">; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d31dbd531a..496e0202eb 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; + } diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index 3042767af8..29a9302c5c 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -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. diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 16fd7e6184..7668b7ba67 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -15,12 +15,15 @@ #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 { + class ASTNodeImporter : public TypeVisitor, + public DeclVisitor { 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 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(*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(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); +}