[flang-commits] [flang] [llvm] [flang][cuda][semantics] Reject unguarded host-only and device-only calls in CUDA HOST, DEVICE procedures (PR #228176)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Mon Oct 5 17:51:31 PDT 2026
================
@@ -644,42 +751,111 @@ template <bool IsCUFKernelDo> class DeviceContextChecker {
ErrorIfHostSymbol(assign->rhs, source);
}
if (auto msg{ActionStmtChecker<IsCUFKernelDo>::WhyNotOk(
- context_, x, isHostDevice)}) {
+ context_, x, callContext_)}) {
context_.Say(source, std::move(*msg));
}
},
[&](const auto &x) {
if (auto msg{ActionStmtChecker<IsCUFKernelDo>::WhyNotOk(
- context_, x, isHostDevice)}) {
+ context_, x, callContext_)}) {
context_.Say(source, std::move(*msg));
}
},
},
stmt.u);
}
void Check(const parser::IfConstruct &ic) {
+ const CallContext incoming{callContext_};
const auto &ifS{std::get<parser::Statement<parser::IfThenStmt>>(ic.t)};
- CheckUnwrappedExpr(context_, ifS.source,
- std::get<parser::ScalarLogicalExpr>(ifS.statement.t), isHostDevice);
+ const auto &condition{std::get<parser::ScalarLogicalExpr>(ifS.statement.t)};
+ CheckUnwrappedExpr(context_, ifS.source, condition, callContext_);
+ AllowGuardedCalls(condition);
+ const CallContext branchContext{callContext_};
Check(std::get<parser::Block>(ic.t));
- for (const auto &eib :
- std::get<std::list<parser::IfConstruct::ElseIfBlock>>(ic.t)) {
+ const auto &elseIfBlocks{
+ std::get<std::list<parser::IfConstruct::ElseIfBlock>>(ic.t)};
+ for (const auto &eib : elseIfBlocks) {
+ // An ELSEIF guard applies to its own arm and its plain ELSE, not to a
+ // later unrelated ELSEIF. Preserve a guard on the initial IF, if any.
+ callContext_ = branchContext;
----------------
eugeneepshteyn wrote:
(This comment was generated with the help of AI)
An arm after `else if (on_device())` only runs when `on_device()` was false, so only in the host copy. Resetting to the IF's context makes these host-only calls errors; both are accepted on main:
```fortran
module elseif_after_guard
contains
subroutine host_only()
end subroutine
attributes(device) subroutine device_only()
end subroutine
! The ELSE IF (y) arm runs only when on_device() is false.
attributes(host,device) subroutine later_else_if(x, y)
logical, value :: x, y
if (x) then
continue
else if (on_device()) then
call device_only()
else if (y) then
call host_only()
end if
end subroutine
! The plain ELSE also runs only when on_device() is false.
attributes(host,device) subroutine later_else(x, y)
logical, value :: x, y
if (x) then
continue
else if (on_device()) then
call device_only()
else if (y) then
continue
else
call host_only()
end if
end subroutine
end module
```
```console
$ flang -fsyntax-only -x cuda elseif_after_guard.cuf
error: Semantic errors in elseif_after_guard.cuf
elseif_after_guard.cuf:16:7: error: 'host_only' may not be called in device code
call host_only()
^^^^^^^^^^^^^^^^
elseif_after_guard.cuf:30:7: error: 'host_only' may not be called in device code
call host_only()
^^^^^^^^^^^^^^^^
```
The same control flow written as a nested IF inside the ELSE is accepted. Since the PR prefers not to error on valid code, I'd drop the reset: the arms then stay under the ELSE IF's guard. That also stops the `device_only` call at `cuf-hostdevice-return-guard.cuf:194` from being diagnosed, which belongs to the already-documented permissive class. Line 190 there currently pins the false positive.
https://github.com/llvm/llvm-project/pull/228176
More information about the flang-commits
mailing list