diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index bdbabf2cd98d0..5de035d9a78a6 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -551,6 +551,12 @@ features cannot lower the translation-unit ABI level; inside a union caused the union to be treated as a polymorphic class. (#GH213854) +- Fixed an assertion failure when an array whose element type was still + incomplete when the array type was formed (for example, an array of a class + template specialization that is only instantiated later) turned out to be too + large once the element type was completed. Clang now diagnoses the oversized + array instead of asserting. (#GH213855) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4650bd53775f7..05ad0eb1ab3e5 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15601,7 +15601,8 @@ class Sema final : public SemaBase { /// this routine then attempts to perform class template /// instantiation. If instantiation fails, or if @p T is incomplete /// and cannot be completed, issues the diagnostic @p diag (giving it - /// the type @p T) and returns true. + /// the type @p T) and returns true. The same applies to an array type + /// that turns out to be too large once its element type is complete. /// /// @param Loc The location in the source that the incomplete type /// diagnostic should refer to. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 07c6157ab8f31..7a1f79a64c841 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14857,6 +14857,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { Var->setInvalidDecl(); return; } + // Completing the element type may reveal that the array is too large. + if (Type->isConstantArrayType() && + RequireCompleteType(Var->getLocation(), Type, + diag::err_typecheck_decl_incomplete_type)) { + Var->setInvalidDecl(); + return; + } } else { return; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ad9204b12524b..270cf22ba4369 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9687,6 +9687,28 @@ static void assignInheritanceModel(Sema &S, CXXRecordDecl *RD) { } } +/// Return the (possibly nested) constant array type in \p T whose size cannot +/// be represented, if any. BuildArrayType can only check the element count if +/// the element type is still incomplete when the array type is formed. +static const ConstantArrayType *findArrayTypeTooLarge(const ASTContext &Context, + QualType T) { + const auto *CAT = dyn_cast(T.getCanonicalType()); + if (!CAT) + return nullptr; + + // Check nested arrays from the inside out. + QualType ElementType = CAT->getElementType(); + if (const ConstantArrayType *Inner = + findArrayTypeTooLarge(Context, ElementType)) + return Inner; + + if (ConstantArrayType::getNumAddressingBits(Context, ElementType, + CAT->getSize()) > + ConstantArrayType::getMaxSizeBits(Context)) + return CAT; + return nullptr; +} + bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, CompleteTypeKind Kind, TypeDiagnoser *Diagnoser) { @@ -9738,6 +9760,20 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, /*Recover*/ TreatAsComplete); return !TreatAsComplete; } + + // The element type may have been incomplete when the array type was + // formed, in which case BuildArrayType could not check the array's size. + if (T->isConstantArrayType() && !T->isDependentType() && + !T->isVariablyModifiedType() && !T->isUndeducedType()) { + if (const ConstantArrayType *CAT = findArrayTypeTooLarge(Context, T)) { + if (Diagnoser) + Diag(Loc, diag::err_array_too_large) + << toString(CAT->getSize(), 10, /*Signed=*/false, + /*formatAsCLiteral=*/false, /*UpperCase=*/false, + /*InsertSeparators=*/true); + return true; + } + } return false; } diff --git a/clang/test/SemaTemplate/GH213855.cpp b/clang/test/SemaTemplate/GH213855.cpp new file mode 100644 index 0000000000000..de4bea8b05907 --- /dev/null +++ b/clang/test/SemaTemplate/GH213855.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-linux-gnu %s + +// An array whose element type is incomplete when the array type is formed can +// only have its size checked once the element type is completed. + +namespace GH213855 { +template struct S : public CBdVfsImpl { // expected-error {{expected class name}} + double A[Size]; +}; +template struct SS { + S A[Size]; // expected-error {{array is too large (4'294'967'173 elements)}} +void foo() { SS<-123> ss; } // expected-error {{non-type template argument evaluates to -123, which cannot be narrowed to type 'unsigned int'}} \ + // expected-note {{in instantiation of template class 'GH213855::SS<4294967173>' requested here}} +}; +} // namespace GH213855 + +namespace array_variable { +template struct S { double A[Size]; }; +S<4294967173u> arr[4294967173u]; // expected-error {{array is too large (4'294'967'173 elements)}} +} // namespace array_variable + +namespace incomplete_element_type { +struct Incomplete; +extern Incomplete ok[2]; +extern Incomplete arr[4294967173]; +extern Incomplete arr2[2][4294967173]; +struct Incomplete { double A[4294967173]; }; +Incomplete arr3[4294967173]; // expected-error {{array is too large (4'294'967'173 elements)}} + +unsigned long n0 = sizeof(ok); +unsigned long n1 = sizeof(arr); // expected-error {{array is too large (4'294'967'173 elements)}} +unsigned long n2 = sizeof(arr2); // expected-error {{array is too large (4'294'967'173 elements)}} +} // namespace incomplete_element_type