Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -651,7 +651,28 @@ linkage specification, it's name *will* be mangled in the same way as it would in C. -Query for this feature with ``__has_extension(attribute_overloadable)``. +For the purpose of backwards compatibility, at most one function with the same +name as other ``overloadable`` functions may omit the ``overloadable`` +attribute. In this case, the function without the ``overloadable`` attribute +will not have its name mangled. + +For example: + +.. code-block:: c + + // Notes with mangled names assume Itanium mangling. + int f(int); + int f(double) __attribute__((overloadable)); + void foo() { + f(5); // Emits a call to f (not _Z1fi, as it would with an overload that + // was marked with overloadable). + f(1.0); // Emits a call to _Z1fd. + } + +Support for unmarked overloads is not present in some versions of clang. You may +query for it using ``__has_feature(overloadable_unmarked)``. + +Query for this attribute with ``__has_attribute(overloadable)``. }]; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3284,13 +3284,15 @@ "IBOutletCollection properties should be copy/strong and not assign">, InGroup; -def err_attribute_overloadable_missing : Error< - "%select{overloaded function|redeclaration of}0 %1 must have the " - "'overloadable' attribute">; +def err_attribute_overloadable_mismatch : Error< + "redeclaration of %0 must %select{not |}1have the 'overloadable' attribute">; def note_attribute_overloadable_prev_overload : Note< - "previous overload of function is here">; + "previous %select{unmarked |}0overload of function is here">; def err_attribute_overloadable_no_prototype : Error< "'overloadable' function %0 must have a prototype">; +def err_attribute_overloadable_multiple_unmarked_overloads : Error< + "at most one 'overloadable' function for a given name may lack the " + "'overloadable' attribute">; 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 @@ -528,7 +528,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/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1133,6 +1133,7 @@ .Case("enumerator_attributes", true) .Case("nullability", true) .Case("nullability_on_arrays", true) + .Case("overloadable_unmarked", true) .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory)) .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread)) .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow)) Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1327,15 +1327,17 @@ /// overloaded function declaration or has the "overloadable" /// attribute. static bool AllowOverloadingOfFunction(LookupResult &Previous, - ASTContext &Context) { + ASTContext &Context, + const FunctionDecl *New) { if (Context.getLangOpts().CPlusPlus) return true; if (Previous.getResultKind() == LookupResult::FoundOverloaded) return true; - return (Previous.getResultKind() == LookupResult::Found - && Previous.getFoundDecl()->hasAttr()); + return Previous.getResultKind() == LookupResult::Found && + (Previous.getFoundDecl()->hasAttr() || + New->hasAttr()); } /// Add this decl to the scope shadowed decl chains. @@ -2933,6 +2935,44 @@ New->dropAttr(); } + if (!getLangOpts().CPlusPlus) { + const NamedDecl *MostRecentOld = Old->getMostRecentDecl(); + bool OldOvl = MostRecentOld->hasAttr(); + if (OldOvl != New->hasAttr() && + !MostRecentOld->isImplicit()) { + Diag(New->getLocation(), diag::err_attribute_overloadable_mismatch) + << New << OldOvl; + + // Try our best to find a decl that actually has (or lacks) the + // overloadable attribute for the note. In most cases (e.g. programs with + // only one broken declaration/definition), this won't matter. + // + // FIXME: We could do this if we juggled some extra state in + // OverloadableAttr, rather than just removing it. + const Decl *DiagOld = MostRecentOld; + if (OldOvl) { + auto OldIter = + llvm::find_if(MostRecentOld->redecls(), [](const Decl *D) { + const auto *A = D->getAttr(); + return A && !A->isImplicit(); + }); + // If we've implicitly added *all* of the overloadable attrs to this + // chain, emitting a "previous redecl" note is pointless. + DiagOld = OldIter == MostRecentOld->redecls_end() ? nullptr : *OldIter; + } + + if (DiagOld) + Diag(DiagOld->getLocation(), + diag::note_attribute_overloadable_prev_overload) + << OldOvl; + + if (OldOvl) + New->addAttr(OverloadableAttr::CreateImplicit(Context)); + else + New->dropAttr(); + } + } + // 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. @@ -5540,9 +5580,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(); } @@ -7143,9 +7186,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; } } @@ -9155,6 +9201,7 @@ bool MergeTypeWithPrevious = !getLangOpts().CPlusPlus && !Previous.isShadowed(); + bool MayNeedOverloadableChecks = false; bool Redeclaration = false; NamedDecl *OldDecl = nullptr; @@ -9165,19 +9212,17 @@ // a declaration that requires merging. If it's an overload, // there's no more work to do here; we'll just add the new // function to the scope. - if (!AllowOverloadingOfFunction(Previous, Context)) { + if (!AllowOverloadingOfFunction(Previous, Context, NewFD)) { NamedDecl *Candidate = Previous.getRepresentativeDecl(); if (shouldLinkPossiblyHiddenDecl(Candidate, NewFD)) { Redeclaration = true; OldDecl = Candidate; } } else { + MayNeedOverloadableChecks = true; switch (CheckOverload(S, NewFD, Previous, OldDecl, - /*NewIsUsingDecl*/ false)) { + /*NewIsUsingDecl=*/false)) { case Ovl_Match: - Redeclaration = true; - break; - case Ovl_NonFunction: Redeclaration = true; break; @@ -9186,45 +9231,55 @@ Redeclaration = false; break; } - - 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)); - } } } // 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)); - } - if (IsOverload(NewFD, cast(OldDecl), false)) { - Redeclaration = false; - OldDecl = nullptr; - } - } + 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: + // We're not guaranteed to be handed the most recent decl, and + // __attribute__((overloadable)) depends somewhat on source order. + OldDecl = OldDecl->getMostRecentDecl(); + 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) { + MayNeedOverloadableChecks = true; + FoundOverloadableAttr = + NewFD->hasAttr() || + llvm::any_of(Previous, [](const NamedDecl *ND) { + return ND->getMostRecentDecl()->hasAttr(); + }); + } + + 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; + } } } @@ -9315,6 +9370,32 @@ NewFD->setAccess(OldDecl->getAccess()); } } + } else if (!getLangOpts().CPlusPlus && MayNeedOverloadableChecks && + !NewFD->getAttr()) { + assert((Previous.empty() || + llvm::any_of( + Previous, + [](const NamedDecl *ND) { + return ND->getMostRecentDecl()->hasAttr(); + })) && + "Non-redecls shouldn't happen without overloadable present"); + + auto OtherUnmarkedIter = llvm::find_if(Previous, [](const NamedDecl *ND) { + const auto *FD = dyn_cast(ND); + return FD && !FD->getMostRecentDecl()->hasAttr(); + }); + + if (OtherUnmarkedIter != Previous.end()) { + const auto *OtherUnmarked = + cast((*OtherUnmarkedIter)->getMostRecentDecl()); + Diag(NewFD->getLocation(), + diag::err_attribute_overloadable_multiple_unmarked_overloads); + Diag(OtherUnmarked->getLocation(), + diag::note_attribute_overloadable_prev_overload) + << false; + + NewFD->addAttr(OverloadableAttr::CreateImplicit(Context)); + } } // 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,12 @@ // CHECK: define void @"\01?f@@$$J0YAXP6AX@Z@Z" __attribute__((overloadable)) void f(void (*x)()) {} + +// CHECK: define void @f +void f(void (*x)(int)) {} + +// CHECK: define void @g +void g(void (*x)(int)) {} + +// CHECK: define void @"\01?g@@$$J0YAXP6AX@Z@Z" +__attribute__((overloadable)) void g(void (*x)()) {} 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 unmarked. +// CHECK: @f0 +void 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,13 @@ extern "C" void __attribute__((overloadable)) overloaded_fn() {} // CHECK-DAG: @"\01?overloaded_fn@@$$J0YAXXZ" +extern "C" void overloaded_fn2() {} +// CHECK-DAG: @overloaded_fn2 +// +extern "C" void __attribute__((overloadable)) overloaded_fn3(); +extern "C" void overloaded_fn3() {} +// CHECK-DAG: @overloaded_fn3 + namespace UnnamedType { struct S { typedef struct {} *T1[1]; Index: test/PCH/attrs.c =================================================================== --- test/PCH/attrs.c +++ test/PCH/attrs.c @@ -13,8 +13,9 @@ #else +float f(float); double f(double); // expected-error{{overloadable}} - // expected-note@11{{previous overload}} + // expected-note@-2{{previous unmarked overload}} void h() { g(0); } #endif Index: test/Sema/overloadable.c =================================================================== --- test/Sema/overloadable.c +++ test/Sema/overloadable.c @@ -3,12 +3,15 @@ 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 *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) __attribute__((overloadable)); // expected-note{{previous overload of function is here}} +float *f(float); int *f(int); // expected-error{{redeclaration of 'f' must have the 'overloadable' attribute}} \ // expected-note{{previous declaration is here}} double *f(double) __attribute__((overloadable)); // okay, new +// Ensure we don't complain about overloadable on implicitly declared functions. +int isdigit(int) __attribute__((overloadable)); + void test_f(int iv, float fv, double dv) { int *ip = f(iv); float *fp = f(fv); @@ -71,19 +74,19 @@ f1(); } -void before_local_1(int) __attribute__((overloadable)); // expected-note {{here}} +void before_local_1(int) __attribute__((overloadable)); 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_1(char); + void before_local_2(char); // 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_1(char); + void after_local_2(char) __attribute__((overloadable)); 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_1(int) __attribute__((overloadable)); +void after_local_2(int); void after_local_3(int) __attribute__((overloadable)); // Make sure we allow C-specific conversions in C. @@ -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,8 +151,89 @@ 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}} + +#if !__has_feature(overloadable_unmarked) +#error "We should have unmarked overload support" +#endif + +void unmarked_overloadable() { + void to_foo0(int); + void to_foo0(double) __attribute__((overloadable)); // expected-note{{previous overload}} + void to_foo0(int); + void to_foo0(double); // expected-error{{must have the 'overloadable' attribute}} + void to_foo0(int); + + void to_foo1(int) __attribute__((overloadable)); // expected-note 2{{previous overload}} + void to_foo1(double); + void to_foo1(int); // expected-error{{must have the 'overloadable' attribute}} + void to_foo1(double); + void to_foo1(int); // expected-error{{must have the 'overloadable' attribute}} + + void to_foo2(int); // expected-note{{previous unmarked overload}} + void to_foo2(double) __attribute__((overloadable)); // expected-note 2{{previous overload}} + void to_foo2(int) __attribute__((overloadable)); // expected-error {{must not have the 'overloadable' attribute}} + void to_foo2(double); // expected-error{{must have the 'overloadable' attribute}} + void to_foo2(int); + void to_foo2(double); // expected-error{{must have the 'overloadable' attribute}} + void to_foo2(int); + + void to_foo3(int); + 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) __attribute__((overloadable)); // expected-note{{previous overload}} + void to_foo4(int); // expected-error{{must have the 'overloadable' attribute}} + void to_foo4(double) __attribute__((overloadable)); + + void to_foo5(int); + void to_foo5(int); // expected-note 3{{previous unmarked overload}} + void to_foo5(float) __attribute__((overloadable)); + void to_foo5(double); // expected-error{{at most one 'overloadable' function for a given name may lack the 'overloadable' attribute}} + void to_foo5(float) __attribute__((overloadable)); + void to_foo5(short); // expected-error{{at most one 'overloadable' function for a given name may lack the 'overloadable' attribute}} + void to_foo5(long); // expected-error{{at most one 'overloadable' function for a given name may lack the 'overloadable' attribute}} + void to_foo5(double) __attribute__((overloadable)); + + void to_foo6(int) __attribute__((enable_if(1, ""), overloadable)); // expected-note{{previous overload}} + void to_foo6(int) __attribute__((enable_if(1, ""))); // expected-error{{must have the 'overloadable' attribute}} + void to_foo6(int) __attribute__((enable_if(1, ""), overloadable)); + + void to_foo7(int) __attribute__((enable_if(1, ""))); // expected-note{{previous unmarked overload}} + void to_foo7(int) __attribute__((enable_if(1, ""), overloadable)); // expected-error{{must not have the 'overloadable' attribute}} + void to_foo7(int) __attribute__((enable_if(1, ""))); + + void to_foo8(char *__attribute__((pass_object_size(0)))) + __attribute__((enable_if(1, ""))); + void to_foo8(char *__attribute__((pass_object_size(0)))) + __attribute__((overloadable)); + + void to_foo9(int); // expected-note{{previous unmarked overload}} + // FIXME: It would be nice if we did better with the "previous unmarked + // overload" diag. + void to_foo9(int) __attribute__((overloadable)); // expected-error{{must not have the 'overloadable' attribute}} expected-note{{previous declaration}} expected-note{{previous unmarked overload}} + void to_foo9(float); // expected-error{{conflicting types for 'to_foo9'}} + void to_foo9(float) __attribute__((overloadable)); + void to_foo9(double); // expected-error{{at most one 'overloadable' function for a given name may lack the 'overloadable' attribute}} + void to_foo9(double) __attribute__((overloadable)); + + void to_foo10(int) __attribute__((overloadable)); + void to_foo10(double); // expected-note{{previous unmarked overload}} + // no "note: previous redecl" if no previous decl has `overloadable` + // spelled out + void to_foo10(float); // expected-error{{at most one 'overloadable' function for a given name may lack the 'overloadable' attribute}} + void to_foo10(float); // expected-error{{must have the 'overloadable' attribute}} + void to_foo10(float); // expected-error{{must have the 'overloadable' attribute}} } // Bug: we used to treat `__typeof__(foo)` as though it was `__typeof__(&foo)`