-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[flang][preprocessing] Handle #include after & line continuation #93382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-flang-parser Author: Peter Klausler (klausler) ChangesSome 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. Full diff: https://github.com/llvm/llvm-project/pull/93382.diff 5 Files Affected:
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..f310d322ee66d 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() {
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index 4eb3713bd3e37..590ac9ec77952 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -35,7 +35,7 @@ class Prescanner {
public:
Prescanner(Messages &, CookedSource &, Preprocessor &,
common::LanguageFeatureControl);
- Prescanner(const Prescanner &);
+ Prescanner(const Prescanner &, bool isNestedInIncludeDirective);
const AllSources &allSources() const { return allSources_; }
AllSources &allSources() { return allSources_; }
@@ -155,6 +155,7 @@ class Prescanner {
common::LanguageFeature::ClassicCComments)));
}
+ void CheckAndEmitLine(TokenSequence &, Provenance newlineProvenance);
void LabelField(TokenSequence &);
void EnforceStupidEndStatementRules(const TokenSequence &);
void SkipToEndOfLine();
@@ -198,6 +199,7 @@ class Prescanner {
Preprocessor &preprocessor_;
AllSources &allSources_;
common::LanguageFeatureControl features_;
+ bool isNestedInIncludeDirective_{false};
bool backslashFreeFormContinuation_{false};
bool inFixedForm_{false};
int fixedFormColumnLimit_{72};
@@ -206,6 +208,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
|
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.
A patch for fixed-form source is still needed |
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.