From b0e61de7075942ef5ac8af9ca1ec918317f62152 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 14 Apr 2023 07:18:46 -0400 Subject: [PATCH] Model list initialization more directly; fixes an assert with coverage mapping Instead of using the validity of a brace's source location as a flag for list initialization, this now uses a PointerIntPair to model it so we do not increase the size of the AST node to track this information. This allows us to retain the valid source location information, which fixes the coverage assertion. Fixes https://github.com/llvm/llvm-project/issues/62105 Differential Revision: https://reviews.llvm.org/D148245 --- clang/docs/ReleaseNotes.rst | 2 ++ clang/include/clang/AST/ExprCXX.h | 26 +++++++++++--------- clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/AST/ExprCXX.cpp | 22 ++++++++--------- clang/lib/Sema/SemaExprCXX.cpp | 14 +++-------- clang/lib/Serialization/ASTReaderStmt.cpp | 3 ++- clang/lib/Serialization/ASTWriterStmt.cpp | 1 + clang/test/Coverage/unresolved-ctor-expr.cpp | 24 ++++++++++++++++++ 8 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 clang/test/Coverage/unresolved-ctor-expr.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b838858c0917..d854590363a5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -300,6 +300,8 @@ Bug Fixes in This Version - Work around with a clang coverage crash which happens when visiting expressions/statements with invalid source locations in non-assert builds. Assert builds may still see assertions triggered from this. +- Fix a failed assertion due to an invalid source location when trying to form + a coverage report for an unresolved constructor expression. (`#62105 `_) Bug Fixes to Compiler Builtins diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 032fd199b030..724904b4d204 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3504,8 +3504,9 @@ class CXXUnresolvedConstructExpr final friend class ASTStmtReader; friend TrailingObjects; - /// The type being constructed. - TypeSourceInfo *TSI; + /// The type being constructed, and whether the construct expression models + /// list initialization or not. + llvm::PointerIntPair TypeAndInitForm; /// The location of the left parentheses ('('). SourceLocation LParenLoc; @@ -3515,30 +3516,31 @@ class CXXUnresolvedConstructExpr final CXXUnresolvedConstructExpr(QualType T, TypeSourceInfo *TSI, SourceLocation LParenLoc, ArrayRef Args, - SourceLocation RParenLoc); + SourceLocation RParenLoc, bool IsListInit); CXXUnresolvedConstructExpr(EmptyShell Empty, unsigned NumArgs) - : Expr(CXXUnresolvedConstructExprClass, Empty), TSI(nullptr) { + : Expr(CXXUnresolvedConstructExprClass, Empty) { CXXUnresolvedConstructExprBits.NumArgs = NumArgs; } public: - static CXXUnresolvedConstructExpr *Create(const ASTContext &Context, - QualType T, TypeSourceInfo *TSI, - SourceLocation LParenLoc, - ArrayRef Args, - SourceLocation RParenLoc); + static CXXUnresolvedConstructExpr * + Create(const ASTContext &Context, QualType T, TypeSourceInfo *TSI, + SourceLocation LParenLoc, ArrayRef Args, + SourceLocation RParenLoc, bool IsListInit); static CXXUnresolvedConstructExpr *CreateEmpty(const ASTContext &Context, unsigned NumArgs); /// Retrieve the type that is being constructed, as specified /// in the source code. - QualType getTypeAsWritten() const { return TSI->getType(); } + QualType getTypeAsWritten() const { return getTypeSourceInfo()->getType(); } /// Retrieve the type source information for the type being /// constructed. - TypeSourceInfo *getTypeSourceInfo() const { return TSI; } + TypeSourceInfo *getTypeSourceInfo() const { + return TypeAndInitForm.getPointer(); + } /// Retrieve the location of the left parentheses ('(') that /// precedes the argument list. @@ -3553,7 +3555,7 @@ public: /// Determine whether this expression models list-initialization. /// If so, there will be exactly one subexpression, which will be /// an InitListExpr. - bool isListInitialization() const { return LParenLoc.isInvalid(); } + bool isListInitialization() const { return TypeAndInitForm.getInt(); } /// Retrieve the number of arguments. unsigned getNumArgs() const { return CXXUnresolvedConstructExprBits.NumArgs; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 10392d294524..44a5f77fa6c2 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8147,7 +8147,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXUnresolvedConstructExpr( return CXXUnresolvedConstructExpr::Create( Importer.getToContext(), ToType, ToTypeSourceInfo, ToLParenLoc, - llvm::ArrayRef(ToArgs), ToRParenLoc); + llvm::ArrayRef(ToArgs), ToRParenLoc, E->isListInitialization()); } ExpectedStmt diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 2a9e33595013..9e1c39ff2f94 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1392,17 +1392,16 @@ ExprWithCleanups *ExprWithCleanups::Create(const ASTContext &C, return new (buffer) ExprWithCleanups(empty, numObjects); } -CXXUnresolvedConstructExpr::CXXUnresolvedConstructExpr(QualType T, - TypeSourceInfo *TSI, - SourceLocation LParenLoc, - ArrayRef Args, - SourceLocation RParenLoc) +CXXUnresolvedConstructExpr::CXXUnresolvedConstructExpr( + QualType T, TypeSourceInfo *TSI, SourceLocation LParenLoc, + ArrayRef Args, SourceLocation RParenLoc, bool IsListInit) : Expr(CXXUnresolvedConstructExprClass, T, (TSI->getType()->isLValueReferenceType() ? VK_LValue : TSI->getType()->isRValueReferenceType() ? VK_XValue : VK_PRValue), OK_Ordinary), - TSI(TSI), LParenLoc(LParenLoc), RParenLoc(RParenLoc) { + TypeAndInitForm(TSI, IsListInit), LParenLoc(LParenLoc), + RParenLoc(RParenLoc) { CXXUnresolvedConstructExprBits.NumArgs = Args.size(); auto **StoredArgs = getTrailingObjects(); for (unsigned I = 0; I != Args.size(); ++I) @@ -1411,11 +1410,12 @@ CXXUnresolvedConstructExpr::CXXUnresolvedConstructExpr(QualType T, } CXXUnresolvedConstructExpr *CXXUnresolvedConstructExpr::Create( - const ASTContext &Context, QualType T, TypeSourceInfo *TSI, SourceLocation LParenLoc, - ArrayRef Args, SourceLocation RParenLoc) { + const ASTContext &Context, QualType T, TypeSourceInfo *TSI, + SourceLocation LParenLoc, ArrayRef Args, SourceLocation RParenLoc, + bool IsListInit) { void *Mem = Context.Allocate(totalSizeToAlloc(Args.size())); - return new (Mem) - CXXUnresolvedConstructExpr(T, TSI, LParenLoc, Args, RParenLoc); + return new (Mem) CXXUnresolvedConstructExpr(T, TSI, LParenLoc, Args, + RParenLoc, IsListInit); } CXXUnresolvedConstructExpr * @@ -1426,7 +1426,7 @@ CXXUnresolvedConstructExpr::CreateEmpty(const ASTContext &Context, } SourceLocation CXXUnresolvedConstructExpr::getBeginLoc() const { - return TSI->getTypeLoc().getBeginLoc(); + return TypeAndInitForm.getPointer()->getTypeLoc().getBeginLoc(); } CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ccd605ac4ace..0617d29aed71 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1522,16 +1522,10 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); } - if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs)) { - // FIXME: CXXUnresolvedConstructExpr does not model list-initialization - // directly. We work around this by dropping the locations of the braces. - SourceRange Locs = ListInitialization - ? SourceRange() - : SourceRange(LParenOrBraceLoc, RParenOrBraceLoc); - return CXXUnresolvedConstructExpr::Create(Context, Ty.getNonReferenceType(), - TInfo, Locs.getBegin(), Exprs, - Locs.getEnd()); - } + if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs)) + return CXXUnresolvedConstructExpr::Create( + Context, Ty.getNonReferenceType(), TInfo, LParenOrBraceLoc, Exprs, + RParenOrBraceLoc, ListInitialization); // C++ [expr.type.conv]p1: // If the expression list is a parenthesized single expression, the type diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 5b16765e2971..032f685a872e 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2006,9 +2006,10 @@ ASTStmtReader::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E) { Record.skipInts(1); for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) E->setArg(I, Record.readSubExpr()); - E->TSI = readTypeSourceInfo(); + E->TypeAndInitForm.setPointer(readTypeSourceInfo()); E->setLParenLoc(readSourceLocation()); E->setRParenLoc(readSourceLocation()); + E->TypeAndInitForm.setInt(Record.readInt()); } void ASTStmtReader::VisitOverloadExpr(OverloadExpr *E) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 9f10c01390d1..021765b69ede 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1921,6 +1921,7 @@ ASTStmtWriter::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E) { Record.AddTypeSourceInfo(E->getTypeSourceInfo()); Record.AddSourceLocation(E->getLParenLoc()); Record.AddSourceLocation(E->getRParenLoc()); + Record.push_back(E->isListInitialization()); Code = serialization::EXPR_CXX_UNRESOLVED_CONSTRUCT; } diff --git a/clang/test/Coverage/unresolved-ctor-expr.cpp b/clang/test/Coverage/unresolved-ctor-expr.cpp new file mode 100644 index 000000000000..10286c79f569 --- /dev/null +++ b/clang/test/Coverage/unresolved-ctor-expr.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -fcoverage-mapping %s +// expected-no-diagnostics + +// GH62105 demonstrated a crash with this example code when calculating +// coverage mapping because some source location information was being dropped. +// Demonstrate that we do not crash on this code. +namespace std { template class initializer_list {}; } + +template struct T { + T(std::initializer_list, int = int()); + bool b; +}; + +template struct S1 { + static void foo() { + class C; + (void)(0 ? T{} : T{}); + } +}; + +void bar() { + S1::foo(); +} +