Skip to content

Commit 3b9b377

Browse files
[Flang] [OpenMP] Add semantic checks for detach clause in task (#119172)
Fixes: - Add semantic checks along with the tests - Move the detach clause to allowedOnceClauses list in Task construct Restrictions:\ OpenMP 5.0: Task construct - At most one detach clause can appear on the directive. - If a detach clause appears on the directive, then a mergeable clause cannot appear on the same directive. OpenMP 5.2: Detach contruct - If a detach clause appears on a directive, then the encountering task must not be a final task. - A variable that appears in a detach clause cannot appear as a list item on a data-environment attribute clause on the same construct. - A variable that is part of another variable (as an array element or a structure element) cannot appear in a detach clause. - event-handle must not have the POINTER attribute.
1 parent cfb057a commit 3b9b377

File tree

5 files changed

+217
-46
lines changed

5 files changed

+217
-46
lines changed

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

Lines changed: 129 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &c) {
16051605
const auto &dir{std::get<parser::Verbatim>(c.t)};
16061606
const auto &objectList{std::get<parser::OmpObjectList>(c.t)};
16071607
CheckSymbolNames(dir.source, objectList);
1608-
CheckIsVarPartOfAnotherVar(dir.source, objectList);
1608+
CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
16091609
CheckThreadprivateOrDeclareTargetVar(objectList);
16101610
dirContext_.pop_back();
16111611
}
@@ -1746,7 +1746,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
17461746
for (const auto &clause : clauseList.v) {
17471747
CheckAlignValue(clause);
17481748
}
1749-
CheckIsVarPartOfAnotherVar(dir.source, objectList);
1749+
CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
17501750
}
17511751

17521752
void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
@@ -1912,7 +1912,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
19121912
if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) {
19131913
deviceConstructFound_ = true;
19141914
CheckSymbolNames(dir.source, *objectList);
1915-
CheckIsVarPartOfAnotherVar(dir.source, *objectList);
1915+
CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
19161916
CheckThreadprivateOrDeclareTargetVar(*objectList);
19171917
} else if (const auto *clauseList{
19181918
parser::Unwrap<parser::OmpClauseList>(spec.u)}) {
@@ -1925,18 +1925,18 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
19251925
toClauseFound = true;
19261926
auto &objList{std::get<parser::OmpObjectList>(toClause.v.t)};
19271927
CheckSymbolNames(dir.source, objList);
1928-
CheckIsVarPartOfAnotherVar(dir.source, objList);
1928+
CheckVarIsNotPartOfAnotherVar(dir.source, objList);
19291929
CheckThreadprivateOrDeclareTargetVar(objList);
19301930
},
19311931
[&](const parser::OmpClause::Link &linkClause) {
19321932
CheckSymbolNames(dir.source, linkClause.v);
1933-
CheckIsVarPartOfAnotherVar(dir.source, linkClause.v);
1933+
CheckVarIsNotPartOfAnotherVar(dir.source, linkClause.v);
19341934
CheckThreadprivateOrDeclareTargetVar(linkClause.v);
19351935
},
19361936
[&](const parser::OmpClause::Enter &enterClause) {
19371937
enterClauseFound = true;
19381938
CheckSymbolNames(dir.source, enterClause.v);
1939-
CheckIsVarPartOfAnotherVar(dir.source, enterClause.v);
1939+
CheckVarIsNotPartOfAnotherVar(dir.source, enterClause.v);
19401940
CheckThreadprivateOrDeclareTargetVar(enterClause.v);
19411941
},
19421942
[&](const parser::OmpClause::DeviceType &deviceTypeClause) {
@@ -2019,7 +2019,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
20192019
CheckAlignValue(clause);
20202020
}
20212021
if (objectList) {
2022-
CheckIsVarPartOfAnotherVar(dir.source, *objectList);
2022+
CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
20232023
}
20242024
}
20252025

