Reland [clang] Make predefined expressions string literals under -fms-extensions
authorArthur Eubanks <aeubanks@google.com>
Sun, 7 May 2023 18:24:48 +0000 (11:24 -0700)
committerArthur Eubanks <aeubanks@google.com>
Wed, 10 May 2023 17:54:58 +0000 (10:54 -0700)
MSVC makes these string literals [1][2].

[1] https://godbolt.org/z/6vnTzbExx
[2] https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170

Fixes #114

Initial commit didn't check if there was a function name when stepping through expressions ignoring parens.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D146764

15 files changed:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Expr.h
clang/include/clang/AST/IgnoreExpr.h
clang/include/clang/AST/Stmt.h
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Expr.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/Modules/predefined.cpp [new file with mode: 0644]
clang/test/Sema/ms_predefined_expr.cpp [new file with mode: 0644]
clang/test/SemaCXX/predefined-expr-msvc.cpp [new file with mode: 0644]

index c0820bd01fd5f8d171b8f0b6648eb87f4370fbd8..8b6232a6b9e6f2fc8965f4810c02248b18bad79e 100644 (file)
@@ -438,6 +438,9 @@ Bug Fixes to C++ Support
 - Fix a crash when expanding a pack as the index of a subscript expression.
 - Fix handling of constexpr dynamic memory allocations in template
   arguments. (`#62462 <https://github.com/llvm/llvm-project/issues/62462>`_)
