george.burgess.iv updated this revision to Diff 98569.
george.burgess.iv marked 7 inline comments as done.
george.burgess.iv added a comment.
- Addressed all feedback
- Now we emit a warning when `transparently_overloadable` "overrides"
`overloadable`, rather than an error
https://reviews.llvm.org/D32332
Files:
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/Sema/SemaDecl.cpp
test/CodeGen/mangle-ms.c
test/CodeGen/mangle.c
test/CodeGenCXX/mangle-ms.cpp
test/Sema/overloadable.c
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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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 '<overloaded function 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,91 @@
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)); // expected-note{{previous transparent overload}}
+ void to_foo2(int); // expected-error{{must have the 'overloadable' attribute}}
+ void to_foo2(double); // expected-error{{must have the 'transparently_overloadable' attribute}}
+ 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) __attribute__((transparently_overloadable));
+ 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-warning{{mismatched transparency}} expected-note{{previous transparent overload}}
+ void to_foo5(int) __attribute__((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(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-warning{{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}}
+
+ void to_foo13(char *) __attribute__((overloadable)); // expected-note{{previous overload}}
+ void to_foo13(char *) __attribute__((transparently_overloadable)); // expected-warning{{mismatched transparency}} expected-note 3{{previous transparent overload}}
+ void to_foo13(char *) __attribute__((overloadable)); // expected-error{{mismatched transparency}}
+ void to_foo13(char *) __attribute__((overloadable)); // expected-error{{mismatched transparency}}
+ void to_foo13(int) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}}
+
+ void to_foo14(char *) __attribute__((overloadable)); // expected-note{{previous overload}}
+ void to_foo14(int) __attribute__((transparently_overloadable)); // expected-note{{previous transparent overload}}
+ void to_foo14(char *) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} expected-warning{{mismatched transparency}}
+ void to_foo14(int) __attribute__((transparently_overloadable));
+ // Since we emit an error about the transparently_overloadable void(char *)
+ // overload above, it's reasonable to be quiet here.
+ void to_foo14(char *) __attribute__((transparently_overloadable));
+
+ void to_foo15(char *) __attribute__((overloadable)); // expected-note{{previous overload}}
+ void to_foo15(int) __attribute__((overloadable)); // expected-note{{previous overload}}
+ void to_foo15(char *) __attribute__((transparently_overloadable)); // expected-warning{{mismatched transparency}} expected-note{{previous transparent}}
+ void to_foo15(int) __attribute__((transparently_overloadable)); // expected-error{{only one 'overloadable' overload may be transparent}} expected-warning{{mismatched transparency}}
+ void to_foo15(char *) __attribute__((transparently_overloadable)); // expected-note{{previous transparent}}
+ void to_foo15(char *) __attribute__((overloadable)); // expected-error{{mismatched transparency}}
+ void to_foo15(char *) __attribute__((transparently_overloadable));
+
}
// Bug: we used to treat `__typeof__(foo)` as though it was `__typeof__(&foo)`
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 __attribute__((transparently_overloadable)) overloaded_fn2() {}
+// CHECK-DAG: @overloaded_fn2
+//
+extern "C" void __attribute__((overloadable)) overloaded_fn3();
+extern "C" void __attribute__((transparently_overloadable)) overloaded_fn3() {}
+// CHECK-DAG: @overloaded_fn3
+
namespace UnnamedType {
struct S {
typedef struct {} *T1[1];
Index: test/CodeGen/mangle.c
===================================================================
--- test/CodeGen/mangle.c
+++ test/CodeGen/mangle.c
@@ -9,6 +9,14 @@
// CHECK: @_Z2f0l
void __attribute__((__overloadable__)) f0(long b) {}
+// Unless it's transparent.
+// CHECK: @f0
+void __attribute__((__transparently_overloadable__)) f0(float b) {}
+
+void __attribute__((__overloadable__)) f1(float b);
+// CHECK: @f1
+void __attribute__((__transparently_overloadable__)) f1(float b) {}
+
// CHECK: @bar
// These should get merged.
Index: test/CodeGen/mangle-ms.c
===================================================================
--- test/CodeGen/mangle-ms.c
+++ test/CodeGen/mangle-ms.c
@@ -2,3 +2,10 @@
// 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)) {}
+
+__attribute__((overloadable)) void g(void (*x)(int));
+// CHECK: define void @g
+__attribute__((transparently_overloadable)) void g(void (*x)(int)) {}
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -2806,6 +2806,40 @@
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<const NamedDecl *, const LookupResult *> DiagInfo,
+ bool SpelledTransparent = false) {
+ auto GetRedeclWithOverloadable =
+ [&](const NamedDecl *Base) -> const NamedDecl * {
+ for (const Decl *ND : Base->redecls())
+ if (const auto *Ovl = ND->getAttr<OverloadableAttr>())
+ if (!Ovl->isImplicit() &&
+ (!SpelledTransparent || Ovl->isSpelledTransparent()))
+ return cast<NamedDecl>(ND);
+ return nullptr;
+ };
+
+ if (const auto *Base = DiagInfo.dyn_cast<const NamedDecl *>()) {
+ if (const NamedDecl *Redecl = GetRedeclWithOverloadable(Base))
+ return Redecl;
+ // Possible in ill-formed programs.
+ return Base;
+ }
+
+ const auto *Lookup = DiagInfo.get<const LookupResult *>();
+ 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,
@@ -2880,6 +2914,36 @@
New->dropAttr<InternalLinkageAttr>();
}
+ if (!getLangOpts().CPlusPlus) {
+ const NamedDecl *MostRecentOld = Old->getMostRecentDecl();
+ if (const auto *OldOvl = MostRecentOld->getAttr<OverloadableAttr>()) {
+ auto *NewOvl = New->getAttr<OverloadableAttr>();
+ assert(NewOvl && "Later redecl should always have an overloadable attr!");
+ if (NewOvl->isTransparent() != OldOvl->isTransparent()) {
+ assert(!NewOvl->isImplicit() &&
+ "We generated an overloadable attr with bad transparency?");
+
+ bool TransitionToTransparent = NewOvl->isTransparent();
+ // Assume the user's intent was to make New a transparent overload. We
+ // can catch more issues later if we pretend this was spelled
+ // "transparent".
+ if (!TransitionToTransparent)
+ NewOvl->forceTransparency();
+
+ Diag(NewOvl->getLocation(),
+ TransitionToTransparent
+ ? diag::warn_attribute_overloadable_mismatched_transparency
+ : diag::err_attribute_overloadable_mismatched_transparency);
+
+ const NamedDecl *Other = findDeclWithOverloadableAttr(
+ {MostRecentOld}, /*SpelledTransparent=*/OldOvl->isTransparent());
+ Diag(Other->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.
@@ -5433,9 +5497,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<NamedDecl *> *AllResults) {
auto Result = Context.getExternCContextDecl()->lookup(Name);
+ if (AllResults)
+ AllResults->append(Result.begin(), Result.end());
return Result.empty() ? nullptr : *Result.begin();
}
@@ -7030,9 +7097,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<NamedDecl *, 4> Prevs;
+ if (S.findLocallyScopedExternCDecl(ND->getDeclName(), &Prevs)) {
Previous.clear();
- Previous.addDecl(Prev);
+ for (NamedDecl *ND : Prevs)
+ Previous.addDecl(ND);
+ Previous.resolveKind();
return true;
}
}
@@ -9009,6 +9079,75 @@
D->getFriendObjectKind() != Decl::FOK_None);
}
+static void fixMissingOverloadableAttr(
+ Sema &S, FunctionDecl *FixOn, bool Redeclaration,
+ bool IsTransparentOverload,
+ llvm::PointerUnion<const NamedDecl *, const LookupResult *> DiagInfo) {
+ assert(!S.getLangOpts().CPlusPlus &&
+ "We don't care about overloadable in C++");
+
+ const NamedDecl *OldDecl = findDeclWithOverloadableAttr(DiagInfo);
+
+ S.Diag(FixOn->getLocation(), diag::err_attribute_overloadable_missing)
+ << Redeclaration << FixOn << IsTransparentOverload;
+ S.Diag(OldDecl->getLocation(),
+ diag::note_attribute_overloadable_prev_overload)
+ << IsTransparentOverload;
+
+ FixOn->addAttr(OverloadableAttr::CreateImplicit(
+ S.Context, IsTransparentOverload
+ ? OverloadableAttr::GNU_transparently_overloadable
+ : OverloadableAttr::GNU_overloadable));
+}
+
+// Given a new FunctionDecl (and, if it's a redecl, the previous decl), ensure
+// that the transparency of the new FunctionDecl is sane. If not, this will emit
+// diagnostics.
+static void checkPotentialNewTransparentOverload(Sema &S,
+ const FunctionDecl *NewFD,
+ const NamedDecl *OldDecl,
+ const LookupResult &Previous) {
+ assert(!S.getLangOpts().CPlusPlus &&
+ "We don't care about overloadable in C++");
+
+ const auto *NewOvl = NewFD->getAttr<OverloadableAttr>();
+ // Use isSpelledTransparent so that we don't complain about vanilla
+ // `overloadable` functions.
+ if (!NewOvl || !NewOvl->isSpelledTransparent())
+ return;
+
+ // If the transparency hasn't changed, we can skip this (...which is necessary
+ // in some ill-formed programs, so we don't bombard the user with N diags when
+ // one would do just as well).
+ if (OldDecl)
+ if (const auto *OldOvl = OldDecl->getAttr<OverloadableAttr>())
+ if (OldOvl->isTransparent())
+ return;
+
+ auto Transparent = llvm::find_if(Previous, [&](const NamedDecl *ND) {
+ if (ND == OldDecl)
+ return false;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+ if (const auto *Ovl =
+ FD->getMostRecentDecl()->getAttr<OverloadableAttr>())
+ return Ovl->isTransparent();
+ return false;
+ });
+
+ if (Transparent == Previous.end())
+ return;
+
+ const NamedDecl *Prev = (*Transparent)->getMostRecentDecl();
+ const NamedDecl *ComplainAt =
+ findDeclWithOverloadableAttr({Prev}, /*SpelledTransparent=*/true);
+ S.Diag(NewOvl->getLocation(),
+ diag::err_attribute_overloadable_too_many_transparent_overloads);
+ S.Diag(ComplainAt->getLocation(),
+ diag::note_attribute_overloadable_prev_overload)
+ << true;
+}
+
/// \brief Perform semantic checking of a new function declaration.
///
/// Performs semantic analysis of the new function declaration
@@ -9043,6 +9182,10 @@
// Merge or overload the declaration with an existing declaration of
// the same name, if appropriate.
+ //
+ // Note that we need to preserve the invariant in non-C++ that, if a previous
+ // declaration or overload of this function has an OverloadableAttr, this
+ // declaration has one as well.
if (!Previous.empty()) {
// Determine whether NewFD is an overload of PrevDecl or
// a declaration that requires merging. If it's an overload,
@@ -9056,58 +9199,98 @@
}
} 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;
}
+ // Consistent transparency is checked later.
if (!getLangOpts().CPlusPlus && !NewFD->hasAttr<OverloadableAttr>()) {
- // 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 IsTransparentOverload = false;
+ if (Redeclaration)
+ if (const auto *OldOvl = OldDecl->getAttr<OverloadableAttr>())
+ IsTransparentOverload = OldOvl->isTransparent();
+
+ llvm::PointerUnion<const NamedDecl *, const LookupResult *> DiagInfo;
+ if (Redeclaration)
+ DiagInfo = OldDecl;
+ else
+ DiagInfo = &Previous;
+ fixMissingOverloadableAttr(*this, NewFD, Redeclaration,
+ IsTransparentOverload, 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<OverloadableAttr>()) {
- if (!getLangOpts().CPlusPlus && !NewFD->hasAttr<OverloadableAttr>()) {
- 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<OverloadableAttr>()) {
+ // 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<OverloadableAttr>()) {
+ OldDecl = NewestRedecl;
+ fixMissingOverloadableAttr(
+ *this, NewFD, /*Redeclaration=*/true,
+ /*IsTransparentOverload=*/OldOvl->isTransparent(),
+ {NewestRedecl});
}
- if (IsOverload(NewFD, cast<FunctionDecl>(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<FunctionDecl>(ND); }));
+
+ bool FoundOverloadableAttr = false;
+ if (!getLangOpts().CPlusPlus) {
+ for (const NamedDecl *InitialND : Previous) {
+ const auto *ND = cast<NamedDecl>(InitialND->getMostRecentDecl());
+ const auto *OldOvl = ND->getAttr<OverloadableAttr>();
+ if (!OldOvl)
+ continue;
+
+ FoundOverloadableAttr = true;
+ if (!NewFD->hasAttr<OverloadableAttr>()) {
+ fixMissingOverloadableAttr(*this, NewFD, /*Redeclaration=*/false,
+ /*IsTransparentOverload=*/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;
}
}
@@ -9157,6 +9340,9 @@
return Redeclaration;
}
+ if (!getLangOpts().CPlusPlus)
+ checkPotentialNewTransparentOverload(*this, NewFD, OldDecl, Previous);
+
Previous.clear();
Previous.addDecl(OldDecl);
@@ -9196,7 +9382,9 @@
NewFD->setAccess(OldDecl->getAccess());
}
}
- }
+ } else if (!getLangOpts().CPlusPlus)
+ checkPotentialNewTransparentOverload(*this, NewFD, /*OldDecl=*/nullptr,
+ Previous);
// Semantic checking for this function declaration (in isolation).
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<FunctionDecl>(D)) {
LanguageLinkage L = FD->getLanguageLinkage();
- // Overloadable functions need mangling.
- if (FD->hasAttr<OverloadableAttr>())
- return true;
+ // Non-transparent overloadable functions need mangling.
+ if (const auto *A = FD->getAttr<OverloadableAttr>())
+ 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/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -579,9 +579,10 @@
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
if (FD) {
LanguageLinkage L = FD->getLanguageLinkage();
- // Overloadable functions need mangling.
- if (FD->hasAttr<OverloadableAttr>())
- return true;
+ // Non-transparent overloadable functions need mangling.
+ if (const auto *A = FD->getAttr<OverloadableAttr>())
+ if (!A->isTransparent())
+ return true;
// "main" is not mangled.
if (FD->isMain())
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -528,7 +528,10 @@
llvm::SmallPtrSet<const Decl*, 4> 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<NamedDecl *> *AllResults = nullptr);
typedef LazyVector<VarDecl *, ExternalSemaSource,
&ExternalSemaSource::ReadTentativeDefinitions, 2, 2>
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -3280,11 +3280,19 @@
def err_attribute_overloadable_missing : Error<
"%select{overloaded function|redeclaration of}0 %1 must have the "
- "'overloadable' attribute">;
+ "'%select{|transparently_}2overloadable' 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 warn_attribute_overloadable_mismatched_transparency : Warning<
+ "mismatched transparency on redeclaration of an 'overloadable' function">,
+ InGroup<DiagGroup<"overload-changes-transparency">>;
+def err_attribute_overloadable_mismatched_transparency : Error<
+ "mismatched transparency on redeclaration of a 'transparently_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/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -334,6 +334,7 @@
def ObjCPointerIntrospect : DiagGroup<"deprecated-objc-pointer-introspection", [ObjCPointerIntrospectPerformSelector]>;
def ObjCMultipleMethodNames : DiagGroup<"objc-multiple-method-names">;
def OpenCLUnsupportedRGBA: DiagGroup<"opencl-unsupported-rgba">;
+def OverloadChangesTransparency : DiagGroup<"overload-changes-transparency">;
def DeprecatedObjCIsaUsage : DiagGroup<"deprecated-objc-isa-usage">;
def ExplicitInitializeCall : DiagGroup<"explicit-initialize-call">;
def Packed : DiagGroup<"packed">;
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -651,6 +651,60 @@
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. You can designate a transparent
+overload by using the ``transparently_overloadable`` attribute instead of
+``overloadable``.
+
+For example:
+
+.. code-block:: c
+
+ // Notes with mangled names assume Itanium mangling.
+ int f(int) __attribute__((transparently_overloadable));
+ int f(double) __attribute__((overloadable));
+ void foo() {
+ f(5); // Emits a call to f (not _Z1fi, 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));
+
+When clang sees a redeclaration of a function tagged with
+``transparently_overloadable``, but prior declarations are tagged with
+``overloadable``, clang will emit a warning and act as though prior declarations
+were marked with ``transparently_overloadable``. Beware: this behavior can cause
+subtle bugs, since it allows code like the following:
+
+.. code-block:: c
+
+ // foo.h
+ int g(int) __attribute__((overloadable));
+ int g(double) __attribute__((overloadable));
+ void bar(void);
+ void baz(void);
+
+ // foo.c
+ // Warning: g(int) is not declared transparent above, but is here.
+ int g(int) __attribute__((transparently_overloadable));
+ void bar() {
+ g(1); // Emits a call to g (not _Z1gi)
+ g(1.); // Emits a call to _Z1gf
+ }
+
+ // bar.c
+ // Warning: g(double) is not declared transparent above, but is here.
+ int g(double) __attribute__((transparently_overloadable));
+ void bar() {
+ g(1); // Emits a call to _Z1gi
+ g(1.); // Emits a call to g (not _Z1gf)
+ }
+
Query for this feature with ``__has_extension(attribute_overloadable)``.
}];
}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1563,8 +1563,21 @@
}
def Overloadable : Attr {
- let Spellings = [GNU<"overloadable">];
+ let Spellings = [GNU<"overloadable">, GNU<"transparently_overloadable">];
let Subjects = SubjectList<[Function], ErrorDiag>;
+ let Accessors = [Accessor<"isSpelledTransparent",
+ [GNU<"transparently_overloadable">]>];
+ // ForcedTransparency is only used so we can provide better diagnostics in the
+ // case of an ill-formed program.
+ let Args = [BoolArgument<"ForcedTransparency", /*opt=*/0, /*fake=*/1>];
+ let AdditionalMembers = [{
+ void forceTransparency() { forcedTransparency = true; }
+
+ bool isTransparent() const {
+ return forcedTransparency || isSpelledTransparent();
+ }
+ }];
+
let Documentation = [OverloadableDocs];
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits