erik.pilkington updated this revision to Diff 302733.
erik.pilkington marked 20 inline comments as done.
erik.pilkington added a comment.

Address review comments, thanks!


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

https://reviews.llvm.org/D90188

Files:
  clang/include/clang/AST/DeclCXX.h
  clang/include/clang/AST/RecursiveASTVisitor.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DeclNodes.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/lib/AST/DeclBase.cpp
  clang/lib/AST/DeclCXX.cpp
  clang/lib/CodeGen/CGDecl.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTCommon.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/SemaCXX/attr-deprecated.cpp
  clang/test/SemaCXX/using-if-exists-attr.cpp
  clang/test/SemaCXX/using-if-exists.cpp
  clang/tools/libclang/CIndex.cpp

Index: clang/tools/libclang/CIndex.cpp
===================================================================
--- clang/tools/libclang/CIndex.cpp
+++ clang/tools/libclang/CIndex.cpp
@@ -6411,6 +6411,7 @@
   case Decl::Concept:
   case Decl::LifetimeExtendedTemporary:
   case Decl::RequiresExprBody:
+  case Decl::UnresolvedUsingIfExists:
     return C;
 
   // Declaration kinds that don't make any sense here, but are
Index: clang/test/SemaCXX/using-if-exists.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/using-if-exists.cpp
@@ -0,0 +1,193 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
+
+#define UIE __attribute__((using_if_exists))
+
+namespace test_basic {
+namespace NS {}
+
+using NS::x UIE; // expected-note{{using declaration annotated with 'using_if_exists' here}}
+x usex(); // expected-error{{reference to unresolved using declaration}}
+
+using NotNS::x UIE; // expected-error{{use of undeclared identifier 'NotNS'}}
+} // test_basic
+
+namespace test_redecl {
+namespace NS {}
+
+using NS::x UIE;
+using NS::x UIE;
+
+namespace NS1 {}
+namespace NS2 {}
+namespace NS3 {
+int A(); // expected-note{{target of using declaration}}
+struct B {}; // expected-note{{target of using declaration}}
+int C(); // expected-note{{conflicting declaration}}
+struct D {}; // expected-note{{conflicting declaration}}
+}
+
+using NS1::A UIE;
+using NS2::A UIE; // expected-note{{using declaration annotated with 'using_if_exists' here}} expected-note{{conflicting declaration}}
+using NS3::A UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}}
+int i = A(); // expected-error{{reference to unresolved using declaration}}
+
+using NS1::B UIE;
+using NS2::B UIE; // expected-note{{conflicting declaration}} expected-note{{using declaration annotated with 'using_if_exists' here}}
+using NS3::B UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}}
+B myB; // expected-error{{reference to unresolved using declaration}}
+
+using NS3::C UIE;
+using NS2::C UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}} expected-note{{target of using declaration}}
+int j = C();
+
+using NS3::D UIE;
+using NS2::D UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}} expected-note{{target of using declaration}}
+D myD;
+} // test_redecl
+
+namespace test_dependent {
+template <class B>
+struct S : B {
+  using B::mf UIE; // expected-note 3 {{using declaration annotated with 'using_if_exists' here}}
+  using typename B::mt UIE; // expected-note{{using declaration annotated with 'using_if_exists' here}}
+};
+
+struct BaseEmpty {
+};
+struct BaseNonEmpty {
+  void mf();
+  typedef int mt;
+};
+
+void f() {
+  S<BaseEmpty> empty;
+  S<BaseNonEmpty> nonempty;
+  empty.mf(); // expected-error {{reference to unresolved using declaration}}
+  nonempty.mf();
+  (&empty)->mf(); // expected-error {{reference to unresolved using declaration}}
+  (&nonempty)->mf();
+
+  S<BaseEmpty>::mt y; // expected-error {{reference to unresolved using declaration}}
+  S<BaseNonEmpty>::mt z;
+
+  S<BaseEmpty>::mf(); // expected-error {{reference to unresolved using declaration}}
+}
+
+template <class B>
+struct Implicit : B {
+  using B::mf UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}}
+  using typename B::mt UIE; // expected-note 2 {{using declaration annotated with 'using_if_exists' here}}
+
+  void use() {
+    mf(); // expected-error {{reference to unresolved using declaration}}
+    mt x; // expected-error {{reference to unresolved using declaration}}
+  }
+
+  mt alsoUse(); // expected-error {{reference to unresolved using declaration}}
+};
+
+void testImplicit() {
+  Implicit<BaseNonEmpty> nonempty;
+  Implicit<BaseEmpty> empty; // expected-note {{in instantiation}}
+  nonempty.use();
+  empty.use(); // expected-note {{in instantiation}}
+}
+
+template <class>
+struct NonDep : BaseEmpty {
+  using BaseEmpty::x UIE; // expected-note{{using declaration annotated with 'using_if_exists' here}}
+  x y(); // expected-error{{reference to unresolved using declaration}}
+};
+} // test_dependent
+
+namespace test_using_pack {
+template <class... Ts>
+struct S : Ts... {
+  using typename Ts::x... UIE; // expected-error 2 {{target of using declaration conflicts with declaration already in scope}} expected-note{{conflicting declaration}} expected-note{{target of using declaration}}
+};
+
+struct E1 {};
+struct E2 {};
+S<E1, E2> a;
+
+struct F1 { typedef int x; }; // expected-note 2 {{conflicting declaration}}
+struct F2 { typedef int x; }; // expected-note 2 {{target of using declaration}}
+S<F1, F2> b;
+
+S<E1, F2> c; // expected-note{{in instantiation of template class}}
+S<F1, E2> d; // expected-note{{in instantiation of template class}}
+
+template <class... Ts>
+struct S2 : Ts... {
+  using typename Ts::x... UIE; // expected-error 2 {{target of using declaration conflicts with declaration already in scope}} expected-note 3 {{using declaration annotated with 'using_if_exists' here}} expected-note{{conflicting declaration}} expected-note{{target of using declaration}}
+
+  x mem(); // expected-error 3 {{reference to unresolved using declaration}}
+};
+
+S2<E1, E2> e; // expected-note{{in instantiation of template class}}
+S2<F1, F2> f;
+S2<E1, F2> g; // expected-note{{in instantiation of template class}}
+S2<F1, E2> h; // expected-note{{in instantiation of template class}}
+
+template <class... Ts>
+struct S3 : protected Ts... {
+  using Ts::m... UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}} expected-note{{target of using declaration}}
+};
+struct B1 { enum {m}; }; // expected-note{{conflicting declaration}}
+struct B2 {};
+
+S3<B1, B2> i; // expected-note{{in instantiation of template}}
+S<B2, B1> j;
+
+} // test_using_pack
+
+namespace test_nested {
+namespace NS {}
+
+using NS::x UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}}
+
+namespace NS2 {
+using ::test_nested::x UIE;
+}
+
+NS2::x y; // expected-error {{reference to unresolved using declaration}}
+} // test_nested
+
+namespace test_scope {
+int x; // expected-note{{conflicting declaration}}
+void f() {
+  int x; // expected-note{{conflicting declaration}}
+  {
+    using ::x UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}}
+    (void)x; // expected-error {{reference to unresolved using declaration}}
+  }
+
+  {
+    using test_scope::x;
+    using ::x UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}} expected-note{{target of using declaration}}
+    (void)x;
+  }
+
+  (void)x;
+
+  using ::x UIE; // expected-error{{target of using declaration conflicts with declaration already in scope}} expected-note{{target of using declaration}}
+  (void)x;
+}
+} // test_scope
+
+typedef int *FILE;
+int printf();
+
+namespace std {
+using ::FILE UIE;
+using ::printf UIE;
+using ::fopen UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}}
+using ::size_t UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}}
+}
+
+int main() {
+  std::FILE file;
+  file = std::fopen(); // expected-error {{reference to unresolved using declaration}} expected-error{{incompatible integer to pointer}}
+  std::size_t size; // expected-error {{reference to unresolved using declaration}}
+  size = printf();
+}
Index: clang/test/SemaCXX/using-if-exists-attr.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/using-if-exists-attr.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
+
+#define UIE __attribute__((using_if_exists))
+
+namespace NS { typedef int x; }
+
+using NS::x UIE;
+
+// FIXME: Should we support the C++ spelling here?
+using NS::x [[clang::using_if_exists]]; // expected-error {{an attribute list cannot appear here}}
+
+// FIXME: This diagnostics is wrong.
+using alias UIE = NS::x; // expected-error {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}}
+
+template <class>
+using template_alias UIE = NS::x; // expected-error {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}}
+
+void f() UIE; // expected-error {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}}
+
+using namespace NS UIE; // expected-error {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}}
Index: clang/test/SemaCXX/attr-deprecated.cpp
===================================================================
--- clang/test/SemaCXX/attr-deprecated.cpp
+++ clang/test/SemaCXX/attr-deprecated.cpp
@@ -256,3 +256,15 @@
 } TDS __attribute__((deprecated)); // expected-note {{'TDS' has been explicitly marked deprecated here}}
 TDS tds; // expected-warning {{'TDS' is deprecated}}
 struct TDS tds2; // no warning, attribute only applies to the typedef.
