[flang-commits] [flang] [Flang][OpenMP] Implement OMP 5.2 implicit DECLARE TARGET procedure handling (PR #196826)

via flang-commits flang-commits at lists.llvm.org
Sun May 10 11:21:47 PDT 2026


https://github.com/blazie2004 created https://github.com/llvm/llvm-project/pull/196826

This implements OpenMP 5.2 && 7.8.2 , which states that if a name appears in a `[DECLARE TARGET] `directive and has not been explicitly typed as a variable or procedure, it should be treated as an external subroutine.

The following OpenMP code was being rejected when compiled with `-fopenmp -fopenmp-version=52 -c `
```
program my_fib
integer :: N = 8
!$omp declare target(fib)
   !$omp target
      call fib(N)
   !$omp end target
end program
subroutine fib(N)
integer :: N
!$omp declare target
     print*,"hello from fib"
     !...
end subroutine
```
## Error message: 
```
error: Cannot call function 'fib' like a subroutine
        call fib(N)
             ^^^
Implicit declaration of 'fib'
  !$omp declare target(fib)
```

## Summary

This fix enables procedure names (including forward-referenced subroutines) to be correctly handled in OpenMP `DECLARE TARGET` directives.

### Changes

- `resolve-names.cpp`  
  Creates an implicit `ProcEntityDetails` symbol for unknown names at program/module scope while respecting `IMPLICIT NONE` and avoiding incorrect forward references to local variables inside subprograms.

- `resolve-directives.cpp`  
  Promotes unspecialized `EntityDetails` symbols to `ProcEntityDetails`, adding the `EXTERNAL` attribute and `Subroutine` flag when needed for OpenMP procedure handling.

- `openmp-utils.cpp`  
  Extends `IsExtendedListItem()` to recognize `ProcEntityDetails` as valid extended-list items in OpenMP directives.

- `check-omp-structure.cpp`  
  Removes the outdated TODO and updates semantic checks to allow procedure names in `DECLARE TARGET` directives.



>From daeb7d373ffc4d44ea932a3f1b20d58a0b5067ba Mon Sep 17 00:00:00 2001
From: Jay Satish Kumar Patel <kumarpat at pe31.hpc.amslabs.hpecorp.net>
Date: Sun, 10 May 2026 13:04:32 -0500
Subject: [PATCH] Implement OMP 5.2 implicit DECLARE TARGET procedure handling

---
 flang/lib/Semantics/check-omp-structure.cpp |  6 +++--
 flang/lib/Semantics/openmp-utils.cpp        |  4 +++-
 flang/lib/Semantics/resolve-directives.cpp  | 13 +++++++++++
 flang/lib/Semantics/resolve-names.cpp       | 25 ++++++++++++++++++---
 4 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 33ea727343cf4..4792aa6b36bbf 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1297,11 +1297,13 @@ void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
   llvm::omp::Directive directive{GetContext().directive};
 
   if (name->symbol->GetUltimate().IsSubprogram()) {
-    if (directive == llvm::omp::Directive::OMPD_threadprivate)
+    if (directive == llvm::omp::Directive::OMPD_threadprivate) {
       context_.Say(name->source,
           "The procedure name cannot be in a %s directive"_err_en_US,
           ContextDirectiveAsFortran());
-    // TODO: Check for procedure name in declare target directive.
+    }
+    // OMP 5.2 7.8.2 p10: a procedure name in DECLARE TARGET is valid
+    // (treated as external subroutine if not otherwise specified).
   } else if (name->symbol->attrs().test(Attr::PARAMETER)) {
     if (directive == llvm::omp::Directive::OMPD_threadprivate)
       context_.Say(name->source,
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 58aefe2e1fc52..167d4dc936d7e 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -190,7 +190,9 @@ bool IsVariableListItem(const Symbol &sym) {
 }
 
 bool IsExtendedListItem(const Symbol &sym) {
-  return IsVariableListItem(sym) || sym.IsSubprogram();
+  // Extended-list item: variable, procedure, or procedure pointer
+  return IsVariableListItem(sym) || sym.IsSubprogram() ||
+      sym.has<ProcEntityDetails>();
 }
 
 bool IsTypeParamInquiry(const Symbol &sym) {
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index b97f7ce58a1c0..495c128ee3275 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2972,6 +2972,19 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
           name->symbol = func;
         }
       }
+      // OMP 5.2 §7.8.2 ¶10: If a name appears in a declare target directive
+      // and has not been explicitly typed as a variable or procedure, and
+      // no other declaration gives it a procedure property, treat it as an
+      // external subroutine.
+      if (symbol->has<EntityDetails>() &&
+          !symbol->attrs().test(Attr::EXTERNAL) &&
+          !symbol->test(Symbol::Flag::Function) &&
+          !symbol->test(Symbol::Flag::Subroutine)) {
+        // Symbol is still unspecialized EntityDetails - promote to external proc
+        symbol->attrs().set(Attr::EXTERNAL);
+        symbol->set_details(ProcEntityDetails{});
+        symbol->set(Symbol::Flag::Subroutine);
+      }
     }
     if (directive == llvm::omp::Directive::OMPD_target_data) {
       checkExclusivelists(symbol, Symbol::Flag::OmpUseDevicePtr, symbol,
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 4f824ef1321e9..7558e5ae9d8da 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -9165,18 +9165,37 @@ bool DeclarationVisitor::FindAndMarkDeclareTargetSymbol(
               return true;
             }
             // if we find a symbol that is not a function or subroutine, we
-            // currently escape without doing anything.
-            break;
+            // currently escape without doing anything. Do NOT fall through
+            // to create an implicit external procedure.
+            return false;
           }
 
           // This is our loop exit condition, as parent() has an inbuilt assert
           // if you call it on a top level scope, rather than returning a null
           // value.
           if (scope->IsTopLevel()) {
-            return false;
+            break;
           }
         }
       }
+
+      // OpenMP 5.2, 7.8.2 p10: a procedure name in DECLARE TARGET with no
+      // explicit data/procedure properties is treated as an external
+      // subroutine. Only apply this at program/module scope level, not inside
+      // subprograms where local variables could be forward-declared.
+      if (!isImplicitNoneType() &&
+          currScope().kind() != Scope::Kind::Subprogram &&
+          currScope().kind() != Scope::Kind::BlockConstruct) {
+        auto [it, inserted]{
+            currScope().try_emplace(name.source, Attrs{}, ProcEntityDetails{})};
+        Symbol &symbol{*it->second};
+        if (inserted || symbol.has<ProcEntityDetails>()) {
+          name.symbol = &symbol;
+          symbol.set(Symbol::Flag::Subroutine);
+          SetImplicitAttr(symbol, Attr::EXTERNAL);
+          return true;
+        }
+      }
     }
   }
   return false;



More information about the flang-commits mailing list