[cfe-dev] Clang 3.2 assertion failure writing AST files: RecordIdx == Vals.size() && "Not all record operands emitted!"

Tom Honermann thonermann at coverity.com
Tue Feb 26 19:16:22 PST 2013


The following code causes Clang (3.2 on Linux) to fail an assertion test 
when serializing an AST to an AST file.

template<typename T>
void f() {
   enum E {
     enumerator
   };

   T t = enumerator;
}

template void f<int>();

$ clang -c t.cpp
<no error, object file is generated successfully>

$ clang -emit-ast t.cpp
clang: 
/nfs/thonermann/clang-3.2/llvm-3.2/include/llvm/Bitcode/BitstreamWriter.h:398: 
void llvm::BitstreamWriter::EmitRecordWithAbbrevImpl(unsigned int, 
llvm::SmallVectorImpl<T>&, llvm::StringRef) [with uintty = long unsigned 
int]: Assertion `RecordIdx == Vals.size() && "Not all record operands 
emitted!"' failed.
clang: error: unable to execute command: Segmentation fault (core dumped)
clang: error: clang frontend command failed due to signal (use -v to see 
invocation)
clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
clang: note: diagnostic msg: PLEASE submit a bug report to 
http://llvm.org/bugs/ and include the crash backtrace, preprocessed 
source, and associated run script.
clang: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/t-dqKSIG.cpp
clang: note: diagnostic msg: /tmp/t-dqKSIG.sh
clang: note: diagnostic msg:

********************

/tmp/t-dqKSIG.cpp contains the sample code above.
/tmp/t-dqKSIG.sh contains:
/path/to/clang -cc1 -triple x86_64-unknown-linux-gnu -emit-pch 
-disable-free -main-file-name t.cpp -mrelocation-model static 
-mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases 
-munwind-tables -target-cpu x86-64 -target-linker-version 2.22 
-momit-leaf-frame-pointer -fdeprecated-macro -ferror-limit 19 
-fmessage-length 197 -mstackrealign -fobjc-runtime=gcc -fcxx-exceptions 
-fexceptions -fdiagnostics-show-option -fcolor-diagnostics -x c++ 
t-dqKSIG.cpp

This assertion failure (with a different test case) was previously 
reported here:
   http://llvm.org/bugs/show_bug.cgi?id=13020
   Bug 13020 - Clang 3.1 assertion failures reading and writing AST files

The assertion failure occurs at line 398 below:

include/llvm/Bitcode/BitstreamWriter.h:
  25 class BitstreamWriter {
...
313   template<typename uintty>
314   void EmitRecordWithAbbrevImpl(unsigned Abbrev, 
SmallVectorImpl<uintty> &Vals,
315                                 StringRef Blob) {
...
398     assert(RecordIdx == Vals.size() && "Not all record operands 
emitted!");
399     assert(BlobData == 0 &&
400            "Blob data specified for record that doesn't use it!");
401   }

The problem is that ASTDeclWriter::VisitEnumDecl() in 
lib/Serialization/ASTWriterDecl.cpp incorrectly determines that the 
abbreviated form of the EnumDecl bit stream can be used when serializing 
the enum member declaration of the f<int> specialization and ends up 
passing more operands than are expected for the abbreviation.

The problematic code is here:

lib/Serialization/ASTWriterDecl.cpp:
  224 void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
  ...
  235   if (MemberSpecializationInfo *MemberInfo = 
D->getMemberSpecializationInfo()) {
  236     Writer.AddDeclRef(MemberInfo->getInstantiatedFrom(), Record);
  237     Record.push_back(MemberInfo->getTemplateSpecializationKind());
  238 
Writer.AddSourceLocation(MemberInfo->getPointOfInstantiation(), Record);
  239   } else {
  240     Writer.AddDeclRef(0, Record);
  241   }
  242
  243   if (!D->hasAttrs() &&
  244       !D->isImplicit() &&
  245       !D->isUsed(false) &&
  246       !D->hasExtInfo() &&
  247       D->getFirstDeclaration() == D->getMostRecentDecl() &&
  248       !D->isInvalidDecl() &&
  249       !D->isReferenced() &&
  250       !D->isTopLevelDeclInObjCContainer() &&
  251       D->getAccess() == AS_none &&
  252       !D->isModulePrivate() &&
  253       !CXXRecordDecl::classofKind(D->getKind()) &&
  254       !D->getIntegerTypeSourceInfo() &&
  255       D->getDeclName().getNameKind() == DeclarationName::Identifier &&
  256       !D->getMemberSpecializationInfo())
  257     AbbrevToUse = Writer.getDeclEnumAbbrev();
  258
  259   Code = serialization::DECL_ENUM;
  260 }

Lines 235-241 will queue a conditional number of record operands 
depending on whether the declaration has member specialization info. 
The abbreviated bit stream expects just one (matching the else body). 
Adding a check for '!D->getMemberSpecializationInfo()' to the if 
conditions at lines 243-256 above appears to correct this problem:

@@ -252,7 +252,8 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
        !D->isModulePrivate() &&
        !CXXRecordDecl::classofKind(D->getKind()) &&
        !D->getIntegerTypeSourceInfo() &&
-      D->getDeclName().getNameKind() == DeclarationName::Identifier)
+      D->getDeclName().getNameKind() == DeclarationName::Identifier &&
+      !D->getMemberSpecializationInfo())
      AbbrevToUse = Writer.getDeclEnumAbbrev();

For reference, here is the corresponding code to build the enum 
declaration abbreviation that matches the else body at line 240 above.

lib/Serialization/ASTWriterDecl.cpp:
1300 void ASTWriter::WriteDeclsBlockAbbrevs() {
....
1372   // Abbreviation for DECL_ENUM
1373   Abv = new BitCodeAbbrev();
....
1413   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));   // 
InstantiatedMembEnum
....
1650 }

Tom.




More information about the cfe-dev mailing list