+
+namespace test8 {
+struct A {
+  // expected-note@+1 {{'B' has been explicitly marked deprecated here}}
+  struct __attribute__((deprecated)) B {};
+};
+template <typename T> struct D : T {
+  using typename T::B;
+  B b; // expected-warning {{'B' is deprecated}}
+};
+D<A> da; // expected-note {{in instantiation of template class}}
+}
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -69,6 +69,7 @@
     void VisitTypedefDecl(TypedefDecl *D);
     void VisitTypeAliasDecl(TypeAliasDecl *D);
     void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
+    void VisitUnresolvedUsingIfExistsDecl(UnresolvedUsingIfExistsDecl *D);
     void VisitTagDecl(TagDecl *D);
     void VisitEnumDecl(EnumDecl *D);
     void VisitRecordDecl(RecordDecl *D);
@@ -1339,6 +1340,12 @@
   Code = serialization::DECL_UNRESOLVED_USING_TYPENAME;
 }
 
+void ASTDeclWriter::VisitUnresolvedUsingIfExistsDecl(
+    UnresolvedUsingIfExistsDecl *D) {
+  VisitNamedDecl(D);
+  Code = serialization::DECL_UNRESOLVED_USING_IF_EXISTS;
+}
+
 void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
   VisitRecordDecl(D);
 
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -328,6 +328,7 @@
     void VisitTypedefDecl(TypedefDecl *TD);
     void VisitTypeAliasDecl(TypeAliasDecl *TD);
     void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
