[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.
This commit is contained in:
parent
4e3e4f25bc
commit
743428688f
5 changed files with 230 additions and 123 deletions
|
|
@ -49,8 +49,8 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
// 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 @@ private:
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -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)}) {
|
||||
|
|
|
|||
|
|
@ -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)}) {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ public:
|
|||
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 @@ private:
|
|||
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 @@ private:
|
|||
}
|
||||
|
||||
void EmitInsertedChar(TokenSequence &tokens, char ch) {
|
||||
Provenance provenance{allSources_.CompilerInsertionProvenance(ch)};
|
||||
Provenance provenance{allSources().CompilerInsertionProvenance(ch)};
|
||||
tokens.PutNextTokenChar(ch, provenance);
|
||||
}
|
||||
|
||||
|
|
@ -166,18 +170,29 @@ private:
|
|||
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 @@ private:
|
|||
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 @@ private:
|
|||
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_
|
||||
|
|
|
|||
10
flang/test/Preprocessing/bug178481.F90
Normal file
10
flang/test/Preprocessing/bug178481.F90
Normal file
|
|
@ -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
|
||||
Loading…
Reference in a new issue