cjdb updated this revision to Diff 515497.
cjdb marked 6 inline comments as done.
cjdb added a comment.
Herald added subscribers: aheejin, dschuff.

- undoes whitespace changes as requested
- documents feature

I think the only thing left is to address the global new/delete and static 
member function discussion, which should be quickly implementable!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129951/new/

https://reviews.llvm.org/D129951

Files:
  clang/include/clang/AST/ExprCXX.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/TokenKinds.def
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaLookup.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/SemaCXX/disable-adl.cpp

Index: clang/test/SemaCXX/disable-adl.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/disable-adl.cpp
@@ -0,0 +1,179 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++20
+// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++2b
+
+namespace NS1 {
+  struct S1 {};
+  S1 inhibited(S1); // expected-note 2 {{candidate function}}
+
+  namespace NNS1 {
+    struct S2 {};
+    __disable_adl void hidden(S2);   // expected-note{{declared here}}
+    __disable_adl int inhibited(S1); // expected-note 4 {{candidate function}}
+  }
+}
+
+namespace NS2 {
+  __disable_adl void inhibited(NS1::S1); // expected-note 2 {{candidate function}}
+}
+
+void test_functions() {
+  hidden(NS1::NNS1::S2{}); // expected-error{{use of undeclared identifier 'hidden'; did you mean 'NS1::NNS1::hidden'?}}
+  {
+    NS1::S1 x = inhibited(NS1::S1{}); // no error
+  }
+  {
+    using namespace NS1::NNS1;
+    int x = inhibited(NS1::S1{}); // no error
+
+    using namespace NS1;
+    S1 y = inhibited(NS1::S1{}); // expected-error{{call to 'inhibited' is ambiguous}}
+  }
+  {
+    using NS1::NNS1::inhibited;
+    int x = inhibited(NS1::S1{}); // no error
+
+    using NS1::inhibited;
+    NS1::S1 y = inhibited(NS1::S1{}); // expected-error{{call to 'inhibited' is ambiguous}}
+  }
+  {
+    using namespace NS2;
+    inhibited(NS1::S1{}); // no error
+
+    using namespace NS1::NNS1;
+    inhibited(NS1::S1{}); // expected-error{{call to 'inhibited' is ambiguous}}
+  }
+  {
+    using NS2::inhibited;
+    inhibited(NS1::S1{}); // no error
+
+    using NS1::NNS1::inhibited;
+    inhibited(NS1::S1{}); // expected-error{{call to 'inhibited' is ambiguous}}
+  }
+}
+
+namespace NS1 {
+  template<typename T>
+  S1 inhibited_template(T); // expected-note 2 {{candidate function}}
+
+  namespace NNS1 {
+    template<typename T>
+    __disable_adl void hidden_template(T); // expected-note{{declared here}}
+
+    template<typename T>
+    __disable_adl int inhibited_template(T); // expected-note 4 {{candidate function}}
+  }
+}
+
+namespace NS2 {
+  template<typename T>
+  __disable_adl int inhibited_template(T); // expected-note 2 {{candidate function}}
+}
+
+void test_function_templates() {
+  hidden_template(NS1::NNS1::S2{}); // expected-error{{use of undeclared identifier 'hidden_template'; did you mean 'NS1::NNS1::hidden_template'?}}
+
+  {
+    NS1::S1 x = inhibited_template(NS1::S1{}); // no error
+  }
+  {
+    using namespace NS1::NNS1;
+    int x = inhibited_template(NS1::S1{}); // no error
+
+    using namespace NS1;
+    S1 y = inhibited_template(NS1::S1{}); // expected-error{{call to 'inhibited_template' is ambiguous}}
+  }
+  {
+    using NS1::NNS1::inhibited_template;
+    int x = inhibited_template(NS1::S1{}); // no error
+
+    using NS1::inhibited_template;
+    NS1::S1 y = inhibited_template(NS1::S1{}); // expected-error{{call to 'inhibited_template' is ambiguous}}
+  }
+  {
+    using namespace NS2;
+    inhibited_template(NS1::S1{}); // no error
+
+    using namespace NS1::NNS1;
+    inhibited_template(NS1::S1{}); // expected-error{{call to 'inhibited_template' is ambiguous}}
+  }
+  {
+    using NS2::inhibited_template;
+    inhibited_template(NS1::S1{}); // no error
+
+    using NS1::NNS1::inhibited_template;
+    inhibited_template(NS1::S1{}); // expected-error{{call to 'inhibited_template' is ambiguous}}
+  }
+}
+
+namespace NS1 {
+  S1 inhibited_mixed(S1);
+
+  namespace NNS1 {
+    template<typename T>
+    __disable_adl int inhibited_mixed(T);
+  }
+}
+
+void test_mixed() {
+  using namespace NS1::NNS1;
+  int x = inhibited_mixed(NS1::S1{}); // no error
+}
+
+// Should be covered by the hidden functions checks, but just to be sure.
+void test_NNS1_hidden() {
+  {
+    NS1::S1 a = inhibited(NS1::S1{});
+    NS1::S1 b = inhibited_template(NS1::S1{});
+    NS1::S1 c = inhibited_mixed(NS1::S1{});
+  }
+  {
+    using namespace NS1;
+    NS1::S1 a = inhibited(NS1::S1{});
+    NS1::S1 b = inhibited_template(NS1::S1{});
+    NS1::S1 c = inhibited_mixed(NS1::S1{});
+  }
+}
+
+namespace NS1 {
+  namespace NNS1 {
+    __disable_adl void operator-(S2); // expected-error{{can't apply '__disable_adl' to operators, since they're supposed to be used with ADL}}
+
+    struct hidden_friend_operator {
+      friend void operator-(hidden_friend_operator i, int) {}
+    };
+
+    struct hidden_friend_swap {
+      __disable_adl friend void swap(hidden_friend_swap, hidden_friend_swap) {}
+    };
+  }
+}
+
+void test_friends_and_operators() {
+  -NS1::NNS1::S2{};                        // no error
+  NS1::NNS1::hidden_friend_operator{} - 1; // no error
+
+  swap(NS1::NNS1::hidden_friend_swap{}, NS1::NNS1::hidden_friend_swap{}); // expected-error{{use of undeclared identifier 'swap'}}
+}
+
+struct S {
+  __disable_adl void f();        // expected-error{{can't apply '__disable_adl' to member functions}}
+  __disable_adl static void g(); // expected-error{{can't apply '__disable_adl' to member functions}}
+};
+
+template <class> using common_comparison_category_t = int;
+template <class T> T declval;
+template <class> __disable_adl auto synth_three_way();
+template <class T, class U = T> using synth_three_way_result = decltype(synth_three_way(declval<U>));
+template <class> concept three_way_comparable_synthesisable = requires { synth_three_way; };
+template <class T2> struct pair {
+  template <three_way_comparable_synthesisable U = T2>
+  auto operator>(pair) -> common_comparison_category_t<synth_three_way_result<U>>;
+};
+struct pair_spaceship_invalid {
+  pair_spaceship_invalid() { test<pair<int>>(); } // expected-note{{}}
+  template <class> void test() noexcept;
+};
+template <class T> void pair_spaceship_invalid::test() noexcept {
+  auto p1 = T(), p2 = T();
+  requires { p1 > p2; }; // expected-warning 2 {{expression result unused}}
+}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -6528,7 +6528,7 @@
       NamedDecl *ND = Function;
       if (auto *SpecInfo = Function->getTemplateSpecializationInfo())
         ND = SpecInfo->getTemplate();
