From: Douglas Gregor Date: Wed, 10 Feb 2010 19:54:31 +0000 (+0000) Subject: Implement basic support for merging function declarations across X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a404ea673cbee5e74af710a5f1ab571e71580b67;p=clang Implement basic support for merging function declarations across translation units. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95794 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 935d15ac13..b1d3920ae0 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -34,5 +34,8 @@ 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">; +def err_odr_function_type_inconsistent : Error< + "external function %0 declared with incompatible types in different " + "translation units (%1 vs. %2)">; def err_unsupported_ast_node: Error<"cannot import unsupported AST node %0">; } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 5201f29b90..3918a5811c 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -73,8 +73,17 @@ namespace { QualType VisitObjCObjectPointerType(ObjCObjectPointerType *T); // Importing declarations + bool ImportDeclParts(NamedDecl *D, DeclContext *&DC, + DeclContext *&LexicalDC, DeclarationName &Name, + SourceLocation &Loc); + bool ImportDeclParts(DeclaratorDecl *D, + DeclContext *&DC, DeclContext *&LexicalDC, + DeclarationName &Name, SourceLocation &Loc, + QualType &T); Decl *VisitDecl(Decl *D); + Decl *VisitFunctionDecl(FunctionDecl *D); Decl *VisitVarDecl(VarDecl *D); + Decl *VisitParmVarDecl(ParmVarDecl *D); Decl *VisitTypedefDecl(TypedefDecl *D); }; } @@ -448,37 +457,156 @@ QualType ASTNodeImporter::VisitObjCObjectPointerType(ObjCObjectPointerType *T) { //---------------------------------------------------------------------------- // Import Declarations //---------------------------------------------------------------------------- -Decl *ASTNodeImporter::VisitDecl(Decl *D) { - Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) - << D->getDeclKindName(); - return 0; -} - -Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) { +bool ASTNodeImporter::ImportDeclParts(NamedDecl *D, DeclContext *&DC, + DeclContext *&LexicalDC, + DeclarationName &Name, + SourceLocation &Loc) { // Import the context of this declaration. - DeclContext *DC = Importer.ImportContext(D->getDeclContext()); + DC = Importer.ImportContext(D->getDeclContext()); if (!DC) - return 0; - - DeclContext *LexicalDC = DC; + return true; + + LexicalDC = DC; if (D->getDeclContext() != D->getLexicalDeclContext()) { LexicalDC = Importer.ImportContext(D->getLexicalDeclContext()); if (!LexicalDC) - return 0; + return true; } - + // Import the name of this declaration. - DeclarationName Name = Importer.Import(D->getDeclName()); + Name = Importer.Import(D->getDeclName()); if (D->getDeclName() && !Name) - return 0; + return true; + + // Import the location of this declaration. + Loc = Importer.Import(D->getLocation()); + return false; +} + +bool ASTNodeImporter::ImportDeclParts(DeclaratorDecl *D, + DeclContext *&DC, + DeclContext *&LexicalDC, + DeclarationName &Name, + SourceLocation &Loc, + QualType &T) { + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return true; // Import the type of this declaration. - QualType T = Importer.Import(D->getType()); + T = Importer.Import(D->getType()); if (T.isNull()) + return true; + + return false; +} + +Decl *ASTNodeImporter::VisitDecl(Decl *D) { + Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) + << D->getDeclKindName(); + return 0; +} + +Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { + // Import the major distinguishing characteristics of this function. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + QualType T; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T)) return 0; - // Import the location of this declaration. - SourceLocation Loc = Importer.Import(D->getLocation()); + // Try to find a function in our own ("to") context with the same name, same + // type, and in the same context as the function we're importing. + if (!LexicalDC->isFunctionOrMethod()) { + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + if (FunctionDecl *FoundFunction = dyn_cast(*Lookup.first)) { + if (isExternalLinkage(FoundFunction->getLinkage()) && + isExternalLinkage(D->getLinkage())) { + if (Importer.getToContext().typesAreCompatible(T, + FoundFunction->getType())) { + // FIXME: Actually try to merge the body and other attributes. + Importer.getImportedDecls()[D] = FoundFunction; + return FoundFunction; + } + + // FIXME: Check for overloading more carefully, e.g., by boosting + // Sema::IsOverload out to the AST library. + + // Function overloading is okay in C++. + if (Importer.getToContext().getLangOptions().CPlusPlus) + continue; + + // Complain about inconsistent function types. + Importer.ToDiag(Loc, diag::err_odr_function_type_inconsistent) + << Name << T << FoundFunction->getType(); + Importer.ToDiag(FoundFunction->getLocation(), + diag::note_odr_value_here) + << FoundFunction->getType(); + } + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + // Import the function parameters. + llvm::SmallVector Parameters; + for (FunctionDecl::param_iterator P = D->param_begin(), PEnd = D->param_end(); + P != PEnd; ++P) { + ParmVarDecl *ToP = cast_or_null(Importer.Import(*P)); + if (!ToP) + return 0; + + Parameters.push_back(ToP); + } + + // Create the imported function. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + FunctionDecl *ToFunction + = FunctionDecl::Create(Importer.getToContext(), DC, Loc, + Name, T, TInfo, D->getStorageClass(), + D->isInlineSpecified(), + D->hasWrittenPrototype()); + ToFunction->setLexicalDeclContext(LexicalDC); + Importer.getImportedDecls()[D] = ToFunction; + LexicalDC->addDecl(ToFunction); + + // Set the parameters. + for (unsigned I = 0, N = Parameters.size(); I != N; ++I) { + Parameters[I]->setOwningFunction(ToFunction); + ToFunction->addDecl(Parameters[I]); + } + ToFunction->setParams(Importer.getToContext(), + Parameters.data(), Parameters.size()); + + // FIXME: Other bits to merge? + + return ToFunction; +} + +Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) { + // Import the major distinguishing characteristics of a variable. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + QualType T; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T)) + return 0; // 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. @@ -589,32 +717,47 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) { return ToVar; } -Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) { - // Import the context of this declaration. - DeclContext *DC = Importer.ImportContext(D->getDeclContext()); - if (!DC) - return 0; - - DeclContext *LexicalDC = DC; - if (D->getDeclContext() != D->getLexicalDeclContext()) { - LexicalDC = Importer.ImportContext(D->getLexicalDeclContext()); - if (!LexicalDC) - return 0; - } - +Decl *ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { + // Parameters are created in the translation unit's context, then moved + // into the function declaration's context afterward. + DeclContext *DC = Importer.getToContext().getTranslationUnitDecl(); + // 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->getUnderlyingType()); + // Import the location of this declaration. + SourceLocation Loc = Importer.Import(D->getLocation()); + + // Import the parameter's type. + QualType T = Importer.Import(D->getType()); if (T.isNull()) return 0; - // Import the location of this declaration. - SourceLocation Loc = Importer.Import(D->getLocation()); + // Create the imported parameter. + TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + ParmVarDecl *ToParm = ParmVarDecl::Create(Importer.getToContext(), DC, + Loc, Name.getAsIdentifierInfo(), + T, TInfo, D->getStorageClass(), + /*FIXME: Default argument*/ 0); + Importer.getImportedDecls()[D] = ToParm; + return ToParm; +} +Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) { + // Import the major distinguishing characteristics of this typedef. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Import the underlying type of this typedef; + QualType T = Importer.Import(D->getUnderlyingType()); + if (T.isNull()) + return 0; + // If this typedef is not in block scope, determine whether we've // seen a typedef with the same name (that we can merge with) or any // other entity by that name (which name lookup could conflict with). diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index f2de09a74c..fd34ddae12 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -57,14 +57,21 @@ void ASTMergeAction::ExecuteAction() { for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); D != DEnd; ++D) { - // FIXME: We only merge variables whose names start with x. Why - // would anyone want anything else? - if (VarDecl *VD = dyn_cast(*D)) + // FIXME: We only merge variables whose names start with x and functions + // whose names start with 'f'. Why would anyone want anything else? + if (VarDecl *VD = dyn_cast(*D)) { if (VD->getIdentifier() && *VD->getIdentifier()->getNameStart() == 'x') { Decl *Merged = Importer.Import(VD); (void)Merged; } + } else if (FunctionDecl *FD = dyn_cast(*D)) { + if (FD->getIdentifier() && + *FD->getIdentifier()->getNameStart() == 'f') { + Decl *Merged = Importer.Import(FD); + (void)Merged; + } + } } delete Unit; diff --git a/test/ASTMerge/Inputs/function1.c b/test/ASTMerge/Inputs/function1.c new file mode 100644 index 0000000000..b999123266 --- /dev/null +++ b/test/ASTMerge/Inputs/function1.c @@ -0,0 +1,6 @@ +void f0(int); +void f1(int, float); +void f2(); +void f3(void); +void f4(int, int); + diff --git a/test/ASTMerge/Inputs/function2.c b/test/ASTMerge/Inputs/function2.c new file mode 100644 index 0000000000..ad81c07375 --- /dev/null +++ b/test/ASTMerge/Inputs/function2.c @@ -0,0 +1,6 @@ +typedef int Int; +void f0(Int); +void f1(Int, double); +void f2(int, int); +void f3(int); +static void f4(float, float); diff --git a/test/ASTMerge/function.c b/test/ASTMerge/function.c new file mode 100644 index 0000000000..581b6ec588 --- /dev/null +++ b/test/ASTMerge/function.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/function1.c +// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/function2.c +// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: function2.c:3:6: error: external function 'f1' declared with incompatible types in different translation units ('void (Int, double)' vs. 'void (int, float)') +// CHECK: function1.c:2:6: note: declared here with type 'void (int, float)' +// CHECK: function2.c:5:6: error: external function 'f3' declared with incompatible types in different translation units ('void (int)' vs. 'void (void)') +// CHECK: function1.c:4:6: note: declared here with type 'void (void)' +// CHECK: 4 diagnostics generated