Author: Erik Pilkington Date: 2021-06-02T10:30:24-04:00 New Revision: 369c64839946d89cf5697550b6feeea031b2f270
URL: https://github.com/llvm/llvm-project/commit/369c64839946d89cf5697550b6feeea031b2f270 DIFF: https://github.com/llvm/llvm-project/commit/369c64839946d89cf5697550b6feeea031b2f270.diff LOG: [clang] Implement the using_if_exists attribute This attribute applies to a using declaration, and permits importing a declaration without knowing if that declaration exists. This is useful for libc++ C wrapper headers that re-export declarations in std::, in cases where the base C library doesn't provide all declarations. This attribute was proposed in http://lists.llvm.org/pipermail/cfe-dev/2020-June/066038.html. rdar://69313357 Differential Revision: https://reviews.llvm.org/D90188 Added: clang/test/Parser/using-if-exists-attr.cpp clang/test/SemaCXX/using-if-exists.cpp Modified: 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/tools/libclang/CIndex.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index c9efc4b454968..5c7cdd67e3d32 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -3776,6 +3776,28 @@ class UnresolvedUsingTypenameDecl 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; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 0623a5c8d5ea8..ab49b39307be8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1846,6 +1846,8 @@ DEF_TRAVERSE_DECL(UnresolvedUsingTypenameDecl, { // 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 index 5bfcec7329070..714621087cb77 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3750,6 +3750,14 @@ def NoBuiltin : Attr { 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 index cdbbb38573dab..6594e77d0beef 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5775,6 +5775,31 @@ once. }]; } +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 diff icult 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 index 4771a3549426b..5e60226db7b57 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -75,6 +75,7 @@ def Named : DeclNode<Decl, "named declarations", 1>; 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>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 07835eb584e96..82dae0fa01d22 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -574,6 +574,12 @@ def err_using_decl_conflict_reverse : Error< 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 index 347a909d99574..2978f7249ea48 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5720,11 +5720,14 @@ class Sema final { 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<NamedDecl *> Expansions); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 83a6f6a2c4402..ff316904c0a38 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1426,6 +1426,9 @@ enum DeclCode { /// \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 index 6d438cf05590a..4e6d2da1e0d51 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -811,6 +811,9 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { 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 index 2bb7acc21a931..5b459686d8795 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3150,6 +3150,25 @@ UnresolvedUsingTypenameDecl::CreateDeserialized(ASTContext &C, unsigned ID) { 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 index 22bd8d14b87a8..3be0f6a79b804 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -99,6 +99,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { 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 index 612b7067777e5..0abdd31b43ec3 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -434,10 +434,14 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, // 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))) { - if (!IIDecl || (*Res)->getLocation() < IIDecl->getLocation()) - IIDecl = *Res; + NamedDecl *RealRes = (*Res)->getUnderlyingDecl(); + if (isa<TypeDecl, ObjCInterfaceDecl, UnresolvedUsingIfExistsDecl>( + RealRes) || + (AllowDeducedTemplate && getAsTypeTemplateDecl(RealRes))) { + if (!IIDecl || + // Make the selection of the recovery decl deterministic. + RealRes->getLocation() < IIDecl->getLocation()) + IIDecl = RealRes; } } @@ -486,6 +490,10 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, (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), @@ -502,7 +510,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, // 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; @@ -1161,6 +1169,11 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, return NameClassification::Concept( TemplateName(cast<TemplateDecl>(FirstDecl))); + if (auto *EmptyD = dyn_cast<UnresolvedUsingIfExistsDecl>(FirstDecl)) { + (void)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)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 130ec76758203..1dd5f2728b6ea 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8332,6 +8332,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_BuiltinAlias: handleBuiltinAliasAttr(S, D, AL); break; + + case ParsedAttr::AT_UsingIfExists: + handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL); + break; } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7739625c1b9f5..f6c907b2f98a1 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11604,10 +11604,11 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS, } } - 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 @@ IsEquivalentForUsingDecl(ASTContext &Context, NamedDecl *D1, NamedDecl *D2) { 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; } @@ -11737,6 +11744,20 @@ bool Sema::CheckUsingShadowDecl(UsingDecl *Using, NamedDecl *Orig, 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) != + (isa_and_nonnull<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, @@ -12001,7 +12022,8 @@ NamedDecl *Sema::BuildUsingDeclaration( 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 @@ NamedDecl *Sema::BuildUsingDeclaration( 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 @@ NamedDecl *Sema::BuildUsingDeclaration( 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 @@ NamedDecl *Sema::BuildUsingDeclaration( 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(), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 253a658f80924..ba38b1089413f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -83,6 +83,9 @@ bool Sema::CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid) { cast<Decl>(CurContext)->getAvailability() != AR_Unavailable) return false; + if (isa<UnresolvedUsingIfExistsDecl>(D)) + return false; + return true; } @@ -348,6 +351,12 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, 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); @@ -3208,8 +3217,7 @@ ExprResult Sema::BuildDeclarationNameExpr( } // 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); @@ -3220,9 +3228,11 @@ ExprResult Sema::BuildDeclarationNameExpr( // 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<ValueDecl>(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 index e2cbdcf028636..92a2cc4210bf5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3093,9 +3093,15 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) { 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; @@ -3218,13 +3224,16 @@ Decl *TemplateDeclInstantiator::instantiateUnresolvedUsingDecl( 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; } @@ -3239,6 +3248,11 @@ Decl *TemplateDeclInstantiator::VisitUnresolvedUsingValueDecl( 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()) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 2da8618ef44b9..1935bffccb6dd 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14394,7 +14394,11 @@ QualType TreeTransform<Derived>::RebuildUnresolvedUsingType(SourceLocation Loc, // 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"); diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 368e0392746d4..fb0438db7605d 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -427,6 +427,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Concept: case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: + case Decl::UnresolvedUsingIfExists: return false; // These indirectly derive from Redeclarable<T> but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 50eb3bb62485c..41b44c0e5260d 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -328,6 +328,7 @@ namespace clang { 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 @@ void ASTDeclReader::VisitUnresolvedUsingTypenameDecl( 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 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { 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 index 7674ecdda4589..03f2a0c451c80 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -68,6 +68,7 @@ namespace clang { 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 @@ void ASTDeclWriter::VisitUnresolvedUsingTypenameDecl( 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 index 0000000000000..ba34b9beb6bc4 --- /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 index 5ba55f0c23b56..5c427ad8fef10 100644 --- a/clang/test/SemaCXX/attr-deprecated.cpp +++ b/clang/test/SemaCXX/attr-deprecated.cpp @@ -256,3 +256,15 @@ typedef struct TDS { } 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}} +} // namespace test8 diff --git a/clang/test/SemaCXX/using-if-exists.cpp b/clang/test/SemaCXX/using-if-exists.cpp new file mode 100644 index 0000000000000..36fbbb171fb9a --- /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 <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; +}; + +template <class Base> +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<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}} + + UseCtor<BaseCtor> usector; +} + +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}} +}; +} // namespace 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; + +} // 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 <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}} +} // 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 index 5ce74e5f474f0..76f6ef3f66a74 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6466,6 +6466,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits