Author: Mariya Podchishchaeva
Date: 2025-04-17T15:15:07+02:00
New Revision: 40417915a161e87b398f1cc3e9b7c159207abd77

URL: 
https://github.com/llvm/llvm-project/commit/40417915a161e87b398f1cc3e9b7c159207abd77
DIFF: 
https://github.com/llvm/llvm-project/commit/40417915a161e87b398f1cc3e9b7c159207abd77.diff

LOG: [clang] Implement StmtPrinter for EmbedExpr (#135957)

Tries to avoid memory leaks previously caused by saving filename by
allocating memory in the preprocessor.

Fixes https://github.com/llvm/llvm-project/issues/132641
Fixes https://github.com/llvm/llvm-project/issues/107869

---------

Co-authored-by: Aaron Ballman <aa...@aaronballman.com>

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/Expr.h
    clang/include/clang/Lex/Preprocessor.h
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/StmtPrinter.cpp
    clang/lib/Lex/PPDirectives.cpp
    clang/lib/Parse/ParseInit.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/test/Preprocessor/embed_weird.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4f640697e1817..0ea8498351c24 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -426,6 +426,9 @@ Bug Fixes in This Version
   using C++23 "deducing this" did not have a diagnostic location (#GH135522)
 
 - Fixed a crash when a ``friend`` function is redefined as deleted. (#GH135506)
+- Fixed a crash when ``#embed`` appears as a part of a failed constant
+  evaluation. The crashes were happening during diagnostics emission due to
+  unimplemented statement printer. (#GH132641)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 529c6228bfa19..a83320a7ddec2 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -4961,6 +4961,9 @@ class SourceLocExpr final : public Expr {
 /// Stores data related to a single #embed directive.
 struct EmbedDataStorage {
   StringLiteral *BinaryData;
+  // FileName string already includes braces, i.e. it is <files/my_file> for a
+  // directive #embed <files/my_file>.
+  StringRef FileName;
   size_t getDataElementCount() const { return BinaryData->getByteLength(); }
 };
 
@@ -5007,6 +5010,7 @@ class EmbedExpr final : public Expr {
   SourceLocation getEndLoc() const { return EmbedKeywordLoc; }
 
   StringLiteral *getDataStringLiteral() const { return Data->BinaryData; }
+  StringRef getFileName() const { return Data->FileName; }
   EmbedDataStorage *getData() const { return Data; }
 
   unsigned getStartingElementPos() const { return Begin; }

diff  --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index 24bb524783e93..19d54edf23faf 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2762,7 +2762,7 @@ class Preprocessor {
                             const FileEntry *LookupFromFile = nullptr);
   void HandleEmbedDirectiveImpl(SourceLocation HashLoc,
                                 const LexEmbedParametersResult &Params,
-                                StringRef BinaryContents);
+                                StringRef BinaryContents, StringRef FileName);
 
   // File inclusion.
   void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok,
@@ -3066,6 +3066,7 @@ class EmptylineHandler {
 /// preprocessor to the parser through an annotation token.
 struct EmbedAnnotationData {
   StringRef BinaryData;
+  StringRef FileName;
 };
 
 /// Registry of pragma handlers added by plugins

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fe37fd7701ce3..c167f8df28acd 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7256,7 +7256,7 @@ class Sema final : public SemaBase {
 
   // #embed
   ExprResult ActOnEmbedExpr(SourceLocation EmbedKeywordLoc,
-                            StringLiteral *BinaryData);
+                            StringLiteral *BinaryData, StringRef FileName);
 
   // Build a potentially resolved SourceLocExpr.
   ExprResult BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy,

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index aae10fd3bd885..c6c49c6c1ba4d 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1284,7 +1284,11 @@ void StmtPrinter::VisitSourceLocExpr(SourceLocExpr 
*Node) {
 }
 
 void StmtPrinter::VisitEmbedExpr(EmbedExpr *Node) {
-  llvm::report_fatal_error("Not implemented");
+  // FIXME: Embed parameters are not reflected in the AST, so there is no way 
to
+  // print them yet.
+  OS << "#embed ";
+  OS << Node->getFileName();
+  OS << NL;
 }
 
 void StmtPrinter::VisitConstantExpr(ConstantExpr *Node) {

diff  --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 8411526019f3e..49a4e24923a5e 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -3909,7 +3909,7 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool 
ForHasEmbed) {
 
 void Preprocessor::HandleEmbedDirectiveImpl(
     SourceLocation HashLoc, const LexEmbedParametersResult &Params,
-    StringRef BinaryContents) {
+    StringRef BinaryContents, StringRef FileName) {
   if (BinaryContents.empty()) {
     // If we have no binary contents, the only thing we need to emit are the
     // if_empty tokens, if any.
@@ -3940,6 +3940,7 @@ void Preprocessor::HandleEmbedDirectiveImpl(
 
   EmbedAnnotationData *Data = new (BP) EmbedAnnotationData;
   Data->BinaryData = BinaryContents;
+  Data->FileName = FileName;
 
   Toks[CurIdx].startToken();
   Toks[CurIdx].setKind(tok::annot_embed);
@@ -4049,5 +4050,16 @@ void Preprocessor::HandleEmbedDirective(SourceLocation 
HashLoc, Token &EmbedTok,
   if (Callbacks)
     Callbacks->EmbedDirective(HashLoc, Filename, isAngled, MaybeFileRef,
                               *Params);
-  HandleEmbedDirectiveImpl(HashLoc, *Params, BinaryContents);
+  // getSpelling() may return a buffer from the token itself or it may use the
+  // SmallString buffer we provided. getSpelling() may also return a string 
that
+  // is actually longer than FilenameTok.getLength(), so we first pass a
+  // locally created buffer to getSpelling() to get the string of real length
+  // and then we allocate a long living buffer because the buffer we used
+  // previously will only live till the end of this function and we need
+  // filename info to live longer.
+  void *Mem = BP.Allocate(OriginalFilename.size(), alignof(char *));
+  memcpy(Mem, OriginalFilename.data(), OriginalFilename.size());
+  StringRef FilenameToGo =
+      StringRef(static_cast<char *>(Mem), OriginalFilename.size());
+  HandleEmbedDirectiveImpl(HashLoc, *Params, BinaryContents, FilenameToGo);
 }

diff  --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 471b3eaf28287..86696329ea02e 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -451,7 +451,7 @@ ExprResult Parser::createEmbedExpr() {
 
     StringLiteral *BinaryDataArg = CreateStringLiteralFromStringRef(
         Data->BinaryData, Context.UnsignedCharTy);
-    Res = Actions.ActOnEmbedExpr(StartLoc, BinaryDataArg);
+    Res = Actions.ActOnEmbedExpr(StartLoc, BinaryDataArg, Data->FileName);
   }
   return Res;
 }

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 6830bb5c01c7d..01117e7785bc4 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -16903,9 +16903,10 @@ ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind 
Kind, QualType ResultTy,
 }
 
 ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc,
-                                StringLiteral *BinaryData) {
+                                StringLiteral *BinaryData, StringRef FileName) 
{
   EmbedDataStorage *Data = new (Context) EmbedDataStorage;
   Data->BinaryData = BinaryData;
+  Data->FileName = FileName;
   return new (Context)
       EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0,
                 Data->getDataElementCount());

diff  --git a/clang/test/Preprocessor/embed_weird.cpp 
b/clang/test/Preprocessor/embed_weird.cpp
index 9a984e40d4aa2..e44aff55c59e2 100644
--- a/clang/test/Preprocessor/embed_weird.cpp
+++ b/clang/test/Preprocessor/embed_weird.cpp
@@ -136,3 +136,43 @@ constexpr struct HasChar c = {
                                 c-error {{constexpr initializer evaluates to 
255 which is not exactly representable in type 'signed char'}}
 
 };
+
+#if __cplusplus
+namespace std {
+typedef decltype(sizeof(int)) size_t;
+
+template <class _E> class initializer_list {
+  const _E *__begin_;
+  size_t __size_;
+
+  constexpr initializer_list(const _E *__b, size_t __s)
+      : __begin_(__b), __size_(__s) {}
+
+public:
+  constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
+};
+} // namespace std
+
+class S2 {
+public:
+  constexpr S2(std::initializer_list<char>)  { // cxx-error {{constexpr 
constructor never produces a constant expression}}
+    1/0; // cxx-warning {{division by zero is undefined}}
+         // cxx-warning@-1 {{unused}}
+         // cxx-note@-2 4{{division by zero}}
+  }
+};
+
+
+constexpr S2 s2 { // cxx-error {{must be initialized by a constant expression}}
+                  // cxx-note-re@-1 {{in call to 'S2{{.*}} #embed <jk.txt>}}
+#embed <jk.txt> prefix(0x2c, 0x20, )limit(5)
+};
+constexpr S2 s3 {1, // cxx-error {{must be initialized by a constant 
expression}}
+                    // cxx-note-re@-1 {{in call to 'S2{{.*}} #embed "jk.txt"}}
+#embed "jk.txt"
+};
+constexpr S2 s4 { // cxx-error {{must be initialized by a constant expression}}
+                  // cxx-note-re@-1 {{in call to 'S2{{.*}}"jk"}}
+#embed "jk.txt"
+};
+#endif


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to