https://github.com/firstmoonlight updated https://github.com/llvm/llvm-project/pull/190597
>From a64173fcc8f4bd095bb14530cee6a9f6b9deea4c Mon Sep 17 00:00:00 2001 From: victorl <[email protected]> Date: Wed, 8 Apr 2026 21:41:05 +0800 Subject: [PATCH] [clang][Sema]fix crash of invalid friend declaration with storage-class specifier --- clang/include/clang/Sema/DeclSpec.h | 2 + clang/lib/Sema/DeclSpec.cpp | 157 ++++++++++++----------- clang/test/CXX/class/class.friend/p6.cpp | 1 + 3 files changed, 83 insertions(+), 77 deletions(-) diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 6e5421c7072c7..d9e41679ba6ea 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -887,6 +887,8 @@ class DeclSpec { /// DeclSpec is guaranteed self-consistent, even if an error occurred. void Finish(Sema &S, const PrintingPolicy &Policy); + void CheckTypeSpec(Sema &S, const PrintingPolicy &Policy); + const WrittenBuiltinSpecs& getWrittenBuiltinSpecs() const { return writtenBS; } diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 479a959e0aadc..68197dd872c21 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1162,6 +1162,76 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { // Check the type specifier components first. No checking for an invalid // type. + CheckTypeSpec(S, Policy); + + // C++ [class.friend]p6: + // No storage-class-specifier shall appear in the decl-specifier-seq + // of a friend declaration. + if (isFriendSpecified() && + (getStorageClassSpec() || getThreadStorageClassSpec())) { + SmallString<32> SpecName; + SourceLocation SCLoc; + FixItHint StorageHint, ThreadHint; + + if (DeclSpec::SCS SC = getStorageClassSpec()) { + SpecName = getSpecifierName(SC); + SCLoc = getStorageClassSpecLoc(); + StorageHint = FixItHint::CreateRemoval(SCLoc); + } + + if (DeclSpec::TSCS TSC = getThreadStorageClassSpec()) { + if (!SpecName.empty()) + SpecName += " "; + SpecName += getSpecifierName(TSC); + SCLoc = getThreadStorageClassSpecLoc(); + ThreadHint = FixItHint::CreateRemoval(SCLoc); + } + + S.Diag(SCLoc, diag::err_friend_decl_spec) + << SpecName << StorageHint << ThreadHint; + + ClearStorageClassSpecs(); + } + + // C++11 [dcl.fct.spec]p5: + // The virtual specifier shall be used only in the initial + // declaration of a non-static class member function; + // C++11 [dcl.fct.spec]p6: + // The explicit specifier shall be used only in the declaration of + // a constructor or conversion function within its class + // definition; + if (isFriendSpecified() && (isVirtualSpecified() || hasExplicitSpecifier())) { + StringRef Keyword; + FixItHint Hint; + SourceLocation SCLoc; + + if (isVirtualSpecified()) { + Keyword = "virtual"; + SCLoc = getVirtualSpecLoc(); + Hint = FixItHint::CreateRemoval(SCLoc); + } else { + Keyword = "explicit"; + SCLoc = getExplicitSpecLoc(); + Hint = FixItHint::CreateRemoval(getExplicitSpecRange()); + } + + S.Diag(SCLoc, diag::err_friend_decl_spec) << Keyword << Hint; + + FS_virtual_specified = false; + FS_explicit_specifier = ExplicitSpecifier(); + FS_virtualLoc = FS_explicitLoc = SourceLocation(); + } + + assert(!TypeSpecOwned || isDeclRep((TST)TypeSpecType)); + + // Okay, now we can infer the real type. + + // TODO: return "auto function" and other bad things based on the real type. + + // 'data definition has no type or storage class'? +} + +void DeclSpec::CheckTypeSpec(Sema &S, const PrintingPolicy &Policy) { if (TypeSpecType == TST_error) return; @@ -1228,8 +1298,8 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { (TypeSpecType != TST_int) && (TypeSpecType != TST_int128)) || TypeAltiVecPixel) { S.Diag(TSTLoc, diag::err_invalid_vector_bool_decl_spec) - << (TypeAltiVecPixel ? "__pixel" : - getSpecifierName((TST)TypeSpecType, Policy)); + << (TypeAltiVecPixel ? "__pixel" + : getSpecifierName((TST)TypeSpecType, Policy)); } // vector bool __int128 requires Power10 (or ZVector). if ((TypeSpecType == TST_int128) && @@ -1345,10 +1415,8 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { // use. Need information about the backend. if (TypeSpecComplex != TSC_unspecified) { if (TypeSpecType == TST_unspecified) { - S.Diag(TSCLoc, diag::ext_plain_complex) - << FixItHint::CreateInsertion( - S.getLocForEndOfToken(getTypeSpecComplexLoc()), - " double"); + S.Diag(TSCLoc, diag::ext_plain_complex) << FixItHint::CreateInsertion( + S.getLocForEndOfToken(getTypeSpecComplexLoc()), " double"); TypeSpecType = TST_double; // _Complex -> _Complex double. } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { // Note that this intentionally doesn't include _Complex _Bool. @@ -1378,14 +1446,14 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { if (S.getSourceManager().isBeforeInTranslationUnit( getThreadStorageClassSpecLoc(), getStorageClassSpecLoc())) S.Diag(getStorageClassSpecLoc(), - diag::err_invalid_decl_spec_combination) - << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) - << SourceRange(getThreadStorageClassSpecLoc()); + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) + << SourceRange(getThreadStorageClassSpecLoc()); else S.Diag(getThreadStorageClassSpecLoc(), - diag::err_invalid_decl_spec_combination) - << DeclSpec::getSpecifierName(getStorageClassSpec()) - << SourceRange(getStorageClassSpecLoc()); + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getStorageClassSpec()) + << SourceRange(getStorageClassSpecLoc()); // Discard the thread storage class specifier to recover. ThreadStorageClassSpec = TSCS_unspecified; ThreadStorageClassSpecLoc = SourceLocation(); @@ -1442,71 +1510,6 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { S.Diag(ConstexprLoc, diag::warn_cxx20_compat_consteval); else if (getConstexprSpecifier() == ConstexprSpecKind::Constinit) S.Diag(ConstexprLoc, diag::warn_cxx20_compat_constinit); - // C++ [class.friend]p6: - // No storage-class-specifier shall appear in the decl-specifier-seq - // of a friend declaration. - if (isFriendSpecified() && - (getStorageClassSpec() || getThreadStorageClassSpec())) { - SmallString<32> SpecName; - SourceLocation SCLoc; - FixItHint StorageHint, ThreadHint; - - if (DeclSpec::SCS SC = getStorageClassSpec()) { - SpecName = getSpecifierName(SC); - SCLoc = getStorageClassSpecLoc(); - StorageHint = FixItHint::CreateRemoval(SCLoc); - } - - if (DeclSpec::TSCS TSC = getThreadStorageClassSpec()) { - if (!SpecName.empty()) SpecName += " "; - SpecName += getSpecifierName(TSC); - SCLoc = getThreadStorageClassSpecLoc(); - ThreadHint = FixItHint::CreateRemoval(SCLoc); - } - - S.Diag(SCLoc, diag::err_friend_decl_spec) - << SpecName << StorageHint << ThreadHint; - - ClearStorageClassSpecs(); - } - - // C++11 [dcl.fct.spec]p5: - // The virtual specifier shall be used only in the initial - // declaration of a non-static class member function; - // C++11 [dcl.fct.spec]p6: - // The explicit specifier shall be used only in the declaration of - // a constructor or conversion function within its class - // definition; - if (isFriendSpecified() && (isVirtualSpecified() || hasExplicitSpecifier())) { - StringRef Keyword; - FixItHint Hint; - SourceLocation SCLoc; - - if (isVirtualSpecified()) { - Keyword = "virtual"; - SCLoc = getVirtualSpecLoc(); - Hint = FixItHint::CreateRemoval(SCLoc); - } else { - Keyword = "explicit"; - SCLoc = getExplicitSpecLoc(); - Hint = FixItHint::CreateRemoval(getExplicitSpecRange()); - } - - S.Diag(SCLoc, diag::err_friend_decl_spec) - << Keyword << Hint; - - FS_virtual_specified = false; - FS_explicit_specifier = ExplicitSpecifier(); - FS_virtualLoc = FS_explicitLoc = SourceLocation(); - } - - assert(!TypeSpecOwned || isDeclRep((TST) TypeSpecType)); - - // Okay, now we can infer the real type. - - // TODO: return "auto function" and other bad things based on the real type. - - // 'data definition has no type or storage class'? } bool DeclSpec::isMissingDeclaratorOk() { diff --git a/clang/test/CXX/class/class.friend/p6.cpp b/clang/test/CXX/class/class.friend/p6.cpp index e4c59f781e3de..a96dd8a3d4e4a 100644 --- a/clang/test/CXX/class/class.friend/p6.cpp +++ b/clang/test/CXX/class/class.friend/p6.cpp @@ -19,4 +19,5 @@ class A { #else friend thread_local class G; // expected-error {{'thread_local' is invalid in friend declarations}} #endif + friend register enum; // expected-error {{expected identifier or '{'}} expected-error {{'register' is invalid in friend declarations}} }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