+    void VisitUnresolvedUsingIfExistsDecl(UnresolvedUsingIfExistsDecl *D);
     RedeclarableResult VisitTagDecl(TagDecl *TD);
     void VisitEnumDecl(EnumDecl *ED);
     RedeclarableResult VisitRecordDeclImpl(RecordDecl *RD);
@@ -1690,6 +1691,11 @@
   mergeMergeable(D);
 }
 
+void ASTDeclReader::VisitUnresolvedUsingIfExistsDecl(
+    UnresolvedUsingIfExistsDecl *D) {
+  VisitNamedDecl(D);
+}
+
 void ASTDeclReader::ReadCXXDefinitionData(
     struct CXXRecordDecl::DefinitionData &Data, const CXXRecordDecl *D) {
   #define FIELD(Name, Width, Merge) \
@@ -3829,6 +3835,9 @@
   case DECL_UNRESOLVED_USING_TYPENAME:
     D = UnresolvedUsingTypenameDecl::CreateDeserialized(Context, ID);
     break;
+  case DECL_UNRESOLVED_USING_IF_EXISTS:
+    D = UnresolvedUsingIfExistsDecl::CreateDeserialized(Context, ID);
+    break;
   case DECL_CXX_RECORD:
     D = CXXRecordDecl::CreateDeserialized(Context, ID);
     break;
Index: clang/lib/Serialization/ASTCommon.cpp
===================================================================
--- clang/lib/Serialization/ASTCommon.cpp
+++ clang/lib/Serialization/ASTCommon.cpp
@@ -416,6 +416,7 @@
   case Decl::Concept:
   case Decl::LifetimeExtendedTemporary:
   case Decl::RequiresExprBody:
+  case Decl::UnresolvedUsingIfExists:
     return false;
 
   // These indirectly derive from Redeclarable<T> but are not actually
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -14111,7 +14111,11 @@
 
     // A valid resolved using typename decl points to exactly one type decl.
     assert(++Using->shadow_begin() == Using->shadow_end());
-    Ty = cast<TypeDecl>((*Using->shadow_begin())->getTargetDecl());
+
+    NamedDecl *Target = Using->shadow_begin()->getTargetDecl();
+    if (SemaRef.DiagnoseUseOfDecl(Target, Loc))
+      return QualType();
+    Ty = cast<TypeDecl>(Target);
   } else {
     assert(isa<UnresolvedUsingTypenameDecl>(D) &&
            "UnresolvedUsingTypenameDecl transformed to non-using decl");
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3035,9 +3035,15 @@
       if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl())
         OldTarget = BaseShadow;
 
