Author: Emery Conrad Date: 2026-09-16T09:49:45+03:00 New Revision: 5835ef200a1fdd0aa92578ee824772eea4143279
URL: https://github.com/llvm/llvm-project/commit/5835ef200a1fdd0aa92578ee824772eea4143279 DIFF: https://github.com/llvm/llvm-project/commit/5835ef200a1fdd0aa92578ee824772eea4143279.diff LOG: [clang] Fix Itanium mangler crash on lambdas in top-level statements (#217041) `clang-repl` asserts when a lambda in a top-level statement needs a mangled name, for example `ns::call([]{});`. Since #84150 a `TopLevelStmtDecl` is a `DeclContext`, and `manglePrefix` casts it to `NamedDecl`. Treat `TopLevelStmtDecl` as a local container and give it a synthesized internal encoding (`L9__stmt__0v`), like `Ub_` for block literals. Entities in a top-level statement then mangle as `<local-name>`s with discriminators, so same-named locals in two statements do not collide. Each `TopLevelStmtDecl` gets an ordinal at creation, in parse order; it is serialized. Tests: `Interpreter/lambda-top-level-stmt.cpp` (crashes without the fix) and `CodeGenCXX/top-level-stmt-local-names.cpp` (two lambdas, two same-named local classes). The interpreter test is `UNSUPPORTED: system-windows`: MSVC compat enables `-fdelayed-template-parsing`, and clang-repl asserts on any late-parsed template in a later fragment, lambda or not. Co-authored-by: Emery Conrad <[email protected]> Added: clang/test/CodeGenCXX/top-level-stmt-local-names.cpp clang/test/Interpreter/lambda-top-level-stmt.cpp Modified: clang/include/clang/AST/ASTContext.h clang/include/clang/AST/Decl.h clang/lib/AST/Decl.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 5bb132f03db71..8d48c2b7c2cba 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -3658,6 +3658,9 @@ class ASTContext : public RefCountedBase<ASTContext> { void setStaticLocalNumber(const VarDecl *VD, unsigned Number); unsigned getStaticLocalNumber(const VarDecl *VD) const; + /// Ordinal for the next TopLevelStmtDecl; counts created and loaded ones. + unsigned NumTopLevelStmtDecls = 0; + bool hasSeenTypeAwareOperatorNewOrDelete() const { return !TypeAwareOperatorNewAndDeletes.empty(); } diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index eafeeecac7794..c64dc8c02d40e 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4773,6 +4773,8 @@ class TopLevelStmtDecl : public Decl, public DeclContext { Stmt *Statement = nullptr; bool IsSemiMissing = false; + /// Position among all top-level statements of the session, in parse order. + unsigned Ordinal = 0; TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S) : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {} @@ -4789,6 +4791,7 @@ class TopLevelStmtDecl : public Decl, public DeclContext { void setStmt(Stmt *S); bool isSemiMissing() const { return IsSemiMissing; } void setSemiMissing(bool Missing = true) { IsSemiMissing = Missing; } + unsigned getOrdinal() const { return Ordinal; } static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == TopLevelStmt; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 34f5d0abe5f74..8c1418625bf33 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5938,7 +5938,9 @@ TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, Stmt *Statement) { SourceLocation Loc = Statement ? Statement->getBeginLoc() : SourceLocation(); DeclContext *DC = C.getTranslationUnitDecl(); - return new (C, DC) TopLevelStmtDecl(DC, Loc, Statement); + auto *D = new (C, DC) TopLevelStmtDecl(DC, Loc, Statement); + D->Ordinal = C.NumTopLevelStmtDecls++; + return D; } TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C, diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 3a3cde3448f44..af2ffaca4a6b2 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -45,7 +45,8 @@ namespace UnsupportedItaniumManglingKind = namespace { static bool isLocalContainerContext(const DeclContext *DC) { - return isa<FunctionDecl, ObjCMethodDecl, BlockDecl, CXXExpansionStmtDecl>(DC); + return isa<FunctionDecl, ObjCMethodDecl, BlockDecl, CXXExpansionStmtDecl, + TopLevelStmtDecl>(DC); } static const FunctionDecl *getStructor(const FunctionDecl *fn) { @@ -516,6 +517,7 @@ class CXXNameMangler { ArrayRef<StringRef> AdditionalAbiTags = {}); void mangleBlockForPrefix(const BlockDecl *Block); void mangleUnqualifiedBlock(const BlockDecl *Block); + void mangleTopLevelStmtEncoding(const TopLevelStmtDecl *D); void mangleTemplateParamDecl(const NamedDecl *Decl); void mangleTemplateParameterList(const TemplateParameterList *Params); void mangleTypeConstraint(TemplateName Concept, @@ -1906,6 +1908,8 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD, mangleObjCMethodName(MD); } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) { mangleBlockForPrefix(BD); + } else if (const auto *TLSD = dyn_cast<TopLevelStmtDecl>(DC)) { + mangleTopLevelStmtEncoding(TLSD); } else { mangleFunctionEncoding(getParentOfLocalEntity(DC)); } @@ -2043,6 +2047,13 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { Out << '_'; } +void CXXNameMangler::mangleTopLevelStmtEncoding(const TopLevelStmtDecl *D) { + // Numbered internal function, like Ub_ for blocks: locals get <local-name>s. + SmallString<16> Name("__stmt__"); + Name += llvm::utostr(D->getOrdinal()); + Out << 'L' << Name.size() << Name << 'v'; +} + // <template-param-decl> // ::= Ty # template type parameter // ::= Tk <concept name> [<template-args>] # constrained type parameter diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index d4c47a81ed32f..7fc585b12153b 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1778,6 +1778,10 @@ void ASTDeclReader::VisitFileScopeAsmDecl(FileScopeAsmDecl *AD) { void ASTDeclReader::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { VisitDecl(D); + D->Ordinal = Record.readInt(); + // Keep new statements numbered after the ones loaded from an AST file. + ASTContext &Ctx = Reader.getContext(); + Ctx.NumTopLevelStmtDecls = std::max(Ctx.NumTopLevelStmtDecls, D->Ordinal + 1); D->Statement = Record.readStmt(); } diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 6f67acf9a6e7e..73fe1ee990eb9 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1437,6 +1437,7 @@ void ASTDeclWriter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) { void ASTDeclWriter::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { VisitDecl(D); + Record.push_back(D->getOrdinal()); Record.AddStmt(D->getStmt()); Code = serialization::DECL_TOP_LEVEL_STMT_DECL; } diff --git a/clang/test/CodeGenCXX/top-level-stmt-local-names.cpp b/clang/test/CodeGenCXX/top-level-stmt-local-names.cpp new file mode 100644 index 0000000000000..c94967c25ffa6 --- /dev/null +++ b/clang/test/CodeGenCXX/top-level-stmt-local-names.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++20 -fincremental-extensions -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s +// Entities in a top-level statement mangle as <local-name>s of that statement, +// so same-named locals in diff erent top-level statements do not collide. + +extern "C" int printf(const char *, ...); +namespace ns { template <typename F> void call(F f) { f(); } } + +ns::call([] { printf("ONE\n"); }); +ns::call([] { printf("TWO\n"); }); +// CHECK-DAG: define internal void @"_ZN2ns4callIZL9__stmt__0vE3$_0EEvT_" +// CHECK-DAG: define internal void @"_ZN2ns4callIZL9__stmt__1vE3$_1EEvT_" +// CHECK-DAG: define internal void @"_ZZL9__stmt__0vENK3$_0clEv" +// CHECK-DAG: define internal void @"_ZZL9__stmt__1vENK3$_1clEv" + +{ struct S { int f() { return 1; } }; printf("%d\n", S().f()); } +{ struct S { int f() { return 2; } }; printf("%d\n", S().f()); } +// CHECK-DAG: define internal noundef i32 @_ZZL9__stmt__2vEN1S1fEv +// CHECK-DAG: define internal noundef i32 @_ZZL9__stmt__3vEN1S1fEv diff --git a/clang/test/Interpreter/lambda-top-level-stmt.cpp b/clang/test/Interpreter/lambda-top-level-stmt.cpp new file mode 100644 index 0000000000000..9161b3b8732e5 --- /dev/null +++ b/clang/test/Interpreter/lambda-top-level-stmt.cpp @@ -0,0 +1,20 @@ +// Lambdas in top-level statements used to crash the Itanium mangler, which +// cast their TopLevelStmtDecl context to NamedDecl. Two lambdas verify that +// the closure types still mangle to distinct names. +// REQUIRES: host-supports-jit +// MSVC compat enables -fdelayed-template-parsing, which hits a pre-existing +// Sema::PushDeclContext assert on any late-parsed template instantiation in +// incremental mode, independent of this fix. +// UNSUPPORTED: system-windows +// RUN: cat %s | clang-repl | FileCheck %s + +extern "C" int printf(const char *, ...); + +namespace ns { template <typename F> void call(F f) { f(); } } + +ns::call([] { printf("ONE\n"); }); +// CHECK: ONE +ns::call([] { printf("TWO\n"); }); +// CHECK-NEXT: TWO + +%quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
