Skip to content

Commit

Permalink
Reland [clang] Make predefined expressions string literals under -fms…
Browse files Browse the repository at this point in the history
…-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
  • Loading branch information
aeubanks committed May 10, 2023
1 parent 2e2e110 commit 878e590
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 8 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1992,7 +1992,7 @@ class PredefinedExpr final

private:
PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK,
StringLiteral *SL);
bool IsTransparent, StringLiteral *SL);

explicit PredefinedExpr(EmptyShell Empty, bool HasFunctionName);

Expand All @@ -2007,8 +2007,12 @@ class PredefinedExpr final

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,
Expand All @@ -2018,6 +2022,8 @@ class PredefinedExpr final
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; }

Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/IgnoreExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ class alignas(void *) Stmt {
/// 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;
};
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
Expand All @@ -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">;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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() &&
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
27 changes: 27 additions & 0 deletions clang/test/Modules/predefined.cpp
Original file line number Diff line number Diff line change
@@ -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}}
}
9 changes: 9 additions & 0 deletions clang/test/Sema/ms_predefined_expr.cpp
Original file line number Diff line number Diff line change
@@ -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}}
}
13 changes: 13 additions & 0 deletions clang/test/SemaCXX/predefined-expr-msvc.cpp
Original file line number Diff line number Diff line change
@@ -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__;
}

0 comments on commit 878e590

Please sign in to comment.