Skip to content

Commit 0525c20

Browse files
authored
[flang][preprocessing] Handle #include after & line continuation (#93382)
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.
1 parent 68f4e46 commit 0525c20

File tree

5 files changed

+56
-31
lines changed

5 files changed

+56
-31
lines changed

flang/lib/Parser/preprocessor.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -749,17 +749,18 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) {
749749
}
750750
std::string buf;
751751
llvm::raw_string_ostream error{buf};
752-
const SourceFile *included{
753-
allSources_.Open(include, error, std::move(prependPath))};
754-
if (!included) {
752+
if (const SourceFile *
753+
included{allSources_.Open(include, error, std::move(prependPath))}) {
754+
if (included->bytes() > 0) {
755+
ProvenanceRange fileRange{
756+
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
757+
Prescanner{prescanner, /*isNestedInIncludeDirective=*/true}
758+
.set_encoding(included->encoding())
759+
.Prescan(fileRange);
760+
}
761+
} else {
755762
prescanner.Say(dir.GetTokenProvenanceRange(j), "#include: %s"_err_en_US,
756763
error.str());
757-
} else if (included->bytes() > 0) {
758-
ProvenanceRange fileRange{
759-
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
760-
Prescanner{prescanner}
761-
.set_encoding(included->encoding())
762-
.Prescan(fileRange);
763764
}
764765
} else {
765766
prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),

flang/lib/Parser/prescan.cpp

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ Prescanner::Prescanner(Messages &messages, CookedSource &cooked,
3232
backslashFreeFormContinuation_{preprocessor.AnyDefinitions()},
3333
encoding_{allSources_.encoding()} {}
3434

35-
Prescanner::Prescanner(const Prescanner &that)
35+
Prescanner::Prescanner(const Prescanner &that, bool isNestedInIncludeDirective)
3636
: messages_{that.messages_}, cooked_{that.cooked_},
3737
preprocessor_{that.preprocessor_}, allSources_{that.allSources_},
3838
features_{that.features_},
39+
isNestedInIncludeDirective_{isNestedInIncludeDirective},
3940
backslashFreeFormContinuation_{that.backslashFreeFormContinuation_},
4041
inFixedForm_{that.inFixedForm_},
4142
fixedFormColumnLimit_{that.fixedFormColumnLimit_},
@@ -104,11 +105,14 @@ void Prescanner::Statement() {
104105
NextLine();
105106
return;
106107
case LineClassification::Kind::ConditionalCompilationDirective:
107-
case LineClassification::Kind::IncludeDirective:
108108
case LineClassification::Kind::DefinitionDirective:
109109
case LineClassification::Kind::PreprocessorDirective:
110110
preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
111111
return;
112+
case LineClassification::Kind::IncludeDirective:
113+
preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
114+
afterIncludeDirective_ = true;
115+
return;
112116
case LineClassification::Kind::CompilerDirective: {
113117
directiveSentinel_ = line.sentinel;
114118
CHECK(InCompilerDirective());
@@ -213,10 +217,7 @@ void Prescanner::Statement() {
213217
Say(preprocessed->GetProvenanceRange(),
214218
"Preprocessed line resembles a preprocessor directive"_warn_en_US);
215219
}
216-
preprocessed->ToLowerCase()
217-
.CheckBadFortranCharacters(messages_, *this)
218-
.CheckBadParentheses(messages_)
219-
.Emit(cooked_);
220+
CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
220221
break;
221222
case LineClassification::Kind::CompilerDirective:
222223
if (preprocessed->HasRedundantBlanks()) {
@@ -228,10 +229,9 @@ void Prescanner::Statement() {
228229
NormalizeCompilerDirectiveCommentMarker(*preprocessed);
229230
preprocessed->ToLowerCase();
230231
SourceFormChange(preprocessed->ToString());
231-
preprocessed->ClipComment(*this, true /* skip first ! */)
232-
.CheckBadFortranCharacters(messages_, *this)
233-
.CheckBadParentheses(messages_)
234-
.Emit(cooked_);
232+
CheckAndEmitLine(preprocessed->ToLowerCase().ClipComment(
233+
*this, true /* skip first ! */),
234+
newlineProvenance);
235235
break;
236236
case LineClassification::Kind::Source:
237237
if (inFixedForm_) {
@@ -246,14 +246,11 @@ void Prescanner::Statement() {
246246
preprocessed->RemoveRedundantBlanks();
247247
}
248248
}
249-
preprocessed->ToLowerCase()
250-
.ClipComment(*this)
251-
.CheckBadFortranCharacters(messages_, *this)
252-
.CheckBadParentheses(messages_)
253-
.Emit(cooked_);
249+
CheckAndEmitLine(
250+
preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
254251
break;
255252
}
256-
} else {
253+
} else { // no macro replacement
257254
if (line.kind == LineClassification::Kind::CompilerDirective) {
258255
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
259256
newlineProvenance = GetCurrentProvenance();
@@ -266,16 +263,29 @@ void Prescanner::Statement() {
266263
EnforceStupidEndStatementRules(tokens);
267264
}
268265
}
269-
tokens.CheckBadFortranCharacters(messages_, *this)
270-
.CheckBadParentheses(messages_)
271-
.Emit(cooked_);
266+
CheckAndEmitLine(tokens, newlineProvenance);
272267
}
268+
directiveSentinel_ = nullptr;
269+
}
270+
271+
void Prescanner::CheckAndEmitLine(
272+
TokenSequence &tokens, Provenance newlineProvenance) {
273+
tokens.CheckBadFortranCharacters(messages_, *this);
274+
// Parenthesis nesting check does not apply while any #include is
275+
// active, nor on the lines before and after a top-level #include.
276+
// Applications play shenanigans with line continuation before and
277+
// after #include'd subprogram argument lists.
278+
if (!isNestedInIncludeDirective_ && !omitNewline_ &&
279+
!afterIncludeDirective_) {
280+
tokens.CheckBadParentheses(messages_);
281+
}
282+
tokens.Emit(cooked_);
273283
if (omitNewline_) {
274284
omitNewline_ = false;
275285
} else {
276286
cooked_.Put('\n', newlineProvenance);
287+
afterIncludeDirective_ = false;
277288
}
278-
directiveSentinel_ = nullptr;
279289
}
280290

281291
TokenSequence Prescanner::TokenizePreprocessorDirective() {
@@ -985,7 +995,9 @@ void Prescanner::FortranInclude(const char *firstQuote) {
985995
provenance, static_cast<std::size_t>(p - nextLine_)};
986996
ProvenanceRange fileRange{
987997
allSources_.AddIncludedFile(*included, includeLineRange)};
988-
Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange);
998+
Prescanner{*this, /*isNestedInIncludeDirective=*/false}
999+
.set_encoding(included->encoding())
1000+
.Prescan(fileRange);
9891001
}
9901002
}
9911003

flang/lib/Parser/prescan.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ class Prescanner {
3535
public:
3636
Prescanner(Messages &, CookedSource &, Preprocessor &,
3737
common::LanguageFeatureControl);
38-
Prescanner(const Prescanner &);
38+
Prescanner(const Prescanner &, bool isNestedInIncludeDirective);
39+
Prescanner(const Prescanner &) = delete;
40+
Prescanner(Prescanner &&) = delete;
3941

4042
const AllSources &allSources() const { return allSources_; }
4143
AllSources &allSources() { return allSources_; }
@@ -155,6 +157,7 @@ class Prescanner {
155157
common::LanguageFeature::ClassicCComments)));
156158
}
157159

160+
void CheckAndEmitLine(TokenSequence &, Provenance newlineProvenance);
158161
void LabelField(TokenSequence &);
159162
void EnforceStupidEndStatementRules(const TokenSequence &);
160163
void SkipToEndOfLine();
@@ -198,6 +201,7 @@ class Prescanner {
198201
Preprocessor &preprocessor_;
199202
AllSources &allSources_;
200203
common::LanguageFeatureControl features_;
204+
bool isNestedInIncludeDirective_{false};
201205
bool backslashFreeFormContinuation_{false};
202206
bool inFixedForm_{false};
203207
int fixedFormColumnLimit_{72};
@@ -206,6 +210,7 @@ class Prescanner {
206210
int prescannerNesting_{0};
207211
int continuationLines_{0};
208212
bool isPossibleMacroCall_{false};
213+
bool afterIncludeDirective_{false};
209214

210215
Provenance startProvenance_;
211216
const char *start_{nullptr}; // beginning of current source file content

flang/test/Preprocessing/args.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.14159 &
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
! CHECK: call foo(3.14159)
3+
call foo (&
4+
#include "args.h"
5+
)
6+
end

0 commit comments

Comments
 (0)