@@ -2039,7 +2039,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
20392039
for (const auto &clause : clauseList.v) {
20402040
if (const auto *allocClause{
20412041
parser::Unwrap<parser::OmpClause::Allocate>(clause)}) {
2042-
CheckIsVarPartOfAnotherVar(
2042+
CheckVarIsNotPartOfAnotherVar(
20432043
dir.source, std::get<parser::OmpObjectList>(allocClause->v.t));
20442044
}
20452045
}
@@ -3156,6 +3156,65 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
31563156
// clause
31573157
CheckMultListItems();
31583158

3159+
if (GetContext().directive == llvm::omp::Directive::OMPD_task) {
3160+
if (auto *detachClause{FindClause(llvm::omp::Clause::OMPC_detach)}) {
3161+
unsigned version{context_.langOptions().OpenMPVersion};
3162+
if (version == 50 || version == 51) {
3163+
// OpenMP 5.0: 2.10.1 Task construct restrictions
3164+
CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_detach,
3165+
{llvm::omp::Clause::OMPC_mergeable});
3166+
} else if (version >= 52) {
3167+
// OpenMP 5.2: 12.5.2 Detach construct restrictions
3168+
if (FindClause(llvm::omp::Clause::OMPC_final)) {
3169+
context_.Say(GetContext().clauseSource,
3170+
"If a DETACH clause appears on a directive, then the encountering task must not be a FINAL task"_err_en_US);
3171+
}
3172+
3173+
const auto &detach{
3174+
std::get<parser::OmpClause::Detach>(detachClause->u)};
3175+
if (const auto *name{parser::Unwrap<parser::Name>(detach.v.v)}) {
3176+
Symbol *eventHandleSym{name->symbol};
3177+
auto checkVarAppearsInDataEnvClause = [&](const parser::OmpObjectList
3178+
&objs,
3179+
std::string clause) {
3180+
for (const auto &obj : objs.v) {
3181+
if (const parser::Name *
3182+
objName{parser::Unwrap<parser::Name>(obj)}) {
3183+
if (&objName->symbol->GetUltimate() == eventHandleSym) {
3184+
context_.Say(GetContext().clauseSource,
3185+
"A variable: `%s` that appears in a DETACH clause cannot appear on %s clause on the same construct"_err_en_US,
3186+
objName->source, clause);
3187+
}
3188+
}
3189+
}
3190+
};
3191+
if (auto *dataEnvClause{
3192+
FindClause(llvm::omp::Clause::OMPC_private)}) {
3193+
const auto &pClause{
3194+
std::get<parser::OmpClause::Private>(dataEnvClause->u)};
3195+
checkVarAppearsInDataEnvClause(pClause.v, "PRIVATE");
3196+
} else if (auto *dataEnvClause{
3197+
FindClause(llvm::omp::Clause::OMPC_shared)}) {
3198+
const auto &sClause{
3199+
std::get<parser::OmpClause::Shared>(dataEnvClause->u)};
3200+
checkVarAppearsInDataEnvClause(sClause.v, "SHARED");
3201+
} else if (auto *dataEnvClause{
3202+
FindClause(llvm::omp::Clause::OMPC_firstprivate)}) {
3203+
const auto &fpClause{
3204+
std::get<parser::OmpClause::Firstprivate>(dataEnvClause->u)};
3205+
checkVarAppearsInDataEnvClause(fpClause.v, "FIRSTPRIVATE");
3206+
} else if (auto *dataEnvClause{
3207+
FindClause(llvm::omp::Clause::OMPC_in_reduction)}) {
3208+
const auto &irClause{
3209+
std::get<parser::OmpClause::InReduction>(dataEnvClause->u)};
3210+
checkVarAppearsInDataEnvClause(
3211+
std::get<parser::OmpObjectList>(irClause.v.t), "IN_REDUCTION");
3212+
}
3213+
}
3214+
}
3215+
}
3216+
}
3217+
31593218
auto testThreadprivateVarErr = [&](Symbol sym, parser::Name name,
31603219
llvmOmpClause clauseTy) {
31613220
if (sym.test(Symbol::Flag::OmpThreadprivate))
@@ -3238,7 +3297,6 @@ CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
32383297
CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains)
32393298
CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
32403299
CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
3241-
CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach)
32423300
CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
32433301
CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
32443302
CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
@@ -3745,14 +3803,14 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
37453803

37463804
void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) {
37473805
CheckAllowedClause(llvm::omp::Clause::OMPC_shared);
3748-
CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "SHARED");
3806+
CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "SHARED");
37493807
CheckCrayPointee(x.v, "SHARED");
37503808
}
37513809
void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) {
37523810
SymbolSourceMap symbols;
37533811
GetSymbolsInObjectList(x.v, symbols);
37543812
CheckAllowedClause(llvm::omp::Clause::OMPC_private);
3755-
CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "PRIVATE");
3813+
CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "PRIVATE");
37563814
CheckIntentInPointer(symbols, llvm::omp::Clause::OMPC_private);
37573815
CheckCrayPointee(x.v, "PRIVATE");
37583816
}
@@ -3781,50 +3839,50 @@ bool OmpStructureChecker::IsDataRefTypeParamInquiry(
37813839
return dataRefIsTypeParamInquiry;
37823840
}
37833841

3784-
void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
3842+
void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
37853843
const parser::CharBlock &source, const parser::OmpObjectList &objList,
37863844
llvm::StringRef clause) {
37873845
for (const auto &ompObject : objList.v) {
3788-
common::visit(
3789-
common::visitors{
3790-
[&](const parser::Designator &designator) {
3791-
if (const auto *dataRef{
3792-
std::get_if<parser::DataRef>(&designator.u)}) {
3793-
if (IsDataRefTypeParamInquiry(dataRef)) {
3846+
CheckVarIsNotPartOfAnotherVar(source, ompObject, clause);
3847+
}
3848+
}
3849+
3850+
void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
3851+
const parser::CharBlock &source, const parser::OmpObject &ompObject,
3852+
llvm::StringRef clause) {
3853+
common::visit(
3854+
common::visitors{
3855+
[&](const parser::Designator &designator) {
3856+
if (const auto *dataRef{
3857+
std::get_if<parser::DataRef>(&designator.u)}) {
3858+
if (IsDataRefTypeParamInquiry(dataRef)) {
3859+
context_.Say(source,
3860+
"A type parameter inquiry cannot appear on the %s directive"_err_en_US,
3861+
ContextDirectiveAsFortran());
3862+
} else if (parser::Unwrap<parser::StructureComponent>(
3863+
ompObject) ||
3864+
parser::Unwrap<parser::ArrayElement>(ompObject)) {
3865+
if (llvm::omp::nonPartialVarSet.test(GetContext().directive)) {
37943866
context_.Say(source,
3795-
"A type parameter inquiry cannot appear on the %s "
3796-
"directive"_err_en_US,
3867+
"A variable that is part of another variable (as an array or structure element) cannot appear on the %s directive"_err_en_US,
37973868
ContextDirectiveAsFortran());
3798-
} else if (parser::Unwrap<parser::StructureComponent>(
3799-
ompObject) ||
3800-
parser::Unwrap<parser::ArrayElement>(ompObject)) {
3801-
if (llvm::omp::nonPartialVarSet.test(
3802-
GetContext().directive)) {
3803-
context_.Say(source,
3804-
"A variable that is part of another variable (as an "
3805-
"array or structure element) cannot appear on the %s "
3806-
"directive"_err_en_US,
3807-
ContextDirectiveAsFortran());
3808-
} else {
3809-
context_.Say(source,
3810-
"A variable that is part of another variable (as an "
3811-
"array or structure element) cannot appear in a "
3812-
"%s clause"_err_en_US,
3813-
clause.data());
3814-
}
3869+
} else {
3870+
context_.Say(source,
3871+
"A variable that is part of another variable (as an array or structure element) cannot appear in a %s clause"_err_en_US,
3872+
clause.data());
38153873
}
38163874
}
3817-
},
3818-
[&](const parser::Name &name) {},
3819-
},
3820-
ompObject.u);
3821-
}
3875+
}
3876+
},
3877+
[&](const parser::Name &name) {},
3878+
},
3879+
ompObject.u);
38223880
}
38233881

38243882
void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
38253883
CheckAllowedClause(llvm::omp::Clause::OMPC_firstprivate);
38263884

3827-
CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE");
3885+
CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE");
38283886
CheckCrayPointee(x.v, "FIRSTPRIVATE");
38293887
CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v);
38303888

@@ -4147,6 +4205,33 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
41474205
}
41484206
}
41494207

4208+
void OmpStructureChecker::Enter(const parser::OmpClause::Detach &x) {
4209+
unsigned version{context_.langOptions().OpenMPVersion};
4210+
if (version >= 52) {
4211+
SetContextClauseInfo(llvm::omp::Clause::OMPC_detach);
4212+
} else {
4213+
// OpenMP 5.0: 2.10.1 Task construct restrictions
4214+
CheckAllowedClause(llvm::omp::Clause::OMPC_detach);
4215+
}
4216+
// OpenMP 5.2: 12.5.2 Detach clause restrictions
4217+
if (version >= 52) {
4218+
CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v.v, "DETACH");
4219+
}
4220+
4221+
if (const auto *name{parser::Unwrap<parser::Name>(x.v.v)}) {
4222+
if (version >= 52 && IsPointer(*name->symbol)) {
4223+
context_.Say(GetContext().clauseSource,
4224+
"The event-handle: `%s` must not have the POINTER attribute"_err_en_US,
4225+
name->ToString());
4226+
}
4227+
if (!name->symbol->GetType()->IsNumeric(TypeCategory::Integer)) {
4228+
context_.Say(GetContext().clauseSource,
4229+
"The event-handle: `%s` must be of type integer(kind=omp_event_handle_kind)"_err_en_US,
4230+
name->ToString());
4231+
}
4232+
}
4233+
}
4234+
41504235
void OmpStructureChecker::CheckAllowedMapTypes(
41514236
const parser::OmpMapType::Value &type,
41524237
const std::list<parser::OmpMapType::Value> &allowedMapTypeList) {
@@ -4495,7 +4580,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
44954580
CheckAllowedClause(llvm::omp::Clause::OMPC_lastprivate);
44964581

44974582
const auto &objectList{std::get<parser::OmpObjectList>(x.v.t)};
4498-
CheckIsVarPartOfAnotherVar(
4583+
CheckVarIsNotPartOfAnotherVar(
44994584
GetContext().clauseSource, objectList, "LASTPRIVATE");
45004585
CheckCrayPointee(objectList, "LASTPRIVATE");
45014586

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ class OmpStructureChecker
233233
const common::Indirection<parser::ArrayElement> &, const parser::Name &);
234234
void CheckDoacross(const parser::OmpDoacross &doa);
235235
bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef);
236-
void CheckIsVarPartOfAnotherVar(const parser::CharBlock &source,
236+
void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
237+
const parser::OmpObject &obj, llvm::StringRef clause = "");
238+
void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
237239
const parser::OmpObjectList &objList, llvm::StringRef clause = "");
238240
void CheckThreadprivateOrDeclareTargetVar(
239241
const parser::OmpObjectList &objList);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
! REQUIRES: openmp_runtime
2+
! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=52
3+
4+
! OpenMP Version 5.2: 12.5.2
5+
! Various checks for DETACH Clause
6+
7+
program detach01
8+
use omp_lib, only: omp_event_handle_kind
9+
implicit none
10+
real :: e, x
11+
integer(omp_event_handle_kind) :: event_01, event_02(2)
12+
integer(omp_event_handle_kind), pointer :: event_03
13+
14+
type :: t
15+
integer(omp_event_handle_kind) :: event
16+
end type
17+
type(t) :: t_01
18+
19+
!ERROR: The event-handle: `e` must be of type integer(kind=omp_event_handle_kind)
20+
!$omp task detach(e)
21+
x = x + 1
22+
!$omp end task
23+
24+
!ERROR: If a DETACH clause appears on a directive, then the encountering task must not be a FINAL task
25+
!$omp task detach(event_01) final(.false.)
26+
x = x + 1
27+
!$omp end task
28+
29+
!ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on PRIVATE clause on the same construct
30+
!$omp task detach(event_01) private(event_01)
31+
x = x + 1
32+
!$omp end task
33+
34+
!ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on FIRSTPRIVATE clause on the same construct
35+
!$omp task detach(event_01) firstprivate(event_01)
36+
x = x + 1
37+
!$omp end task
38+
39+
!ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on SHARED clause on the same construct
40+
!$omp task detach(event_01) shared(event_01)
41+
x = x + 1
42+
!$omp end task
43+
44+
!ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on IN_REDUCTION clause on the same construct
45+
!$omp task detach(event_01) in_reduction(+:event_01)
46+
x = x + 1
47+
!$omp end task
48+
49+
!ERROR: A variable that is part of another variable (as an array or structure element) cannot appear in a DETACH clause
50+
!$omp task detach(event_02(1))
51+
x = x + 1
52+
!$omp end task
53+
54+
!ERROR: A variable that is part of another variable (as an array or structure element) cannot appear in a DETACH clause
55+
!$omp task detach(t_01%event)
56+
x = x + 1
57+
!$omp end task
58+
59+
!ERROR: The event-handle: `event_03` must not have the POINTER attribute
60+
!$omp task detach(event_03)
61+
x = x + 1
62+
!$omp end task
63+
end program
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
! REQUIRES: openmp_runtime
2+
! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=50
3+
! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=51
4+
5+
! OpenMP Version 5.0: 2.10.1
6+
! Various checks for DETACH Clause
7+
8+
program detach02
9+
use omp_lib, only: omp_event_handle_kind
10+
integer(omp_event_handle_kind) :: event_01, event_02
11+
12+
!ERROR: At most one DETACH clause can appear on the TASK directive
13+
!$omp task detach(event_01) detach(event_02)
14+
x = x + 1
15+
!$omp end task
16+
17+
!ERROR: Clause MERGEABLE is not allowed if clause DETACH appears on the TASK directive
18+
!$omp task detach(event_01) mergeable
19+
x = x + 1
20+
!$omp end task
21+
end program

llvm/include/llvm/Frontend/OpenMP/OMP.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,6 @@ def OMP_Task : Directive<"task"> {
11401140
VersionedClause<OMPC_Affinity, 50>,
11411141
VersionedClause<OMPC_Allocate>,
11421142
VersionedClause<OMPC_Depend>,
1143-
VersionedClause<OMPC_Detach, 50>,
11441143
VersionedClause<OMPC_FirstPrivate>,
11451144
VersionedClause<OMPC_InReduction>,
11461145
VersionedClause<OMPC_Mergeable>,
@@ -1150,6 +1149,7 @@ def OMP_Task : Directive<"task"> {
11501149
];
11511150
let allowedOnceClauses = [
11521151
VersionedClause<OMPC_Default>,
1152+
VersionedClause<OMPC_Detach, 50>,
11531153
VersionedClause<OMPC_Final>,
11541154
VersionedClause<OMPC_If>,
11551155
VersionedClause<OMPC_Priority>,

0 commit comments

Comments
 (0)