-      
+
       if (ND->getFormalLinkage() == Linkage::InternalLinkage) {
         Candidate.Viable = false;
         Candidate.FailureKind = ovl_fail_module_mismatched;
@@ -12933,18 +12933,20 @@
 }
 
 /// Add a single candidate to the overload set.
-static void AddOverloadedCallCandidate(Sema &S,
-                                       DeclAccessPair FoundDecl,
-                                 TemplateArgumentListInfo *ExplicitTemplateArgs,
-                                       ArrayRef<Expr *> Args,
-                                       OverloadCandidateSet &CandidateSet,
-                                       bool PartialOverloading,
-                                       bool KnownValid) {
+static void AddOverloadedCallCandidate(
+    Sema &S, DeclAccessPair FoundDecl,
+    TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+    OverloadCandidateSet &CandidateSet, bool PartialOverloading,
+    bool KnownValid, UnresolvedLookupExpr *ULE) {
   NamedDecl *Callee = FoundDecl.getDecl();
   if (isa<UsingShadowDecl>(Callee))
     Callee = cast<UsingShadowDecl>(Callee)->getTargetDecl();
 
   if (FunctionDecl *Func = dyn_cast<FunctionDecl>(Callee)) {
+    if (Func->hasAttr<DisableADLAttr>() && ULE) {
+      ULE->disableADL();
+    }
+
     if (ExplicitTemplateArgs) {
       assert(!KnownValid && "Explicit template arguments?");
       return;
@@ -12961,6 +12963,10 @@
 
   if (FunctionTemplateDecl *FuncTemplate
       = dyn_cast<FunctionTemplateDecl>(Callee)) {
+    if (FuncTemplate->getAsFunction()->hasAttr<DisableADLAttr>() && ULE) {
+      ULE->disableADL();
+    }
+
     S.AddTemplateOverloadCandidate(FuncTemplate, FoundDecl,
                                    ExplicitTemplateArgs, Args, CandidateSet,
                                    /*SuppressUserConversions=*/false,
@@ -13019,7 +13025,7 @@
          E = ULE->decls_end(); I != E; ++I)
     AddOverloadedCallCandidate(*this, I.getPair(), ExplicitTemplateArgs, Args,
                                CandidateSet, PartialOverloading,
-                               /*KnownValid*/ true);
+                               /*KnownValid=*/true, ULE);
 
   if (ULE->requiresADL())
     AddArgumentDependentLookupCandidates(ULE->getName(), ULE->getExprLoc(),
@@ -13034,7 +13040,8 @@
     ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet) {
   for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
     AddOverloadedCallCandidate(*this, I.getPair(), ExplicitTemplateArgs, Args,
-                               CandidateSet, false, /*KnownValid*/ false);
+                               CandidateSet, false, /*KnownValid*/ false,
+                               nullptr);
 }
 
 /// Determine whether a declaration with the specified name could be moved into
Index: clang/lib/Sema/SemaLookup.cpp
===================================================================
--- clang/lib/Sema/SemaLookup.cpp
+++ clang/lib/Sema/SemaLookup.cpp
@@ -3867,6 +3867,9 @@
           !isa<FunctionTemplateDecl>(Underlying))
         continue;
 
+      if (Underlying->getAsFunction()->hasAttr<DisableADLAttr>())
+        continue;
+
       // The declaration is visible to argument-dependent lookup if either
       // it's ordinarily visible or declared as a friend in an associated
       // class.
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -3834,7 +3834,7 @@
     S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL;
     return;
   }
-  
+
   if (S.getLangOpts().HLSL) {
     S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
     return;
@@ -5757,6 +5757,16 @@
   D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident));
 }
 
