From 878e590503dff0d9097e91c2bec4409f14503b82 Mon Sep 17 00:00:00 2001 From: Arthur Eubanks Date: Sun, 7 May 2023 11:24:48 -0700 Subject: [PATCH] Reland [clang] Make predefined expressions string literals under -fms-extensions 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 --- clang/docs/ReleaseNotes.rst | 3 +++ clang/include/clang/AST/Expr.h | 10 +++++++-- clang/include/clang/AST/IgnoreExpr.h | 5 +++++ clang/include/clang/AST/Stmt.h | 4 ++++ clang/include/clang/Basic/DiagnosticGroups.td | 3 ++- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/AST/ASTImporter.cpp | 3 ++- clang/lib/AST/Expr.cpp | 7 +++--- clang/lib/Sema/SemaExpr.cpp | 3 ++- clang/lib/Sema/SemaInit.cpp | 11 ++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 1 + clang/lib/Serialization/ASTWriterStmt.cpp | 1 + clang/test/Modules/predefined.cpp | 27 ++++++++++++++++++++++++ clang/test/Sema/ms_predefined_expr.cpp | 9 ++++++++ clang/test/SemaCXX/predefined-expr-msvc.cpp | 13 ++++++++++++ 15 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 clang/test/Modules/predefined.cpp create mode 100644 clang/test/Sema/ms_predefined_expr.cpp create mode 100644 clang/test/SemaCXX/predefined-expr-msvc.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c0820bd..8b6232a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 `_) +- Some predefined expressions are now treated as string literals in MSVC + compatibility mode. + (`#114 `_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 0ab778e..db4316d 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -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(PredefinedExprBits.Kind); } + bool isTransparent() const { return PredefinedExprBits.IsTransparent; } + SourceLocation getLocation() const { return PredefinedExprBits.Loc; } void setLocation(SourceLocation L) { PredefinedExprBits.Loc = L; } diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index f8d2d6c..917bada 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -166,6 +166,11 @@ inline Expr *IgnoreParensSingleStep(Expr *E) { return CE->getChosenSubExpr(); } + else if (auto *PE = dyn_cast(E)) { + if (PE->isTransparent() && PE->getFunctionName()) + return PE->getFunctionName(); + } + return E; } diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index ea979d7..e466aa1 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -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; }; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 87e72f0..2ba42f9 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -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">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4b4ce31..0d93727 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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>; +def ext_init_from_predefined : ExtWarn< + "initializing an array from a '%0' predefined identifier is a Microsoft extension">, + InGroup; def warn_float_overflow : Warning< "magnitude of floating-point constant too large for type %0; maximum is %1">, InGroup; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 44a5f77..6e2566b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -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) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 0ff10a51..588a7ad 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -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(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, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2e46334..e7a555b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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, diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 3db7022..4777f2d 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -174,6 +174,8 @@ static void updateStringLiteralType(Expr *E, QualType Ty) { E = GSE->getResultExpr(); } else if (ChooseExpr *CE = dyn_cast(E)) { E = CE->getChosenSubExpr(); + } else if (PredefinedExpr *PE = dyn_cast(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(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() && diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index cfe0dd1..a96b1a2 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -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(Record.readSubExpr())); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index b3fa1cb..90c30fc 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -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 index 0000000..fbe0c4e --- /dev/null +++ b/clang/test/Modules/predefined.cpp @@ -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 +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(); // expected-note {{in instantiation of function template specialization 'f' requested here}} +} diff --git a/clang/test/Sema/ms_predefined_expr.cpp b/clang/test/Sema/ms_predefined_expr.cpp new file mode 100644 index 0000000..be56c59 --- /dev/null +++ b/clang/test/Sema/ms_predefined_expr.cpp @@ -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 index 0000000..a2560f2 --- /dev/null +++ b/clang/test/SemaCXX/predefined-expr-msvc.cpp @@ -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 +StringRef getTypeName() { + StringRef s = __func__; +} + -- 2.7.4