[clang] 369c648 - [clang] Implement the using_if_exists attribute
Louis Dionne via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 2 07:31:08 PDT 2021
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 at +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
More information about the cfe-commits
mailing list