diff --git a/flang/lib/Parser/parsing.cpp b/flang/lib/Parser/parsing.cpp --- a/flang/lib/Parser/parsing.cpp +++ b/flang/lib/Parser/parsing.cpp @@ -96,6 +96,10 @@ return sourceFile; } +constexpr bool IsAlpha(const char ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + void Parsing::EmitPreprocessedSource( llvm::raw_ostream &out, bool lineDirectives) const { const SourceFile *sourceFile{nullptr}; @@ -104,6 +108,11 @@ bool inDirective{false}; bool inContinuation{false}; const AllSources &allSources{allCooked().allSources()}; + // All directives that flang support are known to have a length of 3 chars + constexpr auto directiveNameLength = 3u; + // We need to know the current directive in order to provide correct + // continuation for the directive + std::string directive; for (const char &atChar : cooked().AsCharBlock()) { char ch{atChar}; if (ch == '\n') { @@ -112,14 +121,34 @@ inDirective = false; inContinuation = false; ++sourceLine; + directive.clear(); } else { + auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})}; + + // Preserves original case of the character + const auto getOriginalChar = [&](char ch) { + if (IsAlpha(ch) && provenance && provenance->size() == 1) { + if (const char *orig{allSources.GetSource(*provenance)}) { + auto upper{static_cast(ch + 'A' - 'a')}; + if (*orig == upper) { + return upper; + } + } + } + return ch; + }; + if (ch == '!') { // Other comment markers (C, *, D) in original fixed form source // input card column 1 will have been deleted or normalized to !, // which signifies a comment (directive) in both source forms. inDirective = true; } - auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})}; + if (inDirective && directive.size() < directiveNameLength && + IsAlpha(ch)) { + directive += getOriginalChar(ch); + } + std::optional position{provenance ? allSources.GetSourcePosition(provenance->start()) : std::nullopt}; @@ -145,14 +174,18 @@ } if (column > 72) { // Wrap long lines in a portable fashion that works in both - // of the Fortran source forms. The first free-form continuation + // of the Fortran source forms. The first free-form continuation // marker ("&") lands in column 73, which begins the card commentary // field of fixed form, and the second one is put in column 6, // where it signifies fixed form line continuation. // The standard Fortran fixed form column limit (72) is used // for output, even if the input was parsed with a nonstandard // column limit override option. - out << "&\n &"; + // OpenMP and OpenACC directives' continuations should have the + // corresponding sentinel at the next line. + const auto continuation = + inDirective ? "&\n!$" + directive + "&" : "&\n &"s; + out << continuation; column = 7; // start of fixed form source field ++sourceLine; inContinuation = true; @@ -169,16 +202,7 @@ out << ' '; } } - if (ch >= 'a' && ch <= 'z' && provenance && provenance->size() == 1) { - // Preserve original case - if (const char *orig{allSources.GetSource(*provenance)}) { - auto upper{static_cast(ch + 'A' - 'a')}; - if (*orig == upper) { - ch = upper; - } - } - } - out << ch; + out << getOriginalChar(ch); ++column; } } diff --git a/flang/test/Preprocessing/pp132.f90 b/flang/test/Preprocessing/pp132.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Preprocessing/pp132.f90 @@ -0,0 +1,18 @@ +! RUN: %flang -E -fopenmp -fopenacc %s 2>&1 | FileCheck %s +! CHECK: !$OMP parallel default(shared) private(super_very_long_name_for_the_va& +! CHECK: !$OMP&riable) +! CHECK: !$acc data copyin(super_very_long_name_for_the_variable, another_super& +! CHECK: !$acc&_wordy_variable_to_test) +! Test correct continuations in compiler directives +subroutine foo + integer :: super_very_long_name_for_the_variable + integer :: another_super_wordy_variable_to_test + + super_very_long_name_for_the_variable = 42 + another_super_wordy_variable_to_test = super_very_long_name_for_the_variable * 2 + !$OMP parallel default(shared) private(super_very_long_name_for_the_variable) + !$omp end parallel + + !$acc data copyin(super_very_long_name_for_the_variable, another_super_wordy_variable_to_test) + !$acc end data +end subroutine foo