https://github.com/firstmoonlight created https://github.com/llvm/llvm-project/pull/190597
Fix an assertion failure in Sema::ActOnFriendTypeDecl when parsing an invalid friend type declaration that incorrectly includes a storage-class specifier (e.g., 'static', 'extern', 'register'). Root cause: If the type specifier is marked as invalid, DeclSpec::Finish returns early. However, even when the type specifier is invalid, some other checks can still be performed instead of skipping everything. This change allows necessary checks to proceed, preventing the assertion in ActOnFriendTypeDecl and enabling proper error diagnostics. Fixes: https://github.com/llvm/llvm-project/issues/186569 >From 8a1c2eda94311aa5850267a677250226b52e8834 Mon Sep 17 00:00:00 2001 From: victorl <[email protected]> Date: Mon, 6 Apr 2026 17:25:15 +0800 Subject: [PATCH] [clang][Sema]fix crash of invalid friend declaration with storage-class specifier --- clang/lib/Sema/DeclSpec.cpp | 518 +++++++++++------------ clang/test/CXX/class/class.friend/p6.cpp | 1 + 2 files changed, 260 insertions(+), 259 deletions(-) diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 479a959e0aadc..bc3fbe43c7a94 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1162,286 +1162,286 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { // Check the type specifier components first. No checking for an invalid // type. - if (TypeSpecType == TST_error) - return; - - // If decltype(auto) is used, no other type specifiers are permitted. - if (TypeSpecType == TST_decltype_auto && - (getTypeSpecWidth() != TypeSpecifierWidth::Unspecified || - TypeSpecComplex != TSC_unspecified || - getTypeSpecSign() != TypeSpecifierSign::Unspecified || - TypeAltiVecVector || TypeAltiVecPixel || TypeAltiVecBool || - TypeQualifiers)) { - const unsigned NumLocs = 9; - SourceLocation ExtraLocs[NumLocs] = { - TSWRange.getBegin(), TSCLoc, TSSLoc, - AltiVecLoc, TQ_constLoc, TQ_restrictLoc, - TQ_volatileLoc, TQ_atomicLoc, TQ_unalignedLoc}; - FixItHint Hints[NumLocs]; - SourceLocation FirstLoc; - for (unsigned I = 0; I != NumLocs; ++I) { - if (ExtraLocs[I].isValid()) { - if (FirstLoc.isInvalid() || - S.getSourceManager().isBeforeInTranslationUnit(ExtraLocs[I], - FirstLoc)) - FirstLoc = ExtraLocs[I]; - Hints[I] = FixItHint::CreateRemoval(ExtraLocs[I]); + if (TypeSpecType != TST_error) { + // If decltype(auto) is used, no other type specifiers are permitted. + if (TypeSpecType == TST_decltype_auto && + (getTypeSpecWidth() != TypeSpecifierWidth::Unspecified || + TypeSpecComplex != TSC_unspecified || + getTypeSpecSign() != TypeSpecifierSign::Unspecified || + TypeAltiVecVector || TypeAltiVecPixel || TypeAltiVecBool || + TypeQualifiers)) { + const unsigned NumLocs = 9; + SourceLocation ExtraLocs[NumLocs] = { + TSWRange.getBegin(), TSCLoc, TSSLoc, + AltiVecLoc, TQ_constLoc, TQ_restrictLoc, + TQ_volatileLoc, TQ_atomicLoc, TQ_unalignedLoc}; + FixItHint Hints[NumLocs]; + SourceLocation FirstLoc; + for (unsigned I = 0; I != NumLocs; ++I) { + if (ExtraLocs[I].isValid()) { + if (FirstLoc.isInvalid() || + S.getSourceManager().isBeforeInTranslationUnit(ExtraLocs[I], + FirstLoc)) + FirstLoc = ExtraLocs[I]; + Hints[I] = FixItHint::CreateRemoval(ExtraLocs[I]); + } } + TypeSpecWidth = static_cast<unsigned>(TypeSpecifierWidth::Unspecified); + TypeSpecComplex = TSC_unspecified; + TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unspecified); + TypeAltiVecVector = TypeAltiVecPixel = TypeAltiVecBool = false; + TypeQualifiers = 0; + S.Diag(TSTLoc, diag::err_decltype_auto_cannot_be_combined) + << Hints[0] << Hints[1] << Hints[2] << Hints[3] + << Hints[4] << Hints[5] << Hints[6] << Hints[7]; } - TypeSpecWidth = static_cast<unsigned>(TypeSpecifierWidth::Unspecified); - TypeSpecComplex = TSC_unspecified; - TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unspecified); - TypeAltiVecVector = TypeAltiVecPixel = TypeAltiVecBool = false; - TypeQualifiers = 0; - S.Diag(TSTLoc, diag::err_decltype_auto_cannot_be_combined) - << Hints[0] << Hints[1] << Hints[2] << Hints[3] - << Hints[4] << Hints[5] << Hints[6] << Hints[7]; - } - // Validate and finalize AltiVec vector declspec. - if (TypeAltiVecVector) { - // No vector long long without VSX (or ZVector). - if ((getTypeSpecWidth() == TypeSpecifierWidth::LongLong) && - !S.Context.getTargetInfo().hasFeature("vsx") && - !S.getLangOpts().ZVector) - S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_long_long_decl_spec); - - // No vector __int128 prior to Power8 (or ZVector). - if ((TypeSpecType == TST_int128) && - !S.Context.getTargetInfo().hasFeature("power8-vector") && - !S.getLangOpts().ZVector) - S.Diag(TSTLoc, diag::err_invalid_vector_int128_decl_spec); - - // Complex vector types are not supported. - if (TypeSpecComplex != TSC_unspecified) - S.Diag(TSCLoc, diag::err_invalid_vector_complex_decl_spec); - else if (TypeAltiVecBool) { - // Sign specifiers are not allowed with vector bool. (PIM 2.1) - if (getTypeSpecSign() != TypeSpecifierSign::Unspecified) { - S.Diag(TSSLoc, diag::err_invalid_vector_bool_decl_spec) - << getSpecifierName(getTypeSpecSign()); - } - // Only char/int are valid with vector bool prior to Power10. - // Power10 adds instructions that produce vector bool data - // for quadwords as well so allow vector bool __int128. - if (((TypeSpecType != TST_unspecified) && (TypeSpecType != TST_char) && - (TypeSpecType != TST_int) && (TypeSpecType != TST_int128)) || - TypeAltiVecPixel) { - S.Diag(TSTLoc, diag::err_invalid_vector_bool_decl_spec) - << (TypeAltiVecPixel ? "__pixel" : - getSpecifierName((TST)TypeSpecType, Policy)); - } - // vector bool __int128 requires Power10 (or ZVector). + // Validate and finalize AltiVec vector declspec. + if (TypeAltiVecVector) { + // No vector long long without VSX (or ZVector). + if ((getTypeSpecWidth() == TypeSpecifierWidth::LongLong) && + !S.Context.getTargetInfo().hasFeature("vsx") && + !S.getLangOpts().ZVector) + S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_long_long_decl_spec); + + // No vector __int128 prior to Power8 (or ZVector). if ((TypeSpecType == TST_int128) && - (!S.Context.getTargetInfo().hasFeature("power10-vector") && - !S.getLangOpts().ZVector)) - S.Diag(TSTLoc, diag::err_invalid_vector_bool_int128_decl_spec); - - // Only 'short' and 'long long' are valid with vector bool. (PIM 2.1) - if ((getTypeSpecWidth() != TypeSpecifierWidth::Unspecified) && - (getTypeSpecWidth() != TypeSpecifierWidth::Short) && - (getTypeSpecWidth() != TypeSpecifierWidth::LongLong)) - S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_bool_decl_spec) - << getSpecifierName(getTypeSpecWidth()); - - // Elements of vector bool are interpreted as unsigned. (PIM 2.1) - if ((TypeSpecType == TST_char) || (TypeSpecType == TST_int) || - (TypeSpecType == TST_int128) || - (getTypeSpecWidth() != TypeSpecifierWidth::Unspecified)) + !S.Context.getTargetInfo().hasFeature("power8-vector") && + !S.getLangOpts().ZVector) + S.Diag(TSTLoc, diag::err_invalid_vector_int128_decl_spec); + + // Complex vector types are not supported. + if (TypeSpecComplex != TSC_unspecified) + S.Diag(TSCLoc, diag::err_invalid_vector_complex_decl_spec); + else if (TypeAltiVecBool) { + // Sign specifiers are not allowed with vector bool. (PIM 2.1) + if (getTypeSpecSign() != TypeSpecifierSign::Unspecified) { + S.Diag(TSSLoc, diag::err_invalid_vector_bool_decl_spec) + << getSpecifierName(getTypeSpecSign()); + } + // Only char/int are valid with vector bool prior to Power10. + // Power10 adds instructions that produce vector bool data + // for quadwords as well so allow vector bool __int128. + if (((TypeSpecType != TST_unspecified) && (TypeSpecType != TST_char) && + (TypeSpecType != TST_int) && (TypeSpecType != TST_int128)) || + TypeAltiVecPixel) { + S.Diag(TSTLoc, diag::err_invalid_vector_bool_decl_spec) + << (TypeAltiVecPixel ? "__pixel" : + getSpecifierName((TST)TypeSpecType, Policy)); + } + // vector bool __int128 requires Power10 (or ZVector). + if ((TypeSpecType == TST_int128) && + (!S.Context.getTargetInfo().hasFeature("power10-vector") && + !S.getLangOpts().ZVector)) + S.Diag(TSTLoc, diag::err_invalid_vector_bool_int128_decl_spec); + + // Only 'short' and 'long long' are valid with vector bool. (PIM 2.1) + if ((getTypeSpecWidth() != TypeSpecifierWidth::Unspecified) && + (getTypeSpecWidth() != TypeSpecifierWidth::Short) && + (getTypeSpecWidth() != TypeSpecifierWidth::LongLong)) + S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_bool_decl_spec) + << getSpecifierName(getTypeSpecWidth()); + + // Elements of vector bool are interpreted as unsigned. (PIM 2.1) + if ((TypeSpecType == TST_char) || (TypeSpecType == TST_int) || + (TypeSpecType == TST_int128) || + (getTypeSpecWidth() != TypeSpecifierWidth::Unspecified)) + TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unsigned); + } else if (TypeSpecType == TST_double) { + // vector long double and vector long long double are never allowed. + // vector double is OK for Power7 and later, and ZVector. + if (getTypeSpecWidth() == TypeSpecifierWidth::Long || + getTypeSpecWidth() == TypeSpecifierWidth::LongLong) + S.Diag(TSWRange.getBegin(), + diag::err_invalid_vector_long_double_decl_spec); + else if (!S.Context.getTargetInfo().hasFeature("vsx") && + !S.getLangOpts().ZVector) + S.Diag(TSTLoc, diag::err_invalid_vector_double_decl_spec); + } else if (TypeSpecType == TST_float) { + // vector float is unsupported for ZVector unless we have the + // vector-enhancements facility 1 (ISA revision 12). + if (S.getLangOpts().ZVector && + !S.Context.getTargetInfo().hasFeature("arch12")) + S.Diag(TSTLoc, diag::err_invalid_vector_float_decl_spec); + } else if (getTypeSpecWidth() == TypeSpecifierWidth::Long) { + // Vector long is unsupported for ZVector, or without VSX, and deprecated + // for AltiVec. + // It has also been historically deprecated on AIX (as an alias for + // "vector int" in both 32-bit and 64-bit modes). It was then made + // unsupported in the Clang-based XL compiler since the deprecated type + // has a number of conflicting semantics and continuing to support it + // is a disservice to users. + if (S.getLangOpts().ZVector || + !S.Context.getTargetInfo().hasFeature("vsx") || + S.Context.getTargetInfo().getTriple().isOSAIX()) + S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_long_decl_spec); + else + S.Diag(TSWRange.getBegin(), + diag::warn_vector_long_decl_spec_combination) + << getSpecifierName((TST)TypeSpecType, Policy); + } + + if (TypeAltiVecPixel) { + //TODO: perform validation + TypeSpecType = TST_int; TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unsigned); - } else if (TypeSpecType == TST_double) { - // vector long double and vector long long double are never allowed. - // vector double is OK for Power7 and later, and ZVector. - if (getTypeSpecWidth() == TypeSpecifierWidth::Long || - getTypeSpecWidth() == TypeSpecifierWidth::LongLong) - S.Diag(TSWRange.getBegin(), - diag::err_invalid_vector_long_double_decl_spec); - else if (!S.Context.getTargetInfo().hasFeature("vsx") && - !S.getLangOpts().ZVector) - S.Diag(TSTLoc, diag::err_invalid_vector_double_decl_spec); - } else if (TypeSpecType == TST_float) { - // vector float is unsupported for ZVector unless we have the - // vector-enhancements facility 1 (ISA revision 12). - if (S.getLangOpts().ZVector && - !S.Context.getTargetInfo().hasFeature("arch12")) - S.Diag(TSTLoc, diag::err_invalid_vector_float_decl_spec); - } else if (getTypeSpecWidth() == TypeSpecifierWidth::Long) { - // Vector long is unsupported for ZVector, or without VSX, and deprecated - // for AltiVec. - // It has also been historically deprecated on AIX (as an alias for - // "vector int" in both 32-bit and 64-bit modes). It was then made - // unsupported in the Clang-based XL compiler since the deprecated type - // has a number of conflicting semantics and continuing to support it - // is a disservice to users. - if (S.getLangOpts().ZVector || - !S.Context.getTargetInfo().hasFeature("vsx") || - S.Context.getTargetInfo().getTriple().isOSAIX()) - S.Diag(TSWRange.getBegin(), diag::err_invalid_vector_long_decl_spec); - else - S.Diag(TSWRange.getBegin(), - diag::warn_vector_long_decl_spec_combination) - << getSpecifierName((TST)TypeSpecType, Policy); + TypeSpecWidth = static_cast<unsigned>(TypeSpecifierWidth::Short); + TypeSpecOwned = false; + } } - if (TypeAltiVecPixel) { - //TODO: perform validation - TypeSpecType = TST_int; - TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unsigned); - TypeSpecWidth = static_cast<unsigned>(TypeSpecifierWidth::Short); - TypeSpecOwned = false; + bool IsFixedPointType = + TypeSpecType == TST_accum || TypeSpecType == TST_fract; + + // signed/unsigned are only valid with int/char/wchar_t/_Accum. + if (getTypeSpecSign() != TypeSpecifierSign::Unspecified) { + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. + else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && + TypeSpecType != TST_char && TypeSpecType != TST_wchar && + !IsFixedPointType && TypeSpecType != TST_bitint) { + S.Diag(TSSLoc, diag::err_invalid_sign_spec) + << getSpecifierName((TST)TypeSpecType, Policy); + // signed double -> double. + TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unspecified); + } } - } - bool IsFixedPointType = - TypeSpecType == TST_accum || TypeSpecType == TST_fract; - - // signed/unsigned are only valid with int/char/wchar_t/_Accum. - if (getTypeSpecSign() != TypeSpecifierSign::Unspecified) { - if (TypeSpecType == TST_unspecified) - TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. - else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && - TypeSpecType != TST_char && TypeSpecType != TST_wchar && - !IsFixedPointType && TypeSpecType != TST_bitint) { - S.Diag(TSSLoc, diag::err_invalid_sign_spec) - << getSpecifierName((TST)TypeSpecType, Policy); - // signed double -> double. - TypeSpecSign = static_cast<unsigned>(TypeSpecifierSign::Unspecified); + // Validate the width of the type. + switch (getTypeSpecWidth()) { + case TypeSpecifierWidth::Unspecified: + break; + case TypeSpecifierWidth::Short: // short int + case TypeSpecifierWidth::LongLong: // long long int + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // short -> short int, long long -> long long int. + else if (!(TypeSpecType == TST_int || + (IsFixedPointType && + getTypeSpecWidth() != TypeSpecifierWidth::LongLong))) { + S.Diag(TSWRange.getBegin(), diag::err_invalid_width_spec) + << (int)TypeSpecWidth << getSpecifierName((TST)TypeSpecType, Policy); + TypeSpecType = TST_int; + TypeSpecSat = false; + TypeSpecOwned = false; + } + break; + case TypeSpecifierWidth::Long: // long double, long int + if (TypeSpecType == TST_unspecified) + TypeSpecType = TST_int; // long -> long int. + else if (TypeSpecType != TST_int && TypeSpecType != TST_double && + !IsFixedPointType) { + S.Diag(TSWRange.getBegin(), diag::err_invalid_width_spec) + << (int)TypeSpecWidth << getSpecifierName((TST)TypeSpecType, Policy); + TypeSpecType = TST_int; + TypeSpecSat = false; + TypeSpecOwned = false; + } + break; } - } - // Validate the width of the type. - switch (getTypeSpecWidth()) { - case TypeSpecifierWidth::Unspecified: - break; - case TypeSpecifierWidth::Short: // short int - case TypeSpecifierWidth::LongLong: // long long int - if (TypeSpecType == TST_unspecified) - TypeSpecType = TST_int; // short -> short int, long long -> long long int. - else if (!(TypeSpecType == TST_int || - (IsFixedPointType && - getTypeSpecWidth() != TypeSpecifierWidth::LongLong))) { - S.Diag(TSWRange.getBegin(), diag::err_invalid_width_spec) - << (int)TypeSpecWidth << getSpecifierName((TST)TypeSpecType, Policy); - TypeSpecType = TST_int; - TypeSpecSat = false; - TypeSpecOwned = false; - } - break; - case TypeSpecifierWidth::Long: // long double, long int - if (TypeSpecType == TST_unspecified) - TypeSpecType = TST_int; // long -> long int. - else if (TypeSpecType != TST_int && TypeSpecType != TST_double && - !IsFixedPointType) { - S.Diag(TSWRange.getBegin(), diag::err_invalid_width_spec) - << (int)TypeSpecWidth << getSpecifierName((TST)TypeSpecType, Policy); - TypeSpecType = TST_int; - TypeSpecSat = false; - TypeSpecOwned = false; + // TODO: if the implementation does not implement _Complex, disallow their + // 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"); + TypeSpecType = TST_double; // _Complex -> _Complex double. + } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { + // Note that this intentionally doesn't include _Complex _Bool. + if (!S.getLangOpts().CPlusPlus) + S.Diag(TSTLoc, diag::ext_integer_complex); + } else if (TypeSpecType != TST_float && TypeSpecType != TST_double && + TypeSpecType != TST_float128 && TypeSpecType != TST_float16 && + TypeSpecType != TST_ibm128) { + // FIXME: __fp16? + S.Diag(TSCLoc, diag::err_invalid_complex_spec) + << getSpecifierName((TST)TypeSpecType, Policy); + TypeSpecComplex = TSC_unspecified; + } } - break; - } - // TODO: if the implementation does not implement _Complex, disallow their - // 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"); - TypeSpecType = TST_double; // _Complex -> _Complex double. - } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { - // Note that this intentionally doesn't include _Complex _Bool. - if (!S.getLangOpts().CPlusPlus) - S.Diag(TSTLoc, diag::ext_integer_complex); - } else if (TypeSpecType != TST_float && TypeSpecType != TST_double && - TypeSpecType != TST_float128 && TypeSpecType != TST_float16 && - TypeSpecType != TST_ibm128) { - // FIXME: __fp16? - S.Diag(TSCLoc, diag::err_invalid_complex_spec) - << getSpecifierName((TST)TypeSpecType, Policy); - TypeSpecComplex = TSC_unspecified; + // C11 6.7.1/3, C++11 [dcl.stc]p1, GNU TLS: __thread, thread_local and + // _Thread_local can only appear with the 'static' and 'extern' storage class + // specifiers. We also allow __private_extern__ as an extension. + if (ThreadStorageClassSpec != TSCS_unspecified) { + switch (StorageClassSpec) { + case SCS_unspecified: + case SCS_extern: + case SCS_private_extern: + case SCS_static: + break; + default: + if (S.getSourceManager().isBeforeInTranslationUnit( + getThreadStorageClassSpecLoc(), getStorageClassSpecLoc())) + S.Diag(getStorageClassSpecLoc(), + 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()); + // Discard the thread storage class specifier to recover. + ThreadStorageClassSpec = TSCS_unspecified; + ThreadStorageClassSpecLoc = SourceLocation(); + } + if (S.getLangOpts().C23 && + getConstexprSpecifier() == ConstexprSpecKind::Constexpr) { + S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) + << SourceRange(getThreadStorageClassSpecLoc()); + } } - } - // C11 6.7.1/3, C++11 [dcl.stc]p1, GNU TLS: __thread, thread_local and - // _Thread_local can only appear with the 'static' and 'extern' storage class - // specifiers. We also allow __private_extern__ as an extension. - if (ThreadStorageClassSpec != TSCS_unspecified) { - switch (StorageClassSpec) { - case SCS_unspecified: - case SCS_extern: - case SCS_private_extern: - case SCS_static: - break; - default: - if (S.getSourceManager().isBeforeInTranslationUnit( - getThreadStorageClassSpecLoc(), getStorageClassSpecLoc())) - S.Diag(getStorageClassSpecLoc(), - 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()); - // Discard the thread storage class specifier to recover. - ThreadStorageClassSpec = TSCS_unspecified; - ThreadStorageClassSpecLoc = SourceLocation(); - } if (S.getLangOpts().C23 && - getConstexprSpecifier() == ConstexprSpecKind::Constexpr) { + getConstexprSpecifier() == ConstexprSpecKind::Constexpr && + getTypeSpecType() != TST_unspecified && + (StorageClassSpec == SCS_extern || StorageClassSpec == SCS_auto)) { S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination) - << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) - << SourceRange(getThreadStorageClassSpecLoc()); + << DeclSpec::getSpecifierName(getStorageClassSpec()) + << SourceRange(getStorageClassSpecLoc()); } - } - if (S.getLangOpts().C23 && - getConstexprSpecifier() == ConstexprSpecKind::Constexpr && - getTypeSpecType() != TST_unspecified && - (StorageClassSpec == SCS_extern || StorageClassSpec == SCS_auto)) { - S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination) - << DeclSpec::getSpecifierName(getStorageClassSpec()) - << SourceRange(getStorageClassSpecLoc()); + // If no type specifier was provided and we're parsing a language where + // the type specifier is not optional, but we got 'auto' as a storage + // class specifier, then assume this is an attempt to use C++0x's 'auto' + // type specifier. + if (S.getLangOpts().CPlusPlus && + TypeSpecType == TST_unspecified && StorageClassSpec == SCS_auto) { + TypeSpecType = TST_auto; + StorageClassSpec = SCS_unspecified; + TSTLoc = TSTNameLoc = StorageClassSpecLoc; + StorageClassSpecLoc = SourceLocation(); + } + // Diagnose if we've recovered from an ill-formed 'auto' storage class + // specifier in a pre-C++11 dialect of C++ or in a pre-C23 dialect of C. + if (!S.getLangOpts().CPlusPlus11 && !S.getLangOpts().C23 && + TypeSpecType == TST_auto) + S.Diag(TSTLoc, diag::ext_auto_type_specifier) << /*C++*/ 0; + if (S.getLangOpts().HLSL && + S.getLangOpts().getHLSLVersion() < LangOptions::HLSL_202y && + TypeSpecType == TST_auto) + S.Diag(TSTLoc, diag::ext_hlsl_auto_type_specifier) << /*HLSL*/ 1; + if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 && + StorageClassSpec == SCS_auto) + S.Diag(StorageClassSpecLoc, diag::warn_auto_storage_class) + << FixItHint::CreateRemoval(StorageClassSpecLoc); + if (TypeSpecType == TST_char8) + S.Diag(TSTLoc, diag::warn_cxx17_compat_unicode_type); + else if (TypeSpecType == TST_char16 || TypeSpecType == TST_char32) + S.Diag(TSTLoc, diag::warn_cxx98_compat_unicode_type) + << (TypeSpecType == TST_char16 ? "char16_t" : "char32_t"); + if (getConstexprSpecifier() == ConstexprSpecKind::Constexpr) + S.Diag(ConstexprLoc, diag::warn_cxx98_compat_constexpr); + else if (getConstexprSpecifier() == ConstexprSpecKind::Consteval) + S.Diag(ConstexprLoc, diag::warn_cxx20_compat_consteval); + else if (getConstexprSpecifier() == ConstexprSpecKind::Constinit) + S.Diag(ConstexprLoc, diag::warn_cxx20_compat_constinit); } - // If no type specifier was provided and we're parsing a language where - // the type specifier is not optional, but we got 'auto' as a storage - // class specifier, then assume this is an attempt to use C++0x's 'auto' - // type specifier. - if (S.getLangOpts().CPlusPlus && - TypeSpecType == TST_unspecified && StorageClassSpec == SCS_auto) { - TypeSpecType = TST_auto; - StorageClassSpec = SCS_unspecified; - TSTLoc = TSTNameLoc = StorageClassSpecLoc; - StorageClassSpecLoc = SourceLocation(); - } - // Diagnose if we've recovered from an ill-formed 'auto' storage class - // specifier in a pre-C++11 dialect of C++ or in a pre-C23 dialect of C. - if (!S.getLangOpts().CPlusPlus11 && !S.getLangOpts().C23 && - TypeSpecType == TST_auto) - S.Diag(TSTLoc, diag::ext_auto_type_specifier) << /*C++*/ 0; - if (S.getLangOpts().HLSL && - S.getLangOpts().getHLSLVersion() < LangOptions::HLSL_202y && - TypeSpecType == TST_auto) - S.Diag(TSTLoc, diag::ext_hlsl_auto_type_specifier) << /*HLSL*/ 1; - if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 && - StorageClassSpec == SCS_auto) - S.Diag(StorageClassSpecLoc, diag::warn_auto_storage_class) - << FixItHint::CreateRemoval(StorageClassSpecLoc); - if (TypeSpecType == TST_char8) - S.Diag(TSTLoc, diag::warn_cxx17_compat_unicode_type); - else if (TypeSpecType == TST_char16 || TypeSpecType == TST_char32) - S.Diag(TSTLoc, diag::warn_cxx98_compat_unicode_type) - << (TypeSpecType == TST_char16 ? "char16_t" : "char32_t"); - if (getConstexprSpecifier() == ConstexprSpecKind::Constexpr) - S.Diag(ConstexprLoc, diag::warn_cxx98_compat_constexpr); - else if (getConstexprSpecifier() == ConstexprSpecKind::Consteval) - 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. 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