+- Some predefined expressions are now treated as string literals in MSVC
+  compatibility mode.
+  (`#114 <https://github.com/llvm/llvm-project/issues/114>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
index 0ab778e5d8cd3352ae9658eb8dbe44f60eb381fd..db4316d8faf1fcb7ccc2af4ad80464e1db20c1af 100644 (file)
@@ -1992,7 +1992,7 @@ public:
 
 private:
   PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK,
-                 StringLiteral *SL);
+                 bool IsTransparent, StringLiteral *SL);
 
   explicit PredefinedExpr(EmptyShell Empty, bool HasFunctionName);
 
@@ -2007,8 +2007,12 @@ private:
 
 public:
   /// Create a PredefinedExpr.
+  ///
+  /// If IsTransparent, the PredefinedExpr is transparently handled as a
+  /// StringLiteral.
   static PredefinedExpr *Create(const ASTContext &Ctx, SourceLocation L,
-                                QualType FNTy, IdentKind IK, StringLiteral *SL);
+                                QualType FNTy, IdentKind IK, bool IsTransparent,
+                                StringLiteral *SL);
 
   /// Create an empty PredefinedExpr.
   static PredefinedExpr *CreateEmpty(const ASTContext &Ctx,
@@ -2018,6 +2022,8 @@ public:
     return static_cast<IdentKind>(PredefinedExprBits.Kind);
   }
 
+  bool isTransparent() const { return PredefinedExprBits.IsTransparent; }
+
   SourceLocation getLocation() const { return PredefinedExprBits.Loc; }
   void setLocation(SourceLocation L) { PredefinedExprBits.Loc = L; }
 
index f8d2d6c7d00c08e8abccd5ed1e73920229baf595..917bada61fa6fddda6868b68b0425f132824734c 100644 (file)
@@ -166,6 +166,11 @@ inline Expr *IgnoreParensSingleStep(Expr *E) {
       return CE->getChosenSubExpr();
   }
 
+  else if (auto *PE = dyn_cast<PredefinedExpr>(E)) {
+    if (PE->isTransparent() && PE->getFunctionName())
+      return PE->getFunctionName();
+  }
+
   return E;
 }
 
index ea979d791ce7bb04f6fea25c3faddf3b93b436b8..e466aa1755daf36030a8ba19872c379ab16ea20d 100644 (file)
@@ -364,6 +364,10 @@ protected:
     /// for the predefined identifier.
     unsigned HasFunctionName : 1;
 
+    /// True if this PredefinedExpr should be treated as a StringLiteral (for
+    /// MSVC compatibility).
+    unsigned IsTransparent : 1;
+
     /// The location of this PredefinedExpr.
     SourceLocation Loc;
   };
index 87e72f000d4946c154772d6f278929fb071d51aa..2ba42f9b7376374db6be32081f318aff1e1896ac 100644 (file)
@@ -1190,6 +1190,7 @@ def MicrosoftCommentPaste : DiagGroup<"microsoft-comment-paste">;
 def MicrosoftEndOfFile : DiagGroup<"microsoft-end-of-file">;
 def MicrosoftInaccessibleBase : DiagGroup<"microsoft-inaccessible-base">;
 def MicrosoftStaticAssert : DiagGroup<"microsoft-static-assert">;
+def MicrosoftInitFromPredefined : DiagGroup<"microsoft-init-from-predefined">;
 
 // Aliases.
 def : DiagGroup<"msvc-include", [MicrosoftInclude]>;
@@ -1207,7 +1208,7 @@ def Microsoft : DiagGroup<"microsoft",
      MicrosoftFlexibleArray, MicrosoftExtraQualification, MicrosoftCast,
      MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag,
      MicrosoftCommentPaste, MicrosoftEndOfFile, MicrosoftStaticAssert,
-     MicrosoftInconsistentDllImport]>;
+     MicrosoftInitFromPredefined, MicrosoftInconsistentDllImport]>;
 
 def ClangClPch : DiagGroup<"clang-cl-pch">;
 
index 4b4ce319f6e06772ebd5db7977dadb7536b47498..0d93727ccc7b3b82eb07a325dbc27d022aa23571 100644 (file)
@@ -112,6 +112,9 @@ def err_expr_not_string_literal : Error<"expression is not a string literal">;
 def ext_predef_outside_function : Warning<
   "predefined identifier is only valid inside function">,
   InGroup<DiagGroup<"predefined-identifier-outside-function">>;
+def ext_init_from_predefined : ExtWarn<
+  "initializing an array from a '%0' predefined identifier is a Microsoft extension">,
+  InGroup<MicrosoftInitFromPredefined>;
 def warn_float_overflow : Warning<
   "magnitude of floating-point constant too large for type %0; maximum is %1">,
    InGroup<LiteralRange>;
index 44a5f77fa6c246b4c89825bf0eff1214d79e69d6..6e2566b6386092761940d12107bf3370a4460f8b 100644 (file)
@@ -7068,7 +7068,8 @@ ExpectedStmt ASTNodeImporter::VisitPredefinedExpr(PredefinedExpr *E) {
     return std::move(Err);
 
   return PredefinedExpr::Create(Importer.getToContext(), ToBeginLoc, ToType,
-                                E->getIdentKind(), ToFunctionName);
+                                E->getIdentKind(), E->isTransparent(),
+                                ToFunctionName);
 }
 
 ExpectedStmt ASTNodeImporter::VisitDeclRefExpr(DeclRefExpr *E) {
index 0ff10a511ef44acf4c3bf2144c8cd38d7e7cc736..588a7ad322b9a348b83adc4f254a5f7361d6c6be 100644 (file)
@@ -663,13 +663,14 @@ std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context,
 }
 
 PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK,
-                               StringLiteral *SL)
+                               bool IsTransparent, StringLiteral *SL)
     : Expr(PredefinedExprClass, FNTy, VK_LValue, OK_Ordinary) {
   PredefinedExprBits.Kind = IK;
   assert((getIdentKind() == IK) &&
          "IdentKind do not fit in PredefinedExprBitfields!");
   bool HasFunctionName = SL != nullptr;
   PredefinedExprBits.HasFunctionName = HasFunctionName;
+  PredefinedExprBits.IsTransparent = IsTransparent;
   PredefinedExprBits.Loc = L;
   if (HasFunctionName)
     setFunctionName(SL);
@@ -683,11 +684,11 @@ PredefinedExpr::PredefinedExpr(EmptyShell Empty, bool HasFunctionName)
 
 PredefinedExpr *PredefinedExpr::Create(const ASTContext &Ctx, SourceLocation L,
                                        QualType FNTy, IdentKind IK,
-                                       StringLiteral *SL) {
+                                       bool IsTransparent, StringLiteral *SL) {
   bool HasFunctionName = SL != nullptr;
   void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(HasFunctionName),
                            alignof(PredefinedExpr));
-  return new (Mem) PredefinedExpr(L, FNTy, IK, SL);
+  return new (Mem) PredefinedExpr(L, FNTy, IK, IsTransparent, SL);
 }
 
 PredefinedExpr *PredefinedExpr::CreateEmpty(const ASTContext &Ctx,
index 2e463346e70ff52bbf69153d9ea03bcf5ae0cf14..e7a555b991bf05343afe59b5fa9c27c494d88be3 100644 (file)
@@ -3579,7 +3579,8 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
     }
   }
 
-  return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
+  return PredefinedExpr::Create(Context, Loc, ResTy, IK, LangOpts.MicrosoftExt,
+                                SL);
 }
 
 ExprResult Sema::BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
index 3db70223bb8bae83c1c3ef24bc91e54bfe8371da..4777f2d37b77eaf6ab66fc60ded26c321e3c2401 100644 (file)
@@ -174,6 +174,8 @@ static void updateStringLiteralType(Expr *E, QualType Ty) {
       E = GSE->getResultExpr();
     } else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(E)) {
       E = CE->getChosenSubExpr();
+    } else if (PredefinedExpr *PE = dyn_cast<PredefinedExpr>(E)) {
+      E = PE->getFunctionName();
     } else {
       llvm_unreachable("unexpected expr in string literal init");
     }
@@ -8508,6 +8510,15 @@ ExprResult InitializationSequence::Perform(Sema &S,
         << Init->getSourceRange();
   }
 
+  if (S.getLangOpts().MicrosoftExt && Args.size() == 1 &&
+      isa<PredefinedExpr>(Args[0]) && Entity.getType()->isArrayType()) {
+    // Produce a Microsoft compatibility warning when initializing from a
+    // predefined expression since MSVC treats predefined expressions as string
+    // literals.
+    Expr *Init = Args[0];
+    S.Diag(Init->getBeginLoc(), diag::ext_init_from_predefined) << Init;
+  }
+
   // OpenCL v2.0 s6.13.11.1. atomic variables can be initialized in global scope
   QualType ETy = Entity.getType();
   bool HasGlobalAS = ETy.hasAddressSpace() &&
index cfe0dd121f5f7fb5edba15e4127dbce798d8778a..a96b1a2fa4f31e67026da14be00389f9a00bdd03 100644 (file)
@@ -582,6 +582,7 @@ void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
   bool HasFunctionName = Record.readInt();
   E->PredefinedExprBits.HasFunctionName = HasFunctionName;
   E->PredefinedExprBits.Kind = Record.readInt();
+  E->PredefinedExprBits.IsTransparent = Record.readInt();
   E->setLocation(readSourceLocation());
   if (HasFunctionName)
     E->setFunctionName(cast<StringLiteral>(Record.readSubExpr()));
index b3fa1cb3d5dec33a78e6094c1c1f607bcbfe1689..90c30fce0a8e77b22a750d1b1be53a461fdd4e5f 100644 (file)
@@ -593,6 +593,7 @@ void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
   bool HasFunctionName = E->getFunctionName() != nullptr;
   Record.push_back(HasFunctionName);
   Record.push_back(E->getIdentKind()); // FIXME: stable encoding
+  Record.push_back(E->isTransparent());
   Record.AddSourceLocation(E->getLocation());
   if (HasFunctionName)
     Record.AddStmt(E->getFunctionName());
diff --git a/clang/test/Modules/predefined.cpp b/clang/test/Modules/predefined.cpp
new file mode 100644 (file)
index 0000000..fbe0c4e
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -x c++ -std=c++20 -emit-module-interface a.h -o a.pcm -fms-extensions -verify
+// RUN: %clang_cc1 -std=c++20 a.cpp -fmodule-file=A=a.pcm -fms-extensions -fsyntax-only -verify
+
+//--- a.h
+
+// expected-no-diagnostics
+
+export module A;
+
+export template <typename T>
+void f() {
+    char a[] = __func__;
+}
+
+//--- a.cpp
+
+// expected-warning@a.h:8 {{initializing an array from a '__func__' predefined identifier is a Microsoft extension}}
+
+import A;
+
+void g() {
+    f<int>(); // expected-note {{in instantiation of function template specialization 'f<int>' requested here}}
+}
diff --git a/clang/test/Sema/ms_predefined_expr.cpp b/clang/test/Sema/ms_predefined_expr.cpp
new file mode 100644 (file)
index 0000000..be56c59
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 %s -fsyntax-only -Wmicrosoft -verify -fms-extensions
+
+void f() {
+ const char a[] = __FUNCTION__; // expected-warning{{initializing an array from a '__FUNCTION__' predefined identifier is a Microsoft extension}}
+ const char b[] = __FUNCDNAME__; // expected-warning{{initializing an array from a '__FUNCDNAME__' predefined identifier is a Microsoft extension}}
+ const char c[] = __FUNCSIG__; // expected-warning{{initializing an array from a '__FUNCSIG__' predefined identifier is a Microsoft extension}}
+ const char d[] = __func__; // expected-warning{{initializing an array from a '__func__' predefined identifier is a Microsoft extension}}
+ const char e[] = __PRETTY_FUNCTION__; // expected-warning{{initializing an array from a '__PRETTY_FUNCTION__' predefined identifier is a Microsoft extension}}
+}
diff --git a/clang/test/SemaCXX/predefined-expr-msvc.cpp b/clang/test/SemaCXX/predefined-expr-msvc.cpp
new file mode 100644 (file)
index 0000000..a2560f2
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify -fms-extensions
+
+// expected-no-diagnostics
+
+struct StringRef {
+  StringRef(const char *);
+};
+template <typename T>
+StringRef getTypeName() {
+  StringRef s = __func__;
+}
+