-    NamedDecl *InstTarget =
-        cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
-            Shadow->getLocation(), OldTarget, TemplateArgs));
+    NamedDecl *InstTarget = nullptr;
+    if (auto *EmptyD =
+            dyn_cast<UnresolvedUsingIfExistsDecl>(Shadow->getTargetDecl())) {
+      InstTarget = UnresolvedUsingIfExistsDecl::Create(
+          SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName());
+    } else {
+      InstTarget = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+          Shadow->getLocation(), OldTarget, TemplateArgs));
+    }
     if (!InstTarget)
       return nullptr;
 
@@ -3160,13 +3166,16 @@
   SourceLocation EllipsisLoc =
       InstantiatingSlice ? SourceLocation() : D->getEllipsisLoc();
 
+  bool IsUsingIfExists = D->template hasAttr<UsingIfExistsAttr>();
   NamedDecl *UD = SemaRef.BuildUsingDeclaration(
       /*Scope*/ nullptr, D->getAccess(), D->getUsingLoc(),
       /*HasTypename*/ TD, TypenameLoc, SS, NameInfo, EllipsisLoc,
       ParsedAttributesView(),
-      /*IsInstantiation*/ true);
-  if (UD)
+      /*IsInstantiation*/ true, IsUsingIfExists);
+  if (UD) {
+    SemaRef.InstantiateAttrs(TemplateArgs, D, UD);
     SemaRef.Context.setInstantiatedFromUsingDecl(UD, D);
+  }
 
   return UD;
 }
@@ -3181,6 +3190,11 @@
   return instantiateUnresolvedUsingDecl(D);
 }
 
+Decl *TemplateDeclInstantiator::VisitUnresolvedUsingIfExistsDecl(
+    UnresolvedUsingIfExistsDecl *D) {
+  llvm_unreachable("referring to unresolved decl out of UsingShadowDecl");
+}
+
 Decl *TemplateDeclInstantiator::VisitUsingPackDecl(UsingPackDecl *D) {
   SmallVector<NamedDecl*, 8> Expansions;
   for (auto *UD : D->expansions()) {
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -82,6 +82,9 @@
       cast<Decl>(CurContext)->getAvailability() != AR_Unavailable)
     return false;
 
+  if (isa<UnresolvedUsingIfExistsDecl>(D))
+    return false;
+
   return true;
 }
 
@@ -347,6 +350,12 @@
     return true;
   }
 
+  if (const auto *EmptyD = dyn_cast<UnresolvedUsingIfExistsDecl>(D)) {
+    Diag(Loc, diag::err_use_of_empty_using_if_exists);
+    Diag(EmptyD->getLocation(), diag::note_empty_using_if_exists_here);
+    return true;
+  }
+
   DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess,
                              AvoidPartialAvailabilityChecks, ClassReceiver);
 
@@ -3141,8 +3150,7 @@
   }
 
   // Make sure that we're referring to a value.
