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 represents a using-declaration annotated with +/// __attribute__((using_if_exists)) that failed to resolve to a known +/// declaration. In that case, Sema builds a UsingShadowDecl whose target is an +/// instance of this declaration. 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; 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/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3530,6 +3530,12 @@ let Documentation = [NoBuiltinDocs]; } +def UsingIfExists : InheritableAttr { + let Spellings = [Clang<"using_if_exists", 0>]; + let Subjects = SubjectList<[Using, UnresolvedUsingTypename, UnresolvedUsingValue]>; + 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/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/DeclNodes.td =================================================================== --- clang/include/clang/Basic/DeclNodes.td +++ clang/include/clang/Basic/DeclNodes.td @@ -74,6 +74,7 @@ def UsingPack : DeclNode; def UsingShadow : DeclNode; def ConstructorUsingShadow : DeclNode; + def UnresolvedUsingIfExists : DeclNode; def ObjCMethod : DeclNode, DeclContext; def ObjCContainer : DeclNode, DeclContext; def ObjCCategory : DeclNode; 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/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 Expansions); 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/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_Member | IDNS_Ordinary; + case OMPDeclareReduction: return IDNS_OMPReduction; 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/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/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -434,12 +434,17 @@ // 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))) { + NamedDecl *RealRes = *Res; + if (auto *ShadowD = dyn_cast(*Res)) + RealRes = ShadowD->getTargetDecl(); + if (isa(RealRes) || isa(RealRes) || + isa(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 +493,11 @@ (void)DiagnoseUseOfDecl(IDecl, NameLoc); if (!HasTrailingDot) T = Context.getObjCInterfaceType(IDecl); + } else if (auto *EmptyD = dyn_cast(IIDecl)) { + Diag(NameLoc, diag::err_use_of_empty_using_if_exists); + Diag(EmptyD->getLocation(), diag::note_empty_using_if_exists_here); + // Recover with 'int' + T = Context.IntTy; } else if (AllowDeducedTemplate) { if (auto *TD = getAsTypeTemplateDecl(IIDecl)) T = Context.getDeducedTemplateSpecializationType(TemplateName(TD), @@ -504,7 +514,8 @@ // 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) && + !isa(IIDecl)) { if (WantNontrivialTypeSourceInfo) { // Construct a type with type-source information. TypeLocBuilder Builder; @@ -1163,6 +1174,12 @@ return NameClassification::Concept( TemplateName(cast(FirstDecl))); + if (auto *EmptyD = dyn_cast(FirstDecl)) { + Diag(NameLoc, diag::err_use_of_empty_using_if_exists); + Diag(EmptyD->getLocation(), diag::note_empty_using_if_exists_here); + return NameClassification::Error(); + } + // We can have a type template here if we're classifying a template argument. if (isa(FirstDecl) && !isa(FirstDecl) && !isa(FirstDecl)) 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(S, D, AL); break; + + case ParsedAttr::AT_UsingIfExists: + handleSimpleAttribute(S, D, AL); + break; } } 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(D1) && + isa(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(Target) != + (NonTag && isa(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; } @@ -12026,7 +12048,7 @@ // results, that means the named class has no explicit constructors, and we // suppressed declaring implicit ones (probably because it's dependent or // invalid). - if (R.empty() && + if (R.empty() && !IsUsingIfExists && NameInfo.getName().getNameKind() != DeclarationName::CXXConstructorName) { // HACK: Work around a bug in libstdc++'s detection of ::gets. Sometimes // it will believe that glibc provides a ::gets in cases where it does not, @@ -12094,41 +12116,43 @@ if (R.isAmbiguous()) return BuildInvalid(); - if (HasTypenameKeyword) { - // If we asked for a typename and got a non-type decl, error out. - if (!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(), - diag::note_using_decl_target); - return BuildInvalid(); + if (!IsUsingIfExists || !R.empty()) { + if (HasTypenameKeyword) { + // If we asked for a typename and got a non-type decl, error out. + if (!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(), + diag::note_using_decl_target); + return BuildInvalid(); + } + } else { + // If we asked for a non-typename and we got a type, error out, + // but only if this is an instantiation of an unresolved using + // decl. Otherwise just silently find the type name. + if (IsInstantiation && R.getAsSingle()) { + Diag(IdentLoc, diag::err_using_dependent_value_is_type); + Diag(R.getFoundDecl()->getLocation(), diag::note_using_decl_target); + return BuildInvalid(); + } } - } else { - // If we asked for a non-typename and we got a type, error out, - // but only if this is an instantiation of an unresolved using - // decl. Otherwise just silently find the type name. - if (IsInstantiation && R.getAsSingle()) { - Diag(IdentLoc, diag::err_using_dependent_value_is_type); - Diag(R.getFoundDecl()->getLocation(), diag::note_using_decl_target); + + // C++14 [namespace.udecl]p6: + // A using-declaration shall not name a namespace. + if (R.getAsSingle()) { + Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_namespace) + << SS.getRange(); return BuildInvalid(); } - } - - // C++14 [namespace.udecl]p6: - // A using-declaration shall not name a namespace. - if (R.getAsSingle()) { - Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_namespace) - << SS.getRange(); - return BuildInvalid(); - } - // C++14 [namespace.udecl]p7: - // A using-declaration shall not name a scoped enumerator. - if (auto *ED = R.getAsSingle()) { - if (cast(ED->getDeclContext())->isScoped()) { - Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum) - << SS.getRange(); - return BuildInvalid(); + // C++14 [namespace.udecl]p7: + // A using-declaration shall not name a scoped enumerator. + if (auto *ED = R.getAsSingle()) { + if (cast(ED->getDeclContext())->isScoped()) { + Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum) + << SS.getRange(); + return BuildInvalid(); + } } } @@ -12150,6 +12174,14 @@ BuildUsingShadowDecl(S, UD, *I, PrevDecl); } + if (IsUsingIfExists && R.empty()) { + auto *EmptyD = UnresolvedUsingIfExistsDecl::Create( + Context, CurContext, UD->getLocation(), UsingName.getName()); + UsingShadowDecl *PrevDecl = nullptr; + if (!CheckUsingShadowDecl(UD, EmptyD, Previous, PrevDecl)) + BuildUsingShadowDecl(S, UD, EmptyD, PrevDecl); + } + return UD; } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -3143,6 +3143,12 @@ // Make sure that we're referring to a value. ValueDecl *VD = dyn_cast(D); if (!VD) { + if (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 ExprError(); + } + Diag(Loc, diag::err_ref_non_value) << D << SS.getRange(); Diag(D->getLocation(), diag::note_declared_at); Index: clang/lib/Sema/SemaExprMember.cpp =================================================================== --- clang/lib/Sema/SemaExprMember.cpp +++ clang/lib/Sema/SemaExprMember.cpp @@ -1166,6 +1166,12 @@ Var->getType().getNonReferenceType(), VK_LValue, OK_Ordinary); } + if (auto *EmptyD = dyn_cast(MemberDecl)) { + Diag(MemberLoc, diag::err_use_of_empty_using_if_exists); + Diag(EmptyD->getLocation(), diag::note_empty_using_if_exists_here); + return ExprError(); + } + // We found something that we didn't expect. Complain. if (isa(MemberDecl)) Diag(MemberLoc, diag::err_typecheck_member_reference_type) 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(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; @@ -3160,13 +3166,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; } @@ -3181,6 +3190,12 @@ return instantiateUnresolvedUsingDecl(D); } +Decl *TemplateDeclInstantiator::VisitUnresolvedUsingIfExistsDecl( + UnresolvedUsingIfExistsDecl *D) { + return UnresolvedUsingIfExistsDecl::Create( + SemaRef.Context, Owner, D->getLocation(), D->getDeclName()); +} + Decl *TemplateDeclInstantiator::VisitUsingPackDecl(UsingPackDecl *D) { SmallVector Expansions; for (auto *UD : D->expansions()) { Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -14111,7 +14111,16 @@ // 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 (auto *EmptyD = dyn_cast(Target)) { + getSema().Diag(Loc, diag::err_use_of_empty_using_if_exists); + getSema().Diag(EmptyD->getLocation(), + diag::note_empty_using_if_exists_here); + return QualType(); + } + + Ty = cast(Target); } else { assert(isa(D) && "UnresolvedUsingTypenameDecl transformed to non-using decl"); 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 but are not actually 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/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/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++2a -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-warning {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}} + +template +using template_alias UIE = NS::x; // expected-warning {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}} + +void f() UIE; // expected-warning {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}} + +using namespace NS UIE; // expected-warning {{'using_if_exists' attribute only applies to named declarations, types, and value declarations}} 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++2a -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 +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 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}} +} + +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}} +}; +} // 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; + +} // 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 conversion}} + std::size_t size; // expected-error {{reference to unresolved using declaration}} + size = printf(); +} 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