Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -3798,6 +3798,29 @@ static bool classofKind(Kind K) { return K == UnresolvedUsingTypename; } }; +/// This node is generated when a using-declaration that was annotated with +/// __attribute__((using_if_exists)) failed to resolve to a known declaration. +/// In that case, Sema builds a UsingShadowDecl whose target is an instance of +/// this declaration, adding it to the current scope. Referring to this +/// declaration in any way is an error. +class UnresolvedUsingIfExistsDecl final : public NamedDecl { + UnresolvedUsingIfExistsDecl(DeclContext *DC, SourceLocation Loc, + DeclarationName Name); + + void anchor() override; +public: + static UnresolvedUsingIfExistsDecl *Create(ASTContext &Ctx, DeclContext *DC, + SourceLocation Loc, + DeclarationName Name); + static UnresolvedUsingIfExistsDecl *CreateDeserialized(ASTContext &Ctx, + unsigned ID); + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { + return K == Decl::UnresolvedUsingIfExists; + } +}; + /// Represents a C++11 static_assert declaration. class StaticAssertDecl : public Decl { llvm::PointerIntPair AssertExprAndFailed; Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -1826,6 +1826,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 @@ -3546,6 +3546,14 @@ let Documentation = [NoBuiltinDocs]; } +def UsingIfExists : InheritableAttr { + let Spellings = [Clang<"using_if_exists", 0>]; + let Subjects = SubjectList<[Using, + UnresolvedUsingTypename, + UnresolvedUsingValue], ErrorDiag>; + let Documentation = [UsingIfExistsDocs]; +} + // FIXME: This attribute is not inheritable, it will not be propagated to // redecls. [[clang::lifetimebound]] has the same problems. This should be // fixed in TableGen (by probably adding a new inheritable flag). Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -5329,6 +5329,29 @@ }]; } +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 {}; + [[clang::using_if_exists]] + using empty_namespace::does_not_exist; // 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 @@ -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; Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -686,6 +686,9 @@ def err_using_attribute_ns_conflict : Error< "attribute with scope specifier cannot follow default scope specifier">; def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; +def ext_cxx11_attr_placement : Extension< + "C++ does not allow an attribute list to appear here">, + InGroup>, DefaultIgnore; def err_attributes_misplaced : Error<"misplaced attributes; expected attributes here">; def err_l_square_l_square_not_attribute : Error< "C++11 only allows consecutive left square brackets when " Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -551,6 +551,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/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2645,6 +2645,10 @@ void ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs, unsigned DiagID); + /// Emit warnings for C++11 attributes that are in a position that clang + /// accepts as an extension. + void DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs); + /// Skip C++11 and C2x attributes and return the end location of the /// last one. /// \returns SourceLocation() if there are no attributes. @@ -2993,6 +2997,7 @@ const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, + ParsedAttributesWithRange &Attrs, AccessSpecifier AS = AS_none); Decl *ParseAliasDeclarationAfterDeclarator( const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -5562,7 +5562,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 @@ -1360,6 +1360,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_Ordinary; + case OMPDeclareReduction: return IDNS_OMPReduction; Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -3131,6 +3131,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/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -1601,6 +1601,13 @@ } } +void Parser::DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs) { + for (const ParsedAttr &PA : Attrs) { + if (PA.isCXX11Attribute()) + Diag(PA.getLoc(), diag::ext_cxx11_attr_placement) << PA; + } +} + // Usually, `__attribute__((attrib)) class Foo {} var` means that attribute // applies to var, not the type Foo. // As an exception to the rule, __declspec(align(...)) before the Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -497,12 +497,8 @@ } // Otherwise, it must be a using-declaration or an alias-declaration. - - // Using declarations can't have attributes. - ProhibitAttributes(attrs); - return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd, - AS_none); + attrs, AS_none); } /// ParseUsingDirective - Parse C++ using-directive, assumes @@ -675,6 +671,7 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, + ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) { // Check for misplaced attributes before the identifier in an // alias-declaration. @@ -695,6 +692,8 @@ return nullptr; } + ProhibitAttributes(PrefixAttrs); + // If we had any misplaced attributes from earlier, this is where they // should have been written. if (MisplacedAttrs.Range.isValid()) { @@ -712,10 +711,8 @@ return Actions.ConvertDeclToDeclGroup(AD, DeclFromDeclSpec); } - // C++11 attributes are not allowed on a using-declaration, but GNU ones - // are. ProhibitAttributes(MisplacedAttrs); - ProhibitAttributes(Attrs); + DiagnoseCXX11AttributeExtension(PrefixAttrs); // Diagnose an attempt to declare a templated using-declaration. // In C++11, alias-declarations can be templates: @@ -733,8 +730,11 @@ SmallVector DeclsInGroup; while (true) { - // Parse (optional) attributes (most likely GNU strong-using extension). + // Parse (optional) attributes. MaybeParseGNUAttributes(Attrs); + MaybeParseCXX11Attributes(Attrs); + DiagnoseCXX11AttributeExtension(Attrs); + Attrs.addAll(PrefixAttrs.begin(), PrefixAttrs.end()); if (InvalidDeclarator) SkipUntil(tok::comma, tok::semi, StopBeforeMatch); @@ -2590,8 +2590,6 @@ MaybeParseMicrosoftAttributes(attrs); if (Tok.is(tok::kw_using)) { - ProhibitAttributes(attrs); - // Eat 'using'. SourceLocation UsingLoc = ConsumeToken(); @@ -2610,7 +2608,7 @@ SourceLocation DeclEnd; // Otherwise, it must be a using-declaration or an alias-declaration. return ParseUsingDeclaration(DeclaratorContext::MemberContext, TemplateInfo, - UsingLoc, DeclEnd, AS); + UsingLoc, DeclEnd, attrs, AS); } // Hold late-parsed attributes so we can attach a Decl to them later. Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -434,12 +434,14 @@ // Look to see if we have a type anywhere in the list of results. for (LookupResult::iterator Res = Result.begin(), ResEnd = Result.end(); Res != ResEnd; ++Res) { - if (isa(*Res) || isa(*Res) || - (AllowDeducedTemplate && getAsTypeTemplateDecl(*Res))) { + NamedDecl *RealRes = (*Res)->getUnderlyingDecl(); + if (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 +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), @@ -504,7 +510,7 @@ // constructor or destructor name (in such a case, the scope specifier // will be attached to the enclosing Expr or Decl node). if (SS && SS->isNotEmpty() && !IsCtorOrDtorName && - !isa(IIDecl)) { + !isa(IIDecl)) { if (WantNontrivialTypeSourceInfo) { // Construct a type with type-source information. TypeLocBuilder Builder; @@ -1163,6 +1169,11 @@ return NameClassification::Concept( TemplateName(cast(FirstDecl))); + if (auto *EmptyD = dyn_cast(FirstDecl)) { + 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)) Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -8020,6 +8020,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 @@ -11524,10 +11524,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); @@ -11547,6 +11548,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; } @@ -11657,6 +11664,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, @@ -11921,7 +11941,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."); @@ -11991,9 +12012,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, @@ -12007,6 +12028,7 @@ } D->setAccess(AS); CurContext->addDecl(D); + ProcessDeclAttributeList(S, D, AttrList); return D; } @@ -12017,6 +12039,7 @@ UD->setAccess(AS); CurContext->addDecl(UD); UD->setInvalidDecl(Invalid); + ProcessDeclAttributeList(S, UD, AttrList); return UD; }; auto BuildInvalid = [&]{ return Build(true); }; @@ -12044,6 +12067,10 @@ LookupQualifiedName(R, LookupContext); + if (R.empty() && IsUsingIfExists) + R.addDecl(UnresolvedUsingIfExistsDecl::Create(Context, CurContext, UsingLoc, + UsingName.getName()), AS_public); + // Try to correct typos if possible. If constructor name lookup finds no // results, that means the named class has no explicit constructors, and we // suppressed declaring implicit ones (probably because it's dependent or @@ -12118,7 +12145,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(), @@ -12140,7 +12168,7 @@ // A using-declaration shall not name a namespace. if (R.getAsSingle()) { Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_namespace) - << SS.getRange(); + << SS.getRange(); return BuildInvalid(); } @@ -12149,7 +12177,7 @@ if (auto *ED = R.getAsSingle()) { if (cast(ED->getDeclContext())->isScoped()) { Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum) - << SS.getRange(); + << SS.getRange(); return BuildInvalid(); } } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -82,6 +82,9 @@ cast(CurContext)->getAvailability() != AR_Unavailable) return false; + if (isa(D)) + return false; + return true; } @@ -347,6 +350,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); @@ -3149,8 +3158,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); @@ -3161,9 +3169,11 @@ // this check when we're going to perform argument-dependent lookup // on this function name, because this might not be the function // that overload resolution actually selects. - if (DiagnoseUseOfDecl(VD, Loc)) + if (DiagnoseUseOfDecl(D, Loc)) return ExprError(); + ValueDecl *VD = cast(D); + // Only create DeclRefExpr's for valid Decl's. if (VD->isInvalidDecl() && !AcceptInvalidDecl) return ExprError(); Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3038,9 +3038,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; @@ -3163,13 +3169,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; } @@ -3184,6 +3193,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()) { Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -14111,7 +14111,11 @@ // A valid resolved using typename decl points to exactly one type decl. assert(++Using->shadow_begin() == Using->shadow_end()); - Ty = cast((*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"); Index: clang/lib/Serialization/ASTCommon.cpp =================================================================== --- clang/lib/Serialization/ASTCommon.cpp +++ clang/lib/Serialization/ASTCommon.cpp @@ -422,6 +422,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); @@ -1700,6 +1701,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) \ @@ -3841,6 +3847,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); @@ -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); Index: clang/test/Parser/cxx0x-attributes.cpp =================================================================== --- clang/test/Parser/cxx0x-attributes.cpp +++ clang/test/Parser/cxx0x-attributes.cpp @@ -131,7 +131,7 @@ [[]] static_assert(true, ""); //expected-error {{an attribute list cannot appear here}} [[]] asm(""); // expected-error {{an attribute list cannot appear here}} -[[]] using ns::i; // expected-error {{an attribute list cannot appear here}} +[[]] using ns::i; [[unknown]] using namespace ns; // expected-warning {{unknown attribute 'unknown' ignored}} [[noreturn]] using namespace ns; // expected-error {{'noreturn' attribute only applies to functions}} namespace [[]] ns2 {} // expected-warning {{attributes on a namespace declaration are a C++17 extension}} @@ -157,7 +157,7 @@ [[]] using T = int; // expected-error {{an attribute list cannot appear here}} using T [[]] = int; // ok template using U [[]] = T; -using ns::i [[]]; // expected-error {{an attribute list cannot appear here}} +using ns::i [[]]; using [[]] ns::i; // expected-error {{an attribute list cannot appear here}} using T [[unknown]] = int; // expected-warning {{unknown attribute 'unknown' ignored}} using T [[noreturn]] = int; // expected-error {{'noreturn' attribute only applies to functions}} Index: clang/test/SemaCXX/attr-deprecated.cpp =================================================================== --- clang/test/SemaCXX/attr-deprecated.cpp +++ clang/test/SemaCXX/attr-deprecated.cpp @@ -256,3 +256,15 @@ } TDS __attribute__((deprecated)); // expected-note {{'TDS' has been explicitly marked deprecated here}} TDS tds; // expected-warning {{'TDS' is deprecated}} struct TDS tds2; // no warning, attribute only applies to the typedef. + +namespace test8 { +struct A { + // expected-note@+1 {{'B' has been explicitly marked deprecated here}} + struct __attribute__((deprecated)) B {}; +}; +template struct D : T { + using typename T::B; + B b; // expected-warning {{'B' is deprecated}} +}; +D da; // expected-note {{in instantiation of template class}} +} Index: clang/test/SemaCXX/using-if-exists-attr.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/using-if-exists-attr.cpp @@ -0,0 +1,34 @@ +// 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 UIE; + +using NS::x [[clang::using_if_exists]]; // expected-warning{{C++ does not allow an attribute list to appear here}} + +[[clang::using_if_exists]] // expected-warning{{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 {{C++ does not allow}} + +// 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}} + +struct Base {}; +struct Derived : Base { + [[clang::using_if_exists]] using Base::x; // expected-warning {{C++ does not allow an attribute list to appear here}} + using Base::y [[clang::using_if_exists]]; // expected-warning {{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}} +}; Index: clang/test/SemaCXX/using-if-exists.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/using-if-exists.cpp @@ -0,0 +1,193 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify + +#define UIE [[clang::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}} + 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 @@ -6414,6 +6414,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