[flang-commits] [flang] 7434286 - [flang] Recognize compiler directives after expansion in comment (#183626)
via flang-commits
flang-commits at lists.llvm.org
Mon Mar 2 14:47:12 PST 2026
Author: Peter Klausler
Date: 2026-03-02T14:47:08-08:00
New Revision: 743428688fb0b3e6e6d466688a3930520d741aba
URL: https://github.com/llvm/llvm-project/commit/743428688fb0b3e6e6d466688a3930520d741aba
DIFF: https://github.com/llvm/llvm-project/commit/743428688fb0b3e6e6d466688a3930520d741aba.diff
LOG: [flang] Recognize compiler directives after expansion in comment (#183626)
The compiler can recognize a compiler directive when one results from a
macro expansion at the beginning of a non-comment source line, as in
"#define FOO !$OMP". But it can't recognize a compiler directive that
initially appears as a comment line, as in "!BAR" after "#define BAR
$OMP". Extend the prescanner to recognize such cases in free form
source. (Fixed form is a much more complicated case for this recognition
and will be addressed later if needed.)
This is the 2nd version of this patch; the first was reverted after
problems with continuation lines were encountered.
Fixes https://github.com/llvm/llvm-project/issues/178481.
Added:
flang/test/Preprocessing/bug178481.F90
Modified:
flang/include/flang/Parser/preprocessor.h
flang/lib/Parser/preprocessor.cpp
flang/lib/Parser/prescan.cpp
flang/lib/Parser/prescan.h
Removed:
################################################################################
diff --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h
index 0405d42e64f7b..5db2b020f879a 100644
--- a/flang/include/flang/Parser/preprocessor.h
+++ b/flang/include/flang/Parser/preprocessor.h
@@ -49,8 +49,8 @@ class Definition {
bool set_isDisabled(bool disable);
- TokenSequence Apply(const std::vector<TokenSequence> &args, Prescanner &,
- bool inIfExpression = false);
+ TokenSequence Apply(const std::vector<TokenSequence> &args,
+ const Prescanner &, bool inIfExpression = false) const;
void Print(llvm::raw_ostream &out, const char *macroName = "") const;
@@ -95,7 +95,7 @@ class Preprocessor {
// that result and try again. All other Fortran preprocessors share this
// behavior.
std::optional<TokenSequence> MacroReplacement(const TokenSequence &,
- Prescanner &,
+ const Prescanner &,
std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
bool inIfExpression = false);
@@ -109,7 +109,7 @@ class Preprocessor {
enum class CanDeadElseAppear { No, Yes };
CharBlock SaveTokenAsName(const CharBlock &);
- TokenSequence ReplaceMacros(const TokenSequence &, Prescanner &,
+ TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &,
std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
bool inIfExpression = false);
void SkipDisabledConditionalCode(
diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp
index 529d2c345c112..3f9abf05d796b 100644
--- a/flang/lib/Parser/preprocessor.cpp
+++ b/flang/lib/Parser/preprocessor.cpp
@@ -214,7 +214,7 @@ constexpr bool IsDefinedKeyword(CharBlock token) {
}
TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
- Prescanner &prescanner, bool inIfExpression) {
+ const Prescanner &prescanner, bool inIfExpression) const {
TokenSequence result;
bool skipping{false};
int parenthesesNesting{0};
@@ -254,7 +254,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
CHECK(resultSize > 0 &&
result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1));
result.pop_back();
- result.CopyAll(Stringify(args[index], prescanner.allSources()));
+ AllSources &allSources{
+ *const_cast<AllSources *>(&prescanner.allSources())};
+ result.CopyAll(Stringify(args[index], allSources));
} else {
const TokenSequence *arg{&args[index]};
std::optional<TokenSequence> replaced;
@@ -267,7 +269,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
auto next{replacement_.SkipBlanks(j + 1)};
if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) {
// Apply macro replacement to the actual argument
- replaced = prescanner.preprocessor().MacroReplacement(
+ Preprocessor &preprocessor{
+ *const_cast<Preprocessor *>(&prescanner.preprocessor())};
+ replaced = preprocessor.MacroReplacement(
*arg, prescanner, nullptr, inIfExpression);
if (replaced) {
arg = &*replaced;
@@ -278,9 +282,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
}
} else if (bytes == 11 && isVariadic_ &&
token.ToString() == "__VA_ARGS__") {
- Provenance commaProvenance{
- prescanner.preprocessor().allSources().CompilerInsertionProvenance(
- ',')};
+ AllSources &allSources{
+ *const_cast<AllSources *>(&prescanner.allSources())};
+ Provenance commaProvenance{allSources.CompilerInsertionProvenance(',')};
for (std::size_t k{argumentCount()}; k < args.size(); ++k) {
if (k > argumentCount()) {
result.Put(","s, commaProvenance);
@@ -440,7 +444,7 @@ void Preprocessor::Define(const std::string ¯o, const std::string &value) {
void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }
std::optional<TokenSequence> Preprocessor::MacroReplacement(
- const TokenSequence &input, Prescanner &prescanner,
+ const TokenSequence &input, const Prescanner &prescanner,
std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
// Do quick scan for any use of a defined name.
if (!inIfExpression && definitions_.empty()) {
@@ -663,7 +667,7 @@ std::optional<TokenSequence> Preprocessor::MacroReplacement(
}
TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens,
- Prescanner &prescanner,
+ const Prescanner &prescanner,
std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
if (std::optional<TokenSequence> repl{MacroReplacement(
tokens, prescanner, partialFunctionLikeMacro, inIfExpression)}) {
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 036b2f3fca3e1..8d74568ca4cf6 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -46,7 +46,8 @@ Prescanner::Prescanner(const Prescanner &that, Preprocessor &prepro,
prescannerNesting_{that.prescannerNesting_ + 1},
skipLeadingAmpersand_{that.skipLeadingAmpersand_},
compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
- compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
+ compilerDirectiveSentinels_{that.compilerDirectiveSentinels_},
+ maxSentinelLength_{that.maxSentinelLength_} {}
// Returns number of bytes to skip
static inline int IsSpace(const char *p) {
@@ -212,6 +213,19 @@ void Prescanner::Statement() {
}
break;
}
+ case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
+ directiveSentinel_ = line.sentinel;
+ CHECK(InCompilerDirective());
+ BeginStatementAndAdvance();
+ while (*at_ != '!' && *at_ != '\n') {
+ ++at_, ++column_;
+ }
+ CHECK(*at_ == '!');
+ EmitChar(tokens, '!');
+ tokens.CloseToken();
+ ++at_;
+ ++column_;
+ break;
case LineClassification::Kind::Source: {
BeginStatementAndAdvance();
bool checkLabelField{false};
@@ -238,30 +252,17 @@ void Prescanner::Statement() {
// a comment marker or directive sentinel. If so, disable line
// continuation, so that NextToken() won't consume anything from
// following lines.
- if (IsLegalIdentifierStart(*at_)) {
- // TODO: Only bother with these cases when any keyword macro has
- // been defined with replacement text that could begin a comment
- // or directive sentinel.
- const char *p{at_};
- while (IsLegalInIdentifier(*++p)) {
- }
- CharBlock id{at_, static_cast<std::size_t>(p - at_)};
- if (preprocessor_.IsNameDefined(id) &&
- !preprocessor_.IsFunctionLikeDefinition(id)) {
- checkLabelField = false;
- TokenSequence toks;
- toks.Put(id, GetProvenance(at_));
- if (auto replaced{preprocessor_.MacroReplacement(toks, *this)}) {
- auto newLineClass{ClassifyLine(*replaced, GetCurrentProvenance())};
- if (newLineClass.kind ==
- LineClassification::Kind::CompilerDirective) {
- directiveSentinel_ = newLineClass.sentinel;
- disableSourceContinuation_ = false;
- } else {
- disableSourceContinuation_ = !replaced->empty() &&
- newLineClass.kind != LineClassification::Kind::Source;
- }
- }
+ if (auto kwName{GetKeywordMacroName(at_)}) {
+ checkLabelField = false;
+ Provenance here{GetCurrentProvenance()};
+ TokenSequence replacement{ExpandKeywordMacro(*kwName, here)};
+ auto newLineClass{ClassifyLine(replacement, here)};
+ if (newLineClass.kind == LineClassification::Kind::CompilerDirective) {
+ directiveSentinel_ = newLineClass.sentinel;
+ disableSourceContinuation_ = false;
+ } else {
+ disableSourceContinuation_ = !replacement.empty() &&
+ newLineClass.kind != LineClassification::Kind::Source;
}
}
if (checkLabelField) {
@@ -272,6 +273,7 @@ void Prescanner::Statement() {
while (NextToken(tokens)) {
}
+
if (continuationLines_ > 255) {
if (features_.ShouldWarn(common::LanguageFeature::MiscSourceExtensions)) {
Say(common::LanguageFeature::MiscSourceExtensions,
@@ -304,6 +306,7 @@ void Prescanner::Statement() {
CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
break;
case LineClassification::Kind::CompilerDirective:
+ case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
if (preprocessed->HasRedundantBlanks()) {
preprocessed->RemoveRedundantBlanks();
}
@@ -335,40 +338,41 @@ void Prescanner::Statement() {
preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
break;
}
- } else { // no macro replacement
- if (line.kind == LineClassification::Kind::CompilerDirective) {
- while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
- newlineProvenance = GetCurrentProvenance();
- }
- if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
- nextLine_ < limit_) {
- // In -E mode, when the line after !$ conditional compilation is a
- // regular fixed form continuation line, append a '&' to the line.
- const char *p{nextLine_};
- int col{1};
- while (int n{IsSpace(p)}) {
- if (*p == '\t') {
- break;
- }
- p += n;
- ++col;
- }
- if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
- EmitChar(tokens, '&');
- tokens.CloseToken();
+ } else if (line.kind == LineClassification::Kind::CompilerDirective ||
+ line.kind ==
+ LineClassification::Kind::CompilerDirectiveAfterMacroExpansion) {
+ while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
+ newlineProvenance = GetCurrentProvenance();
+ }
+ if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
+ nextLine_ < limit_) {
+ // In -E mode, when the line after !$ conditional compilation is a
+ // regular fixed form continuation line, append a '&' to the line.
+ const char *p{nextLine_};
+ int col{1};
+ while (int n{IsSpace(p)}) {
+ if (*p == '\t') {
+ break;
}
+ p += n;
+ ++col;
}
- tokens.ToLowerCase();
- if (!SourceFormChange(tokens.ToString())) {
- CheckAndEmitLine(tokens, newlineProvenance);
- }
- } else { // Kind::Source
- tokens.ToLowerCase();
- if (inFixedForm_) {
- EnforceStupidEndStatementRules(tokens);
+ if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
+ EmitChar(tokens, '&');
+ tokens.CloseToken();
}
+ }
+ tokens.ToLowerCase();
+ if (!SourceFormChange(tokens.ToString())) {
CheckAndEmitLine(tokens, newlineProvenance);
}
+ } else {
+ CHECK(line.kind == LineClassification::Kind::Source);
+ tokens.ToLowerCase();
+ if (inFixedForm_) {
+ EnforceStupidEndStatementRules(tokens);
+ }
+ CheckAndEmitLine(tokens, newlineProvenance);
}
directiveSentinel_ = nullptr;
}
@@ -567,7 +571,8 @@ bool Prescanner::MustSkipToEndOfLine() const {
return true; // skip over ignored columns in right margin (73:80)
} else if (*at_ == '!' && !inCharLiteral_ &&
(!inFixedForm_ || tabInCurrentLine_ || column_ != 6)) {
- return InCompilerDirective() || !IsCompilerDirectiveSentinel(at_ + 1);
+ return InCompilerDirective() ||
+ !IsCompilerDirectiveSentinelAfterKeywordMacro(at_ + 1);
} else {
return false;
}
@@ -1463,29 +1468,37 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
if (InCompilerDirective()) {
if (InConditionalLine()) {
if (preprocessingOnly_) {
- // in -E mode, don't treat !$ as a continuation
+ // in -E mode, don't treat !$/!@acc/!@cuf as a continuation
return nullptr;
- } else if (p[0] == '!' && (p[1] == '$' || p[1] == '@')) {
- p += 2;
- if (InOpenACCOrCUDAConditionalLine()) {
- if (IsDirective("acc", p) || IsDirective("cuf", p)) {
- p += 3;
- } else {
- return nullptr;
+ } else if (*p == '!') {
+ if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+ if (lClass->sentinel &&
+ ((IsOpenMPConditionalLine(lClass->sentinel) &&
+ InOpenMPConditionalLine()) ||
+ (IsOpenACCConditionalLine(lClass->sentinel) &&
+ InOpenACCConditionalLine()) ||
+ (IsCUDAConditionalLine(lClass->sentinel) &&
+ InCUDAConditionalLine()))) {
+ p += 1 + lClass->payloadOffset;
}
}
if (*p != '&' && !IsSpaceOrTab(p)) {
return nullptr;
}
}
- } else if (*p++ == '!') {
- for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) {
- if (*s != ToLowerCaseLetter(*p)) {
+ } else if (*p == '!') {
+ if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+ if (lClass->sentinel &&
+ std::strcmp(directiveSentinel_, lClass->sentinel) == 0) {
+ p += 1 + lClass->payloadOffset;
+ } else {
return nullptr; // not the same directive class
}
+ } else {
+ return nullptr; // not a compiler directive
}
} else {
- return nullptr;
+ return nullptr; // not a '!'
}
p = SkipWhiteSpace(p);
if (*p == '&') {
@@ -1501,14 +1514,16 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
}
if (p[0] == '!' && !preprocessingOnly_) {
// Conditional lines can be continuations
- if (p[1] == '$' && features_.IsEnabled(LanguageFeature::OpenMP)) {
- p = lineStart = SkipWhiteSpace(p + 2);
- } else if (IsDirective("@acc", p + 1) &&
- features_.IsEnabled(LanguageFeature::OpenACC)) {
- p = lineStart = SkipWhiteSpace(p + 5);
- } else if (IsDirective("@cuf", p + 1) &&
- features_.IsEnabled(LanguageFeature::CUDA)) {
- p = lineStart = SkipWhiteSpace(p + 5);
+ if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+ if (lClass->sentinel &&
+ ((IsOpenMPConditionalLine(lClass->sentinel) &&
+ features_.IsEnabled(LanguageFeature::OpenMP)) ||
+ (IsOpenACCConditionalLine(lClass->sentinel) &&
+ features_.IsEnabled(LanguageFeature::OpenACC)) ||
+ (IsCUDAConditionalLine(lClass->sentinel) &&
+ features_.IsEnabled(LanguageFeature::CUDA)))) {
+ lineStart = p = SkipWhiteSpace(p + 1 + lClass->payloadOffset);
+ }
}
}
if (*p == '&') {
@@ -1616,6 +1631,9 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
if (!IsFixedFormCommentChar(col1)) {
return std::nullopt;
}
+ // TODO: Handle keyword macros that expand to directives in fixed form.
+ // The comment character can't be 'c' or 'C'. Need to figure out whether
+ // fixed form continuation should apply to the expansions.
char sentinel[5], *sp{sentinel};
int column{2};
for (; column < 6; ++column) {
@@ -1678,20 +1696,28 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
std::optional<Prescanner::LineClassification>
Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
if (const char *p{SkipWhiteSpaceIncludingEmptyMacros(start)};
- p && *p++ == '!') {
- if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
- auto offset{static_cast<std::size_t>(p - start - 1)};
- const char *sentinel{maybePair->first};
- if ((sentinel[0] == '$' && sentinel[1] == '\0') || sentinel[1] == '@') {
- if (const char *comment{IsFreeFormComment(maybePair->second)}) {
- if (*comment == '!') {
- // Conditional line comment - treat as comment
- return std::nullopt;
+ p && *p == '!') {
+ if (auto lnClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+ if (lnClass->kind == LineClassification::Kind::CompilerDirective) {
+ const char *sentinel{lnClass->sentinel};
+ CHECK(sentinel != nullptr);
+ const char *payload{nullptr};
+ if (sentinel[0] == '$' && sentinel[1] == '\0') {
+ payload = p + 2; // !$
+ } else if (sentinel[1] == '@') {
+ payload = p + 5; // !@acc or !@cuf
+ }
+ if (payload) {
+ if (const char *comment{IsFreeFormComment(payload)}) {
+ if (*comment == '!') { // !$ !blah or !@acc !blah
+ // Conditional line comment - treat as comment
+ return std::nullopt;
+ }
}
}
+ lnClass->payloadOffset = static_cast<std::size_t>(p - start);
}
- return {LineClassification{
- LineClassification::Kind::CompilerDirective, offset, sentinel}};
+ return lnClass;
}
}
return std::nullopt;
@@ -1705,9 +1731,38 @@ Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
compilerDirectiveBloomFilter_.set(packed % prime1);
compilerDirectiveBloomFilter_.set(packed % prime2);
compilerDirectiveSentinels_.insert(dir);
+ int len{static_cast<int>(dir.size())};
+ if (len > maxSentinelLength_) {
+ maxSentinelLength_ = len;
+ }
return *this;
}
+std::optional<CharBlock> Prescanner::GetKeywordMacroName(
+ const char *start) const {
+ if (IsLegalIdentifierStart(*start)) {
+ // TODO: Only bother with these cases when any keyword macro has
+ // been defined with replacement text that could begin a comment
+ // or directive sentinel.
+ const char *p{start};
+ while (IsLegalInIdentifier(*++p)) {
+ }
+ CharBlock name{start, static_cast<std::size_t>(p - start)};
+ if (preprocessor_.IsNameDefined(name) &&
+ !preprocessor_.IsFunctionLikeDefinition(name)) {
+ return name;
+ }
+ }
+ return std::nullopt;
+}
+
+TokenSequence Prescanner::ExpandKeywordMacro(
+ CharBlock name, Provenance provenance) const {
+ TokenSequence toks;
+ toks.Put(name, provenance);
+ return preprocessor_.MacroReplacement(toks, *this).value();
+}
+
const char *Prescanner::IsCompilerDirectiveSentinel(
const char *sentinel, std::size_t len) const {
std::uint64_t packed{0};
@@ -1734,27 +1789,29 @@ const char *Prescanner::IsCompilerDirectiveSentinel(CharBlock token) const {
while (end > p && (end[-1] == ' ' || end[-1] == '\t')) {
--end;
}
+ if (end > p + maxSentinelLength_) {
+ end = p + maxSentinelLength_;
+ }
return end > p && IsCompilerDirectiveSentinel(p, end - p) ? p : nullptr;
}
std::optional<std::pair<const char *, const char *>>
Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
char sentinel[8];
+ std::size_t maxLen{static_cast<std::size_t>(maxSentinelLength_)};
for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) {
if (int n{IsSpaceOrTab(p)};
- n || !(IsLetter(*p) || *p == '$' || *p == '@')) {
- if (j > 0) {
- if (j == 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
- // Free form OpenMP conditional compilation line sentinels have to
- // be immediately followed by a space or &, not a digit
- // or anything else. A newline also works for an initial line.
- break;
- }
+ n || j >= maxLen || !(IsLetter(*p) || *p == '$' || *p == '@')) {
+ if (j <= 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
+ // Free form OpenMP conditional compilation line sentinels have to
+ // be immediately followed by a space or &, not a digit
+ // or anything else. A newline also works for an initial line.
+ break;
+ }
+ if (*p != '!') {
sentinel[j] = '\0';
- if (*p != '!') {
- if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
- return std::make_pair(sp, p);
- }
+ if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
+ return std::make_pair(sp, p);
}
}
break;
@@ -1765,8 +1822,26 @@ Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
return std::nullopt;
}
-Prescanner::LineClassification Prescanner::ClassifyLine(
- const char *start) const {
+auto Prescanner::IsCompilerDirectiveSentinelAfterKeywordMacro(
+ const char *p) const -> std::optional<LineClassification> {
+ if (auto name{GetKeywordMacroName(p)}) {
+ Provenance provenance{GetProvenance(p)};
+ TokenSequence expansion{ExpandKeywordMacro(*name, provenance)};
+ expansion.Put("\n", 1, provenance); // termination
+ CharBlock block{expansion.ToLowerCase().ToCharBlock()};
+ if (auto maybePair{IsCompilerDirectiveSentinel(block.begin())}) {
+ return LineClassification{
+ LineClassification::Kind::CompilerDirectiveAfterMacroExpansion,
+ name->size(), maybePair->first};
+ }
+ } else if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
+ return LineClassification{LineClassification::Kind::CompilerDirective,
+ static_cast<std::size_t>(maybePair->second - p), maybePair->first};
+ }
+ return std::nullopt;
+}
+
+auto Prescanner::ClassifyLine(const char *start) const -> LineClassification {
if (inFixedForm_) {
if (std::optional<LineClassification> lc{
IsFixedFormCompilerDirectiveLine(start)}) {
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index efbeb3709a94c..a3726e0f31b33 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -81,6 +81,9 @@ class Prescanner {
TokenSequence TokenizePreprocessorDirective();
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
+ std::optional<CharBlock> GetKeywordMacroName(const char *) const;
+ TokenSequence ExpandKeywordMacro(CharBlock, Provenance) const;
+
const char *IsCompilerDirectiveSentinel(const char *, std::size_t) const;
const char *IsCompilerDirectiveSentinel(CharBlock) const;
// 'first' is the sentinel, 'second' is beginning of payload
@@ -109,6 +112,7 @@ class Prescanner {
PreprocessorDirective,
IncludeLine, // Fortran INCLUDE
CompilerDirective,
+ CompilerDirectiveAfterMacroExpansion, // !MACRO -> !$OMP ...
Source
};
LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
@@ -156,7 +160,7 @@ class Prescanner {
}
void EmitInsertedChar(TokenSequence &tokens, char ch) {
- Provenance provenance{allSources_.CompilerInsertionProvenance(ch)};
+ Provenance provenance{allSources().CompilerInsertionProvenance(ch)};
tokens.PutNextTokenChar(ch, provenance);
}
@@ -166,18 +170,29 @@ class Prescanner {
return *at_;
}
+ bool IsOpenMPConditionalLine(const char *sentinel) const {
+ return sentinel && sentinel[0] == '$' && !sentinel[1];
+ }
+ bool IsOpenACCConditionalLine(const char *sentinel) const {
+ return sentinel && sentinel[0] == '@' && sentinel[1] == 'a' &&
+ sentinel[2] == 'c' && sentinel[3] == 'c' && sentinel[4] == '\0';
+ }
+ bool IsCUDAConditionalLine(const char *sentinel) const {
+ return sentinel && sentinel[0] == '@' && sentinel[1] == 'c' &&
+ sentinel[2] == 'u' && sentinel[3] == 'f' && sentinel[4] == '\0';
+ }
bool InCompilerDirective() const { return directiveSentinel_ != nullptr; }
bool InOpenMPConditionalLine() const {
- return directiveSentinel_ && directiveSentinel_[0] == '$' &&
- !directiveSentinel_[1];
+ return IsOpenMPConditionalLine(directiveSentinel_);
+ }
+ bool InOpenACCConditionalLine() const {
+ return IsOpenACCConditionalLine(directiveSentinel_);
+ }
+ bool InCUDAConditionalLine() const {
+ return IsCUDAConditionalLine(directiveSentinel_);
}
bool InOpenACCOrCUDAConditionalLine() const {
- return directiveSentinel_ && directiveSentinel_[0] == '@' &&
- ((directiveSentinel_[1] == 'a' && directiveSentinel_[2] == 'c' &&
- directiveSentinel_[3] == 'c') ||
- (directiveSentinel_[1] == 'c' && directiveSentinel_[2] == 'u' &&
- directiveSentinel_[3] == 'f')) &&
- directiveSentinel_[4] == '\0';
+ return InOpenACCConditionalLine() || InCUDAConditionalLine();
}
bool InConditionalLine() const {
return InOpenMPConditionalLine() || InOpenACCOrCUDAConditionalLine();
@@ -240,6 +255,8 @@ class Prescanner {
bool SourceFormChange(std::string &&);
bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
bool SourceLineContinuation(TokenSequence &);
+ std::optional<LineClassification>
+ IsCompilerDirectiveSentinelAfterKeywordMacro(const char *p) const;
Messages &messages_;
CookedSource &cooked_;
@@ -298,15 +315,16 @@ class Prescanner {
const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
const Provenance spaceProvenance_{
- allSources_.CompilerInsertionProvenance(' ')};
+ allSources().CompilerInsertionProvenance(' ')};
const Provenance backslashProvenance_{
- allSources_.CompilerInsertionProvenance('\\')};
+ allSources().CompilerInsertionProvenance('\\')};
// To avoid probing the set of active compiler directive sentinel strings
// on every comment line, they're checked first with a cheap Bloom filter.
static const int prime1{1019}, prime2{1021};
std::bitset<prime2> compilerDirectiveBloomFilter_; // 128 bytes
std::unordered_set<std::string> compilerDirectiveSentinels_;
+ int maxSentinelLength_{0};
};
} // namespace Fortran::parser
#endif // FORTRAN_PARSER_PRESCAN_H_
diff --git a/flang/test/Preprocessing/bug178481.F90 b/flang/test/Preprocessing/bug178481.F90
new file mode 100644
index 0000000000000..09a621858814c
--- /dev/null
+++ b/flang/test/Preprocessing/bug178481.F90
@@ -0,0 +1,10 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
+#define OMP_DECLARE_TARGET $OMP declare target
+#define OMP_BANG $OMP
+subroutine s
+ !CHECK: !$OMP DECLARE TARGET
+ !OMP_DECLARE_TARGET
+ !CHECK: !$OMP DECLARE TARGET
+ !OMP_BANG declare &
+ !OMP_BANG target
+end
More information about the flang-commits
mailing list