Skip to content

Commit 9b0ae6c

Browse files
authored
[flang][OpenMP] Issue a warning when parsing future directive spelling (#147765)
OpenMP 6.0 introduced alternative spelling for some directives, with the previous spellings still allowed. Warn the user when a new spelling is encountered with OpenMP version set to an older value.
1 parent c8c0e90 commit 9b0ae6c

File tree

5 files changed

+280
-3
lines changed

5 files changed

+280
-3
lines changed

flang/include/flang/Parser/parse-tree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4612,7 +4612,7 @@ struct OmpDirectiveSpecification {
46124612

46134613
struct OmpMetadirectiveDirective {
46144614
TUPLE_CLASS_BOILERPLATE(OmpMetadirectiveDirective);
4615-
std::tuple<OmpClauseList> t;
4615+
std::tuple<Verbatim, OmpClauseList> t;
46164616
CharBlock source;
46174617
};
46184618

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,7 @@ TYPE_PARSER(sourced(construct<OpenMPUtilityConstruct>(
11941194
sourced(Parser<OmpNothingDirective>{}))))))
11951195

11961196
TYPE_PARSER(sourced(construct<OmpMetadirectiveDirective>(
1197-
"METADIRECTIVE" >> Parser<OmpClauseList>{})))
1197+
verbatim("METADIRECTIVE"_tok), Parser<OmpClauseList>{})))
11981198

11991199
// Omp directives enclosing do loop
12001200
TYPE_PARSER(sourced(construct<OmpLoopDirective>(first(
@@ -1687,7 +1687,7 @@ TYPE_PARSER(sourced(construct<OmpAssumeDirective>(
16871687
verbatim("ASSUME"_tok), Parser<OmpClauseList>{})))
16881688

16891689
TYPE_PARSER(sourced(construct<OmpEndAssumeDirective>(
1690-
verbatim(startOmpLine >> "END ASSUME"_tok))))
1690+
startOmpLine >> verbatim("END ASSUME"_tok))))
16911691

16921692
TYPE_PARSER(sourced(
16931693
construct<OpenMPAssumeConstruct>(Parser<OmpAssumeDirective>{} / endOmpLine,

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,57 @@ void OmpStructureChecker::CheckVariableListItem(
259259
}
260260
}
261261

262+
void OmpStructureChecker::CheckDirectiveSpelling(
263+
parser::CharBlock spelling, llvm::omp::Directive id) {
264+
// Directive names that contain spaces can be spelled in the source without
265+
// any of the spaces. Because of that getOpenMPKind* is not guaranteed to
266+
// work with the source spelling as the argument.
267+
//
268+
// To verify the source spellings, we have to get the spelling for a given
269+
// version, remove spaces and compare it with the source spelling (also
270+
// with spaces removed).
271+
auto removeSpaces = [](llvm::StringRef s) {
272+
std::string n{s.str()};
273+
for (size_t idx{n.size()}; idx > 0; --idx) {
274+
if (isspace(n[idx - 1])) {
275+
n.erase(idx - 1, 1);
276+
}
277+
}
278+
return n;
279+
};
280+
281+
std::string lowerNoWS{removeSpaces(
282+
parser::ToLowerCaseLetters({spelling.begin(), spelling.size()}))};
283+
llvm::StringRef ref(lowerNoWS);
284+
if (ref.starts_with("end")) {
285+
ref = ref.drop_front(3);
286+
}
287+
288+
unsigned version{context_.langOptions().OpenMPVersion};
289+
290+
// For every "future" version v, check if the check if the corresponding
291+
// spelling of id was introduced later than the current version. If so,
292+
// and if that spelling matches the source spelling, issue a warning.
293+
for (unsigned v : llvm::omp::getOpenMPVersions()) {
294+
if (v <= version) {
295+
continue;
296+
}
297+
llvm::StringRef name{llvm::omp::getOpenMPDirectiveName(id, v)};
298+
auto [kind, versions]{llvm::omp::getOpenMPDirectiveKindAndVersions(name)};
299+
assert(kind == id && "Directive kind mismatch");
300+
301+
if (static_cast<int>(version) >= versions.Min) {
302+
continue;
303+
}
304+
if (ref == removeSpaces(name)) {
305+
context_.Say(spelling,
306+
"Directive spelling '%s' is introduced in a later OpenMP version, %s"_warn_en_US,
307+
parser::ToUpperCaseLetters(ref), TryVersion(versions.Min));
308+
break;
309+
}
310+
}
311+
}
312+
262313
void OmpStructureChecker::CheckMultipleOccurrence(
263314
semantics::UnorderedSymbolSet &listVars,
264315
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
@@ -436,7 +487,133 @@ void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) {
436487
}
437488
}
438489

490+
template <typename Checker> struct DirectiveSpellingVisitor {
491+
using Directive = llvm::omp::Directive;
492+
493+
DirectiveSpellingVisitor(Checker &&checker) : checker_(std::move(checker)) {}
494+
495+
template <typename T> bool Pre(const T &) { return true; }
496+
template <typename T> void Post(const T &) {}
497+
498+
bool Pre(const parser::OmpSectionsDirective &x) {
499+
checker_(x.source, x.v);
500+
return false;
501+
}
502+
bool Pre(const parser::OpenMPDeclarativeAllocate &x) {
503+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate);
504+
return false;
505+
}
506+
bool Pre(const parser::OmpDispatchDirective &x) {
507+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_dispatch);
508+
return false;
509+
}
510+
bool Pre(const parser::OmpErrorDirective &x) {
511+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_error);
512+
return false;
513+
}
514+
bool Pre(const parser::OmpNothingDirective &x) {
515+
checker_(x.source, Directive::OMPD_nothing);
516+
return false;
517+
}
518+
bool Pre(const parser::OpenMPExecutableAllocate &x) {
519+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate);
520+
return false;
521+
}
522+
bool Pre(const parser::OpenMPAllocatorsConstruct &x) {
523+
checker_(
524+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocators);
525+
return false;
526+
}
527+
bool Pre(const parser::OmpAssumeDirective &x) {
528+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_assume);
529+
return false;
530+
}
531+
bool Pre(const parser::OmpEndAssumeDirective &x) {
532+
checker_(x.v.source, Directive::OMPD_assume);
533+
return false;
534+
}
535+
bool Pre(const parser::OmpCriticalDirective &x) {
536+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_critical);
537+
return false;
538+
}
539+
bool Pre(const parser::OmpEndCriticalDirective &x) {
540+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_critical);
541+
return false;
542+
}
543+
bool Pre(const parser::OmpMetadirectiveDirective &x) {
544+
checker_(
545+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_metadirective);
546+
return false;
547+
}
548+
bool Pre(const parser::OpenMPDeclarativeAssumes &x) {
549+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_assumes);
550+
return false;
551+
}
552+
bool Pre(const parser::OpenMPDeclareMapperConstruct &x) {
553+
checker_(
554+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_mapper);
555+
return false;
556+
}
557+
bool Pre(const parser::OpenMPDeclareReductionConstruct &x) {
558+
checker_(std::get<parser::Verbatim>(x.t).source,
559+
Directive::OMPD_declare_reduction);
560+
return false;
561+
}
562+
bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
563+
checker_(
564+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_simd);
565+
return false;
566+
}
567+
bool Pre(const parser::OpenMPDeclareTargetConstruct &x) {
568+
checker_(
569+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_target);
570+
return false;
571+
}
572+
bool Pre(const parser::OmpDeclareVariantDirective &x) {
573+
checker_(std::get<parser::Verbatim>(x.t).source,
574+
Directive::OMPD_declare_variant);
575+
return false;
576+
}
577+
bool Pre(const parser::OpenMPThreadprivate &x) {
578+
checker_(
579+
std::get<parser::Verbatim>(x.t).source, Directive::OMPD_threadprivate);
580+
return false;
581+
}
582+
bool Pre(const parser::OpenMPRequiresConstruct &x) {
583+
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_requires);
584+
return false;
585+
}
586+
587+
bool Pre(const parser::OmpBlockDirective &x) {
588+
checker_(x.source, x.v);
589+
return false;
590+
}
591+
592+
bool Pre(const parser::OmpLoopDirective &x) {
593+
checker_(x.source, x.v);
594+
return false;
595+
}
596+
597+
bool Pre(const parser::OmpDirectiveSpecification &x) {
598+
auto &name = std::get<parser::OmpDirectiveName>(x.t);
599+
checker_(name.source, name.v);
600+
return false;
601+
}
602+
603+
private:
604+
Checker checker_;
605+
};
606+
607+
template <typename T>
608+
DirectiveSpellingVisitor(T &&) -> DirectiveSpellingVisitor<T>;
609+
439610
void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
611+
DirectiveSpellingVisitor visitor(
612+
[this](parser::CharBlock source, llvm::omp::Directive id) {
613+
return CheckDirectiveSpelling(source, id);
614+
});
615+
parser::Walk(x, visitor);
616+
440617
// Simd Construct with Ordered Construct Nesting check
441618
// We cannot use CurrentDirectiveIsNested() here because
442619
// PushContextAndClauseSets() has not been called yet, it is
@@ -461,6 +638,12 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
461638
}
462639