-  ValueDecl *VD = dyn_cast<ValueDecl>(D);
-  if (!VD) {
+  if (!isa<ValueDecl, UnresolvedUsingIfExistsDecl>(D)) {
     Diag(Loc, diag::err_ref_non_value)
       << D << SS.getRange();
     Diag(D->getLocation(), diag::note_declared_at);
@@ -3153,9 +3161,11 @@
   // this check when we're going to perform argument-dependent lookup
   // on this function name, because this might not be the function
   // that overload resolution actually selects.
-  if (DiagnoseUseOfDecl(VD, Loc))
+  if (DiagnoseUseOfDecl(D, Loc))
     return ExprError();
 
+  ValueDecl *VD = cast<ValueDecl>(D);
+
   // Only create DeclRefExpr's for valid Decl's.
   if (VD->isInvalidDecl() && !AcceptInvalidDecl)
     return ExprError();
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -11502,10 +11502,11 @@
     }
   }
 
-  NamedDecl *UD =
-      BuildUsingDeclaration(S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc,
-                            SS, TargetNameInfo, EllipsisLoc, AttrList,
-                            /*IsInstantiation*/false);
+  NamedDecl *UD = BuildUsingDeclaration(
+      S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc, SS, TargetNameInfo,
+      EllipsisLoc, AttrList,
+      /*IsInstantiation=*/false,
+      AttrList.hasAttribute(ParsedAttr::AT_UsingIfExists));
   if (UD)
     PushOnScopeChains(UD, S, /*AddToContext*/ false);
 
@@ -11525,6 +11526,12 @@
       return Context.hasSameType(TD1->getUnderlyingType(),
                                  TD2->getUnderlyingType());
 
+  // Two using_if_exists using-declarations are equivalent if both are
+  // unresolved.
+  if (isa<UnresolvedUsingIfExistsDecl>(D1) &&
+      isa<UnresolvedUsingIfExistsDecl>(D2))
+    return true;
+
   return false;
 }
 
@@ -11635,6 +11642,19 @@
   if (FoundEquivalentDecl)
     return false;
 
+  // Always emit a diagnostic for a mismatch between an unresolved
+  // using_if_exists and a resolved using declaration in either direction.
+  if (isa<UnresolvedUsingIfExistsDecl>(Target) !=
+      (NonTag && isa<UnresolvedUsingIfExistsDecl>(NonTag))) {
+    if (!NonTag && !Tag)
+      return false;
+    Diag(Using->getLocation(), diag::err_using_decl_conflict);
+    Diag(Target->getLocation(), diag::note_using_decl_target);
+    Diag((NonTag ? NonTag : Tag)->getLocation(), diag::note_using_decl_conflict);
+    Using->setInvalidDecl();
+    return true;
+  }
+
   if (FunctionDecl *FD = Target->getAsFunction()) {
     NamedDecl *OldDecl = nullptr;
     switch (CheckOverload(nullptr, FD, Previous, OldDecl,
@@ -11899,7 +11919,8 @@
     Scope *S, AccessSpecifier AS, SourceLocation UsingLoc,
     bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS,
     DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc,
-    const ParsedAttributesView &AttrList, bool IsInstantiation) {
+    const ParsedAttributesView &AttrList, bool IsInstantiation,
+    bool IsUsingIfExists) {
   assert(!SS.isInvalid() && "Invalid CXXScopeSpec.");
   SourceLocation IdentLoc = NameInfo.getLoc();
   assert(IdentLoc.isValid() && "Invalid TargetName location.");
@@ -11969,9 +11990,9 @@
     return nullptr;
 
   DeclContext *LookupContext = computeDeclContext(SS);
-  NamedDecl *D;
   NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
   if (!LookupContext || EllipsisLoc.isValid()) {
+    NamedDecl *D;
     if (HasTypenameKeyword) {
       // FIXME: not all declaration name kinds are legal here
       D = UnresolvedUsingTypenameDecl::Create(Context, CurContext,
@@ -11985,6 +12006,7 @@
     }
     D->setAccess(AS);
     CurContext->addDecl(D);
+    ProcessDeclAttributeList(S, D, AttrList);
     return D;
   }
 
@@ -12022,6 +12044,10 @@
 
   LookupQualifiedName(R, LookupContext);
 
+  if (R.empty() && IsUsingIfExists)
+    R.addDecl(UnresolvedUsingIfExistsDecl::Create(Context, CurContext, UsingLoc,
+                                                  UsingName.getName()), AS_public);
+
   // Try to correct typos if possible. If constructor name lookup finds no
   // results, that means the named class has no explicit constructors, and we
   // suppressed declaring implicit ones (probably because it's dependent or
@@ -12096,7 +12122,8 @@
 
   if (HasTypenameKeyword) {
     // If we asked for a typename and got a non-type decl, error out.
-    if (!R.getAsSingle<TypeDecl>()) {
+    if (!R.getAsSingle<TypeDecl>() &&
+        !R.getAsSingle<UnresolvedUsingIfExistsDecl>()) {
       Diag(IdentLoc, diag::err_using_typename_non_type);
       for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
         Diag((*I)->getUnderlyingDecl()->getLocation(),
@@ -12118,7 +12145,7 @@
   // A using-declaration shall not name a namespace.
   if (R.getAsSingle<NamespaceDecl>()) {
     Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_namespace)
-      << SS.getRange();
+        << SS.getRange();
     return BuildInvalid();
   }
 
@@ -12127,7 +12154,7 @@
   if (auto *ED = R.getAsSingle<EnumConstantDecl>()) {
     if (cast<EnumDecl>(ED->getDeclContext())->isScoped()) {
       Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum)
-        << SS.getRange();
+          << SS.getRange();
       return BuildInvalid();
     }
   }
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -7959,6 +7959,10 @@
   case ParsedAttr::AT_UseHandle:
     handleHandleAttr<UseHandleAttr>(S, D, AL);
     break;
+
+  case ParsedAttr::AT_UsingIfExists:
+    handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL);
+    break;
   }
 }
 
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -434,12 +434,14 @@
     // Look to see if we have a type anywhere in the list of results.
     for (LookupResult::iterator Res = Result.begin(), ResEnd = Result.end();
          Res != ResEnd; ++Res) {
-      if (isa<TypeDecl>(*Res) || isa<ObjCInterfaceDecl>(*Res) ||
-          (AllowDeducedTemplate && getAsTypeTemplateDecl(*Res))) {
+      NamedDecl *RealRes = (*Res)->getUnderlyingDecl();
+      if (isa<TypeDecl, ObjCInterfaceDecl, UnresolvedUsingIfExistsDecl>(RealRes) ||
+          (AllowDeducedTemplate && getAsTypeTemplateDecl(RealRes))) {
         if (!IIDecl ||
-            (*Res)->getLocation().getRawEncoding() <
+            // Make the selection of the recovery decl deterministic.
+            (RealRes)->getLocation().getRawEncoding() <
               IIDecl->getLocation().getRawEncoding())
-          IIDecl = *Res;
+          IIDecl = RealRes;
       }
     }
 
@@ -488,6 +490,10 @@
     (void)DiagnoseUseOfDecl(IDecl, NameLoc);
     if (!HasTrailingDot)
       T = Context.getObjCInterfaceType(IDecl);
+  } else if (auto *UD = dyn_cast<UnresolvedUsingIfExistsDecl>(IIDecl)) {
+    (void)DiagnoseUseOfDecl(UD, NameLoc);
+    // Recover with 'int'
+    T = Context.IntTy;
   } else if (AllowDeducedTemplate) {
     if (auto *TD = getAsTypeTemplateDecl(IIDecl))
       T = Context.getDeducedTemplateSpecializationType(TemplateName(TD),
@@ -504,7 +510,7 @@
   // constructor or destructor name (in such a case, the scope specifier
   // will be attached to the enclosing Expr or Decl node).
   if (SS && SS->isNotEmpty() && !IsCtorOrDtorName &&
-      !isa<ObjCInterfaceDecl>(IIDecl)) {
+      !isa<ObjCInterfaceDecl, UnresolvedUsingIfExistsDecl>(IIDecl)) {
     if (WantNontrivialTypeSourceInfo) {
       // Construct a type with type-source information.
       TypeLocBuilder Builder;
@@ -1163,6 +1169,11 @@
     return NameClassification::Concept(
         TemplateName(cast<TemplateDecl>(FirstDecl)));
 
+  if (auto *EmptyD = dyn_cast<UnresolvedUsingIfExistsDecl>(FirstDecl)) {
+    DiagnoseUseOfDecl(EmptyD, NameLoc);
+    return NameClassification::Error();
+  }
+
   // We can have a type template here if we're classifying a template argument.
   if (isa<TemplateDecl>(FirstDecl) && !isa<FunctionTemplateDecl>(FirstDecl) &&
       !isa<VarTemplateDecl>(FirstDecl))
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -99,6 +99,7 @@
   case Decl::ConstructorUsingShadow:
   case Decl::ObjCTypeParam:
   case Decl::Binding:
+  case Decl::UnresolvedUsingIfExists:
     llvm_unreachable("Declaration should not be in declstmts!");
   case Decl::Record:    // struct/union/class X;
   case Decl::CXXRecord: // struct/union/class X; [C++]
Index: clang/lib/AST/DeclCXX.cpp
===================================================================
--- clang/lib/AST/DeclCXX.cpp
+++ clang/lib/AST/DeclCXX.cpp
@@ -3117,6 +3117,25 @@
       SourceLocation(), nullptr, SourceLocation());
 }
 
+UnresolvedUsingIfExistsDecl *
+UnresolvedUsingIfExistsDecl::Create(ASTContext &Ctx, DeclContext *DC,
+                                    SourceLocation Loc, DeclarationName Name) {
+  return new (Ctx, DC) UnresolvedUsingIfExistsDecl(DC, Loc, Name);
+}
+
+UnresolvedUsingIfExistsDecl *
+UnresolvedUsingIfExistsDecl::CreateDeserialized(ASTContext &Ctx, unsigned ID) {
+  return new (Ctx, ID)
+      UnresolvedUsingIfExistsDecl(nullptr, SourceLocation(), DeclarationName());
+}
+
+UnresolvedUsingIfExistsDecl::UnresolvedUsingIfExistsDecl(DeclContext *DC,
+                                                         SourceLocation Loc,
+                                                         DeclarationName Name)
+    : NamedDecl(Decl::UnresolvedUsingIfExists, DC, Loc, Name) {}
+
+void UnresolvedUsingIfExistsDecl::anchor() {}
+
 void StaticAssertDecl::anchor() {}
 
 StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC,
Index: clang/lib/AST/DeclBase.cpp
===================================================================
--- clang/lib/AST/DeclBase.cpp
+++ clang/lib/AST/DeclBase.cpp
@@ -812,6 +812,9 @@
     case TypeAliasTemplate:
       return IDNS_Ordinary | IDNS_Tag | IDNS_Type;
 
+    case UnresolvedUsingIfExists:
+      return IDNS_Type | IDNS_Ordinary;
+
     case OMPDeclareReduction:
       return IDNS_OMPReduction;
 
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -1354,6 +1354,9 @@
       /// An UnresolvedUsingTypenameDecl record.
       DECL_UNRESOLVED_USING_TYPENAME,
 
+      /// An UnresolvedUsingIfExistsDecl record.
+      DECL_UNRESOLVED_USING_IF_EXISTS,
+
       /// A LinkageSpecDecl record.
       DECL_LINKAGE_SPEC,
 
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -5426,7 +5426,8 @@
       Scope *S, AccessSpecifier AS, SourceLocation UsingLoc,
       bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS,
       DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc,
-      const ParsedAttributesView &AttrList, bool IsInstantiation);
+      const ParsedAttributesView &AttrList, bool IsInstantiation,
+      bool IsUsingIfExists);
   NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom,
                                 ArrayRef<NamedDecl *> Expansions);
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -548,6 +548,10 @@
 def note_using_decl : Note<"%select{|previous }0using declaration">;
 def err_using_decl_redeclaration_expansion : Error<
   "using declaration pack expansion at block scope produces multiple values">;
+def err_use_of_empty_using_if_exists : Error<
+  "reference to unresolved using declaration">;
+def note_empty_using_if_exists_here : Note<
+  "using declaration annotated with 'using_if_exists' here">;
 
 def warn_access_decl_deprecated : Warning<
   "access declarations are deprecated; use using declarations instead">,
Index: clang/include/clang/Basic/DeclNodes.td
===================================================================
--- clang/include/clang/Basic/DeclNodes.td
+++ clang/include/clang/Basic/DeclNodes.td
@@ -74,6 +74,7 @@
   def UsingPack : DeclNode<Named>;
   def UsingShadow : DeclNode<Named>;
     def ConstructorUsingShadow : DeclNode<UsingShadow>;
+  def UnresolvedUsingIfExists : DeclNode<Named>;
   def ObjCMethod : DeclNode<Named, "Objective-C methods">, DeclContext;
   def ObjCContainer : DeclNode<Named, "Objective-C containers", 1>, DeclContext;
     def ObjCCategory : DeclNode<ObjCContainer>;
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -5260,6 +5260,28 @@
   }];
 }
 
