diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -3776,6 +3776,28 @@ 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 AssertExprAndFailed; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1846,6 +1846,8 @@ // source. }) +DEF_TRAVERSE_DECL(UnresolvedUsingIfExistsDecl, {}) + DEF_TRAVERSE_DECL(EnumDecl, { TRY_TO(TraverseDeclTemplateParameterLists(D)); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3750,6 +3750,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). diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5775,6 +5775,31 @@ }]; } +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 {}; + __attribute__((using_if_exists)) + using empty_namespace::does_not_exist; // no error! + + does_not_exist x; // error: use of unresolved 'using_if_exists' + +The C++ spelling of the attribte (`[[clang::using_if_exists]]`) is also +supported as a clang extension, since ISO C++ doesn't support attributes in this +position. 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. diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -75,6 +75,7 @@ def UsingPack : DeclNode; def UsingShadow : DeclNode; def ConstructorUsingShadow : DeclNode; + def UnresolvedUsingIfExists : DeclNode; def ObjCMethod : DeclNode, DeclContext; def ObjCContainer : DeclNode, DeclContext; def ObjCCategory : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -574,6 +574,12 @@ 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 err_using_if_exists_on_ctor : Error< + "'using_if_exists' attribute cannot be applied to an inheriting constructor">; def warn_access_decl_deprecated : Warning< "access declarations are deprecated; use using declarations instead">, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5720,11 +5720,14 @@ const DeclarationNameInfo &NameInfo, SourceLocation NameLoc); - NamedDecl *BuildUsingDeclaration( - Scope *S, AccessSpecifier AS, SourceLocation UsingLoc, - bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS, - DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc, - const ParsedAttributesView &AttrList, bool IsInstantiation); + NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS, + SourceLocation UsingLoc, + bool HasTypenameKeyword, + SourceLocation TypenameLoc, CXXScopeSpec &SS, + DeclarationNameInfo NameInfo, + SourceLocation EllipsisLoc, + const ParsedAttributesView &AttrList, + bool IsInstantiation, bool IsUsingIfExists); NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom, ArrayRef Expansions); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1426,6 +1426,9 @@ /// \brief A ConceptDecl record. DECL_CONCEPT, + /// An UnresolvedUsingIfExistsDecl record. + DECL_UNRESOLVED_USING_IF_EXISTS, + /// \brief A StaticAssertDecl record. DECL_STATIC_ASSERT, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -811,6 +811,9 @@ case TypeAliasTemplate: return IDNS_Ordinary | IDNS_Tag | IDNS_Type; + case UnresolvedUsingIfExists: + return IDNS_Type | IDNS_Ordinary; + case OMPDeclareReduction: return IDNS_OMPReduction; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3150,6 +3150,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, diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/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++] diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -434,10 +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(*Res) || isa(*Res) || - (AllowDeducedTemplate && getAsTypeTemplateDecl(*Res))) { - if (!IIDecl || (*Res)->getLocation() < IIDecl->getLocation()) - IIDecl = *Res; + NamedDecl *RealRes = (*Res)->getUnderlyingDecl(); + if (isa( + RealRes) || + (AllowDeducedTemplate && getAsTypeTemplateDecl(RealRes))) { + if (!IIDecl || + // Make the selection of the recovery decl deterministic. + RealRes->getLocation() < IIDecl->getLocation()) + IIDecl = RealRes; } } @@ -486,6 +490,10 @@ (void)DiagnoseUseOfDecl(IDecl, NameLoc); if (!HasTrailingDot) T = Context.getObjCInterfaceType(IDecl); + } else if (auto *UD = dyn_cast(IIDecl)) { + (void)DiagnoseUseOfDecl(UD, NameLoc); + // Recover with 'int' + T = Context.IntTy; } else if (AllowDeducedTemplate) { if (auto *TD = getAsTypeTemplateDecl(IIDecl)) T = Context.getDeducedTemplateSpecializationType(TemplateName(TD), @@ -502,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(IIDecl)) { + !isa(IIDecl)) { if (WantNontrivialTypeSourceInfo) { // Construct a type with type-source information. TypeLocBuilder Builder; @@ -1161,6 +1169,11 @@ return NameClassification::Concept( TemplateName(cast(FirstDecl))); + if (auto *EmptyD = dyn_cast(FirstDecl)) { + (void)DiagnoseUseOfDecl(EmptyD, NameLoc); + return NameClassification::Error(); + } + // We can have a type template here if we're classifying a template argument. if (isa(FirstDecl) && !isa(FirstDecl) && !isa(FirstDecl)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8332,6 +8332,10 @@ case ParsedAttr::AT_BuiltinAlias: handleBuiltinAliasAttr(S, D, AL); break; + + case ParsedAttr::AT_UsingIfExists: + handleSimpleAttribute(S, D, AL); + break; } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11604,10 +11604,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); @@ -11627,6 +11628,12 @@ return Context.hasSameType(TD1->getUnderlyingType(), TD2->getUnderlyingType()); + // Two using_if_exists using-declarations are equivalent if both are + // unresolved. + if (isa(D1) && + isa(D2)) + return true; + return false; } @@ -11737,6 +11744,20 @@ 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(Target) != + (isa_and_nonnull(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, @@ -12001,7 +12022,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."); @@ -12070,6 +12092,13 @@ IdentLoc)) return nullptr; + // 'using_if_exists' doesn't make sense on an inherited constructor. + if (IsUsingIfExists && UsingName.getName().getNameKind() == + DeclarationName::CXXConstructorName) { + Diag(UsingLoc, diag::err_using_if_exists_on_ctor); + return nullptr; + } + DeclContext *LookupContext = computeDeclContext(SS); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); if (!LookupContext || EllipsisLoc.isValid()) { @@ -12126,6 +12155,11 @@ 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 @@ -12199,7 +12233,8 @@ if (HasTypenameKeyword) { // If we asked for a typename and got a non-type decl, error out. - if (!R.getAsSingle()) { + if (!R.getAsSingle() && + !R.getAsSingle()) { Diag(IdentLoc, diag::err_using_typename_non_type); for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) Diag((*I)->getUnderlyingDecl()->getLocation(), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -83,6 +83,9 @@ cast(CurContext)->getAvailability() != AR_Unavailable) return false; + if (isa(D)) + return false; + return true; } @@ -348,6 +351,12 @@ return true; } + if (const auto *EmptyD = dyn_cast(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); @@ -3208,8 +3217,7 @@ } // Make sure that we're referring to a value. - ValueDecl *VD = dyn_cast(D); - if (!VD) { + if (!isa(D)) { Diag(Loc, diag::err_ref_non_value) << D << SS.getRange(); Diag(D->getLocation(), diag::note_declared_at); @@ -3220,9 +3228,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(); + auto *VD = cast(D); + // Only create DeclRefExpr's for valid Decl's. if (VD->isInvalidDecl() && !AcceptInvalidDecl) return ExprError(); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3093,9 +3093,15 @@ if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl()) OldTarget = BaseShadow; - NamedDecl *InstTarget = - cast_or_null(SemaRef.FindInstantiatedDecl( - Shadow->getLocation(), OldTarget, TemplateArgs)); + NamedDecl *InstTarget = nullptr; + if (auto *EmptyD = + dyn_cast(Shadow->getTargetDecl())) { + InstTarget = UnresolvedUsingIfExistsDecl::Create( + SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName()); + } else { + InstTarget = cast_or_null(SemaRef.FindInstantiatedDecl( + Shadow->getLocation(), OldTarget, TemplateArgs)); + } if (!InstTarget) return nullptr; @@ -3218,13 +3224,16 @@ SourceLocation EllipsisLoc = InstantiatingSlice ? SourceLocation() : D->getEllipsisLoc(); + bool IsUsingIfExists = D->template hasAttr(); 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; } @@ -3239,6 +3248,11 @@ return instantiateUnresolvedUsingDecl(D); } +Decl *TemplateDeclInstantiator::VisitUnresolvedUsingIfExistsDecl( + UnresolvedUsingIfExistsDecl *D) { + llvm_unreachable("referring to unresolved decl out of UsingShadowDecl"); +} + Decl *TemplateDeclInstantiator::VisitUsingPackDecl(UsingPackDecl *D) { SmallVector Expansions; for (auto *UD : D->expansions()) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14394,7 +14394,11 @@ // A valid resolved using typename decl points to exactly one type decl. assert(++Using->shadow_begin() == Using->shadow_end()); - Ty = cast((*Using->shadow_begin())->getTargetDecl()); + + NamedDecl *Target = Using->shadow_begin()->getTargetDecl(); + if (SemaRef.DiagnoseUseOfDecl(Target, Loc)) + return QualType(); + Ty = cast(Target); } else { assert(isa(D) && "UnresolvedUsingTypenameDecl transformed to non-using decl"); diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -427,6 +427,7 @@ case Decl::Concept: case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: + case Decl::UnresolvedUsingIfExists: return false; // These indirectly derive from Redeclarable but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/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); @@ -1707,6 +1708,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) \ @@ -3850,6 +3856,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; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -68,6 +68,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); @@ -1333,6 +1334,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); diff --git a/clang/test/Parser/using-if-exists-attr.cpp b/clang/test/Parser/using-if-exists-attr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/using-if-exists-attr.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -pedantic -verify + +#define UIE __attribute__((using_if_exists)) + +namespace NS { +typedef int x; +} + +using NS::x __attribute__((using_if_exists)); + +using NS::x [[clang::using_if_exists]]; // expected-warning{{ISO C++ does not allow an attribute list to appear here}} + +[[clang::using_if_exists]] // expected-warning{{ISO C++ does not allow an attribute list to appear here}} +using NS::not_there, + NS::not_there2; + +using NS::not_there3, // expected-error {{no member named 'not_there3' in namespace 'NS'}} + NS::not_there4 [[clang::using_if_exists]]; // expected-warning{{C++ does not allow an attribute list to appear here}} + +[[clang::using_if_exists]] using NS::not_there5 [[clang::using_if_exists]]; // expected-warning 2 {{ISO C++ does not allow}} + +struct Base {}; +struct Derived : Base { + [[clang::using_if_exists]] using Base::x; // expected-warning {{ISO C++ does not allow an attribute list to appear here}} + using Base::y [[clang::using_if_exists]]; // expected-warning {{ISO C++ does not allow an attribute list to appear here}} + [[clang::using_if_exists]] using Base::z, Base::q; // expected-warning {{C++ does not allow an attribute list to appear here}} +}; diff --git a/clang/test/SemaCXX/attr-deprecated.cpp b/clang/test/SemaCXX/attr-deprecated.cpp --- a/clang/test/SemaCXX/attr-deprecated.cpp +++ b/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 struct D : T { + using typename T::B; + B b; // expected-warning {{'B' is deprecated}} +}; +D da; // expected-note {{in instantiation of template class}} +} // namespace test8 diff --git a/clang/test/SemaCXX/using-if-exists.cpp b/clang/test/SemaCXX/using-if-exists.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/using-if-exists.cpp @@ -0,0 +1,226 @@ +// 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'}} + +using NS::NotNS::x UIE; // expected-error{{no member named 'NotNS' in namespace 'test_basic::NS'}} +} // namespace 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}} +} // namespace NS3 + +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; +} // namespace test_redecl + +namespace test_dependent { +template +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; +}; + +template +struct UseCtor : Base { + using Base::Base UIE; // expected-error{{'using_if_exists' attribute cannot be applied to an inheriting constructor}} +}; +struct BaseCtor {}; + +void f() { + S empty; + S nonempty; + empty.mf(); // expected-error {{reference to unresolved using declaration}} + nonempty.mf(); + (&empty)->mf(); // expected-error {{reference to unresolved using declaration}} + (&nonempty)->mf(); + + S::mt y; // expected-error {{reference to unresolved using declaration}} + S::mt z; + + S::mf(); // expected-error {{reference to unresolved using declaration}} + + UseCtor usector; +} + +template +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 nonempty; + Implicit empty; // expected-note {{in instantiation}} + nonempty.use(); + empty.use(); // expected-note {{in instantiation}} +} + +template +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}} +}; +} // namespace test_dependent + +namespace test_using_pack { +template +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 a; + +struct F1 { + typedef int x; // expected-note 2 {{conflicting declaration}} +}; +struct F2 { + typedef int x; // expected-note 2 {{target of using declaration}} +}; +S b; + +S c; // expected-note{{in instantiation of template class}} +S d; // expected-note{{in instantiation of template class}} + +template +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 e; // expected-note{{in instantiation of template class}} +S2 f; +S2 g; // expected-note{{in instantiation of template class}} +S2 h; // expected-note{{in instantiation of template class}} + +template +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 i; // expected-note{{in instantiation of template}} +S j; + +} // namespace 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}} +} // namespace 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; +} +} // namespace test_scope + +namespace test_appertains_to { +namespace NS { +typedef int x; +} + +// 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 +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}} +} // namespace test_appertains_to + +typedef int *fake_FILE; +int fake_printf(); + +namespace std { +using ::fake_FILE UIE; +using ::fake_printf UIE; +using ::fake_fopen UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}} +using ::fake_size_t UIE; // expected-note {{using declaration annotated with 'using_if_exists' here}} +} // namespace std + +int main() { + std::fake_FILE file; + file = std::fake_fopen(); // expected-error {{reference to unresolved using declaration}} expected-error{{incompatible integer to pointer}} + std::fake_size_t size; // expected-error {{reference to unresolved using declaration}} + size = fake_printf(); + size = std::fake_printf(); +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6466,6 +6466,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