Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -769,6 +769,8 @@ FD->setCachedLinkage(Linkage(Record.readInt())); FD->EndRangeLoc = ReadSourceLocation(); + bool IsFunctionNoProtoType = FD->getType()->getAs(); + switch ((FunctionDecl::TemplatedKind)Record.readInt()) { case FunctionDecl::TK_NonTemplate: mergeRedeclarable(FD, Redecl); @@ -876,6 +878,12 @@ // Read in the parameters. unsigned NumParams = Record.readInt(); + + // Return here if FD is a non-prototype function declaration and has been + // merged with a function prototype. + if (IsFunctionNoProtoType && FD->getType()->getAs()) + return; + SmallVector Params; Params.reserve(NumParams); for (unsigned I = 0; I != NumParams; ++I) @@ -2415,6 +2423,35 @@ llvm_unreachable("merged an unknown kind of redeclarable template"); } +static void mergeCompatibleFunctionDecls(FunctionDecl *OldFD, + FunctionDecl *NewFD, + ASTContext &Context) { + const auto *OldFuncType = OldFD->getType()->getAs(); + const auto *NewFuncType = NewFD->getType()->getAs(); + const FunctionProtoType *OldProto = dyn_cast(OldFuncType); + + if (!OldProto || !isa(NewFuncType)) + return; + + SmallVector ParamTypes(OldProto->param_types()); + QualType NewType = Context.getFunctionType( + NewFuncType->getReturnType(), ParamTypes, OldProto->getExtProtoInfo()); + NewFD->setType(NewType); + NewFD->setHasInheritedPrototype(); + + SmallVector Params; + for (const auto &ParamType : OldProto->param_types()) { + auto *Param = ParmVarDecl::Create(Context, NewFD, SourceLocation(), + SourceLocation(), nullptr, ParamType, + nullptr, SC_None, nullptr); + Param->setScopeInfo(0, Params.size()); + Param->setImplicit(); + Params.push_back(Param); + } + + NewFD->setParams(Params); +} + /// \brief Attempts to merge the given declaration (D) with another declaration /// of the same entity. template @@ -2436,6 +2473,13 @@ ExistingCanon->Used |= D->Used; D->Used = false; + if (!Reader.getContext().getLangOpts().CPlusPlus) { + auto *OldFD = dyn_cast(Existing); + auto *FD = dyn_cast(D); + if (OldFD && FD) + mergeCompatibleFunctionDecls(OldFD, FD, Reader.getContext()); + } + // When we merge a namespace, update its pointer to the first namespace. // We cannot have loaded any redeclarations of this declaration yet, so // there's nothing else that needs to be updated. @@ -2689,6 +2733,10 @@ /// B. Will ignore any overloadable attrs represented in the type of A and B. static bool hasSameOverloadableAttrs(const FunctionDecl *A, const FunctionDecl *B) { + if (A->getType()->getAs()->getNoReturnAttr() != + B->getType()->getAs()->getNoReturnAttr()) + return false; + // Note that pass_object_size attributes are represented in the function's // ExtParameterInfo, so we don't need to check them here. @@ -2780,7 +2828,7 @@ return false; } ASTContext &C = FuncX->getASTContext(); - if (!C.hasSameType(FuncX->getType(), FuncY->getType())) { + if (!C.typesAreCompatible(FuncX->getType(), FuncY->getType())) { // We can get functions with different types on the redecl chain in C++17 // if they have differing exception specifications and at least one of // the excpetion specs is unresolved. Index: test/Modules/Inputs/merge-non-prototype-fn/header2.h =================================================================== --- /dev/null +++ test/Modules/Inputs/merge-non-prototype-fn/header2.h @@ -0,0 +1,7 @@ +#ifndef FUNC1 +#define FUNC1 + +char *func1(const char *, int); +char *func2(); + +#endif Index: test/Modules/Inputs/merge-non-prototype-fn/module.map =================================================================== --- /dev/null +++ test/Modules/Inputs/merge-non-prototype-fn/module.map @@ -0,0 +1,5 @@ +module header2 { + header "header2.h" + export * +} + Index: test/Modules/merge-non-prototype-fn.c =================================================================== --- /dev/null +++ test/Modules/merge-non-prototype-fn.c @@ -0,0 +1,13 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -I%S/Inputs/merge-non-prototype-fn -fmodules -fimplicit-module-maps -x c -fmodules-cache-path=%t -fsyntax-only -verify %s + +char *func1(); +char *func2(const char*, int); + +#include "header2.h" + +void foo1(void) { + func1("abc", 12); + func2(1, 2, 3, 4); // expected-error {{too many arguments to function call, expected 2, have 4}} + // expected-note@header2.h:5 {{'func2' declared here}} +}