[flang-commits] [flang] [flang][preprocessing] Handle #include after & line continuation (PR #93382)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Mon Jun 3 09:22:18 PDT 2024
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/93382
>From 143e9e2fca8d650b499a1becaea0237c6d95184b Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Sat, 25 May 2024 11:08:49 -0700
Subject: [PATCH] [flang][preprocessing] Handle #include after & line
continuation
Some applications like to use a CPP-style #include directive to
pull in a common list of arguments, dummy arguments, or COMMON
block variables after a free-form & line continuation marker.
This works naturally with compilers that run an actual cpp pass
over the input before doing anything specific to Fortran, but it's
a case that I missed with this integrated preprocessor.
---
flang/lib/Parser/preprocessor.cpp | 19 ++++----
flang/lib/Parser/prescan.cpp | 54 ++++++++++++++---------
flang/lib/Parser/prescan.h | 7 ++-
flang/test/Preprocessing/args.h | 1 +
flang/test/Preprocessing/include-args.F90 | 6 +++
5 files changed, 56 insertions(+), 31 deletions(-)
create mode 100644 flang/test/Preprocessing/args.h
create mode 100644 flang/test/Preprocessing/include-args.F90
diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp
index ce95dc4b7aaec..8896980bf4bbf 100644
--- a/flang/lib/Parser/preprocessor.cpp
+++ b/flang/lib/Parser/preprocessor.cpp
@@ -749,17 +749,18 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) {
}
std::string buf;
llvm::raw_string_ostream error{buf};
- const SourceFile *included{
- allSources_.Open(include, error, std::move(prependPath))};
- if (!included) {
+ if (const SourceFile *
+ included{allSources_.Open(include, error, std::move(prependPath))}) {
+ if (included->bytes() > 0) {
+ ProvenanceRange fileRange{
+ allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
+ Prescanner{prescanner, /*isNestedInIncludeDirective=*/true}
+ .set_encoding(included->encoding())
+ .Prescan(fileRange);
+ }
+ } else {
prescanner.Say(dir.GetTokenProvenanceRange(j), "#include: %s"_err_en_US,
error.str());
- } else if (included->bytes() > 0) {
- ProvenanceRange fileRange{
- allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
- Prescanner{prescanner}
- .set_encoding(included->encoding())
- .Prescan(fileRange);
}
} else {
prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index c08a28cb43449..f9b9c3d2c6e56 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -32,10 +32,11 @@ Prescanner::Prescanner(Messages &messages, CookedSource &cooked,
backslashFreeFormContinuation_{preprocessor.AnyDefinitions()},
encoding_{allSources_.encoding()} {}
-Prescanner::Prescanner(const Prescanner &that)
+Prescanner::Prescanner(const Prescanner &that, bool isNestedInIncludeDirective)
: messages_{that.messages_}, cooked_{that.cooked_},
preprocessor_{that.preprocessor_}, allSources_{that.allSources_},
features_{that.features_},
+ isNestedInIncludeDirective_{isNestedInIncludeDirective},
backslashFreeFormContinuation_{that.backslashFreeFormContinuation_},
inFixedForm_{that.inFixedForm_},
fixedFormColumnLimit_{that.fixedFormColumnLimit_},
@@ -104,11 +105,14 @@ void Prescanner::Statement() {
NextLine();
return;
case LineClassification::Kind::ConditionalCompilationDirective:
- case LineClassification::Kind::IncludeDirective:
case LineClassification::Kind::DefinitionDirective:
case LineClassification::Kind::PreprocessorDirective:
preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
return;
+ case LineClassification::Kind::IncludeDirective:
+ preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
+ afterIncludeDirective_ = true;
+ return;
case LineClassification::Kind::CompilerDirective: {
directiveSentinel_ = line.sentinel;
CHECK(InCompilerDirective());
@@ -213,10 +217,7 @@ void Prescanner::Statement() {
Say(preprocessed->GetProvenanceRange(),
"Preprocessed line resembles a preprocessor directive"_warn_en_US);
}
- preprocessed->ToLowerCase()
- .CheckBadFortranCharacters(messages_, *this)
- .CheckBadParentheses(messages_)
- .Emit(cooked_);
+ CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
break;
case LineClassification::Kind::CompilerDirective:
if (preprocessed->HasRedundantBlanks()) {
@@ -228,10 +229,9 @@ void Prescanner::Statement() {
NormalizeCompilerDirectiveCommentMarker(*preprocessed);
preprocessed->ToLowerCase();
SourceFormChange(preprocessed->ToString());
- preprocessed->ClipComment(*this, true /* skip first ! */)
- .CheckBadFortranCharacters(messages_, *this)
- .CheckBadParentheses(messages_)
- .Emit(cooked_);
+ CheckAndEmitLine(preprocessed->ToLowerCase().ClipComment(
+ *this, true /* skip first ! */),
+ newlineProvenance);
break;
case LineClassification::Kind::Source:
if (inFixedForm_) {
@@ -246,14 +246,11 @@ void Prescanner::Statement() {
preprocessed->RemoveRedundantBlanks();
}
}
- preprocessed->ToLowerCase()
- .ClipComment(*this)
- .CheckBadFortranCharacters(messages_, *this)
- .CheckBadParentheses(messages_)
- .Emit(cooked_);
+ CheckAndEmitLine(
+ preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
break;
}
- } else {
+ } else { // no macro replacement
if (line.kind == LineClassification::Kind::CompilerDirective) {
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
newlineProvenance = GetCurrentProvenance();
@@ -266,16 +263,29 @@ void Prescanner::Statement() {
EnforceStupidEndStatementRules(tokens);
}
}
- tokens.CheckBadFortranCharacters(messages_, *this)
- .CheckBadParentheses(messages_)
- .Emit(cooked_);
+ CheckAndEmitLine(tokens, newlineProvenance);
}
+ directiveSentinel_ = nullptr;
+}
+
+void Prescanner::CheckAndEmitLine(
+ TokenSequence &tokens, Provenance newlineProvenance) {
+ tokens.CheckBadFortranCharacters(messages_, *this);
+ // Parenthesis nesting check does not apply while any #include is
+ // active, nor on the lines before and after a top-level #include.
+ // Applications play shenanigans with line continuation before and
+ // after #include'd subprogram argument lists.
+ if (!isNestedInIncludeDirective_ && !omitNewline_ &&
+ !afterIncludeDirective_) {
+ tokens.CheckBadParentheses(messages_);
+ }
+ tokens.Emit(cooked_);
if (omitNewline_) {
omitNewline_ = false;
} else {
cooked_.Put('\n', newlineProvenance);
+ afterIncludeDirective_ = false;
}
- directiveSentinel_ = nullptr;
}
TokenSequence Prescanner::TokenizePreprocessorDirective() {
@@ -985,7 +995,9 @@ void Prescanner::FortranInclude(const char *firstQuote) {
provenance, static_cast<std::size_t>(p - nextLine_)};
ProvenanceRange fileRange{
allSources_.AddIncludedFile(*included, includeLineRange)};
- Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange);
+ Prescanner{*this, /*isNestedInIncludeDirective=*/false}
+ .set_encoding(included->encoding())
+ .Prescan(fileRange);
}
}
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index 4eb3713bd3e37..491b1fe0a7517 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -35,7 +35,9 @@ class Prescanner {
public:
Prescanner(Messages &, CookedSource &, Preprocessor &,
common::LanguageFeatureControl);
- Prescanner(const Prescanner &);
+ Prescanner(const Prescanner &, bool isNestedInIncludeDirective);
+ Prescanner(const Prescanner &) = delete;
+ Prescanner(Prescanner &&) = delete;
const AllSources &allSources() const { return allSources_; }
AllSources &allSources() { return allSources_; }
@@ -155,6 +157,7 @@ class Prescanner {
common::LanguageFeature::ClassicCComments)));
}
+ void CheckAndEmitLine(TokenSequence &, Provenance newlineProvenance);
void LabelField(TokenSequence &);
void EnforceStupidEndStatementRules(const TokenSequence &);
void SkipToEndOfLine();
@@ -198,6 +201,7 @@ class Prescanner {
Preprocessor &preprocessor_;
AllSources &allSources_;
common::LanguageFeatureControl features_;
+ bool isNestedInIncludeDirective_{false};
bool backslashFreeFormContinuation_{false};
bool inFixedForm_{false};
int fixedFormColumnLimit_{72};
@@ -206,6 +210,7 @@ class Prescanner {
int prescannerNesting_{0};
int continuationLines_{0};
bool isPossibleMacroCall_{false};
+ bool afterIncludeDirective_{false};
Provenance startProvenance_;
const char *start_{nullptr}; // beginning of current source file content
diff --git a/flang/test/Preprocessing/args.h b/flang/test/Preprocessing/args.h
new file mode 100644
index 0000000000000..071ebae9cb3c2
--- /dev/null
+++ b/flang/test/Preprocessing/args.h
@@ -0,0 +1 @@
+3.14159 &
diff --git a/flang/test/Preprocessing/include-args.F90 b/flang/test/Preprocessing/include-args.F90
new file mode 100644
index 0000000000000..011e4dba13e73
--- /dev/null
+++ b/flang/test/Preprocessing/include-args.F90
@@ -0,0 +1,6 @@
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+! CHECK: call foo(3.14159)
+call foo (&
+#include "args.h"
+)
+end
More information about the flang-commits
mailing list