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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to