From 28c0250f415ee934bc2ec8516725c35dd7f7ad1e Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Wed, 27 Mar 2024 10:06:28 -0400 Subject: [PATCH] Support dependent splices in namespace alias definitions. Also give more helpful errors for illegal use of dependent splices in using directives. Closes issue #7. --- clang/include/clang/AST/Decl.h | 8 ++- clang/include/clang/AST/DeclCXX.h | 36 ++++++++++++ clang/include/clang/AST/ExprCXX.h | 4 ++ clang/include/clang/AST/RecursiveASTVisitor.h | 4 ++ clang/include/clang/Basic/DeclNodes.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/AST/DeclBase.cpp | 1 + clang/lib/AST/DeclCXX.cpp | 40 ++++++++++--- clang/lib/AST/NestedNameSpecifier.cpp | 6 +- clang/lib/CodeGen/CGDecl.cpp | 1 + clang/lib/Sema/SemaCXXScopeSpec.cpp | 10 +++- clang/lib/Sema/SemaDeclCXX.cpp | 57 +++++++++++++++---- clang/lib/Sema/SemaReflect.cpp | 7 ++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 55 +++++++++++++++++- clang/lib/Serialization/ASTCommon.cpp | 1 + clang/test/Reflection/splice-namespaces.cpp | 13 ++++- 16 files changed, 216 insertions(+), 30 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 65ef6803635fd81..ecae28107e59163 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -562,10 +562,12 @@ class NamespaceDecl : public NamedDecl, public DeclContext, llvm::PointerIntPair AnonOrFirstNamespaceAndFlags; - NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, +protected: + NamespaceDecl(Kind K, ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested); +private: using redeclarable_base = Redeclarable; NamespaceDecl *getNextRedeclarationImpl() override; @@ -689,7 +691,9 @@ class NamespaceDecl : public NamedDecl, public DeclContext, // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } - static bool classofKind(Kind K) { return K == Namespace; } + static bool classofKind(Kind K) { + return K >= firstNamespace && K <= lastNamespace; + } static DeclContext *castToDeclContext(const NamespaceDecl *D) { return static_cast(const_cast(D)); } diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index e88e98c35edbf54..6c552544c390aca 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -61,6 +61,7 @@ class CXXBasePaths; class CXXConstructorDecl; class CXXDestructorDecl; class CXXFinalOverriderMap; +class CXXIndeterminateSpliceExpr; class CXXIndirectPrimaryBaseSet; class CXXMethodDecl; class CXXRecordDecl; @@ -3106,6 +3107,30 @@ class UsingDirectiveDecl : public NamedDecl { static bool classofKind(Kind K) { return K == UsingDirective; } }; +class DependentNamespaceDecl : public NamespaceDecl { + friend class ASTDeclReader; + + CXXIndeterminateSpliceExpr *SpliceExpr; + + DependentNamespaceDecl(ASTContext &C, DeclContext *DC, + CXXIndeterminateSpliceExpr *SpliceExpr); + + void anchor() override; + +public: + static DependentNamespaceDecl *Create(ASTContext &C, DeclContext *DC, + CXXIndeterminateSpliceExpr *SpliceExpr); + + static DependentNamespaceDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + CXXIndeterminateSpliceExpr *getSpliceExpr() const { return SpliceExpr; } + + SourceRange getSourceRange() const override LLVM_READONLY; + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == DependentNamespace; } +}; + /// Represents a C++ namespace alias. /// /// For example: @@ -3197,6 +3222,17 @@ class NamespaceAliasDecl : public NamedDecl, return const_cast(this)->getNamespace(); } + bool isDependent() const { + if (NestedNameSpecifier *Qualifier = getQualifier(); + Qualifier && Qualifier->isDependent()) + return true; + + if (auto *AD = dyn_cast(Namespace)) + return AD->isDependent(); + + return isa(Namespace); + } + /// Returns the location of the alias name, i.e. 'foo' in /// "namespace foo = ns::bar;". SourceLocation getAliasLoc() const { return getLocation(); } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 6f7a805f2c6a7e8..007ea380e51088b 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5491,6 +5491,10 @@ class CXXIndeterminateSpliceExpr : public Expr { return RSpliceLoc; } + SourceRange getSourceRange() const { + return SourceRange(getBeginLoc(), getEndLoc()); + } + child_range children() { return child_range(reinterpret_cast(&Operand), reinterpret_cast(&Operand) + 1); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 5a4ed42d5c0db7c..aa4e0db222c802f 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1646,6 +1646,10 @@ DEF_TRAVERSE_DECL(NamespaceAliasDecl, { ShouldVisitChildren = false; }) +DEF_TRAVERSE_DECL(DependentNamespaceDecl, { + TRY_TO(TraverseStmt(D->getSpliceExpr())); +}) + DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl. }) diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 48396e85c5adac3..94b36988afa0e40 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -16,6 +16,7 @@ def PragmaDetectMismatch : DeclNode; def ExternCContext : DeclNode, DeclContext; def Named : DeclNode; def Namespace : DeclNode, DeclContext; + def DependentNamespace : DeclNode; def UsingDirective : DeclNode; def NamespaceAlias : DeclNode; def Label : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9e51edbd2161157..971772ce25edcf9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3106,6 +3106,8 @@ def err_member_access_splice_not_class_member : Error< "member of a class">; def err_unsupported_splice_kind : Error< "splicing %0 is %select{not|not yet}1 %select{supported|implemented}1">; +def err_using_dependent_namespace : Error< + "dependent namespaces cannot appear in a using directive">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index d903782f615ab83..6614300f96c6fef 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -892,6 +892,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Namespace: case NamespaceAlias: + case DependentNamespace: return IDNS_Namespace; case FunctionTemplate: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 645ec2f7563bca8..50f5be79fe43645 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2959,11 +2959,11 @@ NamespaceDecl *UsingDirectiveDecl::getNominatedNamespace() { return cast_or_null(NominatedNamespace); } -NamespaceDecl::NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, - SourceLocation StartLoc, SourceLocation IdLoc, - IdentifierInfo *Id, NamespaceDecl *PrevDecl, - bool Nested) - : NamedDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace), +NamespaceDecl::NamespaceDecl(Kind K, ASTContext &C, DeclContext *DC, + bool Inline, SourceLocation StartLoc, + SourceLocation IdLoc, IdentifierInfo *Id, + NamespaceDecl *PrevDecl, bool Nested) + : NamedDecl(K, DC, IdLoc, Id), DeclContext(Namespace), redeclarable_base(C), LocStart(StartLoc) { unsigned Flags = 0; if (Inline) @@ -2982,12 +2982,14 @@ NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested) { return new (C, DC) - NamespaceDecl(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, Nested); + NamespaceDecl(Namespace, C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, + Nested); } NamespaceDecl *NamespaceDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NamespaceDecl(C, nullptr, false, SourceLocation(), - SourceLocation(), nullptr, nullptr, false); + return new (C, ID) NamespaceDecl(Namespace, C, nullptr, false, + SourceLocation(), SourceLocation(), nullptr, + nullptr, false); } NamespaceDecl *NamespaceDecl::getOriginalNamespace() { @@ -3054,6 +3056,28 @@ NamespaceAliasDecl::CreateDeserialized(ASTContext &C, unsigned ID) { SourceLocation(), nullptr); } +void DependentNamespaceDecl::anchor() {} + +DependentNamespaceDecl::DependentNamespaceDecl( + ASTContext &C, DeclContext *DC, CXXIndeterminateSpliceExpr *SpliceExpr) + : NamespaceDecl(DependentNamespace, C, DC, false, SpliceExpr->getBeginLoc(), + SpliceExpr->getBeginLoc(), nullptr, nullptr, false), + SpliceExpr(SpliceExpr) {} + +DependentNamespaceDecl *DependentNamespaceDecl::Create( + ASTContext &C, DeclContext *DC, CXXIndeterminateSpliceExpr *SpliceExpr) { + return new (C, DC) DependentNamespaceDecl(C, DC, SpliceExpr); +} + +DependentNamespaceDecl * +DependentNamespaceDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) DependentNamespaceDecl(C, nullptr, nullptr); +} + +SourceRange DependentNamespaceDecl::getSourceRange() const { + return SourceRange(SpliceExpr->getBeginLoc(), SpliceExpr->getEndLoc()); +} + void LifetimeExtendedTemporaryDecl::anchor() {} /// Retrieve the storage duration for the materialized temporary. diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index d773cf4c617d6bb..1917c7b8bff4673 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -229,10 +229,14 @@ NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const { } case Namespace: - case NamespaceAlias: case Global: return NestedNameSpecifierDependence::None; + case NamespaceAlias: + return getAsNamespaceAlias()->isDependent() ? + NestedNameSpecifierDependence::Dependent : + NestedNameSpecifierDependence::None; + case Super: { CXXRecordDecl *RD = static_cast(Specifier); for (const auto &Base : RD->bases()) diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 3cd5c5907ce0b05..92bad7773cae504 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -54,6 +54,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::TranslationUnit: case Decl::ExternCContext: case Decl::Namespace: + case Decl::DependentNamespace: case Decl::UnresolvedUsingTypename: case Decl::ClassTemplateSpecialization: case Decl::ClassTemplatePartialSpecialization: diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index f766cc7ee4a4599..759f776d80a5750 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -165,8 +165,12 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS, case NestedNameSpecifier::Namespace: return NNS->getAsNamespace(); - case NestedNameSpecifier::NamespaceAlias: - return NNS->getAsNamespaceAlias()->getNamespace(); + case NestedNameSpecifier::NamespaceAlias: { + NamespaceAliasDecl *Alias = NNS->getAsNamespaceAlias(); + if (Alias->isDependent()) + return nullptr; + return Alias->getNamespace(); + } case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: { @@ -505,6 +509,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, LookupCtx = computeDeclContext(SS, EnteringContext); isDependent = isDependentScopeSpecifier(SS); Found.setContextRange(SS.getRange()); + } else if (ScopeLookupResult && isa(ScopeLookupResult)) { + isDependent = true; } bool ObjectTypeSearchedInScope = false; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a54830dbde5ec5b..2af4cdf1134a614 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -12258,6 +12258,12 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, if (SS.isSet()) Qualifier = SS.getScopeRep(); + if (Qualifier && Qualifier->isDependent()) { + Diag(SS.getBeginLoc(), diag::err_using_dependent_namespace) + << SourceRange(SS.getBeginLoc(), IdentLoc); + return nullptr; + } + // Lookup namespace name. LookupResult R(*this, NamespcName, IdentLoc, LookupNamespaceName); LookupParsedName(R, S, &SS); @@ -12305,6 +12311,16 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, assert(!SS.isInvalid() && "Invalid CXXScopeSpec."); assert(IdentLoc.isValid() && "Invalid NamespceName location."); + // Check for dependent namespaces. + if (auto *DNSD = dyn_cast(NS)) { + Diag(IdentLoc, diag::err_using_dependent_namespace) + << DNSD->getSpliceExpr()->getSourceRange(); + return nullptr; + } else if (auto *A = dyn_cast(NS); A && A->isDependent()) { + Diag(IdentLoc, diag::err_using_dependent_namespace) << IdentLoc; + return nullptr; + } + // C++ [namespace.udir]p1: // A using-directive specifies that the names in the nominated // namespace can be used in the scope in which the @@ -13746,23 +13762,36 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, IdentifierInfo *Alias, CXXScopeSpec &SS, SourceLocation IdentLoc, IdentifierInfo *Ident) { + NamedDecl *ND; - // Lookup the namespace name. - LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + // Scope may be dependent if it has a splice as a leading component of its + // qualifiers, and that splice is dependent on a template parameter. + if (NestedNameSpecifier *NNS = SS.getScopeRep(); NNS && NNS->isDependent()) { + ND = NamespaceDecl::Create(Context, CurContext, false, IdentLoc, IdentLoc, + Ident, nullptr, true); + } else { + // Lookup the namespace name. + LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - if (R.isAmbiguous()) - return nullptr; + if (S) { + LookupParsedName(R, S, &SS); + } else { + DeclContext *LookupCtx = computeDeclContext(SS, false); + LookupQualifiedName(R, LookupCtx); + } - if (R.empty()) { - if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { - Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + if (R.isAmbiguous()) return nullptr; + + if (R.empty()) { + if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { + Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + return nullptr; + } } + assert(!R.isAmbiguous() && !R.empty()); + ND = R.getRepresentativeDecl(); } - assert(!R.isAmbiguous() && !R.empty()); - NamedDecl *ND = R.getRepresentativeDecl(); - return ActOnNamespaceAliasDef(S, NamespaceLoc, AliasLoc, Alias, SS, IdentLoc, ND); } @@ -13823,7 +13852,11 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, if (Prev) AliasDecl->setPreviousDecl(Prev); - PushOnScopeChains(AliasDecl, S); + if (S) + PushOnScopeChains(AliasDecl, S); + else + CurContext->addDecl(AliasDecl); + return AliasDecl; } diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index a5a9cc8eda710c8..37a7f0289851e04 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -611,9 +611,10 @@ ExprResult Sema::BuildReflectionSpliceExpr(SourceLocation LSplice, DeclResult Sema::BuildReflectionSpliceNamespace(SourceLocation LSplice, Expr *Operand, SourceLocation RSplice) { - if (Operand->isTypeDependent() || Operand->isValueDependent()) - llvm_unreachable("splicing of standalone dependent namespaces not yet " - "implemented"); + if (Operand->isTypeDependent() || Operand->isValueDependent()) { + auto *Splice = cast(Operand); + return DependentNamespaceDecl::Create(Context, CurContext, Splice); + } SmallVector Diags; Expr::EvalResult ER; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index dc972018e7b2815..ee93f9b09546425 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -992,8 +992,61 @@ TemplateDeclInstantiator::VisitNamespaceDecl(NamespaceDecl *D) { llvm_unreachable("Namespaces cannot be instantiated"); } +Decl * +TemplateDeclInstantiator::VisitDependentNamespaceDecl( + DependentNamespaceDecl *D) { + ExprResult ER = SemaRef.SubstExpr(D->getSpliceExpr(), TemplateArgs); + if (ER.isInvalid()) + return nullptr; + auto *Splice = cast(ER.get()); + assert(!Splice->isValueDependent()); + + DeclResult DR = + SemaRef.ActOnCXXSpliceExpectingNamespace(Splice->getLSpliceLoc(), + Splice->getOperand(), + Splice->getRSpliceLoc()); + if (DR.isInvalid()) + return nullptr; + return DR.get(); +} + Decl * TemplateDeclInstantiator::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + NamedDecl *NSDecl = D->getAliasedNamespace(); + + if (D->isDependent()) { + NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc(); + if (NestedNameSpecifier *NNS = QualifierLoc.getNestedNameSpecifier(); + NNS && NNS->isDependent()) { + QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, + TemplateArgs); + + CXXScopeSpec SS; + SS.Adopt(QualifierLoc); + return SemaRef.ActOnNamespaceAliasDef(/*Scope=*/nullptr, + D->getNamespaceLoc(), + D->getAliasLoc(), + D->getIdentifier(), + SS, D->getBeginLoc(), + D->getNamespace()->getIdentifier()); + } else if (auto *DNSD = dyn_cast(NSDecl)) { + assert(!D->getQualifierLoc()); + + Decl *Transformed = Visit(DNSD); + if (!Transformed) + return nullptr; + NSDecl = cast(Transformed); + } else if (auto *SubAlias = dyn_cast(NSDecl)) { + assert(SubAlias->isDependent()); + Decl *SubAliasResult = Visit(SubAlias); + if (!SubAliasResult) + return nullptr; + NSDecl = cast(SubAliasResult); + } else { + llvm_unreachable("unknown dependent namespace alias kind"); + } + } else D->dump(); + NamespaceAliasDecl *Inst = NamespaceAliasDecl::Create(SemaRef.Context, Owner, D->getNamespaceLoc(), @@ -1001,7 +1054,7 @@ TemplateDeclInstantiator::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { D->getIdentifier(), D->getQualifierLoc(), D->getTargetNameLoc(), - D->getNamespace()); + NSDecl); Owner->addDecl(Inst); return Inst; } diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 1dc12c721b81667..f999d6a459d4c0d 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -361,6 +361,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Namespace: case Decl::NamespaceAlias: + case Decl::DependentNamespace: case Decl::Typedef: case Decl::TypeAlias: case Decl::Enum: diff --git a/clang/test/Reflection/splice-namespaces.cpp b/clang/test/Reflection/splice-namespaces.cpp index cce6df48e8d946a..76021078db522b2 100644 --- a/clang/test/Reflection/splice-namespaces.cpp +++ b/clang/test/Reflection/splice-namespaces.cpp @@ -17,7 +17,7 @@ int global_decl; constexpr int x = 1; namespace myns { - namespace inner { int y; } + namespace inner { int y; constexpr int z = 3; } constexpr int x = 2; } // namespace myns @@ -72,6 +72,17 @@ static_assert(&myns::x == &Alias2::x); namespace Alias3 = [:r_global:]::idempotency::inner; static_assert(Alias3::x == 3); + +template +consteval int XPlusY() { + namespace Alias = [:R:]; + namespace ReAlias = Alias; + namespace InnerAlias = [:R:]::inner; + namespace ReAliasInner = InnerAlias; + + return ReAlias::x + ReAliasInner::z; +} +static_assert(XPlusY<^myns>() == 5); } // namespace in_alias_defns // ===================