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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits