Skip to content

Commit cbe3b72

Browse files
authored
[flang][CUDA] Downgrade error to warning (#161570)
The compiler currently emit an error about the lack of an explicit procedure interface when an external procedure that is called via an implicit interface is known to have an dummy argument with a CUDA data attribute, even when the corresponding actual argument does have a CUDA data attribute. This behavior is inconsistent with what happens when such a call is to an external in another source file and its definition is not visible -- the compiler silently accepts an actual argument with a CUDA data attribute across the implicit interface. Harmonize this situation so that an actual argument with a CUDA data attribute in a reference to a procedure with an implicit interface elicits a usage warning encouraging the use of explicit interfaces. Only when the procedure's definition is visible, and incompatible, will an error message appear.
1 parent 1fba01a commit cbe3b72

File tree

8 files changed

+106
-38
lines changed

8 files changed

+106
-38
lines changed

flang/include/flang/Evaluate/characteristics.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ struct DummyDataObject {
251251
std::optional<std::string> *warning = nullptr) const;
252252
static std::optional<DummyDataObject> Characterize(
253253
const semantics::Symbol &, FoldingContext &);
254-
bool CanBePassedViaImplicitInterface(std::string *whyNot = nullptr) const;
254+
bool CanBePassedViaImplicitInterface(
255+
std::string *whyNot = nullptr, bool checkCUDA = true) const;
255256
bool IsPassedByDescriptor(bool isBindC) const;
256257
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
257258

@@ -307,7 +308,8 @@ struct DummyArgument {
307308
void SetOptional(bool = true);
308309
common::Intent GetIntent() const;
309310
void SetIntent(common::Intent);
310-
bool CanBePassedViaImplicitInterface(std::string *whyNot = nullptr) const;
311+
bool CanBePassedViaImplicitInterface(
312+
std::string *whyNot = nullptr, bool checkCUDA = true) const;
311313
bool IsTypelessIntrinsicDummy() const;
312314
bool IsCompatibleWith(const DummyArgument &, std::string *whyNot = nullptr,
313315
std::optional<std::string> *warning = nullptr) const;
@@ -402,7 +404,8 @@ struct Procedure {
402404
return !attrs.test(Attr::ImplicitInterface);
403405
}
404406
std::optional<int> FindPassIndex(std::optional<parser::CharBlock>) const;
405-
bool CanBeCalledViaImplicitInterface(std::string *whyNot = nullptr) const;
407+
bool CanBeCalledViaImplicitInterface(
408+
std::string *whyNot = nullptr, bool checkCUDA = true) const;
406409
bool CanOverride(const Procedure &, std::optional<int> passIndex) const;
407410
bool IsCompatibleWith(const Procedure &, bool ignoreImplicitVsExplicit,
408411
std::string *whyNot = nullptr, const SpecificIntrinsic * = nullptr,

flang/lib/Evaluate/characteristics.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ std::optional<DummyDataObject> DummyDataObject::Characterize(
458458
}
459459

460460
bool DummyDataObject::CanBePassedViaImplicitInterface(
461-
std::string *whyNot) const {
461+
std::string *whyNot, bool checkCUDA) const {
462462
if ((attrs &
463463
Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional,
464464
Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile})
@@ -482,7 +482,7 @@ bool DummyDataObject::CanBePassedViaImplicitInterface(
482482
*whyNot = "a dummy argument is polymorphic";
483483
}
484484
return false; // 15.4.2.2(3)(f)
485-
} else if (cudaDataAttr) {
485+
} else if (checkCUDA && cudaDataAttr) {
486486
if (whyNot) {
487487
*whyNot = "a dummy argument has a CUDA data attribute";
488488
}
@@ -1012,9 +1012,10 @@ common::Intent DummyArgument::GetIntent() const {
10121012
u);
10131013
}
10141014

1015-
bool DummyArgument::CanBePassedViaImplicitInterface(std::string *whyNot) const {
1015+
bool DummyArgument::CanBePassedViaImplicitInterface(
1016+
std::string *whyNot, bool checkCUDA) const {
10161017
if (const auto *object{std::get_if<DummyDataObject>(&u)}) {
1017-
return object->CanBePassedViaImplicitInterface(whyNot);
1018+
return object->CanBePassedViaImplicitInterface(whyNot, checkCUDA);
10181019
} else if (const auto *proc{std::get_if<DummyProcedure>(&u)}) {
10191020
return proc->CanBePassedViaImplicitInterface(whyNot);
10201021
} else {
@@ -1501,7 +1502,8 @@ std::optional<Procedure> Procedure::FromActuals(const ProcedureDesignator &proc,
15011502
return callee;
15021503
}
15031504

1504-
bool Procedure::CanBeCalledViaImplicitInterface(std::string *whyNot) const {
1505+
bool Procedure::CanBeCalledViaImplicitInterface(
1506+
std::string *whyNot, bool checkCUDA) const {
15051507
if (attrs.test(Attr::Elemental)) {
15061508
if (whyNot) {
15071509
*whyNot = "the procedure is elemental";
@@ -1524,7 +1526,7 @@ bool Procedure::CanBeCalledViaImplicitInterface(std::string *whyNot) const {
15241526
return false;
15251527
} else {
15261528
for (const DummyArgument &arg : dummyArguments) {
1527-
if (!arg.CanBePassedViaImplicitInterface(whyNot)) {
1529+
if (!arg.CanBePassedViaImplicitInterface(whyNot, checkCUDA)) {
15281530
return false;
15291531
}
15301532
}

flang/lib/Semantics/check-call.cpp

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,44 @@ static void CheckImplicitInterfaceArg(evaluate::ActualArgument &arg,
5656
"%VAL argument must be a scalar numeric or logical expression"_err_en_US);
5757
}
5858
if (const auto *expr{arg.UnwrapExpr()}) {
59-
if (const Symbol * base{GetFirstSymbol(*expr)};
60-
base && IsFunctionResult(*base)) {
61-
context.NoteDefinedSymbol(*base);
59+
if (const Symbol *base{GetFirstSymbol(*expr)}) {
60+
const Symbol &symbol{GetAssociationRoot(*base)};
61+
if (IsFunctionResult(symbol)) {
62+
context.NoteDefinedSymbol(symbol);
63+
}
6264
}
6365
if (IsBOZLiteral(*expr)) {
64-
messages.Say("BOZ argument requires an explicit interface"_err_en_US);
66+
messages.Say("BOZ argument %s requires an explicit interface"_err_en_US,
67+
expr->AsFortran());
6568
} else if (evaluate::IsNullPointerOrAllocatable(expr)) {
6669
messages.Say(
67-
"Null pointer argument requires an explicit interface"_err_en_US);
70+
"Null pointer argument '%s' requires an explicit interface"_err_en_US,
71+
expr->AsFortran());
6872
} else if (auto named{evaluate::ExtractNamedEntity(*expr)}) {
69-
const Symbol &symbol{named->GetLastSymbol()};
70-
if (IsAssumedRank(symbol)) {
73+
const Symbol &resolved{ResolveAssociations(named->GetLastSymbol())};
74+
if (IsAssumedRank(resolved)) {
7175
messages.Say(
72-
"Assumed rank argument requires an explicit interface"_err_en_US);
76+
"Assumed rank argument '%s' requires an explicit interface"_err_en_US,
77+
expr->AsFortran());
7378
}
79+
const Symbol &symbol{GetAssociationRoot(resolved)};
7480
if (symbol.attrs().test(Attr::ASYNCHRONOUS)) {
7581
messages.Say(
76-
"ASYNCHRONOUS argument requires an explicit interface"_err_en_US);
82+
"ASYNCHRONOUS argument '%s' requires an explicit interface"_err_en_US,
83+
expr->AsFortran());
7784
}
7885
if (symbol.attrs().test(Attr::VOLATILE)) {
7986
messages.Say(
80-
"VOLATILE argument requires an explicit interface"_err_en_US);
87+
"VOLATILE argument '%s' requires an explicit interface"_err_en_US,
88+
expr->AsFortran());
89+
}
90+
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
91+
if (object->cudaDataAttr()) {
92+
messages.Warn(/*inModuleFile=*/false, context.languageFeatures(),
93+
common::UsageWarning::CUDAUsage,
94+
"Actual argument '%s' with CUDA data attributes should be passed via an explicit interface"_warn_en_US,
95+
expr->AsFortran());
96+
}
8197
}
8298
} else if (auto argChars{characteristics::DummyArgument::FromActual(
8399
"actual argument", *expr, context.foldingContext(),
@@ -2387,44 +2403,51 @@ bool CheckArguments(const characteristics::Procedure &proc,
23872403
evaluate::FoldingContext foldingContext{context.foldingContext()};
23882404
parser::ContextualMessages &messages{foldingContext.messages()};
23892405
bool allowArgumentConversions{true};
2406+
parser::Messages implicitBuffer;
23902407
if (!explicitInterface || treatingExternalAsImplicit) {
2391-
parser::Messages buffer;
23922408
{
2393-
auto restorer{messages.SetMessages(buffer)};
2409+
auto restorer{messages.SetMessages(implicitBuffer)};
23942410
for (auto &actual : actuals) {
23952411
if (actual) {
23962412
CheckImplicitInterfaceArg(*actual, messages, context);
23972413
}
23982414
}
23992415
}
2400-
if (!buffer.empty()) {
2416+
if (implicitBuffer.AnyFatalError()) {
24012417
if (auto *msgs{messages.messages()}) {
2402-
msgs->Annex(std::move(buffer));
2418+
msgs->Annex(std::move(implicitBuffer));
24032419
}
24042420
return false; // don't pile on
24052421
}
24062422
allowArgumentConversions = false;
24072423
}
24082424
if (explicitInterface) {
2409-
auto buffer{CheckExplicitInterface(proc, actuals, context, &scope,
2425+
auto explicitBuffer{CheckExplicitInterface(proc, actuals, context, &scope,
24102426
intrinsic, allowArgumentConversions,
24112427
/*extentErrors=*/true, ignoreImplicitVsExplicit)};
2412-
if (!buffer.empty()) {
2428+
if (!explicitBuffer.empty()) {
24132429
if (treatingExternalAsImplicit) {
2414-
if (auto *msg{foldingContext.Warn(
2430+
// Combine all messages into one warning
2431+
if (auto *warning{messages.Warn(/*inModuleFile=*/false,
2432+
context.languageFeatures(),
24152433
common::UsageWarning::KnownBadImplicitInterface,
24162434
"If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) {
2417-
buffer.AttachTo(*msg, parser::Severity::Because);
2418-
} else {
2419-
buffer.clear();
2435+
explicitBuffer.AttachTo(*warning, parser::Severity::Because);
24202436
}
2437+
} else if (auto *msgs{messages.messages()}) {
2438+
msgs->Annex(std::move(explicitBuffer));
24212439
}
2422-
if (auto *msgs{messages.messages()}) {
2423-
msgs->Annex(std::move(buffer));
2424-
}
2440+
// These messages override any in implicitBuffer.
24252441
return false;
24262442
}
24272443
}
2428-
return true;
2444+
if (!implicitBuffer.empty()) {
2445+
if (auto *msgs{messages.messages()}) {
2446+
msgs->Annex(std::move(implicitBuffer));
2447+
}
2448+
return false;
2449+
} else {
2450+
return true; // no messages
2451+
}
24292452
}
24302453
} // namespace Fortran::semantics

flang/lib/Semantics/expression.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3628,7 +3628,7 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
36283628
if (chars) {
36293629
std::string whyNot;
36303630
if (treatExternalAsImplicit &&
3631-
!chars->CanBeCalledViaImplicitInterface(&whyNot)) {
3631+
!chars->CanBeCalledViaImplicitInterface(&whyNot, /*checkCUDA=*/false)) {
36323632
if (auto *msg{Say(callSite,
36333633
"References to the procedure '%s' require an explicit interface"_err_en_US,
36343634
DEREF(procSymbol).name())};

flang/test/Semantics/boz-literal-constants.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ subroutine explicit(n, x, c)
120120
!ERROR: Actual argument 'z'55'' associated with dummy argument 'c=' is not a variable or typed expression
121121
call explicit(z'deadbeef', o'666', b'01010101')
122122

123-
!ERROR: BOZ argument requires an explicit interface
123+
!ERROR: BOZ argument z'12345' requires an explicit interface
124124
call implictSub(Z'12345')
125125

126126
!ERROR: Output item must not be a BOZ literal constant

flang/test/Semantics/call13.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ subroutine s(assumedRank, coarray, class, classStar, typeStar)
2020
real :: array(implicit01()) ! 15.4.2.2(2)
2121
!ERROR: Keyword 'keyword=' may not appear in a reference to a procedure with an implicit interface
2222
call implicit10(1, 2, keyword=3) ! 15.4.2.2(1)
23-
!ERROR: Assumed rank argument requires an explicit interface
23+
!ERROR: Assumed rank argument 'assumedrank' requires an explicit interface
2424
call implicit11(assumedRank) ! 15.4.2.2(3)(c)
2525
call implicit12(coarray) ! ok
2626
call implicit12a(coarray[1]) ! ok

flang/test/Semantics/cuf24.cuf

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1 -fopenacc
2+
3+
subroutine implicitDeviceInSameFile(v)
4+
real, device :: v(10)
5+
end
6+
7+
subroutine implicitNonDeviceInSameFile(v)
8+
real :: v(10)
9+
end
10+
11+
program p
12+
real, device :: dev(10)
13+
real :: host(10)
14+
interface
15+
subroutine explicitDevice(v)
16+
real, device :: v(10)
17+
end
18+
subroutine explicitNonDevice(v)
19+
real :: v(10)
20+
end
21+
end interface
22+
!WARNING: Actual argument 'dev' with CUDA data attributes should be passed via an explicit interface [-Wcuda-usage]
23+
call implicit1(dev)
24+
call implicit2(host)
25+
!WARNING: Actual argument 'dev' with CUDA data attributes should be passed via an explicit interface [-Wcuda-usage]
26+
call implicitDeviceInSameFile(dev)
27+
!WARNING: If the procedure's interface were explicit, this reference would be in error [-Wknown-bad-implicit-interface]
28+
!BECAUSE: dummy argument 'v=' has ATTRIBUTES(DEVICE) but its associated actual argument has no CUDA data attribute
29+
call implicitDeviceInSameFile(host)
30+
!WARNING: If the procedure's interface were explicit, this reference would be in error [-Wknown-bad-implicit-interface]
31+
!BECAUSE: dummy argument 'v=' has no CUDA data attribute but its associated actual argument has ATTRIBUTES(DEVICE)
32+
call implicitNonDeviceInSameFile(dev)
33+
call implicitNonDeviceInSameFile(host)
34+
call explicitDevice(dev)
35+
!ERROR: dummy argument 'v=' has ATTRIBUTES(DEVICE) but its associated actual argument has no CUDA data attribute
36+
call explicitDevice(host)
37+
!ERROR: dummy argument 'v=' has no CUDA data attribute but its associated actual argument has ATTRIBUTES(DEVICE)
38+
call explicitNonDevice(dev)
39+
call explicitNonDevice(host)
40+
end

flang/test/Semantics/null01.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ function f3()
116116
call optionalAllocatable(null(mold=ip0))
117117
call optionalAllocatable(null(mold=ia0)) ! fine
118118
call optionalAllocatable(null()) ! fine
119-
!ERROR: Null pointer argument requires an explicit interface
119+
!ERROR: Null pointer argument 'NULL()' requires an explicit interface
120120
call implicit(null())
121-
!ERROR: Null pointer argument requires an explicit interface
121+
!ERROR: Null pointer argument 'null(mold=ip0)' requires an explicit interface
122122
call implicit(null(mold=ip0))
123123
!ERROR: A NULL() pointer is not allowed for 'x=' intrinsic argument
124124
print *, sin(null(rp0))

0 commit comments

Comments
 (0)