+static void handleDisableADLAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  if (FunctionDecl *F = D->getAsFunction();
+      F->isOverloadedOperator() || F->isCXXClassMember()) {
+    S.Diag(AL.getLoc(), diag::err_disable_adl_no_operators)
+        << F->isCXXClassMember();
+    return;
+  }
+  D->addAttr(::new (S.Context) DisableADLAttr(S.Context, AL));
+}
+
 //===----------------------------------------------------------------------===//
 // Checker-specific attribute handlers.
 //===----------------------------------------------------------------------===//
@@ -9363,6 +9373,10 @@
   case ParsedAttr::AT_UsingIfExists:
     handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL);
     break;
+
+  case ParsedAttr::AT_DisableADL:
+    handleDisableADLAttr(S, D, AL);
+    break;
   }
 }
 
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -3829,6 +3829,15 @@
       ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), LateAttrs);
       continue;
 
+    case tok::kw___disable_adl: {
+      IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+      SourceLocation AttrNameLoc = Tok.getLocation();
+      DS.getAttributes().addNew(
+          AttrName, AttrNameLoc, /*scopeName=*/nullptr, AttrNameLoc,
+          /*args=*/nullptr, /*NumArgs=*/0, tok::kw___disable_adl);
+      break;
+    }
+
     // Microsoft single token adornments.
     case tok::kw___forceinline: {
       isInvalid = DS.setFunctionSpecForceInline(Loc, PrevSpec, DiagID);
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -746,6 +746,7 @@
 KEYWORD(__builtin_bit_cast               , KEYALL)
 KEYWORD(__builtin_available              , KEYALL)
 KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)