+def UsingIfExistsDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{
+The ``using_if_exists`` attribute applies to a using-declaration. It allows
+programmers to import a declaration that potentially does not exist, instead
+deferring any errors to the point of use. For instance:
+
+.. code-block:: c++
+
+  namespace empty_namespace {};
+  using empty_namespace::does_not_exist __attribute__((using_if_exists)); // no error!
+
+  does_not_exist x; // error: use of unresolved using_if_exists
+
+If the entity referred to by the using-declaration is found by name lookup, the
+attribute has no effect. This attribute is useful for libraries (primarily,
+libc++) that wish to redeclare a set of declarations in another namespace, when
+the availability of those declarations is difficult or impossible to detect at
+compile time with the preprocessor.
+  }];
+}
+
 def HandleDocs : DocumentationCategory<"Handle Attributes"> {
   let Content = [{
 Handles are a way to identify resources like files, sockets, and processes.
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3530,6 +3530,14 @@
   let Documentation = [NoBuiltinDocs];
 }
 
+def UsingIfExists : InheritableAttr {
+  let Spellings = [Clang<"using_if_exists", 0>];
+  let Subjects = SubjectList<[Using,
+                              UnresolvedUsingTypename,
+                              UnresolvedUsingValue], ErrorDiag>;
+  let Documentation = [UsingIfExistsDocs];
+}
+
 // FIXME: This attribute is not inheritable, it will not be propagated to
 // redecls. [[clang::lifetimebound]] has the same problems. This should be
 // fixed in TableGen (by probably adding a new inheritable flag).
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1825,6 +1825,8 @@
   // source.
 })
 
