https://github.com/AditiRM updated https://github.com/llvm/llvm-project/pull/208153
>From 78b861d62b432d17881243a6eb3854a7de23258f Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Wed, 8 Jul 2026 07:09:35 +0000 Subject: [PATCH 1/7] [Clang] Fix assertion failure in getASTRecordLayout --- clang/lib/AST/RecordLayoutBuilder.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 854f88f20b2b6..6b5027772885d 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -3384,6 +3384,32 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { // not a complete definition (which is what isCompleteDefinition() tests) // until we *finish* parsing the definition. D = D->getDefinition(); + + // Handle invalid declarations gracefully during error recovery + // This can happen when there are template specialization errors + if (!D || D->isInvalidDecl() || !D->isCompleteDefinition()) { + // Check if we already have a cached layout + const ASTRecordLayout *Entry = ASTRecordLayouts[D]; + if (Entry) + return *Entry; + + // Create a minimal safe layout for error recovery + // Use 1-byte size and alignment to avoid division by zero or other issues + ASTRecordLayout *NewEntry = new (*this) + ASTRecordLayout(*this, + CharUnits::One(), // Size = 1 byte + CharUnits::One(), // Alignment = 1 byte + CharUnits::One(), // PreferredAlignment = 1 byte + CharUnits::One(), // UnadjustedAlignment = 1 byte + CharUnits::One(), // RequiredAlignment = 1 byte + CharUnits::One(), // DataSize = 1 byte + ArrayRef<uint64_t>() // Empty field offsets + ); + + ASTRecordLayouts[D] = NewEntry; + return *NewEntry; + } + assert(D && "Cannot get layout of forward declarations!"); assert(!D->isInvalidDecl() && "Cannot get layout of invalid decl!"); assert(D->isCompleteDefinition() && "Cannot layout type before complete!"); >From e3959f499ea4f8a9e35c922a5fc93332e844396a Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Wed, 8 Jul 2026 10:41:10 +0000 Subject: [PATCH 2/7] Add testcase triggering the assert failure --- ...ecialization-after-instantiation-crash.cpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp diff --git a/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp b/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp new file mode 100644 index 0000000000000..37b00db4d3877 --- /dev/null +++ b/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// +// Test that explicit template specialization after instantiation +// is handled gracefully without assertion failure. +// +// Before the fix, this code triggered an assertion failure: +// Assertion failed: !D->isInvalidDecl() && "Cannot get layout of invalid decl!" +// Location: clang/lib/AST/RecordLayoutBuilder.cpp:3388 +// Exit code: 134 (SIGABRT) +// +// After the fix: +// Clean error messages are reported +// Exit code: 1 +// +// The fix adds error handling in getASTRecordLayout() to return a minimal +// safe layout (1-byte size/alignment) for invalid declarations, allowing +// error recovery to continue and report all errors. + +template <typename T> +struct X { + struct Y { + Y() : v(0) {} + int v; + int getValue(); + } y; +}; + +template <typename T> +int X<T>::Y::getValue() { + return ++v; +} + +// expected-error@+1 {{explicit specialization of 'Y' after instantiation}} +template <> struct X<int>::Y { int getValue() { return 55; } }; +// expected-note@-10 {{implicit instantiation first required here}} + +extern template class X<int>::Y; + +int main() { + X<int> x; + return x.y.getValue(); // expected-error {{no member named 'getValue' in 'X<int>::Y'}} +} >From aa8c8a24b524d13c0717e525ca41ded48fe2814f Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Thu, 9 Jul 2026 06:48:14 +0000 Subject: [PATCH 3/7] [neat] review changes --- clang/lib/AST/RecordLayoutBuilder.cpp | 4 ---- ...cit-specialization-after-instantiation-crash.cpp | 13 ------------- 2 files changed, 17 deletions(-) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 6b5027772885d..662e76ab7cfd2 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -3410,10 +3410,6 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { return *NewEntry; } - assert(D && "Cannot get layout of forward declarations!"); - assert(!D->isInvalidDecl() && "Cannot get layout of invalid decl!"); - assert(D->isCompleteDefinition() && "Cannot layout type before complete!"); - // Look up this layout, if already laid out, return what we have. // Note that we can't save a reference to the entry because this function // is recursive. diff --git a/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp b/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp index 37b00db4d3877..8f78ea6535d07 100644 --- a/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp +++ b/clang/test/SemaCXX/explicit-specialization-after-instantiation-crash.cpp @@ -2,19 +2,6 @@ // // Test that explicit template specialization after instantiation // is handled gracefully without assertion failure. -// -// Before the fix, this code triggered an assertion failure: -// Assertion failed: !D->isInvalidDecl() && "Cannot get layout of invalid decl!" -// Location: clang/lib/AST/RecordLayoutBuilder.cpp:3388 -// Exit code: 134 (SIGABRT) -// -// After the fix: -// Clean error messages are reported -// Exit code: 1 -// -// The fix adds error handling in getASTRecordLayout() to return a minimal -// safe layout (1-byte size/alignment) for invalid declarations, allowing -// error recovery to continue and report all errors. template <typename T> struct X { >From 30070256fe3a39d80629e5336082e26e624ca13e Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Wed, 15 Jul 2026 05:54:41 +0000 Subject: [PATCH 4/7] [neat] update review changes --- clang/lib/AST/RecordLayoutBuilder.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 662e76ab7cfd2..e693c2be0cd34 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -3397,14 +3397,14 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { // Use 1-byte size and alignment to avoid division by zero or other issues ASTRecordLayout *NewEntry = new (*this) ASTRecordLayout(*this, - CharUnits::One(), // Size = 1 byte - CharUnits::One(), // Alignment = 1 byte - CharUnits::One(), // PreferredAlignment = 1 byte - CharUnits::One(), // UnadjustedAlignment = 1 byte - CharUnits::One(), // RequiredAlignment = 1 byte - CharUnits::One(), // DataSize = 1 byte - ArrayRef<uint64_t>() // Empty field offsets - ); + /*Size=*/CharUnits::One(), + /*Alignment=*/CharUnits::One(), + /*PreferredAlignment=*/CharUnits::One(), + /*UnadjustedAlignment=*/CharUnits::One(), + /*RequiredAlignment=*/CharUnits::One(), + /*DataSize=*/CharUnits::One(), + /*FieldOffsets=*/ArrayRef<uint64_t>() + ); ASTRecordLayouts[D] = NewEntry; return *NewEntry; >From 564d2dcc2449da847c103a6d1ebb9ca17e7207b0 Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Wed, 15 Jul 2026 06:06:17 +0000 Subject: [PATCH 5/7] [neat] clang formatting --- clang/lib/AST/RecordLayoutBuilder.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index e693c2be0cd34..221796841859d 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -3395,16 +3395,15 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { // Create a minimal safe layout for error recovery // Use 1-byte size and alignment to avoid division by zero or other issues - ASTRecordLayout *NewEntry = new (*this) - ASTRecordLayout(*this, - /*Size=*/CharUnits::One(), - /*Alignment=*/CharUnits::One(), - /*PreferredAlignment=*/CharUnits::One(), - /*UnadjustedAlignment=*/CharUnits::One(), - /*RequiredAlignment=*/CharUnits::One(), - /*DataSize=*/CharUnits::One(), - /*FieldOffsets=*/ArrayRef<uint64_t>() - ); + ASTRecordLayout *NewEntry = + new (*this) ASTRecordLayout(*this, + /*Size=*/CharUnits::One(), + /*Alignment=*/CharUnits::One(), + /*PreferredAlignment=*/CharUnits::One(), + /*UnadjustedAlignment=*/CharUnits::One(), + /*RequiredAlignment=*/CharUnits::One(), + /*DataSize=*/CharUnits::One(), + /*FieldOffsets=*/ArrayRef<uint64_t>()); ASTRecordLayouts[D] = NewEntry; return *NewEntry; >From 13eda0155eea15f25a4398b6d7521da377ba4c93 Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Tue, 11 Aug 2026 08:29:53 +0000 Subject: [PATCH 6/7] [Clang] Prevent assert on invalid decl by fixing layout computation callers --- clang/lib/AST/RecordLayoutBuilder.cpp | 41 +++++++++++---------------- clang/lib/Sema/SemaChecking.cpp | 12 +++++--- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 221796841859d..1f19a0f2e599f 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -189,6 +189,10 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); assert(BaseDecl != Class && "Class cannot inherit from itself."); + // Skip invalid base declarations to avoid assert in getASTRecordLayout + if (BaseDecl->isInvalidDecl()) + continue; + CharUnits EmptySize; const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl); if (BaseDecl->isEmpty()) { @@ -211,6 +215,10 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { if (!MemberDecl) continue; + // Skip invalid member declarations to avoid assert in getASTRecordLayout + if (MemberDecl->isInvalidDecl()) + continue; + CharUnits EmptySize; const ASTRecordLayout &Layout = Context.getASTRecordLayout(MemberDecl); if (MemberDecl->isEmpty()) { @@ -379,6 +387,12 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, if (!CanPlaceSubobjectAtOffset(RD, Offset)) return false; + // For invalid declarations, be permissive during error recovery. + // Return true to allow placement and avoid triggering assert in getASTRecordLayout. + // Layout constraints don't matter for types that are already marked invalid. + if (RD->isInvalidDecl()) + return true; + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); // Traverse all non-virtual bases. @@ -3384,30 +3398,9 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { // not a complete definition (which is what isCompleteDefinition() tests) // until we *finish* parsing the definition. D = D->getDefinition(); - - // Handle invalid declarations gracefully during error recovery - // This can happen when there are template specialization errors - if (!D || D->isInvalidDecl() || !D->isCompleteDefinition()) { - // Check if we already have a cached layout - const ASTRecordLayout *Entry = ASTRecordLayouts[D]; - if (Entry) - return *Entry; - - // Create a minimal safe layout for error recovery - // Use 1-byte size and alignment to avoid division by zero or other issues - ASTRecordLayout *NewEntry = - new (*this) ASTRecordLayout(*this, - /*Size=*/CharUnits::One(), - /*Alignment=*/CharUnits::One(), - /*PreferredAlignment=*/CharUnits::One(), - /*UnadjustedAlignment=*/CharUnits::One(), - /*RequiredAlignment=*/CharUnits::One(), - /*DataSize=*/CharUnits::One(), - /*FieldOffsets=*/ArrayRef<uint64_t>()); - - ASTRecordLayouts[D] = NewEntry; - return *NewEntry; - } + assert(D && "Cannot get layout of forward declarations!"); + assert(!D->isInvalidDecl() && "Cannot get layout of invalid decl!"); + assert(D->isCompleteDefinition() && "Cannot layout type before complete!"); // Look up this layout, if already laid out, return what we have. // Note that we can't save a reference to the entry because this function diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index d52c12670a57b..3fb50a22c9e57 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4616,10 +4616,14 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType, : VariadicCallType::DoesNotApply; auto *Ctor = cast<CXXConstructorDecl>(FDecl); - CheckArgAlignment( - Loc, FDecl, "'this'", Context.getPointerType(ThisType), - Context.getPointerType(Ctor->getFunctionObjectParameterType())); - + bool InvalidThisType = false; + if (const auto *RT = ThisType->getAs<RecordType>()) + InvalidThisType = RT->getDecl()->isInvalidDecl(); + if (!InvalidThisType){ + CheckArgAlignment( + Loc, FDecl, "'this'", Context.getPointerType(ThisType), + Context.getPointerType(Ctor->getFunctionObjectParameterType())); + } checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true, Loc, SourceRange(), CallType); } >From ff11a50d0f3f609dec786c79bcb885e151315551 Mon Sep 17 00:00:00 2001 From: AditiRM <[email protected]> Date: Tue, 11 Aug 2026 08:43:04 +0000 Subject: [PATCH 7/7] Fix clang formatting --- clang/lib/AST/RecordLayoutBuilder.cpp | 5 +++-- clang/lib/Sema/SemaChecking.cpp | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 1f19a0f2e599f..e077847c39fd8 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -388,8 +388,9 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, return false; // For invalid declarations, be permissive during error recovery. - // Return true to allow placement and avoid triggering assert in getASTRecordLayout. - // Layout constraints don't matter for types that are already marked invalid. + // Return true to allow placement and avoid triggering assert in + // getASTRecordLayout. Layout constraints don't matter for types that are + // already marked invalid. if (RD->isInvalidDecl()) return true; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 3fb50a22c9e57..2c1befaf4fe61 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4616,10 +4616,10 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType, : VariadicCallType::DoesNotApply; auto *Ctor = cast<CXXConstructorDecl>(FDecl); - bool InvalidThisType = false; - if (const auto *RT = ThisType->getAs<RecordType>()) + bool InvalidThisType = false; + if (const auto *RT = ThisType->getAs<RecordType>()) InvalidThisType = RT->getDecl()->isInvalidDecl(); - if (!InvalidThisType){ + if (!InvalidThisType) { CheckArgAlignment( Loc, FDecl, "'this'", Context.getPointerType(ThisType), Context.getPointerType(Ctor->getFunctionObjectParameterType())); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
