Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1413,8 +1413,10 @@ } def Overloadable : Attr { - let Spellings = [GNU<"overloadable">]; + let Spellings = [GNU<"overloadable">, GNU<"transparently_overloadable">]; let Subjects = SubjectList<[Function], ErrorDiag>; + let Accessors = [Accessor<"isTransparent", + [GNU<"transparently_overloadable">]>]; let Documentation = [OverloadableDocs]; } Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -651,6 +651,33 @@ linkage specification, it's name *will* be mangled in the same way as it would in C. +For the purpose of backwards compatibility, overload sets may have at most one +``overloadable`` function marked as transparent. Transparent overloads do not +carry the requirement that the name be mangled, and are not subject to the +requirement that all further declarations must have an accompanying +``overloadable`` attribute on them. You can designate a transparent overload by +using the ``transparently_overloadable`` attribute instead of ``overloadable``. + +An example of all of this: + +.. code-block:: c + + // Notes with mangled names assume Itanium mangling. + int f(int) __attribute__((transparently_overloadable)); + int f(double) __attribute__((overloadable)); + int f(int); // Redeclaration without an overloadable attribute is OK. + int f(int) __attribute__((transparently_overloadable)); // OK + void foo() { + f(5); // Emits a call to f (not _Zf1i, as it would with an overload that + // wasn't marked as transparent). + f(1.0); // Emits a call to _Z1fd. + } + + // Error: f already has a transparent overload + int f(float) __attribute__((transparently_overloadable)); + // Error: f(int) is declared transparent above, but is not here. + int f(int) __attribute__((overloadable)); + Query for this feature with ``__has_extension(attribute_overloadable)``. }]; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3253,9 +3253,13 @@ "%select{overloaded function|redeclaration of}0 %1 must have the " "'overloadable' attribute">; def note_attribute_overloadable_prev_overload : Note< - "previous overload of function is here">; + "previous%select{| transparent}0 overload of function is here">; def err_attribute_overloadable_no_prototype : Error< "'overloadable' function %0 must have a prototype">; +def err_attribute_overloadable_too_many_transparent_overloads : Error< + "only one 'overloadable' overload may be transparent">; +def err_attribute_overloadable_mismatched_transparency : Error< + "mismatched transparency on redeclaration of an 'overloadable' function">; def warn_ns_attribute_wrong_return_type : Warning< "%0 attribute only applies to %select{functions|methods|properties}1 that " "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -514,7 +514,10 @@ llvm::SmallPtrSet ParsingInitForAutoVars; /// \brief Look for a locally scoped extern "C" declaration by the given name. - NamedDecl *findLocallyScopedExternCDecl(DeclarationName Name); + /// + /// If AllResults is not null, any results we find will be appended to it. + NamedDecl *findLocallyScopedExternCDecl( + DeclarationName Name, SmallVectorImpl *AllResults = nullptr); typedef LazyVector Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -579,9 +579,10 @@ const FunctionDecl *FD = dyn_cast(D); if (FD) { LanguageLinkage L = FD->getLanguageLinkage(); - // Overloadable functions need mangling. - if (FD->hasAttr()) - return true; + // Non-transparent overloadable functions need mangling. + if (auto *A = FD->getAttr()) + if (!A->isTransparent()) + return true; // "main" is not mangled. if (FD->isMain()) Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -368,9 +368,10 @@ bool MicrosoftMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) { if (const FunctionDecl *FD = dyn_cast(D)) { LanguageLinkage L = FD->getLanguageLinkage(); - // Overloadable functions need mangling. - if (FD->hasAttr()) - return true; + // Non-transparent overloadable functions need mangling. + if (auto *A = FD->getAttr()) + if (!A->isTransparent()) + return true; // The ABI expects that we would never mangle "typical" user-defined entry // points regardless of visibility or freestanding-ness. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2803,6 +2803,38 @@ return std::equal(A->param_begin(), A->param_end(), B->param_begin(), AttrEq); } +// Finds an overload to use in a diagnostic that says "previous overload was +// here." This is a bit complicated, since sometimes we're looking at an +// overload set, and other times we're only looking at a redecl chain. +// +// Ideally, we'd like to point to overloads with the actual overloadable +// attribute on them, rather than one that gets implicitly added. +static const NamedDecl *findDeclWithOverloadableAttr( + llvm::PointerUnion DiagInfo) { + auto GetRedeclWithOverloadable = + [](const NamedDecl *Base) -> const NamedDecl * { + for (const Decl *ND : Base->redecls()) + if (auto *Ovl = ND->getAttr()) + if (!Ovl->isImplicit()) + return cast(ND); + return nullptr; + }; + + if (const auto *Base = DiagInfo.dyn_cast()) { + if (const NamedDecl *Redecl = GetRedeclWithOverloadable(Base)) + return Redecl; + // Possible in ill-formed programs. + return Base; + } + + const auto *Lookup = DiagInfo.get(); + for (const NamedDecl *Base : *Lookup) + if (const NamedDecl *Redecl = GetRedeclWithOverloadable(Base)) + return Redecl; + + return Lookup->getRepresentativeDecl(); +} + /// 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, @@ -2877,6 +2909,24 @@ New->dropAttr(); } + if (!getLangOpts().CPlusPlus) { + const NamedDecl *MostRecentOld = Old->getMostRecentDecl(); + if (const auto *OldOvl = MostRecentOld->getAttr()) { + // All redecls of a function with the overloadable attribute must have an + // overloadable attribute (even if it's implicit). + const auto *NewOvl = New->getAttr(); + if (NewOvl->isTransparent() != OldOvl->isTransparent()) { + assert(!NewOvl->isImplicit() && + "We generated an overloadable attr with bad transparency?"); + Diag(NewOvl->getLocation(), + diag::err_attribute_overloadable_mismatched_transparency); + Diag(findDeclWithOverloadableAttr({MostRecentOld})->getLocation(), + diag::note_attribute_overloadable_prev_overload) + << OldOvl->isTransparent(); + } + } + } + // If a function is first declared with a calling convention, but is later // declared or defined without one, all following decls assume the calling // convention of the first. @@ -5415,9 +5465,12 @@ Context.getExternCContextDecl()->makeDeclVisibleInContext(ND); } -NamedDecl *Sema::findLocallyScopedExternCDecl(DeclarationName Name) { - // FIXME: We can have multiple results via __attribute__((overloadable)). +NamedDecl * +Sema::findLocallyScopedExternCDecl(DeclarationName Name, + SmallVectorImpl *AllResults) { auto Result = Context.getExternCContextDecl()->lookup(Name); + if (AllResults) + AllResults->append(Result.begin(), Result.end()); return Result.empty() ? nullptr : *Result.begin(); } @@ -7000,9 +7053,12 @@ // variable declared in function scope. We don't need this in C++, because // we find local extern decls in the surrounding file-scope DeclContext. if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) { - if (NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName())) { + SmallVector Prevs; + if (S.findLocallyScopedExternCDecl(ND->getDeclName(), &Prevs)) { Previous.clear(); - Previous.addDecl(Prev); + for (NamedDecl *ND : Prevs) + Previous.addDecl(ND); + Previous.resolveKind(); return true; } } @@ -8972,6 +9028,27 @@ D->getFriendObjectKind() != Decl::FOK_None); } +static void fixMissingOverloadableAttr( + Sema &S, FunctionDecl *FixOn, bool Redeclaration, bool IsImplicitOverload, + llvm::PointerUnion DiagInfo) { + assert(!S.getLangOpts().CPlusPlus && + "We don't care about overloadable in C++"); + if (!IsImplicitOverload) { + const NamedDecl *OldDecl = findDeclWithOverloadableAttr(DiagInfo); + + S.Diag(FixOn->getLocation(), diag::err_attribute_overloadable_missing) + << Redeclaration << FixOn; + S.Diag(OldDecl->getLocation(), + diag::note_attribute_overloadable_prev_overload) + << false; + } + + FixOn->addAttr(OverloadableAttr::CreateImplicit( + S.Context, IsImplicitOverload + ? OverloadableAttr::GNU_transparently_overloadable + : OverloadableAttr::GNU_overloadable)); +} + /// \brief Perform semantic checking of a new function declaration. /// /// Performs semantic analysis of the new function declaration @@ -9019,58 +9096,97 @@ } } else { switch (CheckOverload(S, NewFD, Previous, OldDecl, - /*NewIsUsingDecl*/ false)) { - case Ovl_Match: - Redeclaration = true; - break; - - case Ovl_NonFunction: + /*NewIsUsingDecl=*/false)) { + case Sema::Ovl_Match: + case Sema::Ovl_NonFunction: Redeclaration = true; break; - case Ovl_Overload: + case Sema::Ovl_Overload: Redeclaration = false; break; } + // Transparency is checked later. if (!getLangOpts().CPlusPlus && !NewFD->hasAttr()) { - // If a function name is overloadable in C, then every function - // with that name must be marked "overloadable". - Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing) - << Redeclaration << NewFD; - NamedDecl *OverloadedDecl = - Redeclaration ? OldDecl : Previous.getRepresentativeDecl(); - Diag(OverloadedDecl->getLocation(), - diag::note_attribute_overloadable_prev_overload); - NewFD->addAttr(OverloadableAttr::CreateImplicit(Context)); + bool IsImplicitOverload = false; + if (Redeclaration) + if (const auto *OldOvl = OldDecl->getAttr()) + IsImplicitOverload = OldOvl->isTransparent(); + + llvm::PointerUnion DiagInfo; + if (Redeclaration) + DiagInfo = OldDecl; + else + DiagInfo = &Previous; + fixMissingOverloadableAttr(*this, NewFD, Redeclaration, + IsImplicitOverload, DiagInfo); } } } // Check for a previous extern "C" declaration with this name. if (!Redeclaration && - checkForConflictWithNonVisibleExternC(*this, 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(); - MergeTypeWithPrevious = false; - - // ... 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(OverloadableAttr::CreateImplicit(Context)); + checkForConflictWithNonVisibleExternC(*this, NewFD, Previous) && + !Previous.empty()) { + // This is an extern "C" declaration with the same name as a previous + // declaration, and thus redeclares that entity, unless the overloadable + // attribute is present. + MergeTypeWithPrevious = false; + Redeclaration = true; + + switch (CheckOverload(S, NewFD, Previous, OldDecl, + /*NewIsUsingDecl=*/false)) { + case Ovl_Match: + if (!getLangOpts().CPlusPlus && !NewFD->hasAttr()) { + // We only get the first declaration of local extern decls (redecls + // are hidden). Since the overloadable attribute only needs to be + // applied to all functions with the same name *after* the first + // declaration it was on, we need to check the most recent decl. + NamedDecl *NewestRedecl = OldDecl->getMostRecentDecl(); + if (const auto *OldOvl = NewestRedecl->getAttr()) { + OldDecl = NewestRedecl; + fixMissingOverloadableAttr( + *this, NewFD, /*Redeclaration=*/true, + /*ImplicitOverload=*/OldOvl->isTransparent(), {NewestRedecl}); } - if (IsOverload(NewFD, cast(OldDecl), false)) { - Redeclaration = false; - OldDecl = nullptr; + } + break; + + case Ovl_NonFunction: + break; + + case Ovl_Overload: + // Otherwise, we should be in Ovl_NonFunction. This matters because we + // don't want to hide diags for finding NonFunctions if we find an + // overloadable function. + assert(llvm::all_of( + Previous, [](const NamedDecl *ND) { return isa(ND); })); + + bool FoundOverloadableAttr = false; + if (!getLangOpts().CPlusPlus) { + for (const NamedDecl *InitialND : Previous) { + const auto *ND = cast(InitialND->getMostRecentDecl()); + const auto *OldOvl = ND->getAttr(); + if (!OldOvl) + continue; + + FoundOverloadableAttr = true; + if (!NewFD->hasAttr()) { + fixMissingOverloadableAttr(*this, NewFD, /*Redeclaration=*/false, + /*IsImplicitOverload=*/false, {ND}); + } + break; } } + + Redeclaration = !FoundOverloadableAttr; + // If we didn't find an exact match and none of the overloads have the + // overloadable attribute, we can't do much more than picking an arbitrary + // decl for diags. + if (Redeclaration) + OldDecl = Previous.getRepresentativeDecl(); + break; } } @@ -9159,6 +9275,26 @@ NewFD->setAccess(OldDecl->getAccess()); } } + } else if (!getLangOpts().CPlusPlus) { + if (auto *NewOvl = NewFD->getAttr()) { + if (NewOvl->isTransparent()) { + auto Transparent = llvm::find_if(Previous, [](const NamedDecl *ND) { + if (auto *FD = dyn_cast(ND)) + if (auto *Ovl = + FD->getMostRecentDecl()->getAttr()) + return Ovl->isTransparent(); + return false; + }); + + if (Transparent != Previous.end()) { + Diag(NewOvl->getLocation(), + diag::err_attribute_overloadable_too_many_transparent_overloads); + Diag(findDeclWithOverloadableAttr({*Transparent})->getLocation(), + diag::note_attribute_overloadable_prev_overload) + << true; + } + } + } } // Semantic checking for this function declaration (in isolation). Index: test/CodeGen/mangle-ms.c =================================================================== --- test/CodeGen/mangle-ms.c +++ test/CodeGen/mangle-ms.c @@ -2,3 +2,6 @@ // CHECK: define void @"\01?f@@$$J0YAXP6AX@Z@Z" __attribute__((overloadable)) void f(void (*x)()) {} + +// CHECK: define void @f +__attribute__((transparently_overloadable)) void f(void (*x)(int)) {} Index: test/CodeGen/mangle.c =================================================================== --- test/CodeGen/mangle.c +++ test/CodeGen/mangle.c @@ -9,6 +9,10 @@ // CHECK: @_Z2f0l void __attribute__((__overloadable__)) f0(long b) {} +// Unless it's transparent. +// CHECK: @f0 +void __attribute__((__transparently_overloadable__)) f0(float b) {} + // CHECK: @bar // These should get merged. Index: test/CodeGenCXX/mangle-ms.cpp =================================================================== --- test/CodeGenCXX/mangle-ms.cpp +++ test/CodeGenCXX/mangle-ms.cpp @@ -399,6 +399,9 @@ extern "C" void __attribute__((overloadable)) overloaded_fn() {} // CHECK-DAG: @"\01?overloaded_fn@@$$J0YAXXZ" +extern "C" void __attribute__((transparently_overloadable)) overloaded_fn2() {} +// CHECK-DAG: @overloaded_fn2 + namespace UnnamedType { struct S { typedef struct {} *T1[1]; Index: test/Sema/overloadable.c =================================================================== --- test/Sema/overloadable.c +++ test/Sema/overloadable.c @@ -3,6 +3,9 @@ int var __attribute__((overloadable)); // expected-error{{'overloadable' attribute only applies to functions}} void params(void) __attribute__((overloadable(12))); // expected-error {{'overloadable' attribute takes no arguments}} +int var2 __attribute__((transparently_overloadable)); // expected-error{{'transparently_overloadable' attribute only applies to functions}} +void params2(void) __attribute__((transparently_overloadable(12))); // expected-error {{'transparently_overloadable' attribute takes no arguments}} + int *f(int) __attribute__((overloadable)); // expected-note 2{{previous overload of function is here}} float *f(float); // expected-error{{overloaded function 'f' must have the 'overloadable' attribute}} int *f(int); // expected-error{{redeclaration of 'f' must have the 'overloadable' attribute}} \ @@ -106,8 +109,8 @@ void foo(char *c) __attribute__((overloadable)); void (*ptr1)(void *) = &foo; void (*ptr2)(char *) = &foo; - void (*ambiguous)(int *) = &foo; // expected-error{{initializing 'void (*)(int *)' with an expression of incompatible type ''}} expected-note@105{{candidate function}} expected-note@106{{candidate function}} - void *vp_ambiguous = &foo; // expected-error{{initializing 'void *' with an expression of incompatible type ''}} expected-note@105{{candidate function}} expected-note@106{{candidate function}} + void (*ambiguous)(int *) = &foo; // expected-error{{initializing 'void (*)(int *)' with an expression of incompatible type ''}} expected-note@-4{{candidate function}} expected-note@-3{{candidate function}} + void *vp_ambiguous = &foo; // expected-error{{initializing 'void *' with an expression of incompatible type ''}} expected-note@-5{{candidate function}} expected-note@-4{{candidate function}} void (*specific1)(int *) = (void (*)(void *))&foo; // expected-warning{{incompatible function pointer types initializing 'void (*)(int *)' with an expression of type 'void (*)(void *)'}} void *specific2 = (void (*)(void *))&foo; @@ -117,8 +120,8 @@ void disabled(char *c) __attribute__((overloadable, enable_if(1, "The function name lies."))); // To be clear, these should all point to the last overload of 'disabled' void (*dptr1)(char *c) = &disabled; - void (*dptr2)(void *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type ''}} expected-note@115{{candidate function made ineligible by enable_if}} expected-note@116{{candidate function made ineligible by enable_if}} expected-note@117{{candidate function has type mismatch at 1st parameter (expected 'void *' but has 'char *')}} - void (*dptr3)(int *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(int *)' with an expression of type ''}} expected-note@115{{candidate function made ineligible by enable_if}} expected-note@116{{candidate function made ineligible by enable_if}} expected-note@117{{candidate function has type mismatch at 1st parameter (expected 'int *' but has 'char *')}} + void (*dptr2)(void *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type ''}} expected-note@-5{{candidate function made ineligible by enable_if}} expected-note@-4{{candidate function made ineligible by enable_if}} expected-note@-3{{candidate function has type mismatch at 1st parameter (expected 'void *' but has 'char *')}} + void (*dptr3)(int *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(int *)' with an expression of type ''}} expected-note@-6{{candidate function made ineligible by enable_if}} expected-note@-5{{candidate function made ineligible by enable_if}} expected-note@-4{{candidate function has type mismatch at 1st parameter (expected 'int *' but has 'char *')}} void *specific_disabled = &disabled; } @@ -131,14 +134,14 @@ void foo(char *c) __attribute__((overloadable)); void foo(short *c) __attribute__((overloadable)); foo(charbuf); - foo(ucharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@131{{candidate function}} expected-note@132{{candidate function}} - foo(intbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@131{{candidate function}} expected-note@132{{candidate function}} + foo(ucharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@-3{{candidate function}} expected-note@-2{{candidate function}} + foo(intbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@-4{{candidate function}} expected-note@-3{{candidate function}} void bar(unsigned char *c) __attribute__((overloadable)); void bar(signed char *c) __attribute__((overloadable)); - bar(charbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@137{{candidate function}} expected-note@138{{candidate function}} + bar(charbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@-2{{candidate function}} expected-note@-1{{candidate function}} bar(ucharbuf); - bar(intbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@137{{candidate function}} expected-note@138{{candidate function}} + bar(intbuf); // expected-error{{call to 'bar' is ambiguous}} expected-note@-4{{candidate function}} expected-note@-3{{candidate function}} } void dropping_qualifiers_is_incompatible() { @@ -148,6 +151,66 @@ void foo(char *c) __attribute__((overloadable)); void foo(const volatile unsigned char *c) __attribute__((overloadable)); - foo(ccharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@148{{candidate function}} expected-note@149{{candidate function}} - foo(vcharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@148{{candidate function}} expected-note@149{{candidate function}} + foo(ccharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@-3{{candidate function}} expected-note@-2{{candidate function}} + foo(vcharbuf); // expected-error{{call to 'foo' is ambiguous}} expected-note@-4{{candidate function}} expected-note@-3{{candidate function}} +} + +void overloadable_with_global() { + void wg_foo(void) __attribute__((overloadable)); // expected-note{{previous}} + void wg_foo(int) __attribute__((overloadable)); +} + +int wg_foo; // expected-error{{redefinition of 'wg_foo' as different kind of symbol}} + +void transparent_overloadable() { + void to_foo0(int); // expected-note{{previous declaration}} + void to_foo0(double) __attribute__((overloadable)); // expected-error{{conflicting types}} + + void to_foo1(int) __attribute__((overloadable)); // expected-note 2{{previous overload of function}} + void to_foo1(double); // expected-error{{must have the 'overloadable' attribute}} + void to_foo1(int); // expected-error{{must have the 'overloadable' attribute}} + + void to_foo2(int) __attribute__((overloadable)); // expected-note 2{{previous overload}} + void to_foo2(double) __attribute__((transparently_overloadable)); + void to_foo2(int); // expected-error{{must have the 'overloadable' attribute}} + void to_foo2(double); + void to_foo2(int); // expected-error{{must have the 'overloadable' attribute}} + + void to_foo3(int) __attribute__((transparently_overloadable)); + void to_foo3(double) __attribute__((overloadable)); // expected-note{{previous overload}} + void to_foo3(int); + void to_foo3(double); // expected-error{{must have the 'overloadable' attribute}} + + void to_foo4(int); + void to_foo4(int) __attribute__((transparently_overloadable)); + void to_foo4(double) __attribute__((overloadable)); + + void to_foo5(int) __attribute__((overloadable)); // expected-note {{previous overload}} + void to_foo5(int) __attribute__((transparently_overloadable)); // expected-error{{mismatched transparency}} + + void to_foo6(int); + void to_foo6(int) __attribute__((transparently_overloadable)); // expected-note{{previous transparent overload}} + void to_foo6(double) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} + + void to_foo7(int); + void to_foo7(int) __attribute__((transparently_overloadable)); // expected-note{{previous transparent overload}} + void to_foo7(float) __attribute__((overloadable)); + void to_foo7(double) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} + + void to_foo8(int) __attribute__((transparently_overloadable)); // expected-note{{previous transparent overload}} + void to_foo8(float) __attribute__((overloadable)); + void to_foo8(int); + void to_foo8(double) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} + + void to_foo9(int) __attribute__((enable_if(1, ""), overloadable)); // expected-note{{previous overload}} + void to_foo9(int) __attribute__((enable_if(1, ""), transparently_overloadable)); // expected-error{{mismatched transparency}} + + void to_foo10(int) __attribute__((enable_if(1, ""), transparently_overloadable)); // expected-note{{previous transparent}} + void to_foo10(int) __attribute__((enable_if(1, ""), overloadable)); // expected-error{{mismatched transparency}} + + void to_foo11(char *__attribute__((pass_object_size(0)))) __attribute__((enable_if(1, ""), transparently_overloadable)); // expected-note{{previous transparent}} + void to_foo11(char *__attribute__((pass_object_size(0)))) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} + + void to_foo12(char *) __attribute__((transparently_overloadable)); // expected-note{{previous transparent overload}} + void to_foo12(char *__attribute__((pass_object_size(0)))) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} }