https://github.com/kelbon updated https://github.com/llvm/llvm-project/pull/65851
>From 2f807b312baef8c6038c2452b84232acb6d6d2c2 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Sat, 9 Sep 2023 17:51:15 +0400 Subject: [PATCH 1/9] add define2 pp directive --- clang/include/clang/Basic/TokenKinds.def | 1 + clang/include/clang/Lex/MacroInfo.h | 19 +++++++++---------- clang/include/clang/Lex/Preprocessor.h | 2 +- clang/lib/Basic/IdentifierTable.cpp | 2 ++ clang/lib/Format/WhitespaceManager.cpp | 2 +- clang/lib/Lex/MacroInfo.cpp | 3 ++- clang/lib/Lex/PPDirectives.cpp | 16 +++++++++++----- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 45ebc200b168986..f059d809823ab42 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -115,6 +115,7 @@ PPKEYWORD(__include_macros) // C99 6.10.3 - Macro Replacement. PPKEYWORD(define) +PPKEYWORD(define2) PPKEYWORD(undef) // C99 6.10.4 - Line Control. diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 00c1c3866bbd9ca..4f0c8e987610e50 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -102,6 +102,10 @@ class MacroInfo { /// like \#define A A. bool IsDisabled : 1; + // True if 'define2' used, + // ignores 'IsDisabled' and enables expansion anyway + bool AllowRecurse : 1; + /// True if this macro is either defined in the main file and has /// been used, or if it is not defined in the main file. /// @@ -278,18 +282,13 @@ class MacroInfo { /// Return true if this macro is enabled. /// /// In other words, that we are not currently in an expansion of this macro. - bool isEnabled() const { return !IsDisabled; } - - void EnableMacro() { - assert(IsDisabled && "Cannot enable an already-enabled macro!"); - IsDisabled = false; - } + bool isEnabled() const { return AllowRecurse || !IsDisabled; } + void setAllowRecursive(bool Allow) { AllowRecurse = Allow; } + bool isAllowRecurse() const { return AllowRecurse; } - void DisableMacro() { - assert(!IsDisabled && "Cannot disable an already-disabled macro!"); - IsDisabled = true; - } + void EnableMacro() { IsDisabled = false; } + void DisableMacro() { IsDisabled = true; } /// Determine whether this macro was used for a header guard. bool isUsedForHeaderGuard() const { return UsedForHeaderGuard; } diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 9efe439bc5f2192..de121ce82fd1d7b 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2754,7 +2754,7 @@ class Preprocessor { void replayPreambleConditionalStack(); // Macro handling. - void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterHeaderGuard); + void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterHeaderGuard, bool AllowRecurse); void HandleUndefDirective(); // Conditional Inclusion. diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index afb30268f2973ce..4de3565c8c6d9a8 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -433,6 +433,8 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { unsigned Len = getLength(); if (Len < 2) return tok::pp_not_keyword; const char *Name = getNameStart(); + if (std::string_view(Name, Len) == "define2") + return tok::pp_define2; switch (HASH(Len, Name[0], Name[2])) { default: return tok::pp_not_keyword; CASE( 2, 'i', '\0', if); diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index b7bd8d27dc976b1..d8ab76d6761553e 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -743,7 +743,7 @@ void WhitespaceManager::alignConsecutiveMacros() { if (!Current || Current->isNot(tok::identifier)) return false; - if (!Current->Previous || Current->Previous->isNot(tok::pp_define)) + if (!Current->Previous || !Current->Previous->isOneOf(tok::pp_define, tok::pp_define2)) return false; // For a macro function, 0 spaces are required between the diff --git a/clang/lib/Lex/MacroInfo.cpp b/clang/lib/Lex/MacroInfo.cpp index 39bb0f44eff25ba..9c3619c7c909304 100644 --- a/clang/lib/Lex/MacroInfo.cpp +++ b/clang/lib/Lex/MacroInfo.cpp @@ -50,7 +50,7 @@ static_assert(MacroInfoSizeChecker<sizeof(void *)>::AsExpected, MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc), IsDefinitionLengthCached(false), IsFunctionLike(false), IsC99Varargs(false), IsGNUVarargs(false), IsBuiltinMacro(false), - HasCommaPasting(false), IsDisabled(false), IsUsed(false), + HasCommaPasting(false), IsDisabled(false), AllowRecurse(false), IsUsed(false), IsAllowRedefinitionsWithoutWarning(false), IsWarnIfUnused(false), UsedForHeaderGuard(false) {} @@ -157,6 +157,7 @@ LLVM_DUMP_METHOD void MacroInfo::dump() const { if (IsBuiltinMacro) Out << " builtin"; if (IsDisabled) Out << " disabled"; if (IsUsed) Out << " used"; + if (AllowRecurse) Out << " allow_recurse"; if (IsAllowRedefinitionsWithoutWarning) Out << " allow_redefinitions_without_warning"; if (IsWarnIfUnused) Out << " warn_if_unused"; diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index a4db8e7a84c07d5..4605d331eba6801 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1111,7 +1111,11 @@ void Preprocessor::HandleSkippedDirectiveWhileUsingPCH(Token &Result, if (const IdentifierInfo *II = Result.getIdentifierInfo()) { if (II->getPPKeywordID() == tok::pp_define) { return HandleDefineDirective(Result, - /*ImmediatelyAfterHeaderGuard=*/false); + /*ImmediatelyAfterHeaderGuard=*/false, /*AllowRecurse=*/false); + } + if (II->getPPKeywordID() == tok::pp_define2) { + return HandleDefineDirective(Result, + /*ImmediatelyAfterHeaderGuard=*/false, /*AllowRecurse=*/true); } if (SkippingUntilPCHThroughHeader && II->getPPKeywordID() == tok::pp_include) { @@ -1250,7 +1254,9 @@ void Preprocessor::HandleDirective(Token &Result) { // C99 6.10.3 - Macro Replacement. case tok::pp_define: - return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef); + return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef, false); + case tok::pp_define2: + return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef, true); case tok::pp_undef: return HandleUndefDirective(); @@ -3036,10 +3042,10 @@ static bool isObjCProtectedMacro(const IdentifierInfo *II) { II->isStr("__unsafe_unretained") || II->isStr("__autoreleasing"); } -/// HandleDefineDirective - Implements \#define. This consumes the entire macro +/// HandleDefineDirective - Implements \#define and define2. This consumes the entire macro /// line then lets the caller lex the next real token. void Preprocessor::HandleDefineDirective( - Token &DefineTok, const bool ImmediatelyAfterHeaderGuard) { + Token &DefineTok, const bool ImmediatelyAfterHeaderGuard, bool AllowRecurse) { ++NumDefined; Token MacroNameTok; @@ -3064,7 +3070,7 @@ void Preprocessor::HandleDefineDirective( MacroNameTok, ImmediatelyAfterHeaderGuard); if (!MI) return; - + MI->setAllowRecursive(AllowRecurse); if (MacroShadowsKeyword && !isConfigurationPattern(MacroNameTok, MI, getLangOpts())) { Diag(MacroNameTok, diag::warn_pp_macro_hides_keyword); >From 9705feebf64466c2d095d52c99fedcdc800b194c Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Sun, 10 Sep 2023 16:00:45 +0400 Subject: [PATCH 2/9] add recursion depth limit and tests --- .../include/clang/Basic/DiagnosticLexKinds.td | 2 ++ clang/include/clang/Lex/MacroInfo.h | 33 ++++++++++++------- clang/lib/Lex/MacroInfo.cpp | 4 +-- clang/lib/Lex/TokenLexer.cpp | 3 +- .../test/Preprocessor/macro_vaopt_expand.cpp | 20 +++++++++++ clang/test/Preprocessor/recursive_macro.cpp | 13 ++++++++ 6 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 clang/test/Preprocessor/recursive_macro.cpp diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 940cca67368492f..f940fa01a3d2f38 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -440,6 +440,8 @@ def err_pp_missing_lparen_in_vaopt_use : Error< def err_pp_vaopt_nested_use : Error< "__VA_OPT__ cannot be nested within its own replacement tokens">; +def err_pp_macro_recursion_depth_limit_exceeded : Error< + "macro recursion depth limit exceeded">; def err_vaopt_paste_at_start : Error< "'##' cannot appear at start of __VA_OPT__ argument">; diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 4f0c8e987610e50..267292083058645 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -65,6 +65,12 @@ class MacroInfo { /// Length in characters of the macro definition. mutable unsigned DefinitionLength; + + enum : uint16_t { recursion_depth_limit = 16'000 }; + /// recursion depth, + /// > 0 if we have started an expansion of this macro already. + /// for 'define' max is 1, for 'define2' max is depth limit + uint16_t Depth = 0; mutable bool IsDefinitionLengthCached : 1; /// True if this macro is function-like, false if it is object-like. @@ -96,14 +102,7 @@ class MacroInfo { //===--------------------------------------------------------------------===// // State that changes as the macro is used. - /// True if we have started an expansion of this macro already. - /// - /// This disables recursive expansion, which would be quite bad for things - /// like \#define A A. - bool IsDisabled : 1; - - // True if 'define2' used, - // ignores 'IsDisabled' and enables expansion anyway + // True if 'define2' used, enables expansion anyway bool AllowRecurse : 1; /// True if this macro is either defined in the main file and has @@ -282,13 +281,23 @@ class MacroInfo { /// Return true if this macro is enabled. /// /// In other words, that we are not currently in an expansion of this macro. - bool isEnabled() const { return AllowRecurse || !IsDisabled; } + bool isEnabled() const { + // macro disabled if depth exceeds and stops infinite recursion + if (AllowRecurse) + return Depth < recursion_depth_limit; + return Depth == 0; + } void setAllowRecursive(bool Allow) { AllowRecurse = Allow; } bool isAllowRecurse() const { return AllowRecurse; } - void EnableMacro() { IsDisabled = false; } - - void DisableMacro() { IsDisabled = true; } + void EnableMacro() { + assert(Depth != 0 && "Cannot enable not disabled macro"); + --Depth; + } + // returns false if max recursion depth exceeded + [[nodiscard]] bool TryDisableMacro() { + return ++Depth < recursion_depth_limit; + } /// Determine whether this macro was used for a header guard. bool isUsedForHeaderGuard() const { return UsedForHeaderGuard; } diff --git a/clang/lib/Lex/MacroInfo.cpp b/clang/lib/Lex/MacroInfo.cpp index 9c3619c7c909304..110e3232b66dc14 100644 --- a/clang/lib/Lex/MacroInfo.cpp +++ b/clang/lib/Lex/MacroInfo.cpp @@ -50,7 +50,7 @@ static_assert(MacroInfoSizeChecker<sizeof(void *)>::AsExpected, MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc), IsDefinitionLengthCached(false), IsFunctionLike(false), IsC99Varargs(false), IsGNUVarargs(false), IsBuiltinMacro(false), - HasCommaPasting(false), IsDisabled(false), AllowRecurse(false), IsUsed(false), + HasCommaPasting(false), AllowRecurse(false), IsUsed(false), IsAllowRedefinitionsWithoutWarning(false), IsWarnIfUnused(false), UsedForHeaderGuard(false) {} @@ -155,7 +155,7 @@ LLVM_DUMP_METHOD void MacroInfo::dump() const { // FIXME: Dump locations. Out << "MacroInfo " << this; if (IsBuiltinMacro) Out << " builtin"; - if (IsDisabled) Out << " disabled"; + if (!isEnabled()) Out << " disabled"; if (IsUsed) Out << " used"; if (AllowRecurse) Out << " allow_recurse"; if (IsAllowRedefinitionsWithoutWarning) diff --git a/clang/lib/Lex/TokenLexer.cpp b/clang/lib/Lex/TokenLexer.cpp index 856d5682727fe3d..c51aec4fc17110d 100644 --- a/clang/lib/Lex/TokenLexer.cpp +++ b/clang/lib/Lex/TokenLexer.cpp @@ -88,7 +88,8 @@ void TokenLexer::Init(Token &Tok, SourceLocation ELEnd, MacroInfo *MI, // Mark the macro as currently disabled, so that it is not recursively // expanded. The macro must be disabled only after argument pre-expansion of // function-like macro arguments occurs. - Macro->DisableMacro(); + if (!Macro->TryDisableMacro()) + PP.Diag(Tok, diag::err_pp_macro_recursion_depth_limit_exceeded); } /// Create a TokenLexer for the specified token stream. This does not diff --git a/clang/test/Preprocessor/macro_vaopt_expand.cpp b/clang/test/Preprocessor/macro_vaopt_expand.cpp index 5eb0facb83f7364..9766b081516fd09 100644 --- a/clang/test/Preprocessor/macro_vaopt_expand.cpp +++ b/clang/test/Preprocessor/macro_vaopt_expand.cpp @@ -148,3 +148,23 @@ #undef F #undef G + +#define merge_all_expand2(a, b) a ## b +#define merge_all_expand(a, b) merge_all_expand2(a, b) +#define2 concat_all(head, ...) merge_all_expand(head, __VA_OPT__(concat_all(__VA_ARGS__))) +29: concat_all(aa, bb, cc) +30: [concat_all()] +// CHECK: 29: aabbcc +// CHECK: 30: [] + +#undef merge_all_expand +#undef merge_all_expand2 +#undef concat_all + +#define2 reverse(head, ...) __VA_OPT__(reverse(__VA_ARGS__) , ) head + +31: reverse(1, 2, 3) + +// CHECK: 31: 3, 2, 1 + +#undef reverse diff --git a/clang/test/Preprocessor/recursive_macro.cpp b/clang/test/Preprocessor/recursive_macro.cpp new file mode 100644 index 000000000000000..0fee436bf4d9ce0 --- /dev/null +++ b/clang/test/Preprocessor/recursive_macro.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 %s -Eonly -std=c++11 -pedantic -verify + +#define2 A A + +A //expected-error {{macro recursion depth limit exceeded}} + +#undef A + +#define2 boom(x) boom(x) + +boom(5) //expected-error {{macro recursion depth limit exceeded}} + +#undef boom >From 224a84f6922bf862cc4c04e79e5ab2de996ed273 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Mon, 11 Sep 2023 12:03:36 +0400 Subject: [PATCH 3/9] rewrite Lexer recursion to loop --- clang/lib/Lex/Lexer.cpp | 32 +++++++++---------- clang/lib/Lex/PPMacroExpansion.cpp | 2 ++ .../test/Preprocessor/macro_vaopt_expand.cpp | 4 +-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 37c3e4175d4736e..178bcc08a72e208 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -847,26 +847,26 @@ bool Lexer::isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd) { - assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); + for(SourceLocation expansionLoc; true;loc = expansionLoc) { + assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); - SourceLocation spellLoc = SM.getSpellingLoc(loc); - unsigned tokLen = MeasureTokenLength(spellLoc, SM, LangOpts); - if (tokLen == 0) - return false; + SourceLocation spellLoc = SM.getSpellingLoc(loc); + unsigned tokLen = MeasureTokenLength(spellLoc, SM, LangOpts); + if (tokLen == 0) + return false; - SourceLocation afterLoc = loc.getLocWithOffset(tokLen); - SourceLocation expansionLoc; - if (!SM.isAtEndOfImmediateMacroExpansion(afterLoc, &expansionLoc)) - return false; + SourceLocation afterLoc = loc.getLocWithOffset(tokLen); + if (!SM.isAtEndOfImmediateMacroExpansion(afterLoc, &expansionLoc)) + return false; - if (expansionLoc.isFileID()) { - // No other macro expansions. - if (MacroEnd) - *MacroEnd = expansionLoc; - return true; + if (expansionLoc.isFileID()) { + // No other macro expansions. + if (MacroEnd) + *MacroEnd = expansionLoc; + return true; + } } - - return isAtEndOfMacroExpansion(expansionLoc, SM, LangOpts, MacroEnd); + llvm_unreachable(""); } static CharSourceRange makeRangeFromFileLocs(CharSourceRange Range, diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 775cbfafa999602..a0093fca0ab0afc 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -614,6 +614,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, if (IdentifierInfo *NewII = Identifier.getIdentifierInfo()) { if (MacroInfo *NewMI = getMacroInfo(NewII)) if (!NewMI->isEnabled() || NewMI == MI) { + if (NewMI->isAllowRecurse() && NewMI == MI) + Diag(Identifier, diag::err_pp_macro_recursion_depth_limit_exceeded); Identifier.setFlag(Token::DisableExpand); // Don't warn for "#define X X" like "#define bool bool" from // stdbool.h. diff --git a/clang/test/Preprocessor/macro_vaopt_expand.cpp b/clang/test/Preprocessor/macro_vaopt_expand.cpp index 9766b081516fd09..17be324fdd71392 100644 --- a/clang/test/Preprocessor/macro_vaopt_expand.cpp +++ b/clang/test/Preprocessor/macro_vaopt_expand.cpp @@ -163,8 +163,8 @@ #define2 reverse(head, ...) __VA_OPT__(reverse(__VA_ARGS__) , ) head -31: reverse(1, 2, 3) +31: reverse(1,2,3) -// CHECK: 31: 3, 2, 1 +// CHECK: 31: 3,2,1 #undef reverse >From 1d30904e691f8354ef0a690bd208a76d450f5457 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Mon, 11 Sep 2023 13:54:55 +0400 Subject: [PATCH 4/9] more tests --- .../test/Preprocessor/macro_vaopt_expand.cpp | 20 ------------------- clang/test/Preprocessor/recursive_macro.cpp | 13 ------------ 2 files changed, 33 deletions(-) delete mode 100644 clang/test/Preprocessor/recursive_macro.cpp diff --git a/clang/test/Preprocessor/macro_vaopt_expand.cpp b/clang/test/Preprocessor/macro_vaopt_expand.cpp index 17be324fdd71392..5eb0facb83f7364 100644 --- a/clang/test/Preprocessor/macro_vaopt_expand.cpp +++ b/clang/test/Preprocessor/macro_vaopt_expand.cpp @@ -148,23 +148,3 @@ #undef F #undef G - -#define merge_all_expand2(a, b) a ## b -#define merge_all_expand(a, b) merge_all_expand2(a, b) -#define2 concat_all(head, ...) merge_all_expand(head, __VA_OPT__(concat_all(__VA_ARGS__))) -29: concat_all(aa, bb, cc) -30: [concat_all()] -// CHECK: 29: aabbcc -// CHECK: 30: [] - -#undef merge_all_expand -#undef merge_all_expand2 -#undef concat_all - -#define2 reverse(head, ...) __VA_OPT__(reverse(__VA_ARGS__) , ) head - -31: reverse(1,2,3) - -// CHECK: 31: 3,2,1 - -#undef reverse diff --git a/clang/test/Preprocessor/recursive_macro.cpp b/clang/test/Preprocessor/recursive_macro.cpp deleted file mode 100644 index 0fee436bf4d9ce0..000000000000000 --- a/clang/test/Preprocessor/recursive_macro.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang_cc1 %s -Eonly -std=c++11 -pedantic -verify - -#define2 A A - -A //expected-error {{macro recursion depth limit exceeded}} - -#undef A - -#define2 boom(x) boom(x) - -boom(5) //expected-error {{macro recursion depth limit exceeded}} - -#undef boom >From 0578cc109e029ecab84d5cf2a84f302490f60ed4 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Mon, 11 Sep 2023 13:55:51 +0400 Subject: [PATCH 5/9] test files --- .../Preprocessor/macro_infinite_recursion.cpp | 13 +++++++++ clang/test/Preprocessor/macro_recursion.cpp | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 clang/test/Preprocessor/macro_infinite_recursion.cpp create mode 100644 clang/test/Preprocessor/macro_recursion.cpp diff --git a/clang/test/Preprocessor/macro_infinite_recursion.cpp b/clang/test/Preprocessor/macro_infinite_recursion.cpp new file mode 100644 index 000000000000000..cac8e7abbec5af3 --- /dev/null +++ b/clang/test/Preprocessor/macro_infinite_recursion.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 %s -Eonly -std=c++11 -pedantic -verify + +#define2 A A + +A //expected-error {{macro recursion depth limit exceeded}} + +#undef A + +#define2 A(x) A(x) + +A(5) //expected-error {{macro recursion depth limit exceeded}} + +#undef A diff --git a/clang/test/Preprocessor/macro_recursion.cpp b/clang/test/Preprocessor/macro_recursion.cpp new file mode 100644 index 000000000000000..da3e6157efc0f60 --- /dev/null +++ b/clang/test/Preprocessor/macro_recursion.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -E %s -std=c++20 | FileCheck %s + +#define merge_all_expand2(a, b) a ## b +#define merge_all_expand(a, b) merge_all_expand2(a, b) +#define2 concat_all(head, ...) merge_all_expand(head, __VA_OPT__(concat_all(__VA_ARGS__))) +0: concat_all(aa, bb, cc) +1: [concat_all()] +// CHECK: 0: aabbcc +// CHECK: 1: [] + +#undef merge_all_expand +#undef merge_all_expand2 +#undef concat_all + +#define2 reverse(head, ...) __VA_OPT__(reverse(__VA_ARGS__) , ) head + +2: reverse(1,2,3) + +// CHECK: 2: 3,2,1 + +#undef reverse + +#define2 fold_left(op, head, ...) ( __VA_OPT__(fold_left(op, __VA_ARGS__) op) head ) + +3: fold_left(+, 1, 2, 3, 4) +// CHECK: 3: ((((4) + 3) + 2) + 1) + +#undef fold_left >From b9eb2c1b4b07f1316ec52aecdd9fd3cde7f41b67 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Mon, 11 Sep 2023 14:23:17 +0400 Subject: [PATCH 6/9] whitespaces --- clang/test/Preprocessor/macro_recursion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Preprocessor/macro_recursion.cpp b/clang/test/Preprocessor/macro_recursion.cpp index da3e6157efc0f60..a7a69e4ba08b146 100644 --- a/clang/test/Preprocessor/macro_recursion.cpp +++ b/clang/test/Preprocessor/macro_recursion.cpp @@ -16,7 +16,7 @@ 2: reverse(1,2,3) -// CHECK: 2: 3,2,1 +// CHECK: 2: 3 , 2 , 1 #undef reverse >From c500088d61a92cb13d8b9ccb7f25151c65a2fc3c Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Mon, 11 Sep 2023 16:10:20 +0400 Subject: [PATCH 7/9] whitespaces in tests --- clang/test/Preprocessor/macro_recursion.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/test/Preprocessor/macro_recursion.cpp b/clang/test/Preprocessor/macro_recursion.cpp index a7a69e4ba08b146..cd415ce1a95e463 100644 --- a/clang/test/Preprocessor/macro_recursion.cpp +++ b/clang/test/Preprocessor/macro_recursion.cpp @@ -2,7 +2,7 @@ #define merge_all_expand2(a, b) a ## b #define merge_all_expand(a, b) merge_all_expand2(a, b) -#define2 concat_all(head, ...) merge_all_expand(head, __VA_OPT__(concat_all(__VA_ARGS__))) +#define2 concat_all(head, ...)merge_all_expand(head,__VA_OPT__(concat_all(__VA_ARGS__))) 0: concat_all(aa, bb, cc) 1: [concat_all()] // CHECK: 0: aabbcc @@ -12,17 +12,17 @@ #undef merge_all_expand2 #undef concat_all -#define2 reverse(head, ...) __VA_OPT__(reverse(__VA_ARGS__) , ) head +#define2 reverse(head, ...)__VA_OPT__(reverse(__VA_ARGS__),)head 2: reverse(1,2,3) -// CHECK: 2: 3 , 2 , 1 +// CHECK: 2: 3,2,1 #undef reverse -#define2 fold_left(op, head, ...) ( __VA_OPT__(fold_left(op, __VA_ARGS__) op) head ) +#define2 fold_left(op, head, ...)(__VA_OPT__(fold_left(op,__VA_ARGS__)op)head) 3: fold_left(+, 1, 2, 3, 4) -// CHECK: 3: ((((4) + 3) + 2) + 1) +// CHECK: 3: ((((4)+3)+2)+1) #undef fold_left >From 3e7be9669b72a77b4c0b98c822dcb56ffbd20923 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Tue, 12 Sep 2023 01:53:45 +0400 Subject: [PATCH 8/9] remove redundant HandleDefineDirective argument --- clang/include/clang/Lex/MacroInfo.h | 23 ++++++++++++----------- clang/include/clang/Lex/Preprocessor.h | 2 +- clang/lib/Lex/PPDirectives.cpp | 15 +++++---------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 267292083058645..54b1d7193dbe613 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -66,11 +66,9 @@ class MacroInfo { /// Length in characters of the macro definition. mutable unsigned DefinitionLength; - enum : uint16_t { recursion_depth_limit = 16'000 }; - /// recursion depth, - /// > 0 if we have started an expansion of this macro already. - /// for 'define' max is 1, for 'define2' max is depth limit - uint16_t Depth = 0; + /// True if 'define2' used, enables expansion anyway + bool AllowRecurse : 1; + mutable bool IsDefinitionLengthCached : 1; /// True if this macro is function-like, false if it is object-like. @@ -102,9 +100,6 @@ class MacroInfo { //===--------------------------------------------------------------------===// // State that changes as the macro is used. - // True if 'define2' used, enables expansion anyway - bool AllowRecurse : 1; - /// True if this macro is either defined in the main file and has /// been used, or if it is not defined in the main file. /// @@ -120,6 +115,12 @@ class MacroInfo { /// Whether this macro was used as header guard. bool UsedForHeaderGuard : 1; + enum : uint16_t { recursion_depth_limit = 16'000 }; + /// recursion depth, + /// > 0 if we have started an expansion of this macro already. + /// for 'define' max is 1, for 'define2' max is depth limit + uint16_t Depth = 0; + // Only the Preprocessor gets to create these. MacroInfo(SourceLocation DefLoc); @@ -283,9 +284,7 @@ class MacroInfo { /// In other words, that we are not currently in an expansion of this macro. bool isEnabled() const { // macro disabled if depth exceeds and stops infinite recursion - if (AllowRecurse) - return Depth < recursion_depth_limit; - return Depth == 0; + return AllowRecurse ? Depth < recursion_depth_limit : Depth == 0; } void setAllowRecursive(bool Allow) { AllowRecurse = Allow; } bool isAllowRecurse() const { return AllowRecurse; } @@ -296,8 +295,10 @@ class MacroInfo { } // returns false if max recursion depth exceeded [[nodiscard]] bool TryDisableMacro() { + assert((AllowRecurse || Depth == 0) && "Cannot disable an already-disabled macro!"); return ++Depth < recursion_depth_limit; } + /// Determine whether this macro was used for a header guard. bool isUsedForHeaderGuard() const { return UsedForHeaderGuard; } diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index de121ce82fd1d7b..9efe439bc5f2192 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2754,7 +2754,7 @@ class Preprocessor { void replayPreambleConditionalStack(); // Macro handling. - void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterHeaderGuard, bool AllowRecurse); + void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterHeaderGuard); void HandleUndefDirective(); // Conditional Inclusion. diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 4605d331eba6801..33e7efd4e206568 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1109,13 +1109,9 @@ class Preprocessor::ResetMacroExpansionHelper { void Preprocessor::HandleSkippedDirectiveWhileUsingPCH(Token &Result, SourceLocation HashLoc) { if (const IdentifierInfo *II = Result.getIdentifierInfo()) { - if (II->getPPKeywordID() == tok::pp_define) { + if (II->getPPKeywordID() == tok::pp_define || II->getPPKeywordID() == tok::pp_define2) { return HandleDefineDirective(Result, - /*ImmediatelyAfterHeaderGuard=*/false, /*AllowRecurse=*/false); - } - if (II->getPPKeywordID() == tok::pp_define2) { - return HandleDefineDirective(Result, - /*ImmediatelyAfterHeaderGuard=*/false, /*AllowRecurse=*/true); + /*ImmediatelyAfterHeaderGuard=*/false); } if (SkippingUntilPCHThroughHeader && II->getPPKeywordID() == tok::pp_include) { @@ -1254,9 +1250,8 @@ void Preprocessor::HandleDirective(Token &Result) { // C99 6.10.3 - Macro Replacement. case tok::pp_define: - return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef, false); case tok::pp_define2: - return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef, true); + return HandleDefineDirective(Result, ImmediatelyAfterTopLevelIfndef); case tok::pp_undef: return HandleUndefDirective(); @@ -3045,7 +3040,7 @@ static bool isObjCProtectedMacro(const IdentifierInfo *II) { /// HandleDefineDirective - Implements \#define and define2. This consumes the entire macro /// line then lets the caller lex the next real token. void Preprocessor::HandleDefineDirective( - Token &DefineTok, const bool ImmediatelyAfterHeaderGuard, bool AllowRecurse) { + Token &DefineTok, const bool ImmediatelyAfterHeaderGuard) { ++NumDefined; Token MacroNameTok; @@ -3070,7 +3065,7 @@ void Preprocessor::HandleDefineDirective( MacroNameTok, ImmediatelyAfterHeaderGuard); if (!MI) return; - MI->setAllowRecursive(AllowRecurse); + MI->setAllowRecursive(DefineTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_define2); if (MacroShadowsKeyword && !isConfigurationPattern(MacroNameTok, MI, getLangOpts())) { Diag(MacroNameTok, diag::warn_pp_macro_hides_keyword); >From 10b7329617f2476e95914a362dd36891f42264b1 Mon Sep 17 00:00:00 2001 From: Kelbon Nik <kelbon...@gmail.com> Date: Sun, 24 Sep 2023 13:42:56 +0400 Subject: [PATCH 9/9] __THIS_MACRO__ --- clang/include/clang/Lex/MacroInfo.h | 18 ++++++++---- clang/include/clang/Lex/Preprocessor.h | 6 ++++ clang/lib/Lex/PPMacroExpansion.cpp | 29 +++++++++++++++++++ clang/lib/Lex/Preprocessor.cpp | 1 + .../Preprocessor/macro_infinite_recursion.cpp | 6 ++-- clang/test/Preprocessor/macro_recursion.cpp | 6 ++-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 54b1d7193dbe613..8b91d33e971b752 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -55,7 +55,7 @@ class MacroInfo { IdentifierInfo **ParameterList = nullptr; /// This is the list of tokens that the macro is defined to. - const Token *ReplacementTokens = nullptr; + Token *ReplacementTokens = nullptr; /// \see ParameterList unsigned NumParameters = 0; @@ -186,8 +186,11 @@ class MacroInfo { param_iterator param_begin() const { return ParameterList; } param_iterator param_end() const { return ParameterList + NumParameters; } unsigned getNumParams() const { return NumParameters; } + ArrayRef<IdentifierInfo *> params() { + return ArrayRef<IdentifierInfo *>(ParameterList, NumParameters); + } ArrayRef<const IdentifierInfo *> params() const { - return ArrayRef<const IdentifierInfo *>(ParameterList, NumParameters); + return const_cast<MacroInfo*>(this)->params(); } /// Return the parameter number of the specified identifier, @@ -250,6 +253,9 @@ class MacroInfo { return ReplacementTokens + NumReplacementTokens; } bool tokens_empty() const { return NumReplacementTokens == 0; } + MutableArrayRef<Token> tokens() { + return llvm::MutableArrayRef(ReplacementTokens, NumReplacementTokens); + } ArrayRef<Token> tokens() const { return llvm::ArrayRef(ReplacementTokens, NumReplacementTokens); } @@ -293,10 +299,12 @@ class MacroInfo { assert(Depth != 0 && "Cannot enable not disabled macro"); --Depth; } - // returns false if max recursion depth exceeded - [[nodiscard]] bool TryDisableMacro() { + enum : uint16_t { macro_recursion_depth_limit = 16'000 }; + + [[nodiscard("infinite recursion check ignored")]] + bool TryDisableMacro() { assert((AllowRecurse || Depth == 0) && "Cannot disable an already-disabled macro!"); - return ++Depth < recursion_depth_limit; + return ++Depth < macro_recursion_depth_limit; } /// Determine whether this macro was used for a header guard. diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 9efe439bc5f2192..47da21cf3cd271d 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -160,6 +160,7 @@ class Preprocessor { IdentifierInfo *Ident__identifier; // __identifier IdentifierInfo *Ident__VA_ARGS__; // __VA_ARGS__ IdentifierInfo *Ident__VA_OPT__; // __VA_OPT__ + IdentifierInfo *Ident__THIS_MACRO__; // __THIS_MACRO__ IdentifierInfo *Ident__has_feature; // __has_feature IdentifierInfo *Ident__has_extension; // __has_extension IdentifierInfo *Ident__has_builtin; // __has_builtin @@ -2425,6 +2426,11 @@ class Preprocessor { private: friend void TokenLexer::ExpandFunctionArguments(); + /// If macro definition containts __THIS_MACRO__ creates impl-only recursive + /// version of macro, and replaces all __THIS_MACRO__ tokens + /// with new created recusive version + void appendRecursiveVersionIfRequired(IdentifierInfo*, MacroInfo*); + void PushIncludeMacroStack() { assert(CurLexerKind != CLK_CachingLexer && "cannot push a caching lexer"); IncludeMacroStack.emplace_back(CurLexerKind, CurLexerSubmodule, diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index a0093fca0ab0afc..6ecaa1883a39ca5 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -59,6 +59,34 @@ using namespace clang; +void Preprocessor::appendRecursiveVersionIfRequired(IdentifierInfo* II, MacroInfo* MI) { + if (!MI->isFunctionLike()) + return; + auto is_this_macro_tok = [&] (const Token& t) { + return t.getKind() == tok::identifier && t.getIdentifierInfo() == Ident__THIS_MACRO__; + }; + if (llvm::none_of(MI->tokens(), is_this_macro_tok)) + return; + IdentifierInfo* ImplMacroII = [&] { + std::string ImplMacroName = "__THIS_MACRO__"; + ImplMacroName += II->getName(); + return getIdentifierInfo(ImplMacroName); + }(); + MacroInfo* NewMI = AllocateMacroInfo(MI->getDefinitionLoc()); + NewMI->setIsFunctionLike(); + NewMI->setParameterList(MI->params(), getPreprocessorAllocator()); + NewMI->setDefinitionEndLoc(MI->getDefinitionEndLoc()); + if (MI->isC99Varargs()) NewMI->setIsC99Varargs(); + if (MI->isGNUVarargs()) NewMI->setIsGNUVarargs(); + for (auto& t : MI->tokens()) { + if (is_this_macro_tok(t)) + t.setIdentifierInfo(ImplMacroII); + } + NewMI->setTokens(MI->tokens(), getPreprocessorAllocator()); + NewMI->setAllowRecursive(true); + appendDefMacroDirective(ImplMacroII, NewMI); +} + MacroDirective * Preprocessor::getLocalMacroDirectiveHistory(const IdentifierInfo *II) const { if (!II->hadMacroDefinition()) @@ -91,6 +119,7 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){ II->setHasMacroDefinition(false); if (II->isFromAST()) II->setChangedSinceDeserialization(); + appendRecursiveVersionIfRequired(II, MD->getMacroInfo()); } void Preprocessor::setLoadedMacroDirective(IdentifierInfo *II, diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 8de78a13930ed62..87bfb0d6ab4382a 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -121,6 +121,7 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts, SetPoisonReason(Ident__VA_ARGS__,diag::ext_pp_bad_vaargs_use); (Ident__VA_OPT__ = getIdentifierInfo("__VA_OPT__"))->setIsPoisoned(); SetPoisonReason(Ident__VA_OPT__,diag::ext_pp_bad_vaopt_use); + Ident__THIS_MACRO__ = getIdentifierInfo("__THIS_MACRO__"); // Initialize the pragma handlers. RegisterBuiltinPragmas(); diff --git a/clang/test/Preprocessor/macro_infinite_recursion.cpp b/clang/test/Preprocessor/macro_infinite_recursion.cpp index cac8e7abbec5af3..3306f468543bcb9 100644 --- a/clang/test/Preprocessor/macro_infinite_recursion.cpp +++ b/clang/test/Preprocessor/macro_infinite_recursion.cpp @@ -1,12 +1,12 @@ // RUN: %clang_cc1 %s -Eonly -std=c++11 -pedantic -verify -#define2 A A +#define A() __THIS_MACRO__() -A //expected-error {{macro recursion depth limit exceeded}} +A() //expected-error {{macro recursion depth limit exceeded}} #undef A -#define2 A(x) A(x) +#define A(x) __THIS_MACRO__(x) A(5) //expected-error {{macro recursion depth limit exceeded}} diff --git a/clang/test/Preprocessor/macro_recursion.cpp b/clang/test/Preprocessor/macro_recursion.cpp index cd415ce1a95e463..fc2c2ffd21eadd6 100644 --- a/clang/test/Preprocessor/macro_recursion.cpp +++ b/clang/test/Preprocessor/macro_recursion.cpp @@ -2,7 +2,7 @@ #define merge_all_expand2(a, b) a ## b #define merge_all_expand(a, b) merge_all_expand2(a, b) -#define2 concat_all(head, ...)merge_all_expand(head,__VA_OPT__(concat_all(__VA_ARGS__))) +#define concat_all(head, ...)merge_all_expand(head,__VA_OPT__(__THIS_MACRO__(__VA_ARGS__))) 0: concat_all(aa, bb, cc) 1: [concat_all()] // CHECK: 0: aabbcc @@ -12,7 +12,7 @@ #undef merge_all_expand2 #undef concat_all -#define2 reverse(head, ...)__VA_OPT__(reverse(__VA_ARGS__),)head +#define reverse(head, ...)__VA_OPT__(__THIS_MACRO__(__VA_ARGS__),)head 2: reverse(1,2,3) @@ -20,7 +20,7 @@ #undef reverse -#define2 fold_left(op, head, ...)(__VA_OPT__(fold_left(op,__VA_ARGS__)op)head) +#define fold_left(op, head, ...)(__VA_OPT__(__THIS_MACRO__(op,__VA_ARGS__)op)head) 3: fold_left(+, 1, 2, 3, 4) // CHECK: 3: ((((4)+3)+2)+1) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits