From: Richard Smith Date: Fri, 28 Jun 2013 22:03:51 +0000 (+0000) Subject: PR7927, PR16247: Reimplement handling of matching extern "C" declarations X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5eab8d733ce7867fda4e6d5f5afa6dfe8a105c79;p=clang PR7927, PR16247: Reimplement handling of matching extern "C" declarations across scopes. When we declare an extern "C" name that is not a redeclaration of an entity in the same scope, check whether it redeclares some extern "C" entity from another scope, and if not, check whether it conflicts with a (non-extern-"C") entity in the translation unit. When we declare a name in the translation unit that is not a redeclaration, check whether it conflicts with any extern "C" entities (possibly from other scopes). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@185229 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 54157b317a..77fe76efd1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3493,6 +3493,11 @@ def err_static_non_static : Error< "static declaration of %0 follows non-static declaration">; def err_different_language_linkage : Error< "declaration of %0 has a different language linkage">; +def err_extern_c_global_conflict : Error< + "declaration of %1 %select{with C language linkage|in global scope}0 " + "conflicts with declaration %select{in global scope|with C language linkage}0">; +def note_extern_c_global_conflict : Note< + "declared %select{in global scope|with C language linkage}0 here">; def warn_weak_import : Warning < "an already-declared variable is made a weak_import declaration %0">; def warn_static_non_static : ExtWarn< diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a87cf61ccf..9f9bf1e39c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4359,10 +4359,14 @@ TryToFixInvalidVariablyModifiedTypeSourceInfo(TypeSourceInfo *TInfo, /// function-scope declarations. void Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, Scope *S) { - assert( - !ND->getLexicalDeclContext()->getRedeclContext()->isTranslationUnit() && - "Decl is not a locally-scoped decl!"); + if (!getLangOpts().CPlusPlus && + ND->getLexicalDeclContext()->getRedeclContext()->isTranslationUnit()) + // Don't need to track declarations in the TU in C. + return; + // Note that we have a locally-scoped external with this name. + // FIXME: There can be multiple such declarations if they are functions marked + // __attribute__((overloadable)) declared in function scope in C. LocallyScopedExternCDecls[ND->getDeclName()] = ND; } @@ -4681,6 +4685,32 @@ static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) { return isC99Inline; } +/// Determine whether a variable is extern "C" prior to attaching +/// an initializer. We can't just call isExternC() here, because that +/// will also compute and cache whether the declaration is externally +/// visible, which might change when we attach the initializer. +/// +/// This can only be used if the declaration is known to not be a +/// redeclaration of an internal linkage declaration. +/// +/// For instance: +/// +/// auto x = []{}; +/// +/// Attaching the initializer here makes this declaration not externally +/// visible, because its type has internal linkage. +/// +/// FIXME: This is a hack. +template +static bool isIncompleteDeclExternC(Sema &S, const T *D) { + if (S.getLangOpts().CPlusPlus) { + // In C++, the overloadable attribute negates the effects of extern "C". + if (!D->isInExternCContext() || D->template hasAttr()) + return false; + } + return D->isExternC(); +} + static bool shouldConsiderLinkage(const VarDecl *VD) { const DeclContext *DC = VD->getDeclContext()->getRedeclContext(); if (DC->isFunctionOrMethod()) @@ -5070,16 +5100,10 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, ProcessPragmaWeak(S, NewVD); checkAttributesAfterMerging(*this, *NewVD); - // If this is the first declaration of an extern C variable that is not - // declared directly in the translation unit, update the map of such - // variables. - if (!CurContext->getRedeclContext()->isTranslationUnit() && - !NewVD->getPreviousDecl() && !NewVD->isInvalidDecl() && - // FIXME: We only check isExternC if we're in an extern C context, - // to avoid computing and caching an 'externally visible' flag which - // could change if the variable's type is not visible. - (!getLangOpts().CPlusPlus || NewVD->isInExternCContext()) && - NewVD->isExternC()) + // If this is the first declaration of an extern C variable, update + // the map of such variables. + if (!NewVD->getPreviousDecl() && !NewVD->isInvalidDecl() && + isIncompleteDeclExternC(*this, NewVD)) RegisterLocallyScopedExternCDecl(NewVD, S); return NewVD; @@ -5180,30 +5204,120 @@ void Sema::CheckShadow(Scope *S, VarDecl *D) { CheckShadow(S, D, R); } +/// Check for conflict between this global or extern "C" declaration and +/// previous global or extern "C" declarations. This is only used in C++. template -static bool mayConflictWithNonVisibleExternC(const T *ND) { - const DeclContext *DC = ND->getDeclContext(); - if (DC->getRedeclContext()->isTranslationUnit()) - return true; +static bool checkGlobalOrExternCConflict( + Sema &S, const T *ND, bool IsGlobal, LookupResult &Previous) { + assert(S.getLangOpts().CPlusPlus && "only C++ has extern \"C\""); + NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName()); + + if (!Prev && IsGlobal && !isIncompleteDeclExternC(S, ND)) { + // The common case: this global doesn't conflict with any extern "C" + // declaration. + return false; + } - // We know that is the first decl we see, other than function local - // extern C ones. If this is C++ and the decl is not in a extern C context - // it cannot have C language linkage. Avoid calling isExternC in that case. - // We need to this because of code like - // - // namespace { struct bar {}; } - // auto foo = bar(); - // - // This code runs before the init of foo is set, and therefore before - // the type of foo is known. Not knowing the type we cannot know its linkage - // unless it is in an extern C block. - if (!ND->isInExternCContext()) { - const ASTContext &Context = ND->getASTContext(); - if (Context.getLangOpts().CPlusPlus) + if (Prev) { + if (!IsGlobal || isIncompleteDeclExternC(S, ND)) { + // Both the old and new declarations have C language linkage. This is a + // redeclaration. + Previous.clear(); + Previous.addDecl(Prev); + return true; + } + + // This is a global, non-extern "C" declaration, and there is a previous + // non-global extern "C" declaration. Diagnose. + } else { + // The declaration is extern "C". Check for any declaration in the + // translation unit which might conflict. + if (IsGlobal) { + // We have already performed the lookup into the translation unit. + IsGlobal = false; + for (LookupResult::iterator I = Previous.begin(), E = Previous.end(); + I != E; ++I) { + if (isa(*I) || isa(*I)) { + Prev = *I; + break; + } + } + } else { + DeclContext::lookup_result R = + S.Context.getTranslationUnitDecl()->lookup(ND->getDeclName()); + for (DeclContext::lookup_result::iterator I = R.begin(), E = R.end(); + I != E; ++I) { + if (isa(*I) || isa(*I)) { + Prev = *I; + break; + } + // FIXME: If we have any other entity with this name in global scope, + // the declaration is ill-formed, but that is a defect: it breaks the + // 'stat' hack, for instance. + } + } + + if (!Prev) return false; } - return ND->isExternC(); + // Use the first declaration's location to ensure we point at something which + // is lexically inside an extern "C" linkage-spec. + assert(Prev && "should have found a previous declaration to diagnose"); + if (FunctionDecl *FD = dyn_cast(Prev)) + Prev = FD->getFirstDeclaration(); + else + Prev = cast(Prev)->getFirstDeclaration(); + + S.Diag(ND->getLocation(), diag::err_extern_c_global_conflict) + << IsGlobal << ND; + S.Diag(Prev->getLocation(), diag::note_extern_c_global_conflict) + << IsGlobal; + return false; +} + +/// Apply special rules for handling extern "C" declarations. Returns \c true +/// if we have found that this is a redeclaration of some prior entity. +/// +/// Per C++ [dcl.link]p6: +/// Two declarations [for a function or variable] with C language linkage +/// with the same name that appear in different scopes refer to the same +/// [entity]. An entity with C language linkage shall not be declared with +/// the same name as an entity in global scope. +template +static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND, + LookupResult &Previous) { + if (!S.getLangOpts().CPlusPlus) { + // In C, when declaring a global variable, look for a corresponding 'extern' + // variable declared in function scope. + // + // FIXME: The corresponding case in C++ does not work. We should instead + // set the semantic DC for an extern local variable to be the innermost + // enclosing namespace, and ensure they are only found by redeclaration + // lookup. + if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) { + if (NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName())) { + Previous.clear(); + Previous.addDecl(Prev); + return true; + } + } + return false; + } + + // A declaration in the translation unit can conflict with an extern "C" + // declaration. + if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) + return checkGlobalOrExternCConflict(S, ND, /*IsGlobal*/true, Previous); + + // An extern "C" declaration can conflict with a declaration in the + // translation unit or can be a redeclaration of an extern "C" declaration + // in another scope. + if (isIncompleteDeclExternC(S,ND)) + return checkGlobalOrExternCConflict(S, ND, /*IsGlobal*/false, Previous); + + // Neither global nor extern "C": nothing to do. + return false; } void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { @@ -5386,14 +5500,9 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, // The most important point here is that we're not allowed to // update our understanding of the type according to declarations // not in scope. - bool PreviousWasHidden = false; - if (Previous.empty() && mayConflictWithNonVisibleExternC(NewVD)) { - if (NamedDecl *ExternCPrev = - findLocallyScopedExternCDecl(NewVD->getDeclName())) { - Previous.addDecl(ExternCPrev); - PreviousWasHidden = true; - } - } + bool PreviousWasHidden = + Previous.empty() && + checkForConflictWithNonVisibleExternC(*this, NewVD, Previous); // Filter out any non-conflicting previous declarations. filterNonConflictingPreviousDecls(Context, NewVD, Previous); @@ -6625,12 +6734,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // marking the function. AddCFAuditedAttribute(NewFD); - // If this is the first declaration of an extern C variable that is not - // declared directly in the translation unit, update the map of such - // variables. - if (!CurContext->getRedeclContext()->isTranslationUnit() && - !NewFD->getPreviousDecl() && NewFD->isExternC() && - !NewFD->isInvalidDecl()) + // If this is the first declaration of an extern C variable, update + // the map of such variables. + if (!NewFD->getPreviousDecl() && !NewFD->isInvalidDecl() && + isIncompleteDeclExternC(*this, NewFD)) RegisterLocallyScopedExternCDecl(NewFD, S); // Set this FunctionDecl's range up to the right paren. @@ -6734,15 +6841,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, assert(!NewFD->getResultType()->isVariablyModifiedType() && "Variably modified return types are not handled here"); - // Check for a previous declaration of this name. - if (Previous.empty() && mayConflictWithNonVisibleExternC(NewFD)) { - // Since we did not find anything by this name, look for a non-visible - // extern "C" declaration with the same name. - if (NamedDecl *ExternCPrev = - findLocallyScopedExternCDecl(NewFD->getDeclName())) - Previous.addDecl(ExternCPrev); - } - // Filter out any non-conflicting previous declarations. filterNonConflictingPreviousDecls(Context, NewFD, Previous); @@ -6797,6 +6895,34 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, } } + // Check for a previous extern "C" declaration with this name. + if (!Redeclaration && + checkForConflictWithNonVisibleExternC(*this, NewFD, Previous)) { + filterNonConflictingPreviousDecls(Context, NewFD, Previous); + if (!Previous.empty()) { + // This is an extern "C" declaration with the same name as a previous + // declaration, and thus redeclares that entity... + Redeclaration = true; + OldDecl = Previous.getFoundDecl(); + + // ... except in the presence of __attribute__((overloadable)). + if (OldDecl->hasAttr()) { + if (!getLangOpts().CPlusPlus && !NewFD->hasAttr()) { + Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing) + << Redeclaration << NewFD; + Diag(Previous.getFoundDecl()->getLocation(), + diag::note_attribute_overloadable_prev_overload); + NewFD->addAttr(::new (Context) OverloadableAttr(SourceLocation(), + Context)); + } + if (IsOverload(NewFD, cast(OldDecl), false)) { + Redeclaration = false; + OldDecl = 0; + } + } + } + } + // C++11 [dcl.constexpr]p8: // A constexpr specifier for a non-static member function that is not // a constructor declares that member function to be const. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 3f7ab1c90f..42eced466e 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -977,21 +977,12 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, return Ovl_Overload; } -static bool canBeOverloaded(const FunctionDecl &D) { - if (D.getAttr()) - return true; - if (D.isExternC()) - return false; - - // Main cannot be overloaded (basic.start.main). - if (D.isMain()) +bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, + bool UseUsingDeclRules) { + // C++ [basic.start.main]p2: This function shall not be overloaded. + if (New->isMain()) return false; - return true; -} - -static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, - bool UseUsingDeclRules) { FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate(); FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate(); @@ -1002,8 +993,8 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, return true; // Is the function New an overload of the function Old? - QualType OldQType = S.Context.getCanonicalType(Old->getType()); - QualType NewQType = S.Context.getCanonicalType(New->getType()); + QualType OldQType = Context.getCanonicalType(Old->getType()); + QualType NewQType = Context.getCanonicalType(New->getType()); // Compare the signatures (C++ 1.3.10) of the two functions to // determine whether they are overloads. If we find any mismatch @@ -1024,7 +1015,7 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, if (OldQType != NewQType && (OldType->getNumArgs() != NewType->getNumArgs() || OldType->isVariadic() != NewType->isVariadic() || - !S.FunctionArgTypesAreEqual(OldType, NewType))) + !FunctionArgTypesAreEqual(OldType, NewType))) return true; // C++ [temp.over.link]p4: @@ -1040,9 +1031,9 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, // However, we don't consider either of these when deciding whether // a member introduced by a shadow declaration is hidden. if (!UseUsingDeclRules && NewTemplate && - (!S.TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), - OldTemplate->getTemplateParameters(), - false, S.TPL_TemplateMatch) || + (!TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), + OldTemplate->getTemplateParameters(), + false, TPL_TemplateMatch) || OldType->getResultType() != NewType->getResultType())) return true; @@ -1068,9 +1059,9 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, // declarations with the same name, the same parameter-type-list, and // the same template parameter lists cannot be overloaded if any of // them, but not all, have a ref-qualifier (8.3.5). - S.Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload) + Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload) << NewMethod->getRefQualifier() << OldMethod->getRefQualifier(); - S.Diag(OldMethod->getLocation(), diag::note_previous_declaration); + Diag(OldMethod->getLocation(), diag::note_previous_declaration); } return true; } @@ -1080,7 +1071,7 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, // or non-static member function). Add it now, on the assumption that this // is a redeclaration of OldMethod. unsigned NewQuals = NewMethod->getTypeQualifiers(); - if (!S.getLangOpts().CPlusPlus1y && NewMethod->isConstexpr() && + if (!getLangOpts().CPlusPlus1y && NewMethod->isConstexpr() && !isa(NewMethod)) NewQuals |= Qualifiers::Const; if (OldMethod->getTypeQualifiers() != NewQuals) @@ -1091,19 +1082,6 @@ static bool shouldTryToOverload(Sema &S, FunctionDecl *New, FunctionDecl *Old, return false; } -bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, - bool UseUsingDeclRules) { - if (!shouldTryToOverload(*this, New, Old, UseUsingDeclRules)) - return false; - - // If both of the functions are extern "C", then they are not - // overloads. - if (!canBeOverloaded(*Old) && !canBeOverloaded(*New)) - return false; - - return true; -} - /// \brief Checks availability of the function depending on the current /// function context. Inside an unavailable function, unavailability is ignored. /// diff --git a/test/CXX/class.access/class.friend/p1.cpp b/test/CXX/class.access/class.friend/p1.cpp index 19d94cfdd5..1a519dcc3e 100644 --- a/test/CXX/class.access/class.friend/p1.cpp +++ b/test/CXX/class.access/class.friend/p1.cpp @@ -287,22 +287,22 @@ namespace test9 { // PR7230 namespace test10 { - extern "C" void f(void); - extern "C" void g(void); + extern "C" void test10_f(void); + extern "C" void test10_g(void); namespace NS { class C { void foo(void); // expected-note {{declared private here}} - friend void test10::f(void); + friend void test10::test10_f(void); }; static C* bar; } - void f(void) { + void test10_f(void) { NS::bar->foo(); } - void g(void) { + void test10_g(void) { NS::bar->foo(); // expected-error {{private member}} } } diff --git a/test/Sema/overloadable.c b/test/Sema/overloadable.c index 5d39f15ec8..ad021898f2 100644 --- a/test/Sema/overloadable.c +++ b/test/Sema/overloadable.c @@ -69,3 +69,18 @@ void test() { f0(); f1(); } + +void before_local_1(int) __attribute__((overloadable)); // expected-note {{here}} +void before_local_2(int); // expected-note {{here}} +void before_local_3(int) __attribute__((overloadable)); +void local() { + void before_local_1(char); // expected-error {{must have the 'overloadable' attribute}} + void before_local_2(char) __attribute__((overloadable)); // expected-error {{conflicting types}} + void before_local_3(char) __attribute__((overloadable)); + void after_local_1(char); // expected-note {{here}} + void after_local_2(char) __attribute__((overloadable)); // expected-note {{here}} + void after_local_3(char) __attribute__((overloadable)); +} +void after_local_1(int) __attribute__((overloadable)); // expected-error {{conflicting types}} +void after_local_2(int); // expected-error {{must have the 'overloadable' attribute}} +void after_local_3(int) __attribute__((overloadable)); diff --git a/test/SemaCXX/extern-c.cpp b/test/SemaCXX/extern-c.cpp index 220b2a8dc1..aacc0ffb15 100644 --- a/test/SemaCXX/extern-c.cpp +++ b/test/SemaCXX/extern-c.cpp @@ -3,20 +3,20 @@ namespace test1 { extern "C" { void test1_f() { - void test1_g(int); // expected-note {{previous declaration is here}} + void test1_g(int); // expected-note {{declared with C language linkage here}} } } } -int test1_g(int); // expected-error {{functions that differ only in their return type cannot be overloaded}} +int test1_g(int); // expected-error {{declaration of 'test1_g' in global scope conflicts with declaration with C language linkage}} namespace test2 { extern "C" { void test2_f() { - extern int test2_x; // expected-note {{previous definition is here}} + extern int test2_x; // expected-note {{declared with C language linkage here}} } } } -float test2_x; // expected-error {{redefinition of 'test2_x' with a different type: 'float' vs 'int'}} +float test2_x; // expected-error {{declaration of 'test2_x' in global scope conflicts with declaration with C language linkage}} namespace test3 { extern "C" { @@ -31,18 +31,18 @@ namespace test3 { extern "C" { void test4_f() { - extern int test4_b; // expected-note {{previous definition is here}} + extern int test4_b; // expected-note {{declared with C language linkage here}} } } -static float test4_b; // expected-error {{redefinition of 'test4_b' with a different type: 'float' vs 'int'}} +static float test4_b; // expected-error {{declaration of 'test4_b' in global scope conflicts with declaration with C language linkage}} extern "C" { void test5_f() { - extern int test5_b; // expected-note {{previous definition is here}} + extern int test5_b; // expected-note {{declared with C language linkage here}} } } extern "C" { - static float test5_b; // expected-error {{redefinition of 'test5_b' with a different type: 'float' vs 'int'}} + static float test5_b; // expected-error {{declaration of 'test5_b' in global scope conflicts with declaration with C language linkage}} } extern "C" { @@ -69,11 +69,11 @@ namespace linkage { } } namespace from_outer { - void linkage_from_outer_1(); + void linkage_from_outer_1(); // expected-note {{previous}} void linkage_from_outer_2(); // expected-note {{previous}} extern "C" { - void linkage_from_outer_1(int); // expected-note {{previous}} - void linkage_from_outer_1(); // expected-error {{conflicting types}} + void linkage_from_outer_1(int); + void linkage_from_outer_1(); // expected-error {{different language linkage}} void linkage_from_outer_2(); // expected-error {{different language linkage}} } } @@ -98,11 +98,44 @@ namespace linkage { } } -void lookup_in_global_f(); +void lookup_in_global_f(); // expected-note {{here}} namespace lookup_in_global { void lookup_in_global_f(); + void lookup_in_global_g(); extern "C" { - // FIXME: We should reject this. - void lookup_in_global_f(int); + void lookup_in_global_f(int); // expected-error {{conflicts with declaration in global scope}} + void lookup_in_global_g(int); // expected-note {{here}} } } +void lookup_in_global_g(); // expected-error {{conflicts with declaration with C language linkage}} + +namespace N1 { + extern "C" int different_kind_1; // expected-note {{here}} + extern "C" void different_kind_2(); // expected-note {{here}} +} +namespace N2 { + extern "C" void different_kind_1(); // expected-error {{different kind of symbol}} + extern "C" int different_kind_2; // expected-error {{different kind of symbol}} +} + +extern "C" { + struct stat {}; + void stat(struct stat); +} +namespace X { + extern "C" { + void stat(struct ::stat); + } +} + +extern "C" void name_with_using_decl_1(int); +namespace using_decl { + void name_with_using_decl_1(); + void name_with_using_decl_2(); + void name_with_using_decl_3(); +} +using using_decl::name_with_using_decl_1; +using using_decl::name_with_using_decl_2; +extern "C" void name_with_using_decl_2(int); +extern "C" void name_with_using_decl_3(int); +using using_decl::name_with_using_decl_3; diff --git a/test/SemaCXX/friend.cpp b/test/SemaCXX/friend.cpp index 5daadf0e72..ce2f34ff6b 100644 --- a/test/SemaCXX/friend.cpp +++ b/test/SemaCXX/friend.cpp @@ -134,7 +134,7 @@ namespace test6_3 { namespace test7 { extern "C" { class X { - friend int f() { return 42; } + friend int test7_f() { return 42; } }; } } diff --git a/test/SemaCXX/linkage-spec.cpp b/test/SemaCXX/linkage-spec.cpp index fc14081247..355a878793 100644 --- a/test/SemaCXX/linkage-spec.cpp +++ b/test/SemaCXX/linkage-spec.cpp @@ -41,20 +41,32 @@ namespace pr5430 { using namespace pr5430; extern "C" void pr5430::func(void) { } -// PR5404 -int f2(char *) +// PR5405 +int f2(char *) // expected-note {{here}} { return 0; } extern "C" { - int f2(int) + int f2(int) // expected-error {{with C language linkage conflicts with declaration in global scope}} { return f2((char *)0); } } +namespace PR5405 { + int f2b(char *) { + return 0; + } + + extern "C" { + int f2b(int) { + return f2b((char *)0); // ok + } + } +} + // PR6991 extern "C" typedef int (*PutcFunc_t)(int); @@ -117,3 +129,28 @@ namespace pr14958 { extern "C" void PR16167; // expected-error {{variable has incomplete type 'void'}} extern void PR16167_0; // expected-error {{variable has incomplete type 'void'}} + +// PR7927 +enum T_7927 { + E_7927 +}; + +extern "C" void f_pr7927(int); + +namespace { + extern "C" void f_pr7927(int); + + void foo_pr7927() { + f_pr7927(E_7927); + f_pr7927(0); + ::f_pr7927(E_7927); + ::f_pr7927(0); + } +} + +void bar_pr7927() { + f_pr7927(E_7927); + f_pr7927(0); + ::f_pr7927(E_7927); + ::f_pr7927(0); +} diff --git a/test/SemaCXX/linkage2.cpp b/test/SemaCXX/linkage2.cpp index a811575c61..c97a2f4ba7 100644 --- a/test/SemaCXX/linkage2.cpp +++ b/test/SemaCXX/linkage2.cpp @@ -201,3 +201,15 @@ namespace test18 { } void *h() { return f(); } } + +extern "C" void pr16247_foo(int); // expected-note {{here}} +static void pr16247_foo(double); // expected-error {{conflicts with declaration with C language linkage}} +void pr16247_foo(int) {} +void pr16247_foo(double) {} + +namespace PR16247 { + extern "C" void pr16247_bar(int); + static void pr16247_bar(double); + void pr16247_bar(int) {} + void pr16247_bar(double) {} +}