diff options
-rw-r--r-- | include/clang/AST/Decl.h | 22 | ||||
-rw-r--r-- | include/clang/AST/DeclBase.h | 4 | ||||
-rw-r--r-- | include/clang/Basic/Linkage.h | 8 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 35 | ||||
-rw-r--r-- | lib/AST/DeclBase.cpp | 11 | ||||
-rw-r--r-- | lib/AST/ItaniumMangle.cpp | 19 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 20 | ||||
-rw-r--r-- | test/CodeGenCXX/c-linkage.cpp | 13 | ||||
-rw-r--r-- | test/SemaCXX/function-extern-c.cpp | 10 | ||||
-rw-r--r-- | test/SemaCXX/linkage2.cpp | 43 |
10 files changed, 145 insertions, 40 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index b6a650fea3..947525f691 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -898,11 +898,12 @@ public: /// external, C linkage. bool isExternC() const; - /// Checks if this variable has C language linkage. Note that this is not the - /// same as isExternC since decls with non external linkage can have C - /// language linkage. They can also have C language linkage when they are not - /// declared in an extern C context, but a previous decl is. - bool hasCLanguageLinkage() const; + /// Compute the language linkage. + LanguageLinkage getLanguageLinkage() const; + + bool hasCLanguageLinkage() const { + return getLanguageLinkage() == CLanguageLinkage; + } /// isLocalVarDecl - Returns true for local variable declarations /// other than parameters. Note that this includes static variables @@ -1790,11 +1791,12 @@ public: /// external, C linkage. bool isExternC() const; - /// Checks if this function has C language linkage. Note that this is not the - /// same as isExternC since decls with non external linkage can have C - /// language linkage. They can also have C language linkage when they are not - /// declared in an extern C context, but a previous decl is. - bool hasCLanguageLinkage() const; + /// Compute the language linkage. + LanguageLinkage getLanguageLinkage() const; + + bool hasCLanguageLinkage() const { + return getLanguageLinkage() == CLanguageLinkage; + } /// \brief Determines whether this is a global function. bool isGlobal() const; diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 5a9e9fedfa..3248d23f42 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -1090,6 +1090,10 @@ public: /// a C++ extern "C" linkage spec. bool isExternCContext() const; + /// \brief Determines whether this context is, or is nested within, + /// a C++ extern "C++" linkage spec. + bool isExternCXXContext() const; + /// \brief Determine whether this declaration context is equivalent /// to the declaration context DC. bool Equals(const DeclContext *DC) const { diff --git a/include/clang/Basic/Linkage.h b/include/clang/Basic/Linkage.h index 6bc1f5d5ef..01b8db15f5 100644 --- a/include/clang/Basic/Linkage.h +++ b/include/clang/Basic/Linkage.h @@ -42,6 +42,14 @@ enum Linkage { ExternalLinkage }; +/// \brief Describes the different kinds of language linkage +/// (C++ [dcl.link]) that an entity may have. +enum LanguageLinkage { + CLanguageLinkage, + CXXLanguageLinkage, + NoLanguageLinkage +}; + /// \brief A more specific kind of linkage than enum Linkage. /// /// This is relevant to CodeGen and AST file reading. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d6778ecf39..5c489273d2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -196,6 +196,12 @@ static bool useInlineVisibilityHidden(const NamedDecl *D) { FD->hasBody(Def) && Def->isInlined() && !Def->hasAttr<GNUInlineAttr>(); } +template<typename T> +bool isInExternCContext(T *D) { + const T *First = D->getFirstDeclaration(); + return First->getDeclContext()->isExternCContext(); +} + static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, bool OnlyTemplate) { assert(D->getDeclContext()->getRedeclContext()->isFileContext() && @@ -262,8 +268,8 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, if (D->isInAnonymousNamespace()) { const VarDecl *Var = dyn_cast<VarDecl>(D); const FunctionDecl *Func = dyn_cast<FunctionDecl>(D); - if ((!Var || !Var->hasCLanguageLinkage()) && - (!Func || !Func->hasCLanguageLinkage())) + if ((!Var || !isInExternCContext(Var)) && + (!Func || !isInExternCContext(Func))) return LinkageInfo::uniqueExternal(); } @@ -1210,29 +1216,36 @@ SourceRange VarDecl::getSourceRange() const { } template<typename T> -static bool hasCLanguageLinkageTemplate(const T &D) { +static LanguageLinkage getLanguageLinkageTemplate(const T &D) { // Language linkage is a C++ concept, but saying that everything in C has // C language linkage fits the implementation nicely. ASTContext &Context = D.getASTContext(); if (!Context.getLangOpts().CPlusPlus) - return true; + return CLanguageLinkage; + + // dcl.link 1: All function types, function names with external linkage, and + // variable names with external linkage have a language linkage. + if (!isExternalLinkage(D.getLinkage())) + return NoLanguageLinkage; // dcl.link 4: A C language linkage is ignored in determining the language // linkage of the names of class members and the function type of class member // functions. const DeclContext *DC = D.getDeclContext(); if (DC->isRecord()) - return false; + return CXXLanguageLinkage; // If the first decl is in an extern "C" context, any other redeclaration // will have C language linkage. If the first one is not in an extern "C" // context, we would have reported an error for any other decl being in one. const T *First = D.getFirstDeclaration(); - return First->getDeclContext()->isExternCContext(); + if (First->getDeclContext()->isExternCContext()) + return CLanguageLinkage; + return CXXLanguageLinkage; } -bool VarDecl::hasCLanguageLinkage() const { - return hasCLanguageLinkageTemplate(*this); +LanguageLinkage VarDecl::getLanguageLinkage() const { + return getLanguageLinkageTemplate(*this); } bool VarDecl::isExternC() const { @@ -1757,14 +1770,14 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const { return (proto->getArgType(1).getCanonicalType() == Context.VoidPtrTy); } -bool FunctionDecl::hasCLanguageLinkage() const { +LanguageLinkage FunctionDecl::getLanguageLinkage() const { // Users expect to be able to write // extern "C" void *__builtin_alloca (size_t); // so consider builtins as having C language linkage. if (getBuiltinID()) - return true; + return CLanguageLinkage; - return hasCLanguageLinkageTemplate(*this); + return getLanguageLinkageTemplate(*this); } bool FunctionDecl::isExternC() const { diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index e1f40356e2..f675436fc5 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -806,6 +806,17 @@ bool DeclContext::isExternCContext() const { return false; } +bool DeclContext::isExternCXXContext() const { + const DeclContext *DC = this; + while (DC->DeclKind != Decl::TranslationUnit) { + if (DC->DeclKind == Decl::LinkageSpec) + return cast<LinkageSpecDecl>(DC)->getLanguage() + == LinkageSpecDecl::lang_cxx; + DC = DC->getParent(); + } + return false; +} + bool DeclContext::Encloses(const DeclContext *DC) const { if (getPrimaryContext() != this) return getPrimaryContext()->Encloses(DC); diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index dadf9d3cc5..a7fc16aae3 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -356,17 +356,6 @@ private: } -static bool isInCLinkageSpecification(const Decl *D) { - D = D->getCanonicalDecl(); - for (const DeclContext *DC = getEffectiveDeclContext(D); - !DC->isTranslationUnit(); DC = getEffectiveParentContext(DC)) { - if (const LinkageSpecDecl *Linkage = dyn_cast<LinkageSpecDecl>(DC)) - return Linkage->getLanguage() == LinkageSpecDecl::lang_c; - } - - return false; -} - bool ItaniumMangleContext::shouldMangleDeclName(const NamedDecl *D) { // In C, functions with no attributes never need to be mangled. Fastpath them. if (!getASTContext().getLangOpts().CPlusPlus && !D->hasAttrs()) @@ -405,8 +394,12 @@ bool ItaniumMangleContext::shouldMangleDeclName(const NamedDecl *D) { return true; // C functions and "main" are not mangled. - if ((FD && FD->isMain()) || isInCLinkageSpecification(D)) - return false; + if (FD) + return !FD->isMain() && !FD->hasCLanguageLinkage(); + + // C variables are not mangled. + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) + return !VD->hasCLanguageLinkage(); return true; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 7c31be6c13..9c7fb6b4b2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2065,6 +2065,22 @@ static bool isABIDefaultCC(Sema &S, CallingConv CC, FunctionDecl *D) { return ABIDefaultCC == CC; } +template<typename T> +bool haveIncompatibleLanguageLinkages(const T *Old, const T *New) { + const DeclContext *DC = Old->getDeclContext(); + if (DC->isRecord()) + return false; + + LanguageLinkage OldLinkage = Old->getLanguageLinkage(); + if (OldLinkage == CXXLanguageLinkage && + New->getDeclContext()->isExternCContext()) + return true; + if (OldLinkage == CLanguageLinkage && + New->getDeclContext()->isExternCXXContext()) + return true; + return false; +} + /// MergeFunctionDecl - We just parsed a function 'New' from /// declarator D which has the same name and scope as a previous /// declaration 'Old'. Figure out how to resolve this situation, @@ -2366,7 +2382,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { assert(OldQTypeForComparison.isCanonical()); } - if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) { + if (haveIncompatibleLanguageLinkages(Old, New)) { Diag(New->getLocation(), diag::err_different_language_linkage) << New; Diag(Old->getLocation(), PrevDiag); return true; @@ -2756,7 +2772,7 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { return; } - if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) { + if (haveIncompatibleLanguageLinkages(Old, New)) { Diag(New->getLocation(), diag::err_different_language_linkage) << New; Diag(Old->getLocation(), diag::note_previous_definition); New->setInvalidDecl(); diff --git a/test/CodeGenCXX/c-linkage.cpp b/test/CodeGenCXX/c-linkage.cpp index b1f07b7b75..a0a496d09e 100644 --- a/test/CodeGenCXX/c-linkage.cpp +++ b/test/CodeGenCXX/c-linkage.cpp @@ -11,3 +11,16 @@ extern "C" { } // CHECK: define void @_ZN1N1X1fEv + +extern "C" { + static void test2_f() { + } + // CHECK: define internal void @_Z7test2_fv + static void test2_f(int x) { + } + // CHECK: define internal void @_Z7test2_fi + void test2_use() { + test2_f(); + test2_f(42); + } +} diff --git a/test/SemaCXX/function-extern-c.cpp b/test/SemaCXX/function-extern-c.cpp index 2a073c79fc..a4b8400abc 100644 --- a/test/SemaCXX/function-extern-c.cpp +++ b/test/SemaCXX/function-extern-c.cpp @@ -51,3 +51,13 @@ namespace test2 { }; A f(void); // expected-warning {{'f' has C-linkage specified, but returns user-defined type 'test2::A' which is incompatible with C}} } + +namespace test3 { + struct A { + A(const A&); + }; + extern "C" { + // Don't warn for static functions. + static A f(void); + } +} diff --git a/test/SemaCXX/linkage2.cpp b/test/SemaCXX/linkage2.cpp index 0adc7edca8..d1d8d3a156 100644 --- a/test/SemaCXX/linkage2.cpp +++ b/test/SemaCXX/linkage2.cpp @@ -2,20 +2,22 @@ namespace test1 { int x; // expected-note {{previous definition is here}} - static int y; // expected-note {{previous definition is here}} + static int y; void f() {} // expected-note {{previous definition is here}} extern "C" { extern int x; // expected-error {{declaration of 'x' has a different language linkage}} - extern int y; // expected-error {{declaration of 'y' has a different language linkage}} + extern int y; // OK, has internal linkage, so no language linkage. void f(); // expected-error {{declaration of 'f' has a different language linkage}} } } +// This is OK. Both test2_f don't have language linkage since they have +// internal linkage. extern "C" { - static void test2_f() { // expected-note {{previous definition is here}} + static void test2_f() { } - static void test2_f(int x) { // expected-error {{conflicting types for 'test2_f'}} + static void test2_f(int x) { } } @@ -71,3 +73,36 @@ namespace test6 { shared_future<int&> f1 = get_future<int&>(); } } + +// This is OK. The variables have internal linkage and therefore no language +// linkage. +extern "C" { + static int test7_x; +} +extern "C++" { + extern int test7_x; +} +extern "C++" { + static int test7_y; +} +extern "C" { + extern int test7_y; +} +extern "C" { typedef int test7_F(); static test7_F test7_f; } +extern "C++" { extern test7_F test7_f; } + +// FIXME: This should be invalid. The function has no language linkage, but +// the function type has, so this is redeclaring the function with a different +// type. +extern "C++" { + static void test8_f(); +} +extern "C" { + extern void test8_f(); +} +extern "C" { + static void test8_g(); +} +extern "C++" { + extern void test8_g(); +} |