463640
void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
641+
DirectiveSpellingVisitor visitor(
642+
[this](parser::CharBlock source, llvm::omp::Directive id) {
643+
return CheckDirectiveSpelling(source, id);
644+
});
645+
parser::Walk(x, visitor);
646+
464647
EnterDirectiveNest(DeclarativeNest);
465648
}
466649

flang/lib/Semantics/check-omp-structure.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ class OmpStructureChecker
163163
private:
164164
bool CheckAllowedClause(llvmOmpClause clause);
165165
void CheckVariableListItem(const SymbolSourceMap &symbols);
166+
void CheckDirectiveSpelling(
167+
parser::CharBlock spelling, llvm::omp::Directive id);
166168
void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
167169
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
168170
const std::string &clauseName);
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52 -Werror
2+
3+
! The directives to check:
4+
! cancellation_point
5+
! declare_mapper
6+
! declare_reduction
7+
! declare_simd
8+
! declare_target
9+
! declare_variant
10+
! target_data
11+
! target_enter_data
12+
! target_exit_data
13+
! target_update
14+
15+
subroutine f00
16+
implicit none
17+
integer :: i
18+
19+
!$omp parallel
20+
do i = 1, 10
21+
!WARNING: Directive spelling 'CANCELLATION_POINT' is introduced in a later OpenMP version, try -fopenmp-version=60
22+
!$omp cancellation_point parallel
23+
enddo
24+
!$omp end parallel
25+
end
26+
27+
subroutine f01
28+
type :: t
29+
integer :: x
30+
end type
31+
!WARNING: Directive spelling 'DECLARE_MAPPER' is introduced in a later OpenMP version, try -fopenmp-version=60
32+
!$omp declare_mapper(t :: v) map(v%x)
33+
end
34+
35+
subroutine f02
36+
type :: t
37+
integer :: x
38+
end type
39+
!WARNING: Directive spelling 'DECLARE_REDUCTION' is introduced in a later OpenMP version, try -fopenmp-version=60
40+
!$omp declare_reduction(+ : t : omp_out%x = omp_out%x + omp_in%x)
41+
end
42+
43+
subroutine f03
44+
!WARNING: Directive spelling 'DECLARE_SIMD' is introduced in a later OpenMP version, try -fopenmp-version=60
45+
!$omp declare_simd
46+
end
47+
48+
subroutine f04
49+
!WARNING: Directive spelling 'DECLARE_TARGET' is introduced in a later OpenMP version, try -fopenmp-version=60
50+
!$omp declare_target
51+
end
52+
53+
subroutine f05
54+
implicit none
55+
interface
56+
subroutine g05
57+
end
58+
end interface
59+
!WARNING: Directive spelling 'DECLARE_VARIANT' is introduced in a later OpenMP version, try -fopenmp-version=60
60+
!$omp declare_variant(g05) match(user={condition(.true.)})
61+
end
62+
63+
subroutine f06
64+
implicit none
65+
integer :: i
66+
!WARNING: Directive spelling 'TARGET_DATA' is introduced in a later OpenMP version, try -fopenmp-version=60
67+
!$omp target_data map(tofrom: i)
68+
i = 0
69+
!WARNING: Directive spelling 'TARGET_DATA' is introduced in a later OpenMP version, try -fopenmp-version=60
70+
!$omp end target_data
71+
end
72+
73+
subroutine f07
74+
implicit none
75+
integer :: i
76+
!WARNING: Directive spelling 'TARGET_ENTER_DATA' is introduced in a later OpenMP version, try -fopenmp-version=60
77+
!$omp target_enter_data map(to: i)
78+
end
79+
80+
subroutine f08
81+
implicit none
82+
integer :: i
83+
!WARNING: Directive spelling 'TARGET_EXIT_DATA' is introduced in a later OpenMP version, try -fopenmp-version=60
84+
!$omp target_exit_data map(from: i)
85+
end
86+
87+
subroutine f09
88+
implicit none
89+
integer :: i
90+
!WARNING: Directive spelling 'TARGET_UPDATE' is introduced in a later OpenMP version, try -fopenmp-version=60
91+
!$omp target_update to(i)
92+
end

0 commit comments

Comments
 (0)