+KEYWORD(__disable_adl                    , KEYCXX)
 
 // Clang-specific keywords enabled only in testing.
 TESTING_KEYWORD(__unknown_anytype , KEYALL)
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3165,6 +3165,9 @@
   "vector size not an integral multiple of component size">;
 def err_attribute_zero_size : Error<"zero %0 size">;
 def err_attribute_size_too_large : Error<"%0 size too large">;
+def err_disable_adl_no_operators : Error<
+  "can't apply '__disable_adl' to %select{operators, since they're supposed to "
+  "be used with ADL|member functions}0">;
 def err_typecheck_sve_ambiguous : Error<
   "cannot combine fixed-length and sizeless SVE vectors in expression, result is ambiguous (%0 and %1)">;
 def err_typecheck_sve_rvv_gnu_ambiguous : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6945,8 +6945,74 @@
 def WebAssemblyFuncrefDocs : Documentation {
   let Category = DocCatType;
   let Content = [{
-Clang supports the ``__funcref`` attribute for the WebAssembly target. 
-This attribute may be attached to a function pointer type, where it modifies 
+Clang supports the ``__funcref`` attribute for the WebAssembly target.
+This attribute may be attached to a function pointer type, where it modifies
 its underlying representation to be a WebAssembly ``funcref``.
   }];
 }
+
+def DisableADLDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+    This attribute informs the compiler that overloads in the immediate scope should not be found by
+    argument-dependent lookup (ADL), and that when found by unqualified name lookup, they inhibit
+    ADL. This is useful for implementing libraries whose design is not centred around ADL, but wish
+    to continue writing functions as opposed to function objects (which can impact build times).
+
+    Example:
+
+    .. code-block:: cpp
+
+      namespace NS1 {
+        struct S1 {};
+        S1 inhibited(S1); // expected-note 2 {{candidate function}}
+
+        namespace NNS1 {
+          struct S2 {};
+          __disable_adl void hidden(S2);   // expected-note{{declared here}}
+          __disable_adl int inhibited(S1); // expected-note 4 {{candidate function}}
+        }
+      }
+
+      namespace NS2 {
+        __disable_adl void inhibited(NS1::S1); // expected-note 2 {{candidate function}}
+      }
+
+      int main()
+      {
+        NS1::S1 s;
+        hidden(s); // error: use of undeclared identifier 'hidden'; did you mean 'NS::NNS::hidden'?
+        {
+          NS1::S1 x = inhibited(NS1::S1{}); // no error
+        }
+        {
+          using namespace NS1::NNS1;
+          int x = inhibited(NS1::S1{}); // no error
+
+          using namespace NS1;
+          S1 y = inhibited(NS1::S1{}); // error: call to 'inhibited' is ambiguous
+        }
+        {
+          using NS1::NNS1::inhibited;
+          int x = inhibited(NS1::S1{}); // no error
+
+          using NS1::inhibited;
+          NS1::S1 y = inhibited(NS1::S1{}); // error: call to 'inhibited' is ambiguous
+        }
+        {
+          using namespace NS2;
+          inhibited(NS1::S1{}); // no error
+
+          using namespace NS1::NNS1;
+          inhibited(NS1::S1{}); // error: call to 'inhibited' is ambiguous
+        }
+        {
+          using NS2::inhibited;
+          inhibited(NS1::S1{}); // no error
+
+          using NS1::NNS1::inhibited;
+          inhibited(NS1::S1{}); // error: call to 'inhibited' is ambiguous
+        }
+      }
+  }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -4158,3 +4158,10 @@
   let Subjects = SubjectList<[Record]>;
   let Documentation = [ReadOnlyPlacementDocs];
 }
+
+def DisableADL : InheritableAttr {
+  let Spellings = [Keyword<"__disable_adl">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [DisableADLDocs];
+  let LangOpts = [CPlusPlus];
+}
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -3214,6 +3214,9 @@
   /// argument-dependent lookup.
   bool requiresADL() const { return UnresolvedLookupExprBits.RequiresADL; }
 
+  /// A function marked '__disable_adl' inhibits ADL.
+  void disableADL() { UnresolvedLookupExprBits.RequiresADL = false; }
+
   /// True if this lookup is overloaded.
   bool isOverloaded() const { return UnresolvedLookupExprBits.Overloaded; }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to