+DEF_TRAVERSE_DECL(UnresolvedUsingIfExistsDecl, {})
+
 DEF_TRAVERSE_DECL(EnumDecl, {
   TRY_TO(TraverseDeclTemplateParameterLists(D));
 
Index: clang/include/clang/AST/DeclCXX.h
===================================================================
--- clang/include/clang/AST/DeclCXX.h
+++ clang/include/clang/AST/DeclCXX.h
@@ -3793,6 +3793,29 @@
   static bool classofKind(Kind K) { return K == UnresolvedUsingTypename; }
 };
 
+/// This node is generated when a using-declaration that was annotated with
+/// __attribute__((using_if_exists)) failed to resolve to a known declaration.
+/// In that case, Sema builds a UsingShadowDecl whose target is an instance of
+/// this declaration, adding it to the current scope. Referring to this
+/// declaration in any way is an error.
+class UnresolvedUsingIfExistsDecl final : public NamedDecl {
+  UnresolvedUsingIfExistsDecl(DeclContext *DC, SourceLocation Loc,
+                              DeclarationName Name);
+
+  void anchor() override;
+public:
+  static UnresolvedUsingIfExistsDecl *Create(ASTContext &Ctx, DeclContext *DC,
+                                             SourceLocation Loc,
+                                             DeclarationName Name);
+  static UnresolvedUsingIfExistsDecl *CreateDeserialized(ASTContext &Ctx,
+                                                         unsigned ID);
+
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) {
+    return K == Decl::UnresolvedUsingIfExists;
+  }
+};
+
 /// Represents a C++11 static_assert declaration.
 class StaticAssertDecl : public Decl {
   llvm::PointerIntPair<Expr *, 1, bool> AssertExprAndFailed;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to