offtarget 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (886) hide show
  1. offload/__init__.py +10 -0
  2. offload/__main__.py +5 -0
  3. offload/_bundle.py +74 -0
  4. offload/_root/catalog/acc/claims.yaml +197 -0
  5. offload/_root/catalog/acc/clauses.yaml +391 -0
  6. offload/_root/catalog/acc/no_oracle.yaml +89 -0
  7. offload/_root/catalog/acc/out_of_scope.yaml +17 -0
  8. offload/_root/catalog/acc/rules.yaml +433 -0
  9. offload/_root/catalog/acc/semantic_atoms.yaml +27 -0
  10. offload/_root/catalog/acc/skeleton.yaml +1567 -0
  11. offload/_root/catalog/acc/spec_overrides.yaml +72 -0
  12. offload/_root/catalog/later_forms.yaml +76 -0
  13. offload/_root/catalog/machines.yaml +218 -0
  14. offload/_root/catalog/omp/claims.yaml +2047 -0
  15. offload/_root/catalog/omp/clauses.yaml +914 -0
  16. offload/_root/catalog/omp/derivations.yaml +177 -0
  17. offload/_root/catalog/omp/memory_modes.yaml +40 -0
  18. offload/_root/catalog/omp/no_oracle.yaml +373 -0
  19. offload/_root/catalog/omp/omp50_delta.yaml +377 -0
  20. offload/_root/catalog/omp/out_of_scope.yaml +64 -0
  21. offload/_root/catalog/omp/port_equivalence.yaml +352 -0
  22. offload/_root/catalog/omp/rules.yaml +3131 -0
  23. offload/_root/catalog/omp/rules_coverage.yaml +9939 -0
  24. offload/_root/catalog/omp/semantic_atoms.yaml +34 -0
  25. offload/_root/catalog/omp/skeleton.yaml +9181 -0
  26. offload/_root/catalog/omp/spec_overrides.yaml +932 -0
  27. offload/_root/catalog/toolchains.yaml +472 -0
  28. offload/_root/catalog/vendor_claims_generic.yaml +40925 -0
  29. offload/_root/data/canuse/b.json +1 -0
  30. offload/_root/data/canuse/c.json +1 -0
  31. offload/_root/data/canuse/columns.json +1 -0
  32. offload/_root/data/canuse/d.json +1 -0
  33. offload/_root/data/canuse/f.json +1 -0
  34. offload/_root/data/canuse/index.json +1 -0
  35. offload/_root/data/canuse/p.json +1 -0
  36. offload/_root/data/compilers/afar.json +1 -0
  37. offload/_root/data/compilers/cce19.json +1 -0
  38. offload/_root/data/compilers/cce20.json +1 -0
  39. offload/_root/data/compilers/cce21.json +1 -0
  40. offload/_root/data/compilers/cce21_4.json +1 -0
  41. offload/_root/data/compilers/gcc.json +1 -0
  42. offload/_root/data/compilers/gcc_sm80.json +1 -0
  43. offload/_root/data/compilers/nvhpc.json +1 -0
  44. offload/_root/data/compilers/nvhpc269.json +1 -0
  45. offload/_root/data/findings/AUTHORING.md +107 -0
  46. offload/_root/data/findings/README.md +33 -0
  47. offload/_root/data/findings/afar-c-error-directive-in-target-link.c +37 -0
  48. offload/_root/data/findings/afar-c-lastprivate-distribute-parallel-for-small-type.c +56 -0
  49. offload/_root/data/findings/afar-c-teams-distribute-reduction-first-team.c +59 -0
  50. offload/_root/data/findings/afar-c-update-iterator-copy-mode.c +45 -0
  51. offload/_root/data/findings/afar-c-xnack1-target-is-present.c +31 -0
  52. offload/_root/data/findings/afar-cce19-character-defaultmap.f90 +27 -0
  53. offload/_root/data/findings/afar-cce19-task-in-target-ice.f90 +23 -0
  54. offload/_root/data/findings/afar-clang-requires-device-safesync-hang.c +24 -0
  55. offload/_root/data/findings/afar-flang-firstprivate-map-two-targets.f90 +34 -0
  56. offload/_root/data/findings/afar-flang-loop-bind-teams-reduction.f90 +27 -0
  57. offload/_root/data/findings/afar-flang-task-reduction-max.f90 +31 -0
  58. offload/_root/data/findings/afar-flang-thread-limit-target-parallel-do.f90 +22 -0
  59. offload/_root/data/findings/afar-linear-ref-omp50.f90 +29 -0
  60. offload/_root/data/findings/afar-loop-bind-thread-in-teams.f90 +44 -0
  61. offload/_root/data/findings/afar-nontemporal-assumed-shape-ice.f90 +38 -0
  62. offload/_root/data/findings/afar-udr-derived-type-link.f90 +41 -0
  63. offload/_root/data/findings/afar-xnack1-atomic-min-nested-parallel.f90 +53 -0
  64. offload/_root/data/findings/afar-xnack1-present-not-enforced.f90 +36 -0
  65. offload/_root/data/findings/afar-xnack1-target-is-present.f90 +47 -0
  66. offload/_root/data/findings/apps/afar-cce-teams-private-array-nested-parallel-wrong.f90 +100 -0
  67. offload/_root/data/findings/apps/cce20-device-link-infer-address-spaces-crash.f90 +159 -0
  68. offload/_root/data/findings/apps/cce21-device-link-agpr-copy-mfma-assert.f90 +343 -0
  69. offload/_root/data/findings/apps/cce21-private-array-loaded-subscript-wrong.f90 +113 -0
  70. offload/_root/data/findings/apps/controls/bubble-simple-rddot.f90 +284 -0
  71. offload/_root/data/findings/apps/controls/bubble-two-substeps.f90 +287 -0
  72. offload/_root/data/findings/apps/controls/cbc-inline.f90 +97 -0
  73. offload/_root/data/findings/apps/controls/cbc-integers.f90 +91 -0
  74. offload/_root/data/findings/apps/controls/cbc-only-density.f90 +97 -0
  75. offload/_root/data/findings/apps/controls/cbc-only-velocity.f90 +97 -0
  76. offload/_root/data/findings/apps/controls/gcc-acc-c-tile-control.c +26 -0
  77. offload/_root/data/findings/apps/controls/gcc-acc-tile-control.f90 +29 -0
  78. offload/_root/data/findings/apps/controls/private-array-subscript-probes.f90 +45 -0
  79. offload/_root/data/findings/apps/controls/teams-private-array-read-par.f90 +31 -0
  80. offload/_root/data/findings/apps/controls/teams-private-array-seq.f90 +30 -0
  81. offload/_root/data/findings/apps/controls/teams-private-array-shared.f90 +34 -0
  82. offload/_root/data/findings/apps/controls/teams-private-array-split.f90 +36 -0
  83. offload/_root/data/findings/apps/controls/teams-private-array-write-par.f90 +31 -0
  84. offload/_root/data/findings/apps/gcc-acc-c-tile-partial-tiles-wrong.c +60 -0
  85. offload/_root/data/findings/apps/gcc-acc-tile-partial-tiles-wrong.f90 +64 -0
  86. offload/_root/data/findings/catalog/afar-c-complex-reduction-link.c +66 -0
  87. offload/_root/data/findings/catalog/afar-c-critical-hint-link.c +68 -0
  88. offload/_root/data/findings/catalog/afar-c-ordered-doacross-link.c +62 -0
  89. offload/_root/data/findings/catalog/afar-c-reduction-task-link.c +81 -0
  90. offload/_root/data/findings/catalog/afar-flang-absent-optional-target-crash.f90 +76 -0
  91. offload/_root/data/findings/catalog/afar-flang-critical-target-parallel-wrong.f90 +74 -0
  92. offload/_root/data/findings/catalog/afar-flang-dist-schedule-chunk.f90 +80 -0
  93. offload/_root/data/findings/catalog/afar-flang-linear-parallel-ice.f90 +68 -0
  94. offload/_root/data/findings/catalog/afar-flang-named-critical-link.f90 +67 -0
  95. offload/_root/data/findings/catalog/afar-flang-ordered-wrong.f90 +78 -0
  96. offload/_root/data/findings/catalog/afar-flang-reduction-minus-rejected.f90 +77 -0
  97. offload/_root/data/findings/catalog/afar-flang-reduction-task-ice.f90 +68 -0
  98. offload/_root/data/findings/catalog/afar-flang-sections-in-target-ice.f90 +67 -0
  99. offload/_root/data/findings/catalog/afar-flang-workdistribute-ice.f90 +71 -0
  100. offload/_root/data/findings/catalog/c-allocate-const-mem-firstprivate-wrong.c +79 -0
  101. offload/_root/data/findings/catalog/cce-acc-allocate-in-compute-crash.f90 +65 -0
  102. offload/_root/data/findings/catalog/cce-acc-character-dummy-routine-ice.f90 +81 -0
  103. offload/_root/data/findings/catalog/cce-acc-loop-seq-in-routine-wrong.f90 +81 -0
  104. offload/_root/data/findings/catalog/cce-acc-stop-in-compute-crash.f90 +62 -0
  105. offload/_root/data/findings/catalog/cce-allocators-stack-ice.f90 +81 -0
  106. offload/_root/data/findings/catalog/cce-atomic-default-mem-order-acquire-release-rejected.f90 +61 -0
  107. offload/_root/data/findings/catalog/cce-c-allocate-align-link.c +83 -0
  108. offload/_root/data/findings/catalog/cce-c-allocate-allocator-modifier-rejected.c +77 -0
  109. offload/_root/data/findings/catalog/cce-c-device-type-directive-name-ice.c +70 -0
  110. offload/_root/data/findings/catalog/cce-c-dist-schedule-chunk.c +81 -0
  111. offload/_root/data/findings/catalog/cce-c-f-pointer-device-link.f90 +70 -0
  112. offload/_root/data/findings/catalog/cce-c-lastprivate-parallel-in-seq-loop-wrong.c +64 -0
  113. offload/_root/data/findings/catalog/cce-c-long-double-ice.c +61 -0
  114. offload/_root/data/findings/catalog/cce-c-map-iterator-ice.c +92 -0
  115. offload/_root/data/findings/catalog/cce-c-motion-iterator-rejected.c +68 -0
  116. offload/_root/data/findings/catalog/cce-c-ordered-doacross-link.c +64 -0
  117. offload/_root/data/findings/catalog/cce-c-ordered-link.c +77 -0
  118. offload/_root/data/findings/catalog/cce-c-recursive-function-ice.c +62 -0
  119. offload/_root/data/findings/catalog/cce-c-reduction-inscan-ice.c +67 -0
  120. offload/_root/data/findings/catalog/cce-c-reduction-task-link.c +73 -0
  121. offload/_root/data/findings/catalog/cce-c-sections-in-target-wrong.c +70 -0
  122. offload/_root/data/findings/catalog/cce-c-single-masked-task-link.c +66 -0
  123. offload/_root/data/findings/catalog/cce-c-task-in-target-link.c +75 -0
  124. offload/_root/data/findings/catalog/cce-c-uses-allocators-traits-rejected.c +71 -0
  125. offload/_root/data/findings/catalog/cce-flang-allocate-const-mem-ice.f90 +67 -0
  126. offload/_root/data/findings/catalog/cce-flang-allocate-in-target-crash.f90 +68 -0
  127. offload/_root/data/findings/catalog/cce-flang-lastprivate-parallel-in-seq-loop-wrong.f90 +83 -0
  128. offload/_root/data/findings/catalog/cce-flang-stop-in-target-crash.f90 +64 -0
  129. offload/_root/data/findings/catalog/cce-iterator-link.f90 +67 -0
  130. offload/_root/data/findings/catalog/cce-linear-lastprivate-ice.f90 +69 -0
  131. offload/_root/data/findings/catalog/cce-nohost-link.f90 +90 -0
  132. offload/_root/data/findings/catalog/cce-reduction-inscan-ice.f90 +66 -0
  133. offload/_root/data/findings/catalog/cce-reduction-task-link.f90 +76 -0
  134. offload/_root/data/findings/catalog/cce19-reduction-task-addrspacecast-ice.f90 +77 -0
  135. offload/_root/data/findings/catalog/cce20-c-defaultmap-private-storage-rejected.c +85 -0
  136. offload/_root/data/findings/catalog/cce20-map-self-rejected.f90 +64 -0
  137. offload/_root/data/findings/catalog/cce21-c-declare-target-local-rejected.c +73 -0
  138. offload/_root/data/findings/catalog/cce21-c-target-loop-ice.c +77 -0
  139. offload/_root/data/findings/catalog/defaultmap-present-private-array-abort.f90 +73 -0
  140. offload/_root/data/findings/catalog/nvhpc-acc-associate-illegal-address.f90 +66 -0
  141. offload/_root/data/findings/catalog/nvhpc-c-function-pointer-kernel-missing.c +68 -0
  142. offload/_root/data/findings/catalog/nvhpc-c-long-double-kernel-missing.c +64 -0
  143. offload/_root/data/findings/catalog/nvhpc-c-static-local-kernel-missing.c +73 -0
  144. offload/_root/data/findings/catalog/nvhpc-c-vla-kernel-missing.c +63 -0
  145. offload/_root/data/findings/catalog/nvhpc-flang-type-bound-kernel-missing.f90 +86 -0
  146. offload/_root/data/findings/cce-acc-init-set-runtime-assert.f90 +35 -0
  147. offload/_root/data/findings/cce-acc-reduction-200-int8-scalars-wrong.f90 +1278 -0
  148. offload/_root/data/findings/cce-acc-routine-bind-module-ice.f90 +35 -0
  149. offload/_root/data/findings/cce-acc-self-condition-ignored.f90 +17 -0
  150. offload/_root/data/findings/cce-c-dist-schedule-segv.c +34 -0
  151. offload/_root/data/findings/cce-c-map-delete-refcount.c +47 -0
  152. offload/_root/data/findings/cce-c-map-present-assert.c +45 -0
  153. offload/_root/data/findings/cce-c-openmp45-device-link.c +34 -0
  154. offload/_root/data/findings/cce-c-parallel-for-in-target.c +73 -0
  155. offload/_root/data/findings/cce-defaultmap-tofrom-explicit-map.f90 +92 -0
  156. offload/_root/data/findings/cce-mapper-next-map-copy-back.f90 +48 -0
  157. offload/_root/data/findings/cce-trim-merge-output-one-character.f90 +40 -0
  158. offload/_root/data/findings/cce-uses-allocators-traits-omp50.f90 +28 -0
  159. offload/_root/data/findings/cce19-acc-if-false-link.f90 +17 -0
  160. offload/_root/data/findings/cce19-c-reduction-63-scalars-one-wrong.c +235 -0
  161. offload/_root/data/findings/cce19-target-parallel-guarded-loop.f90 +35 -0
  162. offload/_root/data/findings/cce19-target-parallel-single-ice.f90 +38 -0
  163. offload/_root/data/findings/cce21-alloc-2e32-sum-wrong.f90 +68 -0
  164. offload/_root/data/findings/cce21-defaultmap-aggregate-flag.f90 +52 -0
  165. offload/_root/data/findings/cce21-in-reduction-assumed-shape-write-lost.f90 +61 -0
  166. offload/_root/data/findings/cce21-in-reduction-taskwait-link.f90 +59 -0
  167. offload/_root/data/findings/cce21-linear-modifier-default-none.f90 +59 -0
  168. offload/_root/data/findings/clang-c-declare-target-to-omp52.c +50 -0
  169. offload/_root/data/findings/clang-c-linear-directive-name-hang.c +30 -0
  170. offload/_root/data/findings/clang-c-map-iterator-omp51.c +51 -0
  171. offload/_root/data/findings/clang-c-shared-declare-target-combined.c +58 -0
  172. offload/_root/data/findings/dataenv/cce-value-dummy-target-write-visible.f90 +74 -0
  173. offload/_root/data/findings/defaultmap-none-loop-variable.f90 +69 -0
  174. offload/_root/data/findings/fortran-loop-bind-thread-in-parallel.f90 +53 -0
  175. offload/_root/data/findings/fortran-schedule-static-in-target.f90 +55 -0
  176. offload/_root/data/findings/fresh/cce-common-block-name-in-map-rejected.f90 +47 -0
  177. offload/_root/data/findings/fresh/cce-if-stop-in-target-ice-one-condition.f90 +38 -0
  178. offload/_root/data/findings/fresh/cce-if-stop-in-target-ice.f90 +43 -0
  179. offload/_root/data/findings/fresh/cce-list-directed-print-in-target-copy-crash.f90 +40 -0
  180. offload/_root/data/findings/fresh/cce19-acc-automatic-array-copyout-copy-crash.f90 +56 -0
  181. offload/_root/data/findings/fresh/cce21-combined-if-runtime-expr-loop-skipped.f90 +57 -0
  182. offload/_root/data/findings/fresh/nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.f90 +67 -0
  183. offload/_root/data/findings/fresh/nvhpc-teams-loop-module-allocatable-assign-fort2-crash.f90 +73 -0
  184. offload/_root/data/findings/fuzz/1131-afar.f90 +48 -0
  185. offload/_root/data/findings/fuzz/1391-cce20.f90 +40 -0
  186. offload/_root/data/findings/fuzz/14-cce20.f90 +36 -0
  187. offload/_root/data/findings/fuzz/1505-cce20.f90 +88 -0
  188. offload/_root/data/findings/fuzz/1540-afar.f90 +44 -0
  189. offload/_root/data/findings/fuzz/158-afar.f90 +63 -0
  190. offload/_root/data/findings/fuzz/16-cce20.f90 +39 -0
  191. offload/_root/data/findings/fuzz/1644-cce21.f90 +31 -0
  192. offload/_root/data/findings/fuzz/165-cce19.f90 +52 -0
  193. offload/_root/data/findings/fuzz/1749-cce20.f90 +34 -0
  194. offload/_root/data/findings/fuzz/1936-afar.f90 +61 -0
  195. offload/_root/data/findings/fuzz/1978-cce19.f90 +41 -0
  196. offload/_root/data/findings/fuzz/2014-cce19.f90 +46 -0
  197. offload/_root/data/findings/fuzz/2224-cce19.f90 +30 -0
  198. offload/_root/data/findings/fuzz/2334-cce19.f90 +57 -0
  199. offload/_root/data/findings/fuzz/536-cce19.f90 +38 -0
  200. offload/_root/data/findings/fuzz/591-cce19.f90 +54 -0
  201. offload/_root/data/findings/fuzz/650-afar.f90 +58 -0
  202. offload/_root/data/findings/fuzz/698-afar.f90 +68 -0
  203. offload/_root/data/findings/fuzz/700-afar.f90 +46 -0
  204. offload/_root/data/findings/fuzz/775-cce21.f90 +33 -0
  205. offload/_root/data/findings/fuzz/87-afar.f90 +47 -0
  206. offload/_root/data/findings/fuzz/916-afar.f90 +67 -0
  207. offload/_root/data/findings/fuzz/958-cce20.f90 +44 -0
  208. offload/_root/data/findings/fuzz/98-cce19.f90 +40 -0
  209. offload/_root/data/findings/fuzz/README.md +236 -0
  210. offload/_root/data/findings/fuzz/b1-121-afar.f90 +47 -0
  211. offload/_root/data/findings/fuzz/b1-183-afar.f90 +55 -0
  212. offload/_root/data/findings/fuzz/b1-22-cce20.f90 +38 -0
  213. offload/_root/data/findings/fuzz/b1-231-afar.f90 +56 -0
  214. offload/_root/data/findings/fuzz/b1-268-afar.f90 +46 -0
  215. offload/_root/data/findings/fuzz/b2-183-afar.f90 +57 -0
  216. offload/_root/data/findings/fuzz/b2-209-cce19.f90 +51 -0
  217. offload/_root/data/findings/fuzz/b2-213-afar.f90 +63 -0
  218. offload/_root/data/findings/fuzz/b2-52-afar.f90 +56 -0
  219. offload/_root/data/findings/fuzz/b2-68-afar.f90 +54 -0
  220. offload/_root/data/findings/fuzz/b2-85-cce20.f90 +55 -0
  221. offload/_root/data/findings/fuzz/b3-188-cce19.f90 +90 -0
  222. offload/_root/data/findings/fuzz/b3-190-cce21.f90 +88 -0
  223. offload/_root/data/findings/fuzz/b3-197-cce19.f90 +77 -0
  224. offload/_root/data/findings/fuzz/b3-250-cce20.f90 +89 -0
  225. offload/_root/data/findings/fuzz/b3-278-cce19.f90 +70 -0
  226. offload/_root/data/findings/fuzz/b3-45-cce21.f90 +83 -0
  227. offload/_root/data/findings/fuzz/b3-54-afar.f90 +67 -0
  228. offload/_root/data/findings/fuzz/b3-61-cce20.f90 +100 -0
  229. offload/_root/data/findings/fuzz/b3-9-afar.f90 +100 -0
  230. offload/_root/data/findings/fuzz/b3-9-cce21.f90 +103 -0
  231. offload/_root/data/findings/fuzz/b3-94-afar.f90 +80 -0
  232. offload/_root/data/findings/fuzz/b4-39-afar.f90 +141 -0
  233. offload/_root/data/findings/fuzz/b4-86-cce19.f90 +71 -0
  234. offload/_root/data/findings/fuzz/controls/gcc-complex-pointer-store-control.f90 +28 -0
  235. offload/_root/data/findings/fuzz/controls/gcc-declare-reduction-module-control.f90 +29 -0
  236. offload/_root/data/findings/fuzz/controls/gcc-target-simd-map-nontemporal-control.f90 +21 -0
  237. offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-mapped-control.f90 +22 -0
  238. offload/_root/data/findings/fuzz/controls/nvhpc-allocatable-component-unreferenced-control.f90 +22 -0
  239. offload/_root/data/findings/fuzz/controls/nvhpc-default-none-removed-control.f90 +18 -0
  240. offload/_root/data/findings/fuzz/controls/nvhpc-default-shared-control.f90 +18 -0
  241. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-firstprivate-removed-control.f90 +15 -0
  242. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-reduction-mapped-control.f90 +17 -0
  243. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-to-scalar-removed-control.f90 +17 -0
  244. offload/_root/data/findings/fuzz/controls/nvhpc-defaultmap-tofrom-lastprivate-control.f90 +15 -0
  245. offload/_root/data/findings/fuzz/controls/nvhpc-depend-explicit-map-control.f90 +22 -0
  246. offload/_root/data/findings/fuzz/controls/nvhpc-depend-removed-control.f90 +22 -0
  247. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-end-distribute-control.f90 +16 -0
  248. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-num-threads-removed-control.f90 +26 -0
  249. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-parallel-removed-control.f90 +22 -0
  250. offload/_root/data/findings/fuzz/controls/nvhpc-distribute-simd-no-end-directive-control.f90 +15 -0
  251. offload/_root/data/findings/fuzz/controls/nvhpc-explicit-length-character-dummy-control.f90 +18 -0
  252. offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-array-value-control.f90 +23 -0
  253. offload/_root/data/findings/fuzz/controls/nvhpc-loop-character-distribute-control.f90 +20 -0
  254. offload/_root/data/findings/fuzz/controls/nvhpc-map-always-first-control.f90 +15 -0
  255. offload/_root/data/findings/fuzz/controls/nvhpc-map-always-removed-control.f90 +15 -0
  256. offload/_root/data/findings/fuzz/controls/nvhpc-num-threads-removed-control.f90 +12 -0
  257. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-firstprivate-dropped-control.f90 +15 -0
  258. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-single-nowait-control.f90 +13 -0
  259. offload/_root/data/findings/fuzz/controls/nvhpc-target-parallel-split-control.f90 +15 -0
  260. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-collapse-split-control.f90 +21 -0
  261. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-end-directive-control.f90 +14 -0
  262. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-no-collapse-control.f90 +19 -0
  263. offload/_root/data/findings/fuzz/controls/nvhpc-target-simd-split-no-end-directive-control.f90 +15 -0
  264. offload/_root/data/findings/fuzz/gcc-declare-reduction-module-not-found.f90 +66 -0
  265. offload/_root/data/findings/fuzz/gcc-strided-target-update-rejected.f90 +58 -0
  266. offload/_root/data/findings/fuzz/gcc-target-simd-map-nontemporal-rejected.f90 +54 -0
  267. offload/_root/data/findings/fuzz/nvhpc-allocatable-component-implicit-map-illegal-address.f90 +84 -0
  268. offload/_root/data/findings/fuzz/nvhpc-assumed-length-character-dummy-map-ice.f90 +69 -0
  269. offload/_root/data/findings/fuzz/nvhpc-default-none-reduction-lastprivate-fort1-crash.f90 +70 -0
  270. offload/_root/data/findings/fuzz/nvhpc-defaultmap-firstprivate-lastprivate-ice.f90 +71 -0
  271. offload/_root/data/findings/fuzz/nvhpc-defaultmap-to-scalar-reduction-lost.f90 +82 -0
  272. offload/_root/data/findings/fuzz/nvhpc-depend-implicit-map-illegal-address.f90 +81 -0
  273. offload/_root/data/findings/fuzz/nvhpc-distribute-parallel-num-threads-writes-lost.f90 +79 -0
  274. offload/_root/data/findings/fuzz/nvhpc-end-distribute-simd-rejected.f90 +70 -0
  275. offload/_root/data/findings/fuzz/nvhpc-loop-character-achar-unsupported.f90 +75 -0
  276. offload/_root/data/findings/fuzz/nvhpc-map-always-after-map-to-not-copied-back.f90 +70 -0
  277. offload/_root/data/findings/fuzz/nvhpc-num-threads-reduction-identity-lost.f90 +54 -0
  278. offload/_root/data/findings/fuzz/nvhpc-target-parallel-firstprivate-translate-failure.f90 +66 -0
  279. offload/_root/data/findings/fuzz/nvhpc-target-parallel-single-hang.f90 +68 -0
  280. offload/_root/data/findings/fuzz/nvhpc-target-simd-collapse-translate-failure.f90 +73 -0
  281. offload/_root/data/findings/fuzz/nvhpc-target-simd-no-end-directive-rejected.f90 +68 -0
  282. offload/_root/data/findings/fuzz/par-s1-2082-gcc_g90.f90 +71 -0
  283. offload/_root/data/findings/fuzz/s1-2370-cce21.f90 +67 -0
  284. offload/_root/data/findings/fuzz/s1-2592-cce21.f90 +56 -0
  285. offload/_root/data/findings/fuzz/s1-3463-cce21.f90 +92 -0
  286. offload/_root/data/findings/fuzz/s1-4339-afar.f90 +79 -0
  287. offload/_root/data/findings/fuzz/s1-6085-cce21.f90 +96 -0
  288. offload/_root/data/findings/fuzz/s1-6760-cce20.f90 +65 -0
  289. offload/_root/data/findings/fuzz/s1-6888-cce21.f90 +76 -0
  290. offload/_root/data/findings/fuzz/s1-7882-cce20.f90 +63 -0
  291. offload/_root/data/findings/fuzz/s1-8634-cce21.f90 +45 -0
  292. offload/_root/data/findings/fuzz/s1-91-cce21.f90 +92 -0
  293. offload/_root/data/findings/fuzz/s1-9443-afar.f90 +59 -0
  294. offload/_root/data/findings/fuzz/s1-9914-cce19.f90 +78 -0
  295. offload/_root/data/findings/gcc/controls/gcc-acc-c-detach-control.c +34 -0
  296. offload/_root/data/findings/gcc/controls/gcc-acc-detach-control.f90 +26 -0
  297. offload/_root/data/findings/gcc/controls/gcc-defaultmap-to-pointer-control.f90 +31 -0
  298. offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.c +26 -0
  299. offload/_root/data/findings/gcc/controls/gcc-target-in-task-control.f90 +21 -0
  300. offload/_root/data/findings/gcc/gcc-acc-bind-device-type-rejected.f90 +71 -0
  301. offload/_root/data/findings/gcc/gcc-acc-c-bind-device-type-rejected.c +76 -0
  302. offload/_root/data/findings/gcc/gcc-acc-c-detach-delete-attach-underflow.c +74 -0
  303. offload/_root/data/findings/gcc/gcc-acc-c-gang-dim-rejected.c +79 -0
  304. offload/_root/data/findings/gcc/gcc-acc-c-link-in-routine-rejected.c +82 -0
  305. offload/_root/data/findings/gcc/gcc-acc-detach-delete-attach-underflow.f90 +66 -0
  306. offload/_root/data/findings/gcc/gcc-acc-gang-dim-collapse-force-rejected.f90 +69 -0
  307. offload/_root/data/findings/gcc/gcc-acc-init-set-shutdown-rejected.f90 +70 -0
  308. offload/_root/data/findings/gcc/gcc-acc-link-in-routine-rejected.f90 +82 -0
  309. offload/_root/data/findings/gcc/gcc-acc-use-device-allocatable-rejected.f90 +88 -0
  310. offload/_root/data/findings/gcc/gcc-c-motion-mapper-rejected.c +110 -0
  311. offload/_root/data/findings/gcc/gcc-c-omp60-undocumented-rejected.c +76 -0
  312. offload/_root/data/findings/gcc/gcc-c-omp60-unimplemented-rejected.c +119 -0
  313. offload/_root/data/findings/gcc/gcc-c-target-in-task-mandatory-device-not-found.c +87 -0
  314. offload/_root/data/findings/gcc/gcc-c-uses-allocators-traits-unimplemented.c +85 -0
  315. offload/_root/data/findings/gcc/gcc-declare-mapper-rejected.f90 +85 -0
  316. offload/_root/data/findings/gcc/gcc-defaultmap-to-pointer-copy-back.f90 +66 -0
  317. offload/_root/data/findings/gcc/gcc-loop-bind-default-none-rejected.f90 +93 -0
  318. offload/_root/data/findings/gcc/gcc-omp60-undocumented-rejected.f90 +72 -0
  319. offload/_root/data/findings/gcc/gcc-omp60-unimplemented-rejected.f90 +112 -0
  320. offload/_root/data/findings/gcc/gcc-target-in-task-mandatory-device-not-found.f90 +83 -0
  321. offload/_root/data/findings/gcc/gcc-uses-allocators-traits-unimplemented.f90 +78 -0
  322. offload/_root/data/findings/harvest/afar-flang-strided-target-update.f90 +96 -0
  323. offload/_root/data/findings/harvest/cce-acc-declare-create-module-allocatable-xnack1-fault.f90 +58 -0
  324. offload/_root/data/findings/harvest/cce-c-implicit-struct-map-partly-present.c +100 -0
  325. offload/_root/data/findings/harvest/cce-c-strided-target-update.c +74 -0
  326. offload/_root/data/findings/harvest/cce-ftn-target-parallel-thread-query-ice.f90 +67 -0
  327. offload/_root/data/findings/harvest/cce-named-constant-array-declare-target-routine-rejected.f90 +69 -0
  328. offload/_root/data/findings/harvest/cce-use-device-ptr-c-ptr-not-converted.f90 +64 -0
  329. offload/_root/data/findings/harvest/cce21-declare-target-link-module-variable-write-lost.f90 +63 -0
  330. offload/_root/data/findings/harvest/cce21-internal-procedure-calls-internal-in-target-fault.f90 +71 -0
  331. offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-target-only-control.c +22 -0
  332. offload/_root/data/findings/harvest/controls/cce-c-implicit-struct-map-partly-present-whole-control.c +23 -0
  333. offload/_root/data/findings/harvest/controls/gcc-acc-declare-create-allocatable-control.f90 +34 -0
  334. offload/_root/data/findings/harvest/controls/gcc-c-same-item-two-maps-control.c +17 -0
  335. offload/_root/data/findings/harvest/controls/gcc-c-target-data-struct-pointer-section-control.c +27 -0
  336. offload/_root/data/findings/harvest/controls/gcc-c-target-data-two-maps-control.c +20 -0
  337. offload/_root/data/findings/harvest/controls/gcc-same-item-two-maps-control.f90 +16 -0
  338. offload/_root/data/findings/harvest/controls/gcc-teams-simd-matmul-allocatable-control.f90 +27 -0
  339. offload/_root/data/findings/harvest/gcc-acc-declare-create-allocatable-not-mapped.f90 +66 -0
  340. offload/_root/data/findings/harvest/gcc-c-same-item-two-maps-rejected.c +45 -0
  341. offload/_root/data/findings/harvest/gcc-c-strided-section-rejected.c +70 -0
  342. offload/_root/data/findings/harvest/gcc-c-target-data-struct-pointer-section-ice.c +78 -0
  343. offload/_root/data/findings/harvest/gcc-c-target-data-two-maps-ice.c +49 -0
  344. offload/_root/data/findings/harvest/gcc-same-item-two-maps-rejected.f90 +46 -0
  345. offload/_root/data/findings/harvest/gcc-teams-simd-matmul-allocatable-ice.f90 +53 -0
  346. offload/_root/data/findings/harvest/nvhpc-c-target-data-if-false-use-device-ptr-garbage.c +49 -0
  347. offload/_root/data/findings/harvest/nvhpc-named-constant-array-declare-target-routine-rejected.f90 +65 -0
  348. offload/_root/data/findings/harvest/nvhpc-use-device-ptr-c-ptr-not-found.f90 +60 -0
  349. offload/_root/data/findings/harvest/nvhpc2511-target-nowait-in-parallel-for-ice.c +59 -0
  350. offload/_root/data/findings/hecbench/controls/f16-ctl-float-array.c +28 -0
  351. offload/_root/data/findings/hecbench/controls/pc-ctl-builtin-popcount.c +37 -0
  352. offload/_root/data/findings/hecbench/controls/pc-ctl-shift-sum.c +43 -0
  353. offload/_root/data/findings/hecbench/gcc-c-float16-array-in-target-lto-ice.c +65 -0
  354. offload/_root/data/findings/hecbench/gcc-c-swar-popcount-assembler-error.c +80 -0
  355. offload/_root/data/findings/index.yaml +2453 -0
  356. offload/_root/data/findings/malloc/afar-device-malloc-in-kernel-corrupts-records.c +371 -0
  357. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-direct.c +52 -0
  358. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-fence.c +57 -0
  359. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-flush.c +57 -0
  360. offload/_root/data/findings/malloc/controls/afar-first-use-malloc-stack.c +52 -0
  361. offload/_root/data/findings/multithread-allocatable-section-maps.f90 +91 -0
  362. offload/_root/data/findings/nested/afar-cce-c-call-result-member-in-target-rejected.c +69 -0
  363. offload/_root/data/findings/nested/afar-cce-c-update-struct-and-member-rejected.c +92 -0
  364. offload/_root/data/findings/nested/afar-flang-pointer-root-allocatable-component-enter-data-fault.f90 +121 -0
  365. offload/_root/data/findings/nested/cce-c-struct-and-nested-pointee-sections-ice.c +79 -0
  366. offload/_root/data/findings/nested/cce-fortran-nested-allocatable-component-copy-fault.f90 +64 -0
  367. offload/_root/data/findings/nested/cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.f90 +93 -0
  368. offload/_root/data/findings/nested/cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.f90 +80 -0
  369. offload/_root/data/findings/nested/controls/c3-ctl-nested-data.c +27 -0
  370. offload/_root/data/findings/nested/controls/c5-acc-serial-struct-and-array.c +26 -0
  371. offload/_root/data/findings/nested/controls/e2-ctl-two-directives.c +42 -0
  372. offload/_root/data/findings/nested/controls/e3-ctl-attach-clause.c +42 -0
  373. offload/_root/data/findings/nested/controls/f3-pointer-array-only.c +33 -0
  374. offload/_root/data/findings/nested/controls/f5-ctl-copyout-exit.c +33 -0
  375. offload/_root/data/findings/nested/controls/f6-ctl-pointer-to-struct-array.c +28 -0
  376. offload/_root/data/findings/nested/controls/g2-ctl-allocatable.f90 +34 -0
  377. offload/_root/data/findings/nested/controls/g2-ctl-entered-to.f90 +34 -0
  378. offload/_root/data/findings/nested/controls/g3-ctl-from-struct-no-element.c +27 -0
  379. offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-to.c +27 -0
  380. offload/_root/data/findings/nested/controls/g3-ptrs-two-elements-tofrom.c +27 -0
  381. offload/_root/data/findings/nested/controls/g5-ctl-element-pointers-only.f90 +36 -0
  382. offload/_root/data/findings/nested/controls/g5-ctl-root-pointer-only.f90 +36 -0
  383. offload/_root/data/findings/nested/controls/gp2-ctl-no-mapper-explicit-maps.c +24 -0
  384. offload/_root/data/findings/nested/controls/gt2-ctl-no-always.c +26 -0
  385. offload/_root/data/findings/nested/controls/gt3-ctl-always-on-structure-only.c +26 -0
  386. offload/_root/data/findings/nested/controls/j1-embedded-struct-pointer-to-struct.c +24 -0
  387. offload/_root/data/findings/nested/controls/j2-ctl-pointer-instead-of-embedded.c +24 -0
  388. offload/_root/data/findings/nested/controls/j6-ctl-struct-and-one-pointee-level.c +23 -0
  389. offload/_root/data/findings/nested/controls/j7-ctl-struct-and-embedded-pointee.c +24 -0
  390. offload/_root/data/findings/nested/controls/m2-ctl-variable-root.f90 +32 -0
  391. offload/_root/data/findings/nested/controls/m3-ctl-fixed-array-component.f90 +32 -0
  392. offload/_root/data/findings/nested/controls/m5-ctl-no-enter-data.f90 +33 -0
  393. offload/_root/data/findings/nested/controls/n8-ctl-fixed-array-in-component.f90 +33 -0
  394. offload/_root/data/findings/nested/controls/n9-ctl-allocatable-in-the-variable.f90 +33 -0
  395. offload/_root/data/findings/nested/controls/ns4-ctl-two-directives-struct-then-pointee.c +27 -0
  396. offload/_root/data/findings/nested/controls/ns8-ctl-enter-together-exit-split.c +26 -0
  397. offload/_root/data/findings/nested/controls/nu3-ctl-update-pointee-only.c +28 -0
  398. offload/_root/data/findings/nested/controls/p2-ctl-pointer-variable.c +27 -0
  399. offload/_root/data/findings/nested/controls/p3-ctl-direct-member.c +26 -0
  400. offload/_root/data/findings/nested/controls/q2-ctl-fixed-array.f90 +37 -0
  401. offload/_root/data/findings/nested/controls/q3-ctl-allocatable-component-of-the-variable.f90 +38 -0
  402. offload/_root/data/findings/nested/controls/ta2-ctl-no-allocatable-component.f90 +42 -0
  403. offload/_root/data/findings/nested/controls/ta3-ctl-allocatable-scalar-in-the-variable.f90 +38 -0
  404. offload/_root/data/findings/nested/controls/ta4-ctl-pointer-component-only.f90 +42 -0
  405. offload/_root/data/findings/nested/controls/u3-ctl-update-member-only.c +24 -0
  406. offload/_root/data/findings/nested/controls/u4-ctl-two-updates.c +25 -0
  407. offload/_root/data/findings/nested/controls/u5-ctl-same-list-in-map.c +25 -0
  408. offload/_root/data/findings/nested/controls/ua2-ctl-variable-root.f90 +37 -0
  409. offload/_root/data/findings/nested/controls/ua4-ctl-to-in-place-of-alloc-and-update.f90 +37 -0
  410. offload/_root/data/findings/nested/controls/v2-ctl-parent-first.c +25 -0
  411. offload/_root/data/findings/nested/controls/v4-ctl-embedded-member.c +24 -0
  412. offload/_root/data/findings/nested/gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.c +83 -0
  413. offload/_root/data/findings/nested/gcc-acc-c-parent-and-shared-pointee-one-directive.c +88 -0
  414. offload/_root/data/findings/nested/gcc-acc-c-struct-and-member-array-one-directive.c +84 -0
  415. offload/_root/data/findings/nested/gcc-c-always-on-pointer-member-section-ice.c +75 -0
  416. offload/_root/data/findings/nested/gcc-c-declare-mapper-pointer-member-section-gpu-fault.c +93 -0
  417. offload/_root/data/findings/nested/gcc-c-from-struct-and-pointee-section-rejected.c +83 -0
  418. offload/_root/data/findings/nested/gcc-fortran-pointer-component-alloc-bounds-lost.f90 +62 -0
  419. offload/_root/data/findings/nested/gcc-fortran-root-and-element-pointer-components-ice.f90 +77 -0
  420. offload/_root/data/findings/nested/nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.c +55 -0
  421. offload/_root/data/findings/nested/nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.c +86 -0
  422. offload/_root/data/findings/nvhpc-acc-atomic-update-sum-fort2-crash.f90 +53 -0
  423. offload/_root/data/findings/nvhpc-c-acc-atomic-update-sum-ice.c +43 -0
  424. offload/_root/data/findings/nvhpc-c-dist-schedule-chunk-compile-hang.c +41 -0
  425. offload/_root/data/findings/nvhpc-c-map-present-ignored.c +42 -0
  426. offload/_root/data/findings/nvhpc-c-ordered-in-target-nvcpfe-crash.c +92 -0
  427. offload/_root/data/findings/nvhpc-c-parallel-for-firstprivate-in-target.c +62 -0
  428. offload/_root/data/findings/nvhpc-c-parallel-if-in-target-nvvm-ice.c +56 -0
  429. offload/_root/data/findings/nvhpc-defaultmap-firstprivate-explicit-map.f90 +45 -0
  430. offload/_root/data/findings/nvhpc-defaultmap-from-allocatable-indexing.f90 +60 -0
  431. offload/_root/data/findings/nvhpc-depend-inoutset-ignored.f90 +64 -0
  432. offload/_root/data/findings/nvhpc-dist-schedule-chunk-illegal-address.f90 +52 -0
  433. offload/_root/data/findings/nvhpc-managed-acc-shutdown-host-access.f90 +42 -0
  434. offload/_root/data/findings/nvhpc-parallel-if-in-target-nvvm-ice.f90 +60 -0
  435. offload/_root/data/findings/oop/afar-final-dealloc-device-link.f90 +85 -0
  436. offload/_root/data/findings/oop/afar-pdt-length-parameter-not-implemented.f90 +65 -0
  437. offload/_root/data/findings/oop/cce-class-is-device-link.f90 +86 -0
  438. offload/_root/data/findings/oop/cce-copy-alloc-component-section-fault.f90 +73 -0
  439. offload/_root/data/findings/oop/cce-copy-parent-to-component-tofrom-lost.f90 +65 -0
  440. offload/_root/data/findings/oop/cce-copy-second-member-section-lost.f90 +65 -0
  441. offload/_root/data/findings/oop/cce-element-component-map-lost.f90 +64 -0
  442. offload/_root/data/findings/oop/cce-final-dealloc-device-abort.f90 +85 -0
  443. offload/_root/data/findings/oop/cce-pdt-default-mapper-rejected.f90 +71 -0
  444. offload/_root/data/findings/oop/cce-polymorphic-dispatch-device-link.f90 +100 -0
  445. offload/_root/data/findings/oop/cce-section-of-element-component-overlap.f90 +76 -0
  446. offload/_root/data/findings/oop/cce-select-type-guard-not-taken.f90 +91 -0
  447. offload/_root/data/findings/oop/cce-udr-derived-type-unsupported.f90 +69 -0
  448. offload/_root/data/findings/oop/cce21-element-component-map-lost.f90 +62 -0
  449. offload/_root/data/findings/oop/controls/alloccomp-comp-only.f90 +44 -0
  450. offload/_root/data/findings/oop/controls/alloccomp-whole-explicit-component.f90 +50 -0
  451. offload/_root/data/findings/oop/controls/alloccomp-whole.f90 +49 -0
  452. offload/_root/data/findings/oop/controls/elemsec-element.f90 +47 -0
  453. offload/_root/data/findings/oop/controls/final-local.f90 +64 -0
  454. offload/_root/data/findings/oop/controls/host-class-star-select-type.f90 +58 -0
  455. offload/_root/data/findings/oop/controls/host-firstprivate-defined-assignment.f90 +54 -0
  456. offload/_root/data/findings/oop/controls/host-lastprivate-defined-assignment.f90 +58 -0
  457. offload/_root/data/findings/oop/controls/host-private-finalization.f90 +51 -0
  458. offload/_root/data/findings/oop/controls/host-select-type.f90 +65 -0
  459. offload/_root/data/findings/oop/controls/parent-and-comp-component-only.f90 +45 -0
  460. offload/_root/data/findings/oop/controls/pdt-kind.f90 +46 -0
  461. offload/_root/data/findings/oop/controls/pdt-len-host.f90 +43 -0
  462. offload/_root/data/findings/oop/controls/pdt-len-unparameterized.f90 +46 -0
  463. offload/_root/data/findings/oop/controls/poly-dispatch-static.f90 +75 -0
  464. offload/_root/data/findings/oop/controls/section-alloc-whole-component.f90 +49 -0
  465. offload/_root/data/findings/oop/controls/section-element-whole-array.f90 +50 -0
  466. offload/_root/data/findings/oop/controls/section-element-whole-component.f90 +50 -0
  467. offload/_root/data/findings/oop/controls/section-fixed.f90 +43 -0
  468. offload/_root/data/findings/oop/controls/tbp-override.f90 +64 -0
  469. offload/_root/data/findings/oop/gcc-acc-parent-and-member-data-rejected.f90 +63 -0
  470. offload/_root/data/findings/oop/gcc-pdt-length-parameter-abort.f90 +68 -0
  471. offload/_root/data/findings/oop/gcc-polymorphic-dispatch-abort.f90 +97 -0
  472. offload/_root/data/findings/oop/gcc-vtab-not-marked-for-offload.f90 +94 -0
  473. offload/_root/data/findings/oop/nvhpc-copy-alloc-component-illegal-address.f90 +76 -0
  474. offload/_root/data/findings/oop/nvhpc-copy-pdt-length-parameter-illegal-address.f90 +69 -0
  475. offload/_root/data/findings/oop/nvhpc-named-mapper-rejected.f90 +65 -0
  476. offload/_root/data/findings/oop/nvhpc-polymorphic-dispatch-translate-failure.f90 +90 -0
  477. offload/_root/data/findings/oop/nvhpc-polymorphism-runtime-unsupported.f90 +94 -0
  478. offload/_root/data/findings/oop/nvhpc-type-bound-call-rejected.f90 +89 -0
  479. offload/_root/data/findings/oop/nvhpc-udr-derived-type-rejected.f90 +67 -0
  480. offload/_root/data/findings/reviews/afar-c-error-directive-in-target-link.json +217 -0
  481. offload/_root/data/findings/reviews/afar-c-lastprivate-distribute-parallel-for-small-type.json +113 -0
  482. offload/_root/data/findings/reviews/afar-c-teams-distribute-reduction-first-team.json +130 -0
  483. offload/_root/data/findings/reviews/afar-c-update-iterator-copy-mode.json +113 -0
  484. offload/_root/data/findings/reviews/afar-cce19-character-defaultmap.json +292 -0
  485. offload/_root/data/findings/reviews/afar-clang-requires-device-safesync-hang.json +191 -0
  486. offload/_root/data/findings/reviews/afar-flang-firstprivate-map-two-targets.json +130 -0
  487. offload/_root/data/findings/reviews/afar-flang-loop-bind-teams-reduction.json +126 -0
  488. offload/_root/data/findings/reviews/afar-flang-task-reduction-max.json +113 -0
  489. offload/_root/data/findings/reviews/afar-flang-thread-limit-target-parallel-do.json +113 -0
  490. offload/_root/data/findings/reviews/afar-loop-bind-thread-in-teams.json +191 -0
  491. offload/_root/data/findings/reviews/afar-nontemporal-assumed-shape-ice.json +113 -0
  492. offload/_root/data/findings/reviews/afar-udr-derived-type-link.json +113 -0
  493. offload/_root/data/findings/reviews/afar-xnack1-atomic-min-nested-parallel.json +113 -0
  494. offload/_root/data/findings/reviews/afar-xnack1-target-is-present.json +113 -0
  495. offload/_root/data/findings/reviews/apps--afar-cce-teams-private-array-nested-parallel-wrong.json +113 -0
  496. offload/_root/data/findings/reviews/apps--cce20-device-link-infer-address-spaces-crash.json +113 -0
  497. offload/_root/data/findings/reviews/apps--cce21-device-link-agpr-copy-mfma-assert.json +113 -0
  498. offload/_root/data/findings/reviews/apps--cce21-private-array-loaded-subscript-wrong.json +113 -0
  499. offload/_root/data/findings/reviews/apps--gcc-acc-c-tile-partial-tiles-wrong.json +113 -0
  500. offload/_root/data/findings/reviews/catalog--afar-c-ordered-doacross-link.json +113 -0
  501. offload/_root/data/findings/reviews/catalog--afar-c-reduction-task-link.json +113 -0
  502. offload/_root/data/findings/reviews/catalog--afar-flang-absent-optional-target-crash.json +113 -0
  503. offload/_root/data/findings/reviews/catalog--afar-flang-critical-target-parallel-wrong.json +113 -0
  504. offload/_root/data/findings/reviews/catalog--afar-flang-dist-schedule-chunk.json +113 -0
  505. offload/_root/data/findings/reviews/catalog--afar-flang-linear-parallel-ice.json +113 -0
  506. offload/_root/data/findings/reviews/catalog--afar-flang-named-critical-link.json +126 -0
  507. offload/_root/data/findings/reviews/catalog--afar-flang-ordered-wrong.json +113 -0
  508. offload/_root/data/findings/reviews/catalog--afar-flang-reduction-minus-rejected.json +113 -0
  509. offload/_root/data/findings/reviews/catalog--afar-flang-sections-in-target-ice.json +113 -0
  510. offload/_root/data/findings/reviews/catalog--afar-flang-workdistribute-ice.json +113 -0
  511. offload/_root/data/findings/reviews/catalog--c-allocate-const-mem-firstprivate-wrong.json +113 -0
  512. offload/_root/data/findings/reviews/catalog--cce-acc-character-dummy-routine-ice.json +113 -0
  513. offload/_root/data/findings/reviews/catalog--cce-acc-loop-seq-in-routine-wrong.json +113 -0
  514. offload/_root/data/findings/reviews/catalog--cce-allocators-stack-ice.json +113 -0
  515. offload/_root/data/findings/reviews/catalog--cce-c-allocate-align-link.json +113 -0
  516. offload/_root/data/findings/reviews/catalog--cce-c-allocate-allocator-modifier-rejected.json +113 -0
  517. offload/_root/data/findings/reviews/catalog--cce-c-dist-schedule-chunk.json +113 -0
  518. offload/_root/data/findings/reviews/catalog--cce-c-f-pointer-device-link.json +130 -0
  519. offload/_root/data/findings/reviews/catalog--cce-c-long-double-ice.json +217 -0
  520. offload/_root/data/findings/reviews/catalog--cce-c-map-iterator-ice.json +217 -0
  521. offload/_root/data/findings/reviews/catalog--cce-c-recursive-function-ice.json +113 -0
  522. offload/_root/data/findings/reviews/catalog--cce-c-sections-in-target-wrong.json +113 -0
  523. offload/_root/data/findings/reviews/catalog--cce-flang-allocate-in-target-crash.json +113 -0
  524. offload/_root/data/findings/reviews/catalog--cce-flang-lastprivate-parallel-in-seq-loop-wrong.json +113 -0
  525. offload/_root/data/findings/reviews/catalog--cce-flang-stop-in-target-crash.json +113 -0
  526. offload/_root/data/findings/reviews/catalog--cce-iterator-link.json +113 -0
  527. offload/_root/data/findings/reviews/catalog--cce-linear-lastprivate-ice.json +113 -0
  528. offload/_root/data/findings/reviews/catalog--cce-reduction-inscan-ice.json +113 -0
  529. offload/_root/data/findings/reviews/catalog--cce-reduction-task-link.json +113 -0
  530. offload/_root/data/findings/reviews/catalog--cce19-reduction-task-addrspacecast-ice.json +113 -0
  531. offload/_root/data/findings/reviews/catalog--cce20-c-defaultmap-private-storage-rejected.json +217 -0
  532. offload/_root/data/findings/reviews/catalog--cce20-map-self-rejected.json +204 -0
  533. offload/_root/data/findings/reviews/catalog--cce21-c-target-loop-ice.json +126 -0
  534. offload/_root/data/findings/reviews/catalog--defaultmap-present-private-array-abort.json +236 -0
  535. offload/_root/data/findings/reviews/catalog--nvhpc-acc-associate-illegal-address.json +113 -0
  536. offload/_root/data/findings/reviews/catalog--nvhpc-c-function-pointer-kernel-missing.json +268 -0
  537. offload/_root/data/findings/reviews/cce-acc-init-set-runtime-assert.json +113 -0
  538. offload/_root/data/findings/reviews/cce-acc-reduction-200-int8-scalars-wrong.json +113 -0
  539. offload/_root/data/findings/reviews/cce-acc-routine-bind-module-ice.json +113 -0
  540. offload/_root/data/findings/reviews/cce-c-dist-schedule-segv.json +113 -0
  541. offload/_root/data/findings/reviews/cce-c-map-delete-refcount.json +149 -0
  542. offload/_root/data/findings/reviews/cce-c-map-present-assert.json +126 -0
  543. offload/_root/data/findings/reviews/cce-c-openmp45-device-link.json +113 -0
  544. offload/_root/data/findings/reviews/cce-c-parallel-for-in-target.json +141 -0
  545. offload/_root/data/findings/reviews/cce-mapper-next-map-copy-back.json +208 -0
  546. offload/_root/data/findings/reviews/cce-uses-allocators-traits-omp50.json +191 -0
  547. offload/_root/data/findings/reviews/cce19-c-reduction-63-scalars-one-wrong.json +113 -0
  548. offload/_root/data/findings/reviews/cce19-target-parallel-guarded-loop.json +113 -0
  549. offload/_root/data/findings/reviews/cce19-target-parallel-single-ice.json +113 -0
  550. offload/_root/data/findings/reviews/cce21-alloc-2e32-sum-wrong.json +113 -0
  551. offload/_root/data/findings/reviews/cce21-defaultmap-aggregate-flag.json +210 -0
  552. offload/_root/data/findings/reviews/cce21-in-reduction-assumed-shape-write-lost.json +113 -0
  553. offload/_root/data/findings/reviews/cce21-in-reduction-taskwait-link.json +113 -0
  554. offload/_root/data/findings/reviews/clang-c-declare-target-to-omp52.json +113 -0
  555. offload/_root/data/findings/reviews/clang-c-linear-directive-name-hang.json +113 -0
  556. offload/_root/data/findings/reviews/clang-c-shared-declare-target-combined.json +132 -0
  557. offload/_root/data/findings/reviews/dataenv--cce-value-dummy-target-write-visible.json +113 -0
  558. offload/_root/data/findings/reviews/defaultmap-none-loop-variable.json +113 -0
  559. offload/_root/data/findings/reviews/fortran-schedule-static-in-target.json +113 -0
  560. offload/_root/data/findings/reviews/fresh--cce-common-block-name-in-map-rejected.json +113 -0
  561. offload/_root/data/findings/reviews/fresh--cce-list-directed-print-in-target-copy-crash.json +217 -0
  562. offload/_root/data/findings/reviews/fresh--cce19-acc-automatic-array-copyout-copy-crash.json +113 -0
  563. offload/_root/data/findings/reviews/fresh--cce21-combined-if-runtime-expr-loop-skipped.json +113 -0
  564. offload/_root/data/findings/reviews/fresh--nvhpc-acc-gang-worker-collapse-nested-vector-reduction-lost.json +113 -0
  565. offload/_root/data/findings/reviews/fresh--nvhpc-teams-loop-module-allocatable-assign-fort2-crash.json +113 -0
  566. offload/_root/data/findings/reviews/fuzz--1505-cce20.json +113 -0
  567. offload/_root/data/findings/reviews/fuzz--1540-afar.json +113 -0
  568. offload/_root/data/findings/reviews/fuzz--1978-cce19.json +130 -0
  569. offload/_root/data/findings/reviews/fuzz--2334-cce19.json +113 -0
  570. offload/_root/data/findings/reviews/fuzz--591-cce19.json +147 -0
  571. offload/_root/data/findings/reviews/fuzz--650-afar.json +113 -0
  572. offload/_root/data/findings/reviews/fuzz--916-afar.json +130 -0
  573. offload/_root/data/findings/reviews/fuzz--98-cce19.json +113 -0
  574. offload/_root/data/findings/reviews/fuzz--b1-121-afar.json +160 -0
  575. offload/_root/data/findings/reviews/fuzz--b2-209-cce19.json +141 -0
  576. offload/_root/data/findings/reviews/fuzz--b2-68-afar.json +113 -0
  577. offload/_root/data/findings/reviews/fuzz--b2-85-cce20.json +217 -0
  578. offload/_root/data/findings/reviews/fuzz--b3-188-cce19.json +113 -0
  579. offload/_root/data/findings/reviews/fuzz--b3-278-cce19.json +212 -0
  580. offload/_root/data/findings/reviews/fuzz--b3-54-afar.json +113 -0
  581. offload/_root/data/findings/reviews/fuzz--b3-61-cce20.json +191 -0
  582. offload/_root/data/findings/reviews/fuzz--b4-86-cce19.json +113 -0
  583. offload/_root/data/findings/reviews/fuzz--gcc-declare-reduction-module-not-found.json +113 -0
  584. offload/_root/data/findings/reviews/fuzz--gcc-target-simd-map-nontemporal-rejected.json +113 -0
  585. offload/_root/data/findings/reviews/fuzz--nvhpc-allocatable-component-implicit-map-illegal-address.json +141 -0
  586. offload/_root/data/findings/reviews/fuzz--nvhpc-assumed-length-character-dummy-map-ice.json +113 -0
  587. offload/_root/data/findings/reviews/fuzz--nvhpc-default-none-reduction-lastprivate-fort1-crash.json +113 -0
  588. offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-firstprivate-lastprivate-ice.json +113 -0
  589. offload/_root/data/findings/reviews/fuzz--nvhpc-defaultmap-to-scalar-reduction-lost.json +113 -0
  590. offload/_root/data/findings/reviews/fuzz--nvhpc-depend-implicit-map-illegal-address.json +113 -0
  591. offload/_root/data/findings/reviews/fuzz--nvhpc-distribute-parallel-num-threads-writes-lost.json +113 -0
  592. offload/_root/data/findings/reviews/fuzz--nvhpc-end-distribute-simd-rejected.json +113 -0
  593. offload/_root/data/findings/reviews/fuzz--nvhpc-loop-character-achar-unsupported.json +113 -0
  594. offload/_root/data/findings/reviews/fuzz--nvhpc-map-always-after-map-to-not-copied-back.json +113 -0
  595. offload/_root/data/findings/reviews/fuzz--nvhpc-num-threads-reduction-identity-lost.json +113 -0
  596. offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-firstprivate-translate-failure.json +191 -0
  597. offload/_root/data/findings/reviews/fuzz--nvhpc-target-parallel-single-hang.json +113 -0
  598. offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-collapse-translate-failure.json +113 -0
  599. offload/_root/data/findings/reviews/fuzz--nvhpc-target-simd-no-end-directive-rejected.json +113 -0
  600. offload/_root/data/findings/reviews/fuzz--par-s1-2082-gcc_g90.json +126 -0
  601. offload/_root/data/findings/reviews/fuzz--s1-2370-cce21.json +113 -0
  602. offload/_root/data/findings/reviews/fuzz--s1-2592-cce21.json +113 -0
  603. offload/_root/data/findings/reviews/fuzz--s1-3463-cce21.json +113 -0
  604. offload/_root/data/findings/reviews/fuzz--s1-6760-cce20.json +130 -0
  605. offload/_root/data/findings/reviews/fuzz--s1-6888-cce21.json +113 -0
  606. offload/_root/data/findings/reviews/fuzz--s1-7882-cce20.json +113 -0
  607. offload/_root/data/findings/reviews/fuzz--s1-8634-cce21.json +113 -0
  608. offload/_root/data/findings/reviews/fuzz--s1-91-cce21.json +113 -0
  609. offload/_root/data/findings/reviews/fuzz--s1-9443-afar.json +113 -0
  610. offload/_root/data/findings/reviews/fuzz--s1-9914-cce19.json +141 -0
  611. offload/_root/data/findings/reviews/gcc--gcc-acc-bind-device-type-rejected.json +113 -0
  612. offload/_root/data/findings/reviews/gcc--gcc-acc-detach-delete-attach-underflow.json +314 -0
  613. offload/_root/data/findings/reviews/gcc--gcc-acc-gang-dim-collapse-force-rejected.json +113 -0
  614. offload/_root/data/findings/reviews/gcc--gcc-acc-init-set-shutdown-rejected.json +113 -0
  615. offload/_root/data/findings/reviews/gcc--gcc-acc-link-in-routine-rejected.json +113 -0
  616. offload/_root/data/findings/reviews/gcc--gcc-acc-use-device-allocatable-rejected.json +113 -0
  617. offload/_root/data/findings/reviews/gcc--gcc-c-motion-mapper-rejected.json +113 -0
  618. offload/_root/data/findings/reviews/gcc--gcc-c-omp60-undocumented-rejected.json +217 -0
  619. offload/_root/data/findings/reviews/gcc--gcc-c-omp60-unimplemented-rejected.json +236 -0
  620. offload/_root/data/findings/reviews/gcc--gcc-c-target-in-task-mandatory-device-not-found.json +236 -0
  621. offload/_root/data/findings/reviews/gcc--gcc-c-uses-allocators-traits-unimplemented.json +113 -0
  622. offload/_root/data/findings/reviews/gcc--gcc-declare-mapper-rejected.json +113 -0
  623. offload/_root/data/findings/reviews/gcc--gcc-defaultmap-to-pointer-copy-back.json +217 -0
  624. offload/_root/data/findings/reviews/gcc--gcc-loop-bind-default-none-rejected.json +113 -0
  625. offload/_root/data/findings/reviews/harvest--afar-flang-strided-target-update.json +141 -0
  626. offload/_root/data/findings/reviews/harvest--cce-acc-declare-create-module-allocatable-xnack1-fault.json +113 -0
  627. offload/_root/data/findings/reviews/harvest--cce-c-implicit-struct-map-partly-present.json +217 -0
  628. offload/_root/data/findings/reviews/harvest--cce-c-strided-target-update.json +141 -0
  629. offload/_root/data/findings/reviews/harvest--cce-ftn-target-parallel-thread-query-ice.json +130 -0
  630. offload/_root/data/findings/reviews/harvest--cce-named-constant-array-declare-target-routine-rejected.json +113 -0
  631. offload/_root/data/findings/reviews/harvest--cce-use-device-ptr-c-ptr-not-converted.json +113 -0
  632. offload/_root/data/findings/reviews/harvest--cce21-declare-target-link-module-variable-write-lost.json +113 -0
  633. offload/_root/data/findings/reviews/harvest--cce21-internal-procedure-calls-internal-in-target-fault.json +158 -0
  634. offload/_root/data/findings/reviews/harvest--gcc-acc-declare-create-allocatable-not-mapped.json +204 -0
  635. offload/_root/data/findings/reviews/harvest--gcc-c-same-item-two-maps-rejected.json +143 -0
  636. offload/_root/data/findings/reviews/harvest--gcc-c-strided-section-rejected.json +164 -0
  637. offload/_root/data/findings/reviews/harvest--gcc-c-target-data-struct-pointer-section-ice.json +113 -0
  638. offload/_root/data/findings/reviews/harvest--gcc-c-target-data-two-maps-ice.json +113 -0
  639. offload/_root/data/findings/reviews/harvest--gcc-teams-simd-matmul-allocatable-ice.json +113 -0
  640. offload/_root/data/findings/reviews/harvest--nvhpc-c-target-data-if-false-use-device-ptr-garbage.json +113 -0
  641. offload/_root/data/findings/reviews/harvest--nvhpc-named-constant-array-declare-target-routine-rejected.json +126 -0
  642. offload/_root/data/findings/reviews/harvest--nvhpc-use-device-ptr-c-ptr-not-found.json +113 -0
  643. offload/_root/data/findings/reviews/harvest--nvhpc2511-target-nowait-in-parallel-for-ice.json +130 -0
  644. offload/_root/data/findings/reviews/hecbench--gcc-c-float16-array-in-target-lto-ice.json +217 -0
  645. offload/_root/data/findings/reviews/hecbench--gcc-c-swar-popcount-assembler-error.json +126 -0
  646. offload/_root/data/findings/reviews/malloc--afar-device-malloc-in-kernel-corrupts-records.json +270 -0
  647. offload/_root/data/findings/reviews/multithread-allocatable-section-maps.json +191 -0
  648. offload/_root/data/findings/reviews/nested--afar-cce-c-call-result-member-in-target-rejected.json +113 -0
  649. offload/_root/data/findings/reviews/nested--afar-cce-c-update-struct-and-member-rejected.json +126 -0
  650. offload/_root/data/findings/reviews/nested--afar-flang-pointer-root-allocatable-component-enter-data-fault.json +191 -0
  651. offload/_root/data/findings/reviews/nested--cce-c-struct-and-nested-pointee-sections-ice.json +113 -0
  652. offload/_root/data/findings/reviews/nested--cce-fortran-nested-allocatable-component-copy-fault.json +214 -0
  653. offload/_root/data/findings/reviews/nested--cce-fortran-pointer-to-type-with-allocatable-scalar-component-typed-transfer.json +191 -0
  654. offload/_root/data/findings/reviews/nested--cce-fortran-update-to-pointer-root-allocatable-scalar-component-no-accelerator-object.json +297 -0
  655. offload/_root/data/findings/reviews/nested--gcc-acc-c-exit-data-of-a-pointer-array-element-target-hangs.json +204 -0
  656. offload/_root/data/findings/reviews/nested--gcc-acc-c-parent-and-shared-pointee-one-directive.json +130 -0
  657. offload/_root/data/findings/reviews/nested--gcc-acc-c-struct-and-member-array-one-directive.json +141 -0
  658. offload/_root/data/findings/reviews/nested--gcc-c-always-on-pointer-member-section-ice.json +113 -0
  659. offload/_root/data/findings/reviews/nested--gcc-c-declare-mapper-pointer-member-section-gpu-fault.json +113 -0
  660. offload/_root/data/findings/reviews/nested--gcc-c-from-struct-and-pointee-section-rejected.json +113 -0
  661. offload/_root/data/findings/reviews/nested--gcc-fortran-pointer-component-alloc-bounds-lost.json +217 -0
  662. offload/_root/data/findings/reviews/nested--gcc-fortran-root-and-element-pointer-components-ice.json +217 -0
  663. offload/_root/data/findings/reviews/nested--nvhpc-c-pointee-before-parent-in-one-map-list-copy-fault.json +204 -0
  664. offload/_root/data/findings/reviews/nested--nvhpc-c-update-from-structure-overwrites-attached-pointer-segv.json +191 -0
  665. offload/_root/data/findings/reviews/nvhpc-c-acc-atomic-update-sum-ice.json +277 -0
  666. offload/_root/data/findings/reviews/nvhpc-c-dist-schedule-chunk-compile-hang.json +113 -0
  667. offload/_root/data/findings/reviews/nvhpc-c-map-present-ignored.json +113 -0
  668. offload/_root/data/findings/reviews/nvhpc-c-ordered-in-target-nvcpfe-crash.json +113 -0
  669. offload/_root/data/findings/reviews/nvhpc-c-parallel-for-firstprivate-in-target.json +113 -0
  670. offload/_root/data/findings/reviews/nvhpc-defaultmap-firstprivate-explicit-map.json +113 -0
  671. offload/_root/data/findings/reviews/nvhpc-defaultmap-from-allocatable-indexing.json +132 -0
  672. offload/_root/data/findings/reviews/nvhpc-depend-inoutset-ignored.json +113 -0
  673. offload/_root/data/findings/reviews/nvhpc-dist-schedule-chunk-illegal-address.json +113 -0
  674. offload/_root/data/findings/reviews/nvhpc-managed-acc-shutdown-host-access.json +113 -0
  675. offload/_root/data/findings/reviews/nvhpc-parallel-if-in-target-nvvm-ice.json +113 -0
  676. offload/_root/data/findings/reviews/oop--afar-final-dealloc-device-link.json +132 -0
  677. offload/_root/data/findings/reviews/oop--afar-pdt-length-parameter-not-implemented.json +113 -0
  678. offload/_root/data/findings/reviews/oop--cce-class-is-device-link.json +130 -0
  679. offload/_root/data/findings/reviews/oop--cce-copy-alloc-component-section-fault.json +217 -0
  680. offload/_root/data/findings/reviews/oop--cce-copy-parent-to-component-tofrom-lost.json +217 -0
  681. offload/_root/data/findings/reviews/oop--cce-copy-second-member-section-lost.json +113 -0
  682. offload/_root/data/findings/reviews/oop--cce-element-component-map-lost.json +113 -0
  683. offload/_root/data/findings/reviews/oop--cce-final-dealloc-device-abort.json +208 -0
  684. offload/_root/data/findings/reviews/oop--cce-pdt-default-mapper-rejected.json +191 -0
  685. offload/_root/data/findings/reviews/oop--cce-polymorphic-dispatch-device-link.json +247 -0
  686. offload/_root/data/findings/reviews/oop--cce-section-of-element-component-overlap.json +113 -0
  687. offload/_root/data/findings/reviews/oop--cce-select-type-guard-not-taken.json +191 -0
  688. offload/_root/data/findings/reviews/oop--cce-udr-derived-type-unsupported.json +132 -0
  689. offload/_root/data/findings/reviews/oop--gcc-acc-parent-and-member-data-rejected.json +141 -0
  690. offload/_root/data/findings/reviews/oop--gcc-pdt-length-parameter-abort.json +141 -0
  691. offload/_root/data/findings/reviews/oop--gcc-polymorphic-dispatch-abort.json +217 -0
  692. offload/_root/data/findings/reviews/oop--gcc-vtab-not-marked-for-offload.json +113 -0
  693. offload/_root/data/findings/reviews/oop--nvhpc-copy-alloc-component-illegal-address.json +113 -0
  694. offload/_root/data/findings/reviews/oop--nvhpc-copy-pdt-length-parameter-illegal-address.json +236 -0
  695. offload/_root/data/findings/reviews/oop--nvhpc-named-mapper-rejected.json +113 -0
  696. offload/_root/data/findings/reviews/oop--nvhpc-polymorphic-dispatch-translate-failure.json +223 -0
  697. offload/_root/data/findings/reviews/oop--nvhpc-polymorphism-runtime-unsupported.json +126 -0
  698. offload/_root/data/findings/reviews/oop--nvhpc-type-bound-call-rejected.json +113 -0
  699. offload/_root/data/findings/reviews/oop--nvhpc-udr-derived-type-rejected.json +113 -0
  700. offload/_root/data/findings/reviews/stress--afar-c-lastprivate-combined-target-not-mapped.json +113 -0
  701. offload/_root/data/findings/reviews/stress--afar-target-scalar-implicit-firstprivate.json +175 -0
  702. offload/_root/data/findings/reviews/stress--cce-acc-declare-create-scalar-serial-firstprivate.json +113 -0
  703. offload/_root/data/findings/reviews/stress--cce-c-distribute-lastprivate-lost.json +113 -0
  704. offload/_root/data/findings/reviews/stress--cce-c-flto-offload-dropped.json +204 -0
  705. offload/_root/data/findings/reviews/stress--cce-ftn-cif-record-split-abort.json +113 -0
  706. offload/_root/data/findings/reviews/stress--cce-lastprivate-num-threads-collapse-simd.json +113 -0
  707. offload/_root/data/findings/reviews/stress--cce-teams-reduction-many-teams-wrap.json +113 -0
  708. offload/_root/data/findings/reviews/stress--cce21-declare-target-scalar-map-write-lost.json +113 -0
  709. offload/_root/data/findings/reviews/stress--gcc-declare-target-allocatable-component-ice.json +191 -0
  710. offload/_root/data/findings/reviews/stress--nvhpc-acc-derived-type-component-not-copied.json +126 -0
  711. offload/_root/data/findings/reviews/stress--nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.json +113 -0
  712. offload/_root/data/findings/reviews/stress--nvhpc-declare-target-scalar-write-lost.json +113 -0
  713. offload/_root/data/findings/reviews/stress--nvhpc-lastprivate-combined-target-not-mapped.json +113 -0
  714. offload/_root/data/findings/reviews/stress--nvhpc-target-scalar-implicit-firstprivate.json +130 -0
  715. offload/_root/data/findings/reviews/target-update-sections-in-parallel.json +113 -0
  716. offload/_root/data/findings/stress/afar-c-lastprivate-combined-target-not-mapped.c +72 -0
  717. offload/_root/data/findings/stress/afar-target-scalar-implicit-firstprivate.f90 +47 -0
  718. offload/_root/data/findings/stress/cce-acc-declare-create-scalar-serial-firstprivate.f90 +43 -0
  719. offload/_root/data/findings/stress/cce-c-distribute-lastprivate-lost.c +52 -0
  720. offload/_root/data/findings/stress/cce-c-flto-offload-dropped.c +73 -0
  721. offload/_root/data/findings/stress/cce-ftn-cif-record-split-abort.f90 +109 -0
  722. offload/_root/data/findings/stress/cce-lastprivate-num-threads-collapse-simd.f90 +66 -0
  723. offload/_root/data/findings/stress/cce-teams-reduction-many-teams-wrap.f90 +55 -0
  724. offload/_root/data/findings/stress/cce21-declare-target-scalar-map-write-lost.f90 +58 -0
  725. offload/_root/data/findings/stress/controls/gcc-declare-target-allocatable-component-control.f90 +29 -0
  726. offload/_root/data/findings/stress/gcc-declare-target-allocatable-component-ice.f90 +61 -0
  727. offload/_root/data/findings/stress/nvhpc-acc-derived-type-component-not-copied.f90 +33 -0
  728. offload/_root/data/findings/stress/nvhpc-c-lastprivate-teams-distribute-parallel-for-lost.c +62 -0
  729. offload/_root/data/findings/stress/nvhpc-declare-target-scalar-write-lost.f90 +43 -0
  730. offload/_root/data/findings/stress/nvhpc-lastprivate-combined-target-not-mapped.f90 +60 -0
  731. offload/_root/data/findings/stress/nvhpc-target-scalar-implicit-firstprivate.f90 +41 -0
  732. offload/_root/data/findings/target-update-sections-in-parallel.f90 +53 -0
  733. offload/_root/data/issues.json +1 -0
  734. offload/_root/data/search.json +1 -0
  735. offload/_root/data/vendor/checks/build.py +288 -0
  736. offload/_version.py +24 -0
  737. offload/canuse.py +88 -0
  738. offload/cli.py +52 -0
  739. offload/data.py +85 -0
  740. offload/engine/__init__.py +0 -0
  741. offload/engine/acc_c_gen.py +415 -0
  742. offload/engine/acc_gen.py +1104 -0
  743. offload/engine/acc_rules.py +656 -0
  744. offload/engine/apps.py +804 -0
  745. offload/engine/baseline.py +324 -0
  746. offload/engine/c_gen.py +883 -0
  747. offload/engine/c_program.py +913 -0
  748. offload/engine/c_rules.py +503 -0
  749. offload/engine/campaign.py +1180 -0
  750. offload/engine/campaigns/__init__.py +2 -0
  751. offload/engine/campaigns/hecbench.py +153 -0
  752. offload/engine/campaigns/nested.py +76 -0
  753. offload/engine/campaigns/oop.py +61 -0
  754. offload/engine/canary.py +275 -0
  755. offload/engine/catalog.py +860 -0
  756. offload/engine/citations.py +532 -0
  757. offload/engine/clang_oracle.py +208 -0
  758. offload/engine/combos.py +234 -0
  759. offload/engine/confirm/__init__.py +2 -0
  760. offload/engine/confirm/backlog.py +159 -0
  761. offload/engine/confirm/claims.py +423 -0
  762. offload/engine/confirm/collectors.py +665 -0
  763. offload/engine/confirm/decisions.py +202 -0
  764. offload/engine/confirm/draft.py +195 -0
  765. offload/engine/confirm/engine.py +157 -0
  766. offload/engine/confirm/execute.py +382 -0
  767. offload/engine/confirm/facts.py +467 -0
  768. offload/engine/confirm/findings.py +617 -0
  769. offload/engine/confirm/forms.py +145 -0
  770. offload/engine/confirm/ingest.py +372 -0
  771. offload/engine/confirm/jobs.py +215 -0
  772. offload/engine/confirm/legality.py +323 -0
  773. offload/engine/confirm/machines.py +254 -0
  774. offload/engine/confirm/minimize.py +268 -0
  775. offload/engine/confirm/model.py +249 -0
  776. offload/engine/confirm/ports.py +107 -0
  777. offload/engine/confirm/programs.py +222 -0
  778. offload/engine/confirm/registry.py +151 -0
  779. offload/engine/confirm/regress.py +278 -0
  780. offload/engine/confirm/rules.py +565 -0
  781. offload/engine/confirm/store.py +63 -0
  782. offload/engine/confirm/triage.py +838 -0
  783. offload/engine/confirm/validate.py +607 -0
  784. offload/engine/confirm/witness.py +355 -0
  785. offload/engine/corpus_check.py +388 -0
  786. offload/engine/coverage.py +318 -0
  787. offload/engine/dataenv_gen.py +1318 -0
  788. offload/engine/dataenv_rules.py +278 -0
  789. offload/engine/devtemps.py +146 -0
  790. offload/engine/expected.py +2559 -0
  791. offload/engine/f2c.py +1783 -0
  792. offload/engine/finding_claims.py +330 -0
  793. offload/engine/finding_header.py +229 -0
  794. offload/engine/finding_rows.py +382 -0
  795. offload/engine/flang_oracle.py +299 -0
  796. offload/engine/fsource.py +620 -0
  797. offload/engine/fuzz.py +2455 -0
  798. offload/engine/fuzz_base.py +2016 -0
  799. offload/engine/fuzz_campaign.py +592 -0
  800. offload/engine/fuzz_exec.py +798 -0
  801. offload/engine/fuzz_min.py +1049 -0
  802. offload/engine/fuzz_profile.py +340 -0
  803. offload/engine/fuzz_report.py +1270 -0
  804. offload/engine/fuzz_steer.py +181 -0
  805. offload/engine/gate.py +1013 -0
  806. offload/engine/gate_report.py +711 -0
  807. offload/engine/gate_run.py +118 -0
  808. offload/engine/gate_stored.py +466 -0
  809. offload/engine/gen.py +1114 -0
  810. offload/engine/gen_templates.py +2583 -0
  811. offload/engine/grammar.py +695 -0
  812. offload/engine/harness.py +1472 -0
  813. offload/engine/harvest/__init__.py +3 -0
  814. offload/engine/harvest/c_runtime.py +465 -0
  815. offload/engine/harvest/checks.py +534 -0
  816. offload/engine/harvest/convert.py +784 -0
  817. offload/engine/harvest/f_runtime.py +668 -0
  818. offload/engine/harvest/filters.py +338 -0
  819. offload/engine/harvest/history.py +122 -0
  820. offload/engine/harvest/inventory.py +399 -0
  821. offload/engine/harvest2/__init__.py +5 -0
  822. offload/engine/harvest2/aomp.py +119 -0
  823. offload/engine/harvest2/examples.py +116 -0
  824. offload/engine/harvest2/gomp.py +679 -0
  825. offload/engine/harvest2/hand.py +150 -0
  826. offload/engine/harvest2/split.py +78 -0
  827. offload/engine/harvest_known/__init__.py +2 -0
  828. offload/engine/harvest_known/apps.py +65 -0
  829. offload/engine/harvest_known/cases.py +128 -0
  830. offload/engine/harvest_known/centres.py +184 -0
  831. offload/engine/harvest_known/forum.py +180 -0
  832. offload/engine/harvest_known/relnotes.py +42 -0
  833. offload/engine/harvest_known/textscan.py +107 -0
  834. offload/engine/hdrwrap.py +42 -0
  835. offload/engine/hecbench.py +568 -0
  836. offload/engine/hecbench_report.py +265 -0
  837. offload/engine/hecbench_run.py +483 -0
  838. offload/engine/idioms.py +1427 -0
  839. offload/engine/kbody_cites.py +81 -0
  840. offload/engine/kbody_emit.py +949 -0
  841. offload/engine/kbody_gen.py +795 -0
  842. offload/engine/legality_review.py +236 -0
  843. offload/engine/localcfg.py +58 -0
  844. offload/engine/malloc_dd.py +267 -0
  845. offload/engine/mutate.py +1933 -0
  846. offload/engine/nested_cites.py +196 -0
  847. offload/engine/nested_emit.py +789 -0
  848. offload/engine/nested_gen.py +1721 -0
  849. offload/engine/offload_scope.py +197 -0
  850. offload/engine/ompvv.py +624 -0
  851. offload/engine/oop_gen.py +2249 -0
  852. offload/engine/prerun.py +1435 -0
  853. offload/engine/program_source.py +739 -0
  854. offload/engine/provenance.py +103 -0
  855. offload/engine/reduce.py +2098 -0
  856. offload/engine/report.py +3374 -0
  857. offload/engine/root.py +33 -0
  858. offload/engine/rules.py +4256 -0
  859. offload/engine/run_matrix.py +325 -0
  860. offload/engine/site/__init__.py +1 -0
  861. offload/engine/site/columns.py +53 -0
  862. offload/engine/site/data.py +648 -0
  863. offload/engine/site/header.py +228 -0
  864. offload/engine/site/inputs.py +115 -0
  865. offload/engine/site/matrix.py +434 -0
  866. offload/engine/site/scrub.py +294 -0
  867. offload/engine/skeleton.py +121 -0
  868. offload/engine/spec_text.py +242 -0
  869. offload/engine/stress.py +4122 -0
  870. offload/engine/stress_c.py +1000 -0
  871. offload/engine/stress_exec.py +805 -0
  872. offload/engine/stress_min.py +248 -0
  873. offload/engine/stress_report.py +552 -0
  874. offload/engine/textclean.py +97 -0
  875. offload/engine/toolchains.py +1070 -0
  876. offload/engine/transforms.py +511 -0
  877. offload/engine/usage.py +1252 -0
  878. offload/engine/widen.py +673 -0
  879. offload/engine/widen_progs.py +2396 -0
  880. offload/issues.py +146 -0
  881. offtarget-0.1.0.dist-info/METADATA +86 -0
  882. offtarget-0.1.0.dist-info/RECORD +886 -0
  883. offtarget-0.1.0.dist-info/WHEEL +4 -0
  884. offtarget-0.1.0.dist-info/entry_points.txt +2 -0
  885. offtarget-0.1.0.dist-info/licenses/LICENSE +21 -0
  886. offtarget-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +20 -0
@@ -0,0 +1,4256 @@
1
+ """Restriction rules (layer 3 of the catalog) and the program tree they read.
2
+
3
+ ``parse_program(source, version)`` scans free-form Fortran for its OpenMP
4
+ directives (``!$acc`` for version 34) and returns a ``ProgramTree``:
5
+ ``directives``, ``nesting`` (parent, child pairs), ``assoc`` (the code each
6
+ directive applies to), ``ends`` (paired end directives) and the declaration
7
+ tables ``decls``, ``vartypes``, ``types`` and ``uses``. It raises
8
+ ``ProgramError`` on unmatched or unclosed constructs.
9
+ ``catalog/omp/rules.yaml`` and ``catalog/acc/rules.yaml`` hold the rules.
10
+ Each ``spec`` entry becomes one ``Rule`` with id ``OMP<ver>-<id>`` or
11
+ ``ACC34-<stem>``. A static rule names a predicate registered with
12
+ ``@predicate``; a runtime rule has none and gives a ``why``.
13
+ A predicate takes ``(tree, version)`` and returns the indices of the
14
+ offending directives. ``violations(tree, version)`` returns the sorted ids
15
+ of violated rules; ``findings`` returns ``(id, directive index)`` pairs."""
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ from dataclasses import dataclass, field
20
+ from functools import lru_cache
21
+ from pathlib import Path
22
+ from typing import Callable
23
+
24
+ import yaml
25
+
26
+ from offload.engine.catalog import (DIRECTIVE_NAME_CLASS, catalog_dir,
27
+ default_grammar, default_skeleton, model_of,
28
+ rule_valid)
29
+ from offload.engine.grammar import (_FIXED_FORMS, _SENTINELS, Directive,
30
+ _is_comment, _strip_bang_comment,
31
+ parse_directive)
32
+
33
+ from offload.engine.root import ROOT # noqa: E402
34
+
35
+ RULES_PATH = ROOT / "catalog" / "omp" / "rules.yaml"
36
+
37
+
38
+ class ProgramError(ValueError):
39
+ """Directive structure that parse_program cannot pair or does not
40
+ support."""
41
+
42
+
43
+ @dataclass
44
+ class Assoc:
45
+ kind: str = "standalone" # block, loop, statement, standalone
46
+ line: int = 0
47
+ end_line: int | None = None
48
+ loop_depth: int = 0
49
+ loop_vars: tuple = ()
50
+ stmts: int = 0
51
+ unit: int = 0
52
+ depth_known: bool = True
53
+ scope: tuple = () # enclosing scoping units, outermost first
54
+ # Per loop of the DO chain (loop_vars order): the bounds
55
+ # (lb, ub, incr) as lower-cased text (incr "" when absent; DO
56
+ # CONCURRENT: its first index-spec), and the number of base-language
57
+ # statements directly in that loop's body outside the next loop of the
58
+ # chain (intervening code; 0 for every loop but the innermost means
59
+ # perfectly nested)
60
+ loop_bounds: tuple = ()
61
+ intervening: tuple = ()
62
+
63
+
64
+ @dataclass
65
+ class ProgramTree:
66
+ directives: list[Directive]
67
+ nesting: list[tuple[int, int]]
68
+ assoc: list[Assoc] = field(default_factory=list)
69
+ ends: dict[int, Directive] = field(default_factory=dict)
70
+ decls: dict = field(default_factory=dict) # (scope, name) -> attribute
71
+ vartypes: dict = field(default_factory=dict) # (scope, name) -> type
72
+ types: dict = field(default_factory=dict) # (scope, type) -> components
73
+ uses: dict = field(default_factory=dict) # scope -> [None | {names}]
74
+ # base-language statements (line, lower-cased text, scoping units) and
75
+ # named constants (scope, name): for the default(none) rule
76
+ statements: list = field(default_factory=list)
77
+ params: set = field(default_factory=set)
78
+ # (scope, name) -> intrinsic type keyword of its declaration
79
+ # ("integer", "real", "doubleprecision", "complex", "logical",
80
+ # "character", "type", "class"); names declared with an array spec;
81
+ # names with the SAVE attribute (attribute, SAVE statement, or an
82
+ # initializer); scoping unit -> (kind, name) for program units and
83
+ # subprograms (kind: program, module, submodule, blockdata,
84
+ # subroutine, function); scoping unit -> module names of its USE
85
+ # statements
86
+ vtypes: dict = field(default_factory=dict)
87
+ arrays: set = field(default_factory=set)
88
+ saved: set = field(default_factory=set)
89
+ units: dict = field(default_factory=dict)
90
+ use_modules: dict = field(default_factory=dict)
91
+ # The source language ("f90" or "c", offload.engine.c_program) and
92
+ # the C scanner's extra facts
93
+ lang: str = "f90"
94
+ extra: dict = field(default_factory=dict)
95
+
96
+ def _find(self, scope: tuple, name: str, table: dict):
97
+ """(scope chain up to the declaring unit, entry) of `name` in
98
+ `table` from the innermost scoping unit of `scope` outward (host
99
+ association), or None when no declaration is visible: not
100
+ declared, or a USE statement in a scoping unit on the way (the
101
+ declaring one included) may supply the name."""
102
+ for k in range(len(scope) - 1, -1, -1):
103
+ sid = scope[k]
104
+ if any(u is None or name in u for u in self.uses.get(sid, ())):
105
+ return None
106
+ if (sid, name) in table:
107
+ return scope[:k + 1], table[(sid, name)]
108
+ return None
109
+
110
+ def _lookup(self, scope: tuple, name: str, table: dict):
111
+ hit = self._find(scope, name, table)
112
+ return None if hit is None else hit[1]
113
+
114
+ def attribute(self, i: int, name: str) -> str | None:
115
+ """Declared attribute of `name` seen from directive i: "pointer",
116
+ "allocatable" or "plain"; None when no declaration is visible."""
117
+ return self._lookup(self.assoc[i].scope, name, self.decls)
118
+
119
+ def designator_attribute(self, i: int, item: str) -> str | None:
120
+ """Attribute of a list item `v` or `v%c%d` (C: `v.c`, `v->c`;
121
+ subscripts ignored) seen from directive i: for a component, every
122
+ derived type on the path must be defined in the visible source (a
123
+ use-associated type gives None)."""
124
+ item = re.sub(r"\[[^\[\]]*\]", "", item)
125
+ parts = [re.sub(r"\(.*", "", p).strip() for p in
126
+ re.split(r"%|->|\.", re.sub(r"\([^()]*\)", "", item))]
127
+ scope = self.assoc[i].scope
128
+ hit = self._find(scope, parts[0], self.decls)
129
+ if hit is None:
130
+ return None
131
+ # A type name is resolved where the entity naming it was declared:
132
+ # the variable's declaring unit, then the defining unit of each
133
+ # type on the path (a host's s keeps the host's type t).
134
+ scope, attr = hit
135
+ tname = self.vartypes.get((scope[-1], parts[0]))
136
+ for comp in parts[1:]:
137
+ found = self._find(scope, tname, self.types) if tname else None
138
+ if found is None or comp not in found[1]:
139
+ return None
140
+ scope, comps = found
141
+ attr, tname = comps[comp]
142
+ return attr
143
+
144
+ def __post_init__(self):
145
+ self.assoc += [Assoc() for _ in self.directives[len(self.assoc):]]
146
+
147
+ def parent(self, i: int) -> int | None:
148
+ for p, c in self.nesting:
149
+ if c == i:
150
+ return p
151
+ return None
152
+
153
+ def children(self, i: int) -> list[int]:
154
+ return [c for p, c in self.nesting if p == i]
155
+
156
+ def descendants(self, i: int) -> list[int]:
157
+ out = []
158
+ for c in self.children(i):
159
+ out += [c] + self.descendants(c)
160
+ return out
161
+
162
+
163
+ # --- directive facts from the skeleton ---------------------------------------
164
+
165
+ # Directives associated with one statement whose end directive is optional
166
+ # (5.2 §3.1 / 6.0 §5.1 and §6.3); when the next directive is their end
167
+ # directive they span the statements up to it.
168
+ _ONE_STATEMENT = {"atomic", "allocators", "dispatch"}
169
+
170
+
171
+ def leaves(key: str, model: str = "omp") -> list[str]:
172
+ """Leaf constructs of a directive, outermost first (itself if a leaf)."""
173
+ return list(default_skeleton(model).directive(key).leaves) or [key]
174
+
175
+
176
+ def association(key: str, model: str = "omp") -> str:
177
+ """block, loop, statement, loopseq or standalone."""
178
+ if key in _ONE_STATEMENT:
179
+ return "statement"
180
+ sk = default_skeleton(model)
181
+ assoc = sk.directive(key).association
182
+ if assoc == "FromLeaves":
183
+ assoc = sk.directive(leaves(key, model)[-1]).association
184
+ return {"Block": "block", "LoopNest": "loop",
185
+ "LoopSeq": "loopseq"}.get(assoc, "standalone")
186
+
187
+
188
+ _association = association
189
+
190
+
191
+ # --- the scanner -------------------------------------------------------------
192
+
193
+ _DO = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?do(?![\w=])\s*(.*)$")
194
+ _END_DO = re.compile(r"^end\s*do(?:\s+[a-z_]\w*)?$")
195
+ _UNIT_END = re.compile(r"^(?:end(?:\s*(?:program|subroutine|function|module|"
196
+ r"submodule|procedure|block\s*data)\b.*)?|contains)$")
197
+ _BLOCK = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?block$")
198
+ _END_BLOCK = re.compile(r"^end\s*block(?:\s+[a-z_]\w*)?$")
199
+ _LABEL = re.compile(r"^\d+\s+")
200
+ # Program unit and subprogram headers and ends, for the compilation unit of a
201
+ # directive (Fortran: a program unit; 5.2 §1.2.2, p.3). A separate module
202
+ # procedure (module procedure ... end procedure) is ignored as a pair.
203
+ _UNIT_HEAD = re.compile(
204
+ r"^(?:program\s+\w+|module\s+(?!procedure\b)\w+|submodule\s*\(.*|"
205
+ r"block\s*data\b.*|(?:[\w(),=*:]+\s+)*?(?:subroutine|function)\s+"
206
+ r"[a-z_]\w*\s*(?:\(.*|result\b.*|bind\b.*)?)$")
207
+ _UNIT_TAIL = re.compile(r"^end(?:\s*(?:program|subroutine|function|module|"
208
+ r"submodule|block\s*data)\b.*)?$")
209
+
210
+
211
+ @dataclass
212
+ class _Do:
213
+ var: str | None
214
+ line: int
215
+ owner: int | None = None # loop directive this DO is associated with
216
+ loops: list = field(default_factory=list)
217
+ directive: bool = False
218
+ transform: bool = False # a loop-associated directive in the body
219
+ bounds: tuple = ("", "", "") # lb, ub, incr (lower-cased text)
220
+ stmts: int = 0 # base-language statements directly in the body
221
+
222
+
223
+ @dataclass
224
+ class _Blk:
225
+ line: int
226
+ owner: int | None = None # block directive whose structured block it is
227
+ loops: list = field(default_factory=list) # DO and BLOCK constructs
228
+ directive: bool = False
229
+ transform: bool = False
230
+ stmts: int = 0
231
+
232
+
233
+ @dataclass
234
+ class _Dir:
235
+ index: int
236
+ kind: str # block, loop, statement
237
+ pending: bool = False # waiting for its DO loop / first statement
238
+
239
+
240
+ def _split_statements(text: str) -> list[str]:
241
+ """Split a statement line at ';' outside character literals."""
242
+ parts, cur, quote = [], [], None
243
+ for ch in text:
244
+ if quote:
245
+ quote = None if ch == quote else quote
246
+ elif ch in "'\"":
247
+ quote = ch
248
+ elif ch == ";":
249
+ parts.append("".join(cur))
250
+ cur = []
251
+ continue
252
+ cur.append(ch)
253
+ parts.append("".join(cur))
254
+ return [p for p in parts if p.strip()]
255
+
256
+
257
+ def _logical_lines(source: str, model: str = "omp"):
258
+ """(first line number, kind, text): kind is "omp" (raw directive text
259
+ of the model's sentinel, continuation lines included) or "stmt"
260
+ (lower-cased statement)."""
261
+ _SENTINEL, _FIXED_FORM = _SENTINELS[model], _FIXED_FORMS[model]
262
+ lines = source.splitlines()
263
+ n = 0
264
+ while n < len(lines):
265
+ raw = lines[n]
266
+ start = n + 1
267
+ if _SENTINEL.match(raw) or _FIXED_FORM.match(raw):
268
+ group = [raw] # parse_directive rejects a fixed-form sentinel
269
+ while _strip_bang_comment(
270
+ _SENTINEL.sub("", group[-1], count=1)).rstrip() \
271
+ .endswith("&"):
272
+ n += 1
273
+ while n < len(lines) and _is_comment(lines[n]):
274
+ n += 1
275
+ if n >= len(lines):
276
+ raise ProgramError(f"line {start}: directive continued "
277
+ "past the end of the source")
278
+ group.append(lines[n])
279
+ n += 1
280
+ yield start, "omp", "\n".join(group)
281
+ continue
282
+ stripped = raw.lstrip()
283
+ if _is_comment(raw) or stripped.startswith("#") or (
284
+ stripped.startswith("!$")
285
+ and not re.match(r"!\$[\s&]", stripped)):
286
+ n += 1
287
+ continue
288
+ if stripped.startswith("!$"):
289
+ stripped = stripped[2:]
290
+ text = _strip_bang_comment(stripped).rstrip()
291
+ while text.endswith("&"):
292
+ n += 1
293
+ while n < len(lines) and _is_comment(lines[n]):
294
+ n += 1
295
+ if n >= len(lines):
296
+ break
297
+ nxt = _strip_bang_comment(lines[n].lstrip()).rstrip()
298
+ text = text[:-1] + (nxt[1:] if nxt.startswith("&") else nxt)
299
+ n += 1
300
+ for part in _split_statements(text):
301
+ yield start, "stmt", " ".join(part.lower().split())
302
+
303
+
304
+ def _is_do(stmt: str) -> bool:
305
+ """A DO statement (not an assignment to a variable named do)."""
306
+ m = _DO.match(stmt)
307
+ return bool(m) and not m.group(1).startswith("=")
308
+
309
+
310
+ def _do_var(rest: str, line: int) -> str | None:
311
+ """Iteration variable of a DO statement, None if not canonical."""
312
+ if re.match(r"^\d", rest):
313
+ raise ProgramError(f"line {line}: label DO loops are not supported")
314
+ m = re.match(r"^concurrent\s*\(\s*(?:[^:()]*::)?\s*([a-z_]\w*)\s*=", rest)
315
+ if m:
316
+ return m.group(1)
317
+ m = re.match(r"^,?\s*([a-z_]\w*)\s*=", rest)
318
+ if m and not rest.startswith("while"):
319
+ return m.group(1)
320
+ return None
321
+
322
+
323
+ def _depth(do: _Do) -> tuple:
324
+ """(loop variables, known) of the DO chain starting at `do`: a loop
325
+ body holding exactly one DO loop, possibly inside BLOCK constructs
326
+ (5.2 §4.4.1, p.86), and no directive continues the chain. `known` is
327
+ False when a loop-associated (loop-transforming) directive in a body
328
+ stops it: its generated loops may continue the nest."""
329
+ dos, known = _chain(do)
330
+ return tuple(d.var for d in dos), known
331
+
332
+
333
+ def _chain(do: _Do) -> tuple:
334
+ """(the _Do frames of the chain starting at `do`, known)."""
335
+ if do.var is None:
336
+ return [], True
337
+ inner, known = _body_chain(do)
338
+ return [do] + inner, known
339
+
340
+
341
+ def _body_chain(frame) -> tuple:
342
+ if frame.transform:
343
+ return [], False
344
+ # BLOCK constructs without a loop are intervening code
345
+ loops = [c for c in frame.loops if isinstance(c, _Do) or _has_loop(c)]
346
+ if len(loops) != 1 or frame.directive:
347
+ return [], True
348
+ inner = loops[0]
349
+ return _body_chain(inner) if isinstance(inner, _Blk) else _chain(inner)
350
+
351
+
352
+ def _has_loop(blk: _Blk) -> bool:
353
+ return any(isinstance(c, _Do) or _has_loop(c) for c in blk.loops)
354
+
355
+
356
+ def _intervening(dos: list) -> tuple:
357
+ """Statements directly in each chain loop's body outside the next
358
+ chain loop: its own statements and those of the BLOCK constructs
359
+ around the next loop or beside it (BLOCK constructs are transparent,
360
+ 5.2 §4.4.1)."""
361
+ out = []
362
+ for k, do in enumerate(dos):
363
+ nxt = dos[k + 1] if k + 1 < len(dos) else None
364
+ out.append(do.stmts + _between(do, nxt))
365
+ return tuple(out)
366
+
367
+
368
+ def _contains(frame, target) -> bool:
369
+ return any(c is target or (isinstance(c, _Blk) and _contains(c, target))
370
+ for c in frame.loops)
371
+
372
+
373
+ def _between(frame, target) -> int:
374
+ """Statements in the BLOCK constructs of `frame` (recursively) that are
375
+ not inside `target` (None: all of them)."""
376
+ n = 0
377
+ for c in frame.loops:
378
+ if c is target or not isinstance(c, _Blk):
379
+ continue
380
+ n += c.stmts + _between(c, target)
381
+ return n
382
+
383
+
384
+ def _split_top(text: str, sep: str) -> list[str]:
385
+ """Split at `sep` outside parentheses."""
386
+ parts, cur, depth = [], [], 0
387
+ for ch in text:
388
+ if ch in "([":
389
+ depth += 1
390
+ elif ch in ")]":
391
+ depth -= 1
392
+ elif ch == sep and depth == 0:
393
+ parts.append("".join(cur).strip())
394
+ cur = []
395
+ continue
396
+ cur.append(ch)
397
+ parts.append("".join(cur).strip())
398
+ return parts
399
+
400
+
401
+ def _do_bounds(rest: str) -> tuple:
402
+ """(lb, ub, incr) of a DO statement from the text after DO (DO
403
+ CONCURRENT: its first index-spec); empty strings when unknown."""
404
+ m = re.match(r"^concurrent\s*\((.*)$", rest)
405
+ if m:
406
+ header, depth = [], 1
407
+ for ch in m.group(1):
408
+ depth += (ch == "(") - (ch == ")")
409
+ if depth == 0:
410
+ break
411
+ header.append(ch)
412
+ spec = "".join(header)
413
+ spec = spec.split("::", 1)[1] if "::" in spec else spec
414
+ first = _split_top(spec, ",")[0]
415
+ mm = re.match(r"^\s*[a-z_]\w*\s*=\s*(.*)$", first)
416
+ parts = _split_top(mm.group(1), ":") if mm else []
417
+ else:
418
+ m = re.match(r"^,?\s*[a-z_]\w*\s*=\s*(.*)$", rest)
419
+ parts = _split_top(m.group(1), ",") if m else []
420
+ return tuple((parts + ["", "", ""])[:3])
421
+
422
+
423
+ _TYPE_SPEC = re.compile(
424
+ r"^(?:integer|real|double\s*precision|double\s*complex|complex|logical|"
425
+ r"character|type\s*\(|class\s*\()")
426
+ _ATTR_STMT = re.compile(r"^(pointer|allocatable|dimension|target)\b\s*"
427
+ r"(?:::)?\s*(.*)$")
428
+ _TYPE_DEF = re.compile(r"^type\b(?!\s*\()(?!\s*is\b)\s*(?:,[^:]*)?(?:::)?"
429
+ r"\s*[a-z_]\w*(?:\s*\(.*\))?$")
430
+ _END_TYPE = re.compile(r"^end\s*type\b")
431
+ _RANK = {"pointer": 3, "allocatable": 2, "plain": 1}
432
+ _USE = re.compile(r"^use\b(?:\s*,\s*(?:non_)?intrinsic)?(?:\s*::)?\s*"
433
+ r"[a-z_]\w*\s*(?:,\s*(.*))?$")
434
+ # type(t) / class(t); class(*) and type(*) name no type, so a component of
435
+ # such an entity never resolves (never fires).
436
+ _DECL_TYPE = re.compile(r"^(?:type|class)\s*\(\s*([a-z_]\w*)\s*\)")
437
+
438
+
439
+ def _use_names(stmt: str):
440
+ """For a USE statement: None if it may supply any name (no ONLY), else
441
+ the set of local names of its ONLY list (renames: the local name). Not
442
+ a USE statement: False."""
443
+ m = _USE.match(stmt)
444
+ if not m:
445
+ return False
446
+ rest = (m.group(1) or "").strip()
447
+ only = re.match(r"^only\s*:(.*)$", rest)
448
+ if not only:
449
+ return None
450
+ names = set()
451
+ for part in _split_statements_commas(only.group(1)):
452
+ local = part.split("=>")[0].strip()
453
+ if re.fullmatch(r"[a-z_]\w*", local):
454
+ names.add(local)
455
+ return names
456
+
457
+
458
+ def _entities(text: str) -> list[str]:
459
+ """Names of an entity list (`a(:), b = 1, c`)."""
460
+ names = []
461
+ for part in _split_statements_commas(text):
462
+ m = re.match(r"\s*([a-z_]\w*)", part)
463
+ if m:
464
+ names.append(m.group(1))
465
+ return names
466
+
467
+
468
+ def _split_statements_commas(text: str) -> list[str]:
469
+ parts, cur, depth = [], [], 0
470
+ for ch in text:
471
+ if ch in "([":
472
+ depth += 1
473
+ elif ch in ")]":
474
+ depth -= 1
475
+ elif ch == "," and depth == 0:
476
+ parts.append("".join(cur))
477
+ cur = []
478
+ continue
479
+ cur.append(ch)
480
+ parts.append("".join(cur))
481
+ return parts
482
+
483
+
484
+ def _declaration(stmt: str) -> tuple[str, list[str]] | None:
485
+ """(attribute, names) of a type declaration or attribute statement
486
+ (pointer, allocatable, dimension, target), else None. The attribute is
487
+ pointer, allocatable or plain."""
488
+ m = _ATTR_STMT.match(stmt)
489
+ if m and not re.match(r"^\w+\s*=", stmt):
490
+ attr = m.group(1) if m.group(1) in ("pointer", "allocatable") \
491
+ else "plain"
492
+ return attr, _entities(m.group(2))
493
+ if not _TYPE_SPEC.match(stmt) or re.search(r"\bfunction\b", stmt):
494
+ return None
495
+ if "::" in stmt:
496
+ spec, ents = stmt.split("::", 1)
497
+ else: # old style: type-spec [(kind)] [*len] entities
498
+ m = re.match(r"^((?:double\s*precision|double\s*complex|[a-z]+)"
499
+ r"\s*(?:\([^()]*(?:\([^()]*\)[^()]*)*\))?"
500
+ r"\s*(?:\*\s*\d+)?)\s+(.*)$", stmt)
501
+ if not m:
502
+ return None
503
+ spec, ents = m.group(1), m.group(2)
504
+ attrs = [a.strip().split("(")[0].strip()
505
+ for a in _split_statements_commas(spec)[1:]]
506
+ attr = "pointer" if "pointer" in attrs else (
507
+ "allocatable" if "allocatable" in attrs else "plain")
508
+ return attr, _entities(ents)
509
+
510
+
511
+ _SAVE_STMT = re.compile(r"^save\b\s*(?:::)?\s*(.*)$")
512
+ _USE_MOD = re.compile(r"^use\b(?:\s*,\s*(?:non_)?intrinsic)?(?:\s*::)?\s*"
513
+ r"([a-z_]\w*)")
514
+ # "module integer function f(x)" (a separate module procedure's interface
515
+ # body or definition) is a function, not a module
516
+ _UNIT_KIND = re.compile(r"^(?:(program)|(module)(?=\s+\w+$)|(submodule)|"
517
+ r"(block\s*data)"
518
+ r"|.*?\b(subroutine|function))\s*(?:\([^)]*\)\s*)?"
519
+ r"([a-z_]\w*)?")
520
+
521
+
522
+ def _decl_facts(stmt: str) -> tuple:
523
+ """Facts of a specification statement: (intrinsic type keyword
524
+ or None, names with an array spec, names with SAVE, save_all) where
525
+ save_all marks a bare SAVE statement."""
526
+ m = _SAVE_STMT.match(stmt)
527
+ if m and not re.match(r"^save\s*=", stmt):
528
+ rest = m.group(1).strip()
529
+ return None, set(), set(_entities(re.sub(r"/\w+/", "", rest))), \
530
+ not rest
531
+ m = _ATTR_STMT.match(stmt)
532
+ if m and not re.match(r"^\w+\s*=", stmt):
533
+ ents = m.group(2)
534
+ arrays = {n for n in _entities(ents)
535
+ if re.search(rf"\b{n}\s*\(", ents)}
536
+ return None, arrays, set(), False
537
+ if not _TYPE_SPEC.match(stmt) or re.search(r"\bfunction\b", stmt):
538
+ return None, set(), set(), False
539
+ if "::" in stmt:
540
+ spec, ents = stmt.split("::", 1)
541
+ else:
542
+ m = re.match(r"^((?:double\s*precision|double\s*complex|[a-z]+)"
543
+ r"\s*(?:\([^()]*(?:\([^()]*\)[^()]*)*\))?"
544
+ r"\s*(?:\*\s*\d+)?)\s+(.*)$", stmt)
545
+ if not m:
546
+ return None, set(), set(), False
547
+ spec, ents = m.group(1), m.group(2)
548
+ vtype = re.sub(r"\s+", "", re.match(r"^(double\s*precision|double\s*"
549
+ r"complex|[a-z]+)", spec).group(1))
550
+ attrs = [a.strip().split("(")[0].strip()
551
+ for a in _split_statements_commas(spec)[1:]]
552
+ names, arrays, saved = _entities(ents), set(), set()
553
+ for part in _split_statements_commas(ents):
554
+ mm = re.match(r"\s*([a-z_]\w*)\s*(\()?", part)
555
+ if not mm:
556
+ continue
557
+ if mm.group(2) or "dimension" in attrs:
558
+ arrays.add(mm.group(1))
559
+ if ("save" in attrs or re.search(r"(?<![=<>/])=(?!=)", part)) \
560
+ and "parameter" not in attrs:
561
+ saved.add(mm.group(1))
562
+ return vtype, arrays, saved, False
563
+
564
+
565
+ def parse_program(source: str, version: int,
566
+ lang: str | None = None) -> ProgramTree:
567
+ """Directive tree of a free-form Fortran source (see the module doc),
568
+ or of a C source (``offload.engine.c_program``; `lang` "c", or
569
+ None and the source has a ``#pragma omp`` line)."""
570
+ from offload.engine.c_program import is_c_source, parse_c_program
571
+ if lang == "c" or (lang is None and is_c_source(source)):
572
+ return parse_c_program(source, version)
573
+ tree = _parse_fortran(source, version)
574
+ # Integer object-like macros of a preprocessed source (the
575
+ # omp_vv Fortran tests), for the iterator range rule
576
+ tree.extra["macros"] = {
577
+ k: int(v) for k, v in re.findall(
578
+ r"^\s*#\s*define\s+([A-Za-z_]\w*)\s+\(?\s*(-?\d+)\s*\)?\s*$",
579
+ source, re.M)}
580
+ return tree
581
+
582
+
583
+ def _parse_fortran(source: str, version: int) -> ProgramTree:
584
+ tree = ProgramTree([], [], [], {})
585
+ stack: list = []
586
+ just_closed: int | None = None
587
+ model = model_of(version)
588
+
589
+ def association(key):
590
+ return _association(key, model)
591
+
592
+ def innermost_dir():
593
+ for f in reversed(stack):
594
+ if isinstance(f, _Dir):
595
+ return f
596
+ return None
597
+
598
+ def close_loop_without_do(f: _Dir):
599
+ stack.pop()
600
+ tree.assoc[f.index].end_line = tree.assoc[f.index].line
601
+
602
+ def describe(f):
603
+ if isinstance(f, _Dir):
604
+ a = tree.assoc[f.index]
605
+ return (f"!${model} {tree.directives[f.index].name} "
606
+ f"(line {a.line})")
607
+ return f"{'DO' if isinstance(f, _Do) else 'BLOCK'} (line {f.line})"
608
+
609
+ def end_follows(k: int, name: str) -> bool:
610
+ """Whether the next directive after item k is `end <name>`."""
611
+ for _, kind, text in items[k + 1:]:
612
+ if kind == "omp":
613
+ d = parse_directive(text, "fortran", version)
614
+ return d.end and d.name == name
615
+ return False
616
+
617
+ unit, depth = 0, 0 # program units started; header nesting depth
618
+ scopes: list[int] = [0] # scoping units; 0 is the file outside units
619
+ nscopes = 0
620
+ in_type = None # name of the derived type being defined
621
+ items = list(_logical_lines(source, model))
622
+ for k, (line, kind, text) in enumerate(items):
623
+ top = stack[-1] if stack else None
624
+ if isinstance(top, _Dir) and top.pending:
625
+ if top.kind == "loop":
626
+ if kind == "stmt" and _is_do(_LABEL.sub("", text)):
627
+ pass # the DO is handled below
628
+ elif kind == "omp":
629
+ d = parse_directive(text, "fortran", version)
630
+ if not d.end and association(d.name) == "loop":
631
+ raise ProgramError(
632
+ f"line {line}: loop-transforming or loop "
633
+ f"directive {d.name!r} between a loop directive "
634
+ "and its DO loop is not supported")
635
+ close_loop_without_do(top)
636
+ else:
637
+ close_loop_without_do(top)
638
+ else: # block directive: a BLOCK construct is handled below
639
+ top.pending = kind == "stmt" and bool(_BLOCK.match(text))
640
+ if kind == "omp":
641
+ d = parse_directive(text, "fortran", version)
642
+ if d.end:
643
+ if just_closed is not None \
644
+ and tree.directives[just_closed].name == d.name:
645
+ tree.ends[just_closed] = d
646
+ tree.assoc[just_closed].end_line = line
647
+ just_closed = None
648
+ continue
649
+ top = stack[-1] if stack else None
650
+ if not (isinstance(top, _Dir) and top.kind == "block"
651
+ and tree.directives[top.index].name == d.name):
652
+ opened = describe(top) if top else "no open construct"
653
+ raise ProgramError(f"line {line}: !${model} end "
654
+ f"{d.name} does not match {opened}")
655
+ stack.pop()
656
+ tree.ends[top.index] = d
657
+ tree.assoc[top.index].end_line = line
658
+ just_closed = None
659
+ continue
660
+ just_closed = None
661
+ idx = len(tree.directives)
662
+ parent = innermost_dir()
663
+ tree.directives.append(d)
664
+ if parent is not None:
665
+ tree.nesting.append((parent.index, idx))
666
+ how = association(d.name)
667
+ if stack and isinstance(stack[-1], (_Do, _Blk)):
668
+ stack[-1].directive = True
669
+ stack[-1].transform |= how == "loop"
670
+ if how == "loopseq":
671
+ raise ProgramError(f"line {line}: loop-sequence directive "
672
+ f"{d.name!r} is not supported")
673
+ tree.assoc.append(Assoc(kind=how, line=line, unit=unit,
674
+ scope=tuple(scopes)))
675
+ if how == "statement" and end_follows(k, d.name):
676
+ stack.append(_Dir(idx, "block")) # several statements
677
+ elif how in ("block", "loop", "statement"):
678
+ stack.append(_Dir(idx, how, pending=how != "statement"))
679
+ continue
680
+ # a base-language statement
681
+ stmt = _LABEL.sub("", text)
682
+ tree.statements.append((line, stmt, tuple(scopes)))
683
+ vtype, arrays, saved, save_all = _decl_facts(stmt)
684
+ if not in_type:
685
+ if vtype:
686
+ for name in (_declaration(stmt) or (None, []))[1]:
687
+ tree.vtypes[(scopes[-1], name)] = vtype
688
+ tree.arrays |= {(scopes[-1], x) for x in arrays}
689
+ tree.saved |= {(scopes[-1], x) for x in saved}
690
+ if save_all:
691
+ tree.saved.add((scopes[-1], "*"))
692
+ if just_closed is not None:
693
+ just_closed = None
694
+ owner = innermost_dir()
695
+ if owner is not None:
696
+ tree.assoc[owner.index].stmts += 1
697
+ if _UNIT_HEAD.match(stmt) and not stmt.startswith("end"):
698
+ depth += 1
699
+ if depth == 1:
700
+ unit += 1
701
+ nscopes += 1
702
+ scopes.append(nscopes)
703
+ km = _UNIT_KIND.match(stmt)
704
+ kind = next((g for g in km.groups()[:5] if g), "") if km else ""
705
+ tree.units[nscopes] = (re.sub(r"\s+", "", kind),
706
+ km.group(6) if km else None)
707
+ elif _UNIT_TAIL.match(stmt) and depth:
708
+ depth -= 1
709
+ scopes.pop()
710
+ elif _TYPE_DEF.match(stmt):
711
+ in_type = re.match(r".*?([a-z_]\w*)(?:\s*\(.*\))?$",
712
+ stmt).group(1)
713
+ tree.types.setdefault((scopes[-1], in_type), {})
714
+ elif _END_TYPE.match(stmt):
715
+ in_type = None
716
+ elif _use_names(stmt) is not False:
717
+ tree.uses.setdefault(scopes[-1], []).append(_use_names(stmt))
718
+ tree.use_modules.setdefault(scopes[-1], []).append(
719
+ _USE_MOD.match(stmt).group(1))
720
+ else:
721
+ decl = _declaration(stmt)
722
+ if decl and _TYPE_SPEC.match(stmt) and "::" in stmt and re.search(
723
+ r",\s*parameter\b", stmt.split("::", 1)[0]):
724
+ tree.params |= {(scopes[-1], x) for x in decl[1]}
725
+ if decl:
726
+ attr, names = decl
727
+ m = _DECL_TYPE.match(stmt)
728
+ tname = m.group(1) if m else None
729
+ for name in names:
730
+ if in_type:
731
+ tree.types[(scopes[-1], in_type)][name] = (attr,
732
+ tname)
733
+ continue
734
+ key = (scopes[-1], name)
735
+ old = tree.decls.get(key)
736
+ if old is None or _RANK[attr] > _RANK[old]:
737
+ tree.decls[key] = attr
738
+ if tname:
739
+ tree.vartypes[key] = tname
740
+ if _UNIT_END.match(stmt):
741
+ if stack:
742
+ raise ProgramError(f"line {line}: {stmt!r} while "
743
+ f"{describe(stack[-1])} is still open")
744
+ continue
745
+ if _BLOCK.match(stmt):
746
+ blk = _Blk(line)
747
+ top = stack[-1] if stack else None
748
+ if isinstance(top, (_Do, _Blk)):
749
+ top.loops.append(blk)
750
+ elif isinstance(top, _Dir) and top.kind == "block" and top.pending:
751
+ top.pending = False
752
+ blk.owner = top.index
753
+ tree.assoc[top.index].stmts -= 1 # the block itself
754
+ stack.append(blk)
755
+ continue
756
+ if _END_BLOCK.match(stmt):
757
+ top = stack[-1] if stack else None
758
+ if not isinstance(top, _Blk):
759
+ raise ProgramError(f"line {line}: END BLOCK does not match "
760
+ f"{describe(top) if top else 'any BLOCK'}")
761
+ stack.pop()
762
+ if top.owner is not None:
763
+ stack.pop() # the block directive whose block this is
764
+ tree.assoc[top.owner].stmts -= 1
765
+ tree.assoc[top.owner].end_line = line
766
+ just_closed = top.owner
767
+ continue
768
+ if _is_do(stmt):
769
+ rest = _DO.match(stmt).group(1)
770
+ do = _Do(_do_var(rest, line), line, bounds=_do_bounds(rest))
771
+ top = stack[-1] if stack else None
772
+ if isinstance(top, (_Do, _Blk)):
773
+ top.loops.append(do)
774
+ elif isinstance(top, _Dir) and top.kind == "loop" and top.pending:
775
+ top.pending = False
776
+ do.owner = top.index
777
+ stack.append(do)
778
+ continue
779
+ if _END_DO.match(stmt):
780
+ top = stack[-1] if stack else None
781
+ if not isinstance(top, _Do):
782
+ raise ProgramError(f"line {line}: END DO does not match "
783
+ f"{describe(top) if top else 'any DO'}")
784
+ stack.pop()
785
+ if top.owner is not None:
786
+ stack.pop() # the loop directive owning this DO
787
+ dos, known = _chain(top)
788
+ vars_ = tuple(d.var for d in dos)
789
+ a = tree.assoc[top.owner]
790
+ a.loop_depth, a.loop_vars = len(vars_), vars_
791
+ a.depth_known = known
792
+ a.loop_bounds = tuple(d.bounds for d in dos)
793
+ a.intervening = _intervening(dos)
794
+ a.end_line = line
795
+ just_closed = top.owner
796
+ continue
797
+ top = stack[-1] if stack else None
798
+ if isinstance(top, (_Do, _Blk)) and not _declaration(stmt) \
799
+ and _use_names(stmt) is False \
800
+ and not re.match(r"^implicit\b", stmt):
801
+ top.stmts += 1 # executable statement in a DO or BLOCK body
802
+ if isinstance(top, _Dir) and top.kind == "statement":
803
+ stack.pop()
804
+ tree.assoc[top.index].end_line = line
805
+ just_closed = top.index
806
+ if stack:
807
+ raise ProgramError(f"{describe(stack[-1])} is never closed")
808
+ return tree
809
+
810
+
811
+ # --- rules -------------------------------------------------------------------
812
+
813
+ class RuleError(ValueError):
814
+ """A malformed rules file."""
815
+
816
+
817
+ @dataclass(frozen=True)
818
+ class Rule:
819
+ id: str # OMP<ver>-<construct-or-topic>-<kind>-<nnn>
820
+ text: str # the spec sentence, verbatim
821
+ citation: str # "OpenMP <ver> §<sec>, p.<n>"
822
+ check: str # static | runtime
823
+ status: str # provisional | reviewed
824
+ predicate: str | None
825
+ version: int = 0
826
+ also: tuple = ()
827
+ why: str = ""
828
+ example: str = ""
829
+ notes: str = ""
830
+ lang: str = "both" # "both", "f90" or "c"
831
+
832
+ def applies(self, lang: str) -> bool:
833
+ """Whether the rule holds for a program in `lang`."""
834
+ return self.lang in ("both", lang)
835
+
836
+
837
+ PREDICATES: dict[str, Callable] = {}
838
+
839
+
840
+ def predicate(fn):
841
+ """Register a rule predicate under its function name (a name may be
842
+ registered once: the OpenMP and OpenACC predicates share the
843
+ registry)."""
844
+ if fn.__name__ in PREDICATES and PREDICATES[fn.__name__] is not fn:
845
+ raise RuleError(f"predicate {fn.__name__!r} registered twice")
846
+ PREDICATES[fn.__name__] = fn
847
+ return fn
848
+
849
+
850
+ _ID = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*-[a-z]+-\d{3}$")
851
+ _CITATION = re.compile(r"^OpenMP (\d)\.(\d) §[\d.]+, p\.\d+$")
852
+ _CITATIONS = {"omp": _CITATION,
853
+ "acc": re.compile(r"^OpenACC (\d)\.(\d) §[\d.]+, p\.\d+$")}
854
+ RULE_PREFIX = {"omp": "OMP", "acc": "ACC"}
855
+
856
+
857
+ def rules_from_list(entries: list, model: str = "omp") -> list[Rule]:
858
+ """Expand rules.yaml entries into one Rule per version (see module doc;
859
+ OpenACC rules: ``ACC<ver>-<stem>``)."""
860
+ _CITATION = _CITATIONS[model]
861
+ out, seen = [], set()
862
+ for e in entries:
863
+ stem = e.get("id", "")
864
+ if not _ID.match(stem):
865
+ raise RuleError(f"bad rule id {stem!r}")
866
+ check, pred = e.get("check"), e.get("predicate")
867
+ if check not in ("static", "runtime"):
868
+ raise RuleError(f"{stem}: check must be static or runtime")
869
+ if e.get("status") not in ("provisional", "reviewed"):
870
+ raise RuleError(f"{stem}: status must be provisional or reviewed")
871
+ if check == "static" and pred not in PREDICATES:
872
+ raise RuleError(f"{stem}: static rule needs a registered "
873
+ f"predicate, got {pred!r}")
874
+ if check == "runtime" and (pred is not None or not e.get("why")):
875
+ raise RuleError(f"{stem}: a runtime rule has predicate: null "
876
+ "and says why in `why`")
877
+ spec = e.get("spec") or {}
878
+ if not spec:
879
+ raise RuleError(f"{stem}: no spec entry")
880
+ lang = e.get("lang", "both")
881
+ if lang not in ("both", "f90", "c"):
882
+ raise RuleError(f"{stem}: lang must be both, f90 or c")
883
+ for ver, s in spec.items():
884
+ rid = f"{RULE_PREFIX[model]}{ver}-{stem}"
885
+ if model_of(int(ver)) != model:
886
+ raise RuleError(f"{rid}: version {ver} is not {model}")
887
+ m = _CITATION.match(s.get("citation", ""))
888
+ if not m or int(m.group(1) + m.group(2)) != ver:
889
+ raise RuleError(f"{rid}: bad citation {s.get('citation')!r}")
890
+ if rid in seen:
891
+ raise RuleError(f"duplicate rule id {rid}")
892
+ seen.add(rid)
893
+ out.append(Rule(rid, " ".join(s["text"].split()), s["citation"],
894
+ check, e["status"], pred, int(ver),
895
+ tuple(s.get("also", ())), e.get("why", ""),
896
+ e.get("example", ""), e.get("notes", ""), lang))
897
+ return out
898
+
899
+
900
+ def load_rules(path: Path = RULES_PATH, model: str = "omp") -> list[Rule]:
901
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
902
+ return rules_from_list(data["rules"], model)
903
+
904
+
905
+ @lru_cache(maxsize=1)
906
+ def default_rules() -> tuple:
907
+ """The OpenMP rules (catalog/omp/rules.yaml)."""
908
+ return tuple(load_rules())
909
+
910
+
911
+ @lru_cache(maxsize=1)
912
+ def acc_rules() -> tuple:
913
+ """The OpenACC rules (catalog/acc/rules.yaml); their predicates are
914
+ registered by offload.engine.acc_rules."""
915
+ import offload.engine.acc_rules # noqa: F401 - registers the predicates
916
+ return tuple(load_rules(catalog_dir("acc") / "rules.yaml", "acc"))
917
+
918
+
919
+ def rules_for(version: int) -> tuple:
920
+ """The rules of the model of `version`."""
921
+ return acc_rules() if model_of(version) == "acc" else default_rules()
922
+
923
+
924
+ def findings(tree: ProgramTree, version: int,
925
+ rules=None) -> list[tuple[str, int]]:
926
+ """(rule id, directive index) for every violation of a static rule
927
+ that holds in the tree's language (Rule.lang)."""
928
+ out = set()
929
+ lang = getattr(tree, "lang", "f90")
930
+ for r in rules if rules is not None else rules_for(version):
931
+ if r.version == version and r.check == "static" and r.applies(lang):
932
+ out.update((r.id, i) for i in PREDICATES[r.predicate](tree,
933
+ version))
934
+ return sorted(out)
935
+
936
+
937
+ def violations(tree: ProgramTree, version: int, rules=None) -> list[str]:
938
+ """Sorted ids of the static rules the program violates, each once."""
939
+ return sorted({rid for rid, _ in findings(tree, version, rules)})
940
+
941
+
942
+ # --- helpers for predicates --------------------------------------------------
943
+
944
+ def _norm(text: str) -> str:
945
+ return re.sub(r"\s+", "", text.lower())
946
+
947
+
948
+ def _compact(name: str) -> str:
949
+ return name.replace(" ", "").replace("_", "")
950
+
951
+
952
+ @lru_cache(maxsize=1)
953
+ def _names() -> dict:
954
+ """Directive spelling without blanks or underscores -> skeleton key."""
955
+ out = {}
956
+ for key, spec in default_skeleton().directives.items():
957
+ for s in spec.spellings:
958
+ out.setdefault(_compact(s.text), key)
959
+ for e in default_grammar().spellings:
960
+ out.setdefault(_compact(e.spelling), e.directive)
961
+ return out
962
+
963
+
964
+ def _clauses(tree: ProgramTree, i: int, *names: str, end: bool = False):
965
+ """Clauses of directive i (and of its end directive if `end`)."""
966
+ ds = [tree.directives[i]]
967
+ if end and i in tree.ends:
968
+ ds.append(tree.ends[i])
969
+ return [c for d in ds for c in d.clauses if not names or c.name in names]
970
+
971
+
972
+ def _items(clauses) -> list[str]:
973
+ return [_norm(a) for c in clauses for a in c.args]
974
+
975
+
976
+ def _base(item: str) -> str:
977
+ m = re.match(r"(/\w+/|[a-z_]\w*)", item)
978
+ return m.group(1) if m else item
979
+
980
+
981
+ # List items in C: a[...] for an element or section, s.c / p->c
982
+ # for a structure element
983
+ _C_PART = re.compile(r"\[|->|\.(?=[a-z_])")
984
+
985
+
986
+ def _is_part(item: str) -> bool:
987
+ """A list item that is part of another variable: an array element or
988
+ section, or a structure element (Fortran x(...), s%c; C x[...], s.c,
989
+ p->c)."""
990
+ return "(" in item or "%" in item or bool(_C_PART.search(item))
991
+
992
+
993
+ def _is_component(item: str) -> bool:
994
+ """A structure element (Fortran s%c, C s.c or p->c)."""
995
+ return "%" in item or "->" in item or bool(
996
+ re.search(r"\.(?=[a-z_])", item))
997
+
998
+
999
+ def _section_base(item: str) -> str | None:
1000
+ """The base expression text of an array section item (Fortran
1001
+ b(l:u), C b[l:n]), None if the item is not a section."""
1002
+ m = re.match(r"^(.*?)\((?=[^()]*:)", item) or re.match(
1003
+ r"^(.*?)\[(?=[^\[\]]*:)", item)
1004
+ return m.group(1) if m else None
1005
+
1006
+
1007
+ def _int(expr: str) -> int | None:
1008
+ """The value of an integer literal (optionally signed, kind, parens)."""
1009
+ m = re.fullmatch(r"\(*([+-]?\d+)(?:_\w+)?\)*", _norm(expr))
1010
+ return int(m.group(1)) if m else None
1011
+
1012
+
1013
+ def _leaves(tree: ProgramTree, i: int) -> list[str]:
1014
+ return leaves(tree.directives[i].name)
1015
+
1016
+
1017
+ def _ancestors(tree: ProgramTree, i: int) -> list[int]:
1018
+ out = []
1019
+ p = tree.parent(i)
1020
+ while p is not None:
1021
+ out.append(p)
1022
+ p = tree.parent(p)
1023
+ return out
1024
+
1025
+
1026
+ def _enclosing_leaf(tree: ProgramTree, i: int, leaf: str) -> str | None:
1027
+ """The construct that immediately encloses the `leaf` constituent of
1028
+ directive i: the previous leaf of a compound directive, else the
1029
+ innermost leaf of the lexically enclosing construct, else None."""
1030
+ ls = _leaves(tree, i)
1031
+ k = ls.index(leaf)
1032
+ if k:
1033
+ return ls[k - 1]
1034
+ p = tree.parent(i)
1035
+ return None if p is None else _leaves(tree, p)[-1]
1036
+
1037
+
1038
+ def _dnm(clause, key: str, version: int) -> list[str]:
1039
+ """The directive-name modifiers of a clause, as written."""
1040
+ entry = default_grammar().clauses.get(clause.name)
1041
+ form = entry.form_for(key, version) if entry else None
1042
+ if form is None:
1043
+ return []
1044
+
1045
+ def has_dnm(nodes):
1046
+ return any((n.kind == "mods" and DIRECTIVE_NAME_CLASS in n.values)
1047
+ or (n.kind == "opt" and has_dnm(n.body)) for n in nodes)
1048
+
1049
+ if not any(has_dnm(alt) for alt in form.syntax):
1050
+ return []
1051
+ keywords = {k for mc in form.modifiers.values() for k in mc.keywords}
1052
+ return [m for m in clause.modifiers
1053
+ if _compact(m) in _names() and m not in keywords]
1054
+
1055
+
1056
+ # 6.0 target_data is a composite of three constituent directives (6.0 §15.7,
1057
+ # p.458) that the skeleton does not list as leaves.
1058
+ _TARGET_DATA_60 = ["target enter data", "task", "target exit data"]
1059
+
1060
+
1061
+ def _rule_leaves(key: str, version: int) -> list[str]:
1062
+ """Leaves of a directive for the rules (6.0 target data: its three
1063
+ constituent directives)."""
1064
+ if key == "target data" and version >= 60:
1065
+ return list(_TARGET_DATA_60)
1066
+ return leaves(key)
1067
+
1068
+
1069
+ def _constituents(key: str, version: int) -> set[str]:
1070
+ """The directive and its constituent directives: each leaf and each
1071
+ directive named by consecutive leaves (6.0 glossary, p.35)."""
1072
+ ls = _rule_leaves(key, version)
1073
+ out = {key}
1074
+ for a in range(len(ls)):
1075
+ for b in range(a + 1, len(ls) + 1):
1076
+ hit = _names().get(_compact(" ".join(ls[a:b])))
1077
+ if hit:
1078
+ out.add(hit)
1079
+ return out
1080
+
1081
+
1082
+ def _has_ancestor_device(tree: ProgramTree, i: int) -> bool:
1083
+ return any("ancestor" in c.modifiers for c in _clauses(tree, i, "device"))
1084
+
1085
+
1086
+ _DEVICE_CONSTRUCTS = {"target", "target data", "target enter data",
1087
+ "target exit data", "target update"}
1088
+ _LOOP_TRANSFORMS = {"fuse", "interchange", "reverse", "split", "stripe",
1089
+ "tile", "unroll"}
1090
+ _WORKSHARING = {"do", "for", "sections", "single", "workshare", "scope"}
1091
+ _DSA = ("private", "firstprivate", "lastprivate", "shared", "linear",
1092
+ "reduction", "in_reduction", "is_device_ptr", "use_device_ptr",
1093
+ "has_device_addr", "use_device_addr")
1094
+
1095
+
1096
+ def _each(tree: ProgramTree, test) -> list[int]:
1097
+ return [i for i in range(len(tree.directives)) if test(i)]
1098
+
1099
+
1100
+ def _map_construct(tree: ProgramTree, i: int) -> bool:
1101
+ """Map clauses "on a construct": not those of declare mapper."""
1102
+ return tree.directives[i].name != "declare mapper"
1103
+
1104
+
1105
+ # --- Helpers -----------------------------------------------------------
1106
+
1107
+ # Directives that are not constructs (declarative, informational/utility,
1108
+ # subsidiary and separating directives): the nesting rules about
1109
+ # constructs and regions do not count them (assume is an informational
1110
+ # directive: lenient, it is outside the slice).
1111
+ _NOT_CONSTRUCT = {
1112
+ "declare target", "declare simd", "declare variant", "declare mapper",
1113
+ "declare reduction", "declare_induction", "requires", "threadprivate",
1114
+ "groupprivate", "allocate", "assumes", "begin assumes",
1115
+ "begin declare target", "begin declare variant", "end assumes",
1116
+ "end declare target", "end declare variant", "error", "nothing",
1117
+ "metadirective", "unknown", "scan", "section", "assume"}
1118
+
1119
+
1120
+ def _is_construct(tree: ProgramTree, i: int) -> bool:
1121
+ return tree.directives[i].name not in _NOT_CONSTRUCT
1122
+
1123
+
1124
+ def _extent(tree: ProgramTree, i: int) -> tuple:
1125
+ a = tree.assoc[i]
1126
+ return a.line, (a.end_line if a.end_line is not None else a.line)
1127
+
1128
+
1129
+ def _body(tree: ProgramTree, i: int, strict: bool = False) -> list:
1130
+ """(line, statement) of the base-language statements in the lexical
1131
+ extent of directive i (a loop construct: its DO nest; strict: not
1132
+ inside a nested construct's extent)."""
1133
+ a = tree.assoc[i]
1134
+ if a.kind == "standalone":
1135
+ return []
1136
+ lo, hi = _extent(tree, i)
1137
+ nested = [_extent(tree, c) for c in tree.descendants(i)
1138
+ if tree.assoc[c].kind != "standalone"] if strict else []
1139
+ return [(ln, st) for ln, st, _ in tree.statements
1140
+ if lo < ln <= hi and not any(a_ <= ln <= b_ for a_, b_ in nested)]
1141
+
1142
+
1143
+ _STRING = re.compile(r"'[^']*'|\"[^\"]*\"")
1144
+ _API_CALL = re.compile(r"\b(omp_\w+)\s*\(|\bcall\s+(omp_\w+)")
1145
+
1146
+
1147
+ def _api_calls(stmt: str) -> list[str]:
1148
+ """Names of OpenMP runtime routines referenced (called) in a
1149
+ statement: omp_name(or CALL omp_name."""
1150
+ return [a or b for a, b in _API_CALL.findall(_STRING.sub("", stmt))]
1151
+
1152
+
1153
+ def _outer_leaves(tree: ProgramTree, i: int):
1154
+ """(directive index, leaf) of the constructs enclosing directive i,
1155
+ innermost first (the leaves of each enclosing compound from its
1156
+ innermost leaf outward)."""
1157
+ for p in _ancestors(tree, i):
1158
+ for leaf in reversed(_leaves(tree, p)):
1159
+ yield p, leaf
1160
+
1161
+
1162
+ def _assoc_count(tree: ProgramTree, i: int) -> int | None:
1163
+ """Number of loops associated with loop directive i: collapse(n) (1
1164
+ without), or ordered(n) when larger (5.2 §4.4.4: the loops associated
1165
+ with the construct); None when collapse is not an integer literal."""
1166
+ n = _collapse(tree, i)
1167
+ if n is None:
1168
+ return None
1169
+ o = _items(_clauses(tree, i, "ordered"))
1170
+ if o and _int(o[0]) is not None:
1171
+ n = max(n, _int(o[0]))
1172
+ return n
1173
+
1174
+
1175
+ def _order_concurrent(tree: ProgramTree, i: int) -> bool:
1176
+ """A construct on which order(concurrent) is specified, or with a loop
1177
+ leaf and no order clause (5.2 §11.7, 6.0 §13.8: as if order(concurrent)
1178
+ is present)."""
1179
+ orders = _clauses(tree, i, "order")
1180
+ if orders:
1181
+ return "concurrent" in _items(orders)
1182
+ return "loop" in _leaves(tree, i)
1183
+
1184
+
1185
+ # --- predicates: nesting and placement ---------------------------------------
1186
+
1187
+ @predicate
1188
+ def teams_strictly_nested_in_target(tree, version):
1189
+ """A teams constituent whose immediately enclosing construct (in this
1190
+ source) is not a target construct."""
1191
+ return _each(tree, lambda i: "teams" in _leaves(tree, i) and
1192
+ _enclosing_leaf(tree, i, "teams") not in (None, "target"))
1193
+
1194
+
1195
+ @predicate
1196
+ def target_contains_only_teams(tree, version):
1197
+ """A target construct with a nested teams construct that also contains a
1198
+ statement or another directive outside the teams construct."""
1199
+ def bad(i):
1200
+ if tree.directives[i].name != "target":
1201
+ return False
1202
+ kids = tree.children(i)
1203
+ return any(_leaves(tree, k)[0] == "teams" for k in kids) and (
1204
+ tree.assoc[i].stmts > 0 or len(kids) > 1)
1205
+ return _each(tree, bad)
1206
+
1207
+
1208
+ @predicate
1209
+ def teams_nested_regions(tree, version):
1210
+ """A directive nested directly in a teams construct (or a compound
1211
+ construct ending in teams) whose outermost leaf is not distribute,
1212
+ parallel or loop (6.0: or a loop-transforming construct, or
1213
+ workdistribute, which 6.0 §13.5 requires to be closely nested in teams
1214
+ although its properties omit teams-nestable)."""
1215
+ ok = {"distribute", "parallel", "loop"}
1216
+ if version >= 60:
1217
+ ok |= _LOOP_TRANSFORMS | {"workdistribute"}
1218
+ teams = [i for i in range(len(tree.directives))
1219
+ if _leaves(tree, i)[-1] == "teams"]
1220
+ out = {c for i in teams for c in tree.children(i)
1221
+ if _leaves(tree, c)[0] not in ok}
1222
+ # Routine regions strictly nested in teams: only
1223
+ # omp_get_num_teams and omp_get_team_num (5.2 §10.2; 6.0: the
1224
+ # teams-nestable routines, §22.1, §22.3) may be called in the teams
1225
+ # body outside nested constructs
1226
+ routines = {"omp_get_num_teams", "omp_get_team_num"}
1227
+ out |= {i for i in teams for _, st in _body(tree, i, strict=True)
1228
+ if set(_api_calls(st)) - routines}
1229
+ return sorted(out)
1230
+
1231
+
1232
+ def _in_main_program(tree: ProgramTree, i: int) -> bool:
1233
+ """Directive i is in a main program's scoping unit (no caller can put
1234
+ it into another region)."""
1235
+ return tree.units.get(tree.assoc[i].scope[-1], ("", None))[0] \
1236
+ == "program"
1237
+
1238
+
1239
+ def _outside(tree, i, leaf, want) -> bool:
1240
+ """The constituent `leaf` of directive i is not immediately enclosed by
1241
+ `want`: another enclosing construct, or none in a main program (Task
1242
+ 16: an orphaned construct of a main program is in the implicit parallel
1243
+ region)."""
1244
+ enc = _enclosing_leaf(tree, i, leaf)
1245
+ return enc != want and (enc is not None or _in_main_program(tree, i))
1246
+
1247
+
1248
+ @predicate
1249
+ def distribute_strictly_nested_in_teams(tree, version):
1250
+ """A distribute constituent whose immediately enclosing construct (in
1251
+ this source) is not a teams construct, or that has none in a main
1252
+ program."""
1253
+ return _each(tree, lambda i: "distribute" in _leaves(tree, i) and
1254
+ _outside(tree, i, "distribute", "teams"))
1255
+
1256
+
1257
+ @predicate
1258
+ def loop_orphan_has_bind(tree, version):
1259
+ """A loop directive not nested in any construct, without a bind
1260
+ clause (5.0: only when it appears in a procedure, 5.0 §2.9.5 p.131)."""
1261
+ def in_procedure(i):
1262
+ return any(tree.units.get(s, ("", None))[0] in ("subroutine",
1263
+ "function")
1264
+ for s in tree.assoc[i].scope)
1265
+ return _each(tree, lambda i: tree.directives[i].name == "loop"
1266
+ and tree.parent(i) is None
1267
+ and not _clauses(tree, i, "bind")
1268
+ and (version > 50 or in_procedure(i)))
1269
+
1270
+
1271
+ @predicate
1272
+ def loop_bind_teams_in_teams(tree, version):
1273
+ """A loop constituent with bind(teams) whose immediately enclosing
1274
+ construct (in this source) is not a teams construct, or that has none
1275
+ in a main program."""
1276
+ return _each(tree, lambda i: "loop" in _leaves(tree, i)
1277
+ and "teams" in _items(_clauses(tree, i, "bind"))
1278
+ and _outside(tree, i, "loop", "teams"))
1279
+
1280
+
1281
+ @predicate
1282
+ def device_construct_in_target(tree, version):
1283
+ """A device-affecting construct (target, target data, target enter/exit
1284
+ data, target update) lexically inside a construct with a target leaf,
1285
+ unless it is a target construct with device(ancestor: ...)."""
1286
+ def bad(i):
1287
+ if _leaves(tree, i)[0] not in _DEVICE_CONSTRUCTS:
1288
+ return False
1289
+ if _leaves(tree, i)[0] == "target" and _has_ancestor_device(tree, i):
1290
+ return False
1291
+ return any("target" in _leaves(tree, a) for a in _ancestors(tree, i))
1292
+ return _each(tree, bad)
1293
+
1294
+
1295
+ @predicate
1296
+ def order_concurrent_nesting(tree, version):
1297
+ """A directive nested in a construct with order(concurrent) whose
1298
+ outermost leaf is not loop, parallel or simd (5.2: any nested directive;
1299
+ 6.0: directly nested ones, and also atomic and loop-transforming
1300
+ constructs)."""
1301
+ if version >= 60:
1302
+ ok, nested = ({"loop", "parallel", "simd", "atomic"}
1303
+ | _LOOP_TRANSFORMS), tree.children
1304
+ else:
1305
+ ok, nested = {"loop", "parallel", "simd"}, tree.descendants
1306
+ # A loop construct without an order clause behaves as if
1307
+ # order(concurrent) is present (5.2 §11.7, p.257; 6.0 §13.8, p.423)
1308
+ return sorted({c for i in range(len(tree.directives))
1309
+ if _order_concurrent(tree, i)
1310
+ for c in nested(i) if _is_construct(tree, c)
1311
+ and _leaves(tree, c)[0] not in ok})
1312
+
1313
+
1314
+ @predicate
1315
+ def declare_target_in_specification_part(tree, version):
1316
+ """A declare target directive lexically inside an OpenMP construct (so
1317
+ in an execution part, not a specification part)."""
1318
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
1319
+ and tree.parent(i) is not None)
1320
+
1321
+
1322
+ @predicate
1323
+ def requires_in_specification_part(tree, version):
1324
+ """A requires directive lexically inside an OpenMP construct."""
1325
+ return _each(tree, lambda i: tree.directives[i].name == "requires"
1326
+ and tree.parent(i) is not None)
1327
+
1328
+
1329
+ @predicate
1330
+ def requires_before_device_constructs(tree, version):
1331
+ """A requires directive with reverse_offload, unified_address or
1332
+ unified_shared_memory after a device construct of the same program unit
1333
+ (the Fortran compilation unit)."""
1334
+ req = {"reverse_offload", "unified_address", "unified_shared_memory"}
1335
+
1336
+ def bad(i):
1337
+ return tree.directives[i].name == "requires" \
1338
+ and bool(_clauses(tree, i, *req)) and any(
1339
+ _leaves(tree, j)[0] in _DEVICE_CONSTRUCTS
1340
+ and tree.assoc[j].unit == tree.assoc[i].unit
1341
+ for j in range(i))
1342
+ return _each(tree, bad)
1343
+
1344
+
1345
+ @predicate
1346
+ def requires_same_memory_order(tree, version):
1347
+ """A requires directive whose atomic_default_mem_order argument differs
1348
+ from that of an earlier requires directive of the same program unit
1349
+ (5.0: any second one, 5.0 §2.4 p.63)."""
1350
+ seen, out = {}, []
1351
+ for i in range(len(tree.directives)):
1352
+ unit = tree.assoc[i].unit
1353
+ for o in _items(_clauses(tree, i, "atomic_default_mem_order")):
1354
+ if unit in seen and (version == 50 or seen[unit] != o):
1355
+ out.append(i)
1356
+ seen.setdefault(unit, o)
1357
+ return out
1358
+
1359
+
1360
+ # --- predicates: loop association --------------------------------------------
1361
+
1362
+ def _collapse(tree, i) -> int | None:
1363
+ args = _items(_clauses(tree, i, "collapse"))
1364
+ return _int(args[0]) if args else 1
1365
+
1366
+
1367
+ @predicate
1368
+ def loop_nest_follows(tree, version):
1369
+ """A loop-nest-associated directive not followed by a DO loop of the
1370
+ form DO var = ... (or DO CONCURRENT)."""
1371
+ return _each(tree, lambda i: tree.assoc[i].kind == "loop"
1372
+ and tree.assoc[i].loop_depth == 0)
1373
+
1374
+
1375
+ @predicate
1376
+ def collapse_within_depth(tree, version):
1377
+ """collapse(n) with an integer literal n greater than the depth of the
1378
+ DO nest that follows."""
1379
+ def bad(i):
1380
+ a = tree.assoc[i]
1381
+ n = _collapse(tree, i) if _clauses(tree, i, "collapse") else None
1382
+ return a.kind == "loop" and a.loop_depth > 0 and a.depth_known \
1383
+ and n is not None and n > a.loop_depth
1384
+ return _each(tree, bad)
1385
+
1386
+
1387
+ @predicate
1388
+ def loop_lastprivate_iteration_variable(tree, version):
1389
+ """A lastprivate list item on a directive with a loop leaf that is not
1390
+ the iteration variable of one of the collapsed loops."""
1391
+ def bad(i):
1392
+ a = tree.assoc[i]
1393
+ if "loop" not in _leaves(tree, i) or a.loop_depth == 0 \
1394
+ or not a.depth_known:
1395
+ return False
1396
+ n = _collapse(tree, i)
1397
+ loop_vars = a.loop_vars[:n] if n is not None else a.loop_vars
1398
+ return any(x not in loop_vars
1399
+ for x in _items(_clauses(tree, i, "lastprivate")))
1400
+ return _each(tree, bad)
1401
+
1402
+
1403
+ # --- predicates: data mapping ------------------------------------------------
1404
+
1405
+ _MAP_TYPES = {52: {"alloc", "delete", "from", "release", "to", "tofrom"},
1406
+ 60: {"alloc", "from", "release", "storage", "to", "tofrom"}}
1407
+
1408
+
1409
+ def _map_types(tree, i, version) -> list[str | None]:
1410
+ types = _MAP_TYPES[52 if version < 60 else 60]
1411
+ out = []
1412
+ for c in _clauses(tree, i, "map"):
1413
+ t = [m for m in c.modifiers if m in types]
1414
+ out.append(t[0] if t else None)
1415
+ return out
1416
+
1417
+
1418
+ def _map_type_not_in(allowed):
1419
+ def check(tree, i, version):
1420
+ return any(t not in allowed for t in _map_types(tree, i, version))
1421
+ return check
1422
+
1423
+
1424
+ @predicate
1425
+ def target_map_types(tree, version):
1426
+ """A map clause on a directive with a target leaf whose map-type is not
1427
+ to, from, tofrom or alloc (5.2)."""
1428
+ bad = _map_type_not_in({None, "to", "from", "tofrom", "alloc"})
1429
+ return _each(tree, lambda i: "target" in _leaves(tree, i)
1430
+ and bad(tree, i, version))
1431
+
1432
+
1433
+ @predicate
1434
+ def target_data_map_types(tree, version):
1435
+ """A map clause on target data whose map-type is not to, from, tofrom
1436
+ or alloc (5.2)."""
1437
+ bad = _map_type_not_in({None, "to", "from", "tofrom", "alloc"})
1438
+ return _each(tree, lambda i: tree.directives[i].name == "target data"
1439
+ and bad(tree, i, version))
1440
+
1441
+
1442
+ @predicate
1443
+ def enter_data_map_entering(tree, version):
1444
+ """A map clause on target enter data that is not map-entering: 5.2
1445
+ map-type other than to, tofrom, alloc; 6.0 other than to, tofrom,
1446
+ storage or alloc (alloc stands for storage on map-entering constructs).
1447
+ A clause without a map-type defaults to tofrom (6.0: storage with the
1448
+ delete modifier), both map-entering."""
1449
+ allowed = {None, "to", "tofrom", "alloc"} | (
1450
+ {"storage"} if version >= 60 else set())
1451
+ if version == 50: # 5.0 §2.12.3 p.166: a map-type, to or alloc
1452
+ allowed = {"to", "alloc"}
1453
+ bad = _map_type_not_in(allowed)
1454
+ return _each(tree, lambda i: tree.directives[i].name == "target enter data"
1455
+ and bad(tree, i, version))
1456
+
1457
+
1458
+ @predicate
1459
+ def exit_data_map_exiting(tree, version):
1460
+ """A map clause on target exit data that is not map-exiting: 5.2
1461
+ map-type other than from, tofrom, release, delete; 6.0 other than from,
1462
+ tofrom, storage or release (release stands for storage on map-exiting
1463
+ constructs; delete is a modifier there)."""
1464
+ allowed = {None, "from", "tofrom", "release"} | (
1465
+ {"storage"} if version >= 60 else {"delete"})
1466
+ if version == 50: # 5.0 §2.12.4 p.169: a map-type, from/release/delete
1467
+ allowed = {"from", "release", "delete"}
1468
+ bad = _map_type_not_in(allowed)
1469
+ return _each(tree, lambda i: tree.directives[i].name == "target exit data"
1470
+ and bad(tree, i, version))
1471
+
1472
+
1473
+ def _mapper(clause) -> str:
1474
+ for m in clause.modifiers:
1475
+ mm = re.fullmatch(r"mapper\s*\((.*)\)", m)
1476
+ if mm:
1477
+ return _norm(mm.group(1))
1478
+ return "default"
1479
+
1480
+
1481
+ @predicate
1482
+ def map_same_item_same_mapper(tree, version):
1483
+ """The same list item (same text) in map clauses of one directive with
1484
+ different mapper modifiers (no modifier means mapper(default))."""
1485
+ def bad(i):
1486
+ mappers = {}
1487
+ for c in _clauses(tree, i, "map"):
1488
+ for x in _items([c]):
1489
+ mappers.setdefault(x, set()).add(_mapper(c))
1490
+ return any(len(v) > 1 for v in mappers.values())
1491
+ return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
1492
+
1493
+
1494
+ @predicate
1495
+ def map_whole_and_part(tree, version):
1496
+ """Map clauses of one directive that name a variable and also an array
1497
+ section or element of it (x and x(...)): different list items that
1498
+ share storage, neither the containing structure of the other. Only when
1499
+ x has a visible non-POINTER declaration in the source
1500
+ (ProgramTree.designator_attribute; for s%c the declared type of s must
1501
+ be defined in the source with c non-POINTER): a pointer x maps the
1502
+ pointer and x(...) its target, and an undeclared or possibly
1503
+ use-associated x (or type) is not checked."""
1504
+ def not_pointer(i, x):
1505
+ return tree.designator_attribute(i, x) in ("allocatable", "plain")
1506
+
1507
+ def bad(i):
1508
+ xs = set(_items(_clauses(tree, i, "map")))
1509
+ return any((y.startswith(x + "(") or y.startswith(x + "["))
1510
+ and not_pointer(i, x) for x in xs for y in xs)
1511
+ return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
1512
+
1513
+
1514
+ @predicate
1515
+ def map_self_consistent(tree, version):
1516
+ """A map list item with the self modifier and another map list item of
1517
+ the same base variable without it, on one directive."""
1518
+ def bad(i):
1519
+ cs = _clauses(tree, i, "map")
1520
+ selfish = {_base(x) for c in cs if "self" in c.modifiers
1521
+ for x in _items([c])}
1522
+ return any(_base(x) in selfish for c in cs
1523
+ if "self" not in c.modifiers for x in _items([c]))
1524
+ return _each(tree, lambda i: _map_construct(tree, i) and bad(i))
1525
+
1526
+
1527
+ @predicate
1528
+ def target_dsa_not_mapped(tree, version):
1529
+ """On a target construct, a list item of private, firstprivate,
1530
+ is_device_ptr or has_device_addr with the same base variable as a map
1531
+ list item."""
1532
+ def bad(i):
1533
+ if tree.directives[i].name != "target":
1534
+ return False
1535
+ dsa = {_base(x) for x in _items(_clauses(
1536
+ tree, i, "private", "firstprivate", "is_device_ptr",
1537
+ "has_device_addr"))}
1538
+ return bool(dsa & {_base(x) for x in _items(_clauses(tree, i,
1539
+ "map"))})
1540
+ return _each(tree, bad)
1541
+
1542
+
1543
+ def _categories(tree, i) -> list[str]:
1544
+ """The variable-category of each defaultmap clause of directive i; a
1545
+ clause without one counts as all ("If no variable-category is specified
1546
+ in the clause then the effect is as if all was specified", 5.2 §5.8.7
1547
+ p.161 l.17-18; in 5.0 such a clause applies to every category)."""
1548
+ return [(_norm(c.modifiers[0]) if c.modifiers else "all")
1549
+ for c in _clauses(tree, i, "defaultmap")]
1550
+
1551
+
1552
+ @predicate
1553
+ def defaultmap_category_once(tree, version):
1554
+ """Two defaultmap clauses on one directive whose variable-categories
1555
+ overlap: the same category twice, or all (a clause without a category)
1556
+ beside any other defaultmap clause."""
1557
+ def bad(i):
1558
+ cats = _categories(tree, i)
1559
+ # from 5.2 defaultmap-map-002 refuses all beside another clause
1560
+ return len(cats) != len(set(cats)) or (
1561
+ version < 52 and "all" in cats and len(cats) > 1)
1562
+ return _each(tree, bad)
1563
+
1564
+
1565
+ @predicate
1566
+ def defaultmap_all_alone(tree, version):
1567
+ """A defaultmap clause with the all variable-category and another
1568
+ defaultmap clause on the same directive."""
1569
+ return _each(tree, lambda i: "all" in _categories(tree, i)
1570
+ and len(_clauses(tree, i, "defaultmap")) > 1)
1571
+
1572
+
1573
+ # --- predicates: clauses -----------------------------------------------------
1574
+
1575
+ @predicate
1576
+ def dnm_names_constituent(tree, version):
1577
+ """A directive-name modifier that names neither the directive nor one
1578
+ of its constituent directives."""
1579
+ def bad(i):
1580
+ key = tree.directives[i].name
1581
+ ok = _constituents(key, version)
1582
+ return any(_names()[_compact(m)] not in ok
1583
+ for c in _clauses(tree, i) for m in _dnm(c, key, version))
1584
+ return _each(tree, bad)
1585
+
1586
+
1587
+ @predicate
1588
+ def if_once_per_constituent(tree, version):
1589
+ """Two if clauses that apply to a common leaf construct: an if clause
1590
+ without a directive-name modifier applies to every leaf, one with a
1591
+ modifier naming a constituent to that constituent's leaves."""
1592
+ def bad(i):
1593
+ key = tree.directives[i].name
1594
+ sets = []
1595
+ for c in _clauses(tree, i, "if"):
1596
+ dnm = _dnm(c, key, version)
1597
+ if not dnm:
1598
+ sets.append(set(_rule_leaves(key, version)))
1599
+ continue
1600
+ named = _names()[_compact(dnm[0])]
1601
+ if named in _constituents(key, version):
1602
+ sets.append(set(_rule_leaves(named, version)))
1603
+ return any(a & b for k, a in enumerate(sets) for b in sets[k + 1:])
1604
+ return _each(tree, bad)
1605
+
1606
+
1607
+ def _not_positive(values) -> bool:
1608
+ return any(v is not None and v <= 0 for v in map(_int, values))
1609
+
1610
+
1611
+ @predicate
1612
+ def thread_limit_positive(tree, version):
1613
+ """thread_limit with an integer literal argument less than one."""
1614
+ return _each(tree, lambda i: _not_positive(
1615
+ [c.args[-1] for c in _clauses(tree, i, "thread_limit")]))
1616
+
1617
+
1618
+ def _num_teams_bounds(tree, i, version):
1619
+ """(lower, upper) texts of each num_teams clause (lower None if absent)."""
1620
+ key = tree.directives[i].name
1621
+ out = []
1622
+ for c in _clauses(tree, i, "num_teams"):
1623
+ dnm = set(_dnm(c, key, version))
1624
+ lower = [m for m in c.modifiers if m not in dnm]
1625
+ out.append((lower[0] if lower else None, c.args[-1]))
1626
+ return out
1627
+
1628
+
1629
+ @predicate
1630
+ def num_teams_positive(tree, version):
1631
+ """num_teams with an integer literal lower-bound or upper-bound less
1632
+ than one."""
1633
+ return _each(tree, lambda i: any(
1634
+ _not_positive([lo, up] if lo is not None else [up])
1635
+ for lo, up in _num_teams_bounds(tree, i, version)))
1636
+
1637
+
1638
+ @predicate
1639
+ def num_teams_bounds_ordered(tree, version):
1640
+ """num_teams with integer literal bounds where lower-bound exceeds
1641
+ upper-bound."""
1642
+ def bad(i):
1643
+ for lo, up in _num_teams_bounds(tree, i, version):
1644
+ lo, up = (_int(lo) if lo is not None else None), _int(up)
1645
+ if lo is not None and up is not None and lo > up:
1646
+ return True
1647
+ return False
1648
+ return _each(tree, bad)
1649
+
1650
+
1651
+ @predicate
1652
+ def dist_schedule_chunk_positive(tree, version):
1653
+ """dist_schedule with an integer literal chunk_size less than one."""
1654
+ return _each(tree, lambda i: _not_positive(
1655
+ [c.args[1] for c in _clauses(tree, i, "dist_schedule")
1656
+ if len(c.args) > 1]))
1657
+
1658
+
1659
+ def _reductions(tree, i, modifier):
1660
+ return [c for c in _clauses(tree, i, "reduction") if modifier in
1661
+ c.modifiers]
1662
+
1663
+
1664
+ @predicate
1665
+ def reduction_task_placement(tree, version):
1666
+ """reduction(task, ...) on a directive without a parallel or
1667
+ worksharing leaf, or with a simd or loop leaf."""
1668
+ def bad(i):
1669
+ ls = set(_leaves(tree, i))
1670
+ return bool(_reductions(tree, i, "task")) and (
1671
+ not ls & ({"parallel"} | _WORKSHARING) or ls & {"simd", "loop"})
1672
+ return _each(tree, bad)
1673
+
1674
+
1675
+ @predicate
1676
+ def reduction_inscan_placement(tree, version):
1677
+ """reduction(inscan, ...) on a directive without a do or simd leaf, or
1678
+ with a distribute leaf (6.0: or taskloop; 5.0: on any directive but do,
1679
+ do simd, simd, parallel do and parallel do simd, 5.0 §2.19.5.4
1680
+ p.302)."""
1681
+ banned = {"distribute"} | ({"taskloop"} if version >= 60 else set())
1682
+ ok50 = {"do", "do simd", "simd", "parallel do", "parallel do simd",
1683
+ "for", "for simd", "parallel for", "parallel for simd"}
1684
+
1685
+ def bad(i):
1686
+ ls = set(_leaves(tree, i))
1687
+ if version == 50:
1688
+ return bool(_reductions(tree, i, "inscan")) and \
1689
+ tree.directives[i].name not in ok50
1690
+ return bool(_reductions(tree, i, "inscan")) and (
1691
+ not ls & {"do", "for", "simd"} or ls & banned)
1692
+ return _each(tree, bad)
1693
+
1694
+
1695
+ @predicate
1696
+ def reduction_inscan_not_mixed(tree, version):
1697
+ """A reduction clause with inscan and one without it on one
1698
+ directive."""
1699
+ return _each(tree, lambda i: bool(_reductions(tree, i, "inscan")) and any(
1700
+ "inscan" not in c.modifiers for c in _clauses(tree, i, "reduction")))
1701
+
1702
+
1703
+ @predicate
1704
+ def reduction_inscan_no_ordered_schedule(tree, version):
1705
+ """reduction(inscan, ...) on a directive with an ordered or schedule
1706
+ clause."""
1707
+ return _each(tree, lambda i: bool(_reductions(tree, i, "inscan"))
1708
+ and bool(_clauses(tree, i, "ordered", "schedule")))
1709
+
1710
+
1711
+ @predicate
1712
+ def reduction_task_no_nowait(tree, version):
1713
+ """reduction(task, ...) on a directive with nowait (on the directive or
1714
+ its end directive)."""
1715
+ return _each(tree, lambda i: bool(_reductions(tree, i, "task"))
1716
+ and bool(_clauses(tree, i, "nowait", end=True)))
1717
+
1718
+
1719
+ @predicate
1720
+ def distribute_firstprivate_lastprivate(tree, version):
1721
+ """A list item in both firstprivate and lastprivate on a directive with
1722
+ a distribute leaf."""
1723
+ return _each(tree, lambda i: "distribute" in _leaves(tree, i) and bool(
1724
+ set(_items(_clauses(tree, i, "firstprivate")))
1725
+ & set(_items(_clauses(tree, i, "lastprivate")))))
1726
+
1727
+
1728
+ @predicate
1729
+ def distribute_no_conditional_lastprivate(tree, version):
1730
+ """lastprivate(conditional: ...) on a directive with a distribute
1731
+ leaf."""
1732
+ return _each(tree, lambda i: "distribute" in _leaves(tree, i) and any(
1733
+ "conditional" in c.modifiers
1734
+ for c in _clauses(tree, i, "lastprivate")))
1735
+
1736
+
1737
+ @lru_cache(maxsize=None)
1738
+ def _allowed(leaf: str) -> dict:
1739
+ return default_grammar().allowed(default_skeleton(), leaf)
1740
+
1741
+
1742
+ def _permits(leaf: str, clause: str, version: int) -> bool:
1743
+ rule = _allowed(leaf).get(clause)
1744
+ return rule is not None and rule_valid(rule, version)
1745
+
1746
+
1747
+ def _dsa_split(tree, i, version) -> list[dict]:
1748
+ """Explicit data-sharing clauses of directive i split onto its leaf
1749
+ constructs per 5.2 §17.2 / 6.0 §19.2: for each leaf, list item -> the
1750
+ clause names applied to it there.
1751
+
1752
+ private and linear: the innermost leaf permitting the clause;
1753
+ firstprivate: the listed leaves (distribute; teams without distribute; a
1754
+ worksharing leaf that accepts it; taskloop; parallel without taskloop or
1755
+ an accepting worksharing leaf; target unless the item is in lastprivate
1756
+ or is the base of a map list item), or the only permitting leaf;
1757
+ reduction: every permitting leaf except parallel combined with
1758
+ sections, do, loop or taskloop and teams combined with loop; every
1759
+ other clause (shared, lastprivate, in_reduction, is_device_ptr, ...):
1760
+ every permitting leaf; a clause no leaf permits but the directive does
1761
+ (6.0 target_data's use_device_*) stays on the directive. Not modeled
1762
+ (so never a violation by
1763
+ themselves): the implicit shared clauses the split adds on parallel and
1764
+ teams, the implicit map clauses on target, and the firstprivate /
1765
+ lastprivate effect of linear on the outer leaves."""
1766
+ key = tree.directives[i].name
1767
+ ls = _rule_leaves(key, version)
1768
+ out = [dict() for _ in ls] + [dict()] # last: the directive itself
1769
+ lastp = set(_items(_clauses(tree, i, "lastprivate")))
1770
+ mapped = {_base(x) for x in _items(_clauses(tree, i, "map"))}
1771
+ for c in _clauses(tree, i, *_DSA):
1772
+ ks = [k for k, leaf in enumerate(ls) if _permits(leaf, c.name,
1773
+ version)]
1774
+ if not ks: # no leaf takes it (6.0 target_data: use_device_*)
1775
+ if _permits(key, c.name, version):
1776
+ for x in set(_items([c])):
1777
+ out[-1].setdefault(x, []).append(c.name)
1778
+ continue
1779
+ for x in set(_items([c])):
1780
+ if c.name in ("private", "linear"):
1781
+ targets = [ks[-1]]
1782
+ elif c.name == "firstprivate" and len(ks) > 1:
1783
+ ws = any(ls[k] in _WORKSHARING for k in ks)
1784
+ targets = [k for k in ks if (
1785
+ ls[k] == "distribute"
1786
+ or (ls[k] == "teams" and "distribute" not in ls)
1787
+ or ls[k] in _WORKSHARING or ls[k] == "taskloop"
1788
+ or (ls[k] == "parallel" and not ws
1789
+ and "taskloop" not in ls)
1790
+ or (ls[k] == "target" and x not in lastp
1791
+ and _base(x) not in mapped))]
1792
+ elif c.name == "reduction":
1793
+ targets = [k for k in ks if not (
1794
+ (ls[k] == "parallel" and set(ls) & {
1795
+ "sections", "do", "for", "loop", "taskloop"})
1796
+ or (ls[k] == "teams" and "loop" in ls))]
1797
+ else:
1798
+ targets = ks
1799
+ for k in targets:
1800
+ out[k].setdefault(x, []).append(c.name)
1801
+ return out
1802
+
1803
+
1804
+ @predicate
1805
+ def dsa_list_item_once(tree, version):
1806
+ """After splitting the explicit data-sharing clauses of a directive onto
1807
+ its leaf constructs (see _dsa_split: private, firstprivate, lastprivate,
1808
+ shared, linear, reduction, in_reduction, is_device_ptr, use_device_ptr,
1809
+ has_device_addr, use_device_addr), a list item (same text) in two
1810
+ clauses on one leaf, other than exactly one firstprivate and one
1811
+ lastprivate. Implicit clauses created by the split are not counted."""
1812
+ def bad(i):
1813
+ return any(len(v) > 1 and sorted(v) != ["firstprivate", "lastprivate"]
1814
+ for leaf in _dsa_split(tree, i, version)
1815
+ for v in leaf.values())
1816
+ return _each(tree, bad)
1817
+
1818
+
1819
+ @predicate
1820
+ def list_item_not_part_of_variable(tree, version):
1821
+ """An array element, array section or structure component (a list item
1822
+ with "(" or "%") in a variable list: declare target's extended list and
1823
+ its to/enter/link/local clauses, and private, firstprivate,
1824
+ lastprivate, shared."""
1825
+ def bad(i):
1826
+ d = tree.directives[i]
1827
+ xs = _items(_clauses(tree, i, "private", "firstprivate",
1828
+ "lastprivate", "shared"))
1829
+ if d.name == "declare target":
1830
+ xs += [_norm(a) for a in d.args]
1831
+ xs += _items(_clauses(tree, i, "to", "enter", "link", "local"))
1832
+ return any(_is_part(x) for x in xs)
1833
+ return _each(tree, bad)
1834
+
1835
+
1836
+ @predicate
1837
+ def use_device_no_structure_element(tree, version):
1838
+ """A structure component (a list item with "%") in use_device_ptr or
1839
+ use_device_addr (5.2)."""
1840
+ return _each(tree, lambda i: any(_is_component(x) for x in _items(
1841
+ _clauses(tree, i, "use_device_ptr", "use_device_addr"))))
1842
+
1843
+
1844
+ @predicate
1845
+ def use_device_addr_section_base(tree, version):
1846
+ """An array section in use_device_addr whose array base is not a name
1847
+ (6.0), e.g. s%a(1:n)."""
1848
+ return _each(tree, lambda i: any(
1849
+ _section_base(x) is not None
1850
+ and not re.fullmatch(r"[a-z_]\w*", _section_base(x))
1851
+ for x in _items(_clauses(tree, i, "use_device_addr"))))
1852
+
1853
+
1854
+ @predicate
1855
+ def device_ancestor_only_on_target(tree, version):
1856
+ """device(ancestor: ...) on a directive without a target leaf."""
1857
+ return _each(tree, lambda i: _has_ancestor_device(tree, i)
1858
+ and "target" not in _leaves(tree, i))
1859
+
1860
+
1861
+ @predicate
1862
+ def device_ancestor_is_one(tree, version):
1863
+ """device(ancestor: n) with an integer literal n other than 1."""
1864
+ return _each(tree, lambda i: any(
1865
+ "ancestor" in c.modifiers and _int(c.args[-1]) not in (None, 1)
1866
+ for c in _clauses(tree, i, "device")))
1867
+
1868
+
1869
+ @predicate
1870
+ def device_ancestor_needs_reverse_offload(tree, version):
1871
+ """device(ancestor: ...) in a source without a requires directive that
1872
+ has the reverse_offload clause (anywhere in the source: a requires in a
1873
+ module that the program unit references also counts)."""
1874
+ if any(d.name == "requires" and _clauses(tree, i, "reverse_offload")
1875
+ for i, d in enumerate(tree.directives)):
1876
+ return []
1877
+ return _each(tree, lambda i: _has_ancestor_device(tree, i))
1878
+
1879
+
1880
+ @predicate
1881
+ def target_ancestor_limits(tree, version):
1882
+ """A construct with a target leaf and device(ancestor: ...) that has a
1883
+ clause other than device, firstprivate, private, defaultmap, map (6.0:
1884
+ nowait), a leaf other than target, or a nested directive."""
1885
+ ok = {"device", "firstprivate", "private", "defaultmap", "map"} | (
1886
+ {"nowait"} if version >= 60 else set())
1887
+
1888
+ def bad(i):
1889
+ if "target" not in _leaves(tree, i) or not _has_ancestor_device(
1890
+ tree, i):
1891
+ return False
1892
+ return (any(c.name not in ok for c in _clauses(tree, i, end=True))
1893
+ or len(_leaves(tree, i)) > 1 or bool(tree.children(i))
1894
+ # Calls to runtime routines in the region
1895
+ or any(_api_calls(st) for _, st in _body(tree, i)))
1896
+ return _each(tree, bad)
1897
+
1898
+
1899
+ # --- predicates: declare target ----------------------------------------------
1900
+
1901
+ @predicate
1902
+ def declare_target_extended_list_alone(tree, version):
1903
+ """declare target with both an extended-list argument and clauses."""
1904
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
1905
+ and bool(tree.directives[i].args)
1906
+ and bool(tree.directives[i].clauses))
1907
+
1908
+
1909
+ @predicate
1910
+ def declare_target_enter_or_link(tree, version):
1911
+ """declare target with clauses but no enter, to (its 5.2 synonym) or
1912
+ link clause."""
1913
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
1914
+ and bool(tree.directives[i].clauses)
1915
+ and not _clauses(tree, i, "enter", "to", "link"))
1916
+
1917
+
1918
+ @predicate
1919
+ def declare_target_nohost_link(tree, version):
1920
+ """declare target with device_type(nohost) and a link clause."""
1921
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
1922
+ and "nohost" in _items(_clauses(tree, i, "device_type"))
1923
+ and bool(_clauses(tree, i, "link")))
1924
+
1925
+
1926
+ # --- default(none) ---------------------------------------------------------
1927
+
1928
+ # Clauses that give a list item an explicitly determined data-sharing
1929
+ # attribute (5.2 §5.4, 6.0 §7.5).
1930
+ _EXPLICIT_DSA = ("private", "firstprivate", "lastprivate", "shared", "linear",
1931
+ "induction",
1932
+ "reduction", "in_reduction", "is_device_ptr",
1933
+ "has_device_addr", "use_device_ptr", "use_device_addr")
1934
+ _DO_VAR = re.compile(r"^(?:[a-z_]\w*\s*:\s*)?do\s+(?:\d+\s*,?\s*)?"
1935
+ r"([a-z_]\w*)\s*=")
1936
+
1937
+
1938
+ @predicate
1939
+ def default_none_listed(tree, version):
1940
+ """A construct with default(none) whose structured block or loop nest
1941
+ references, in a base-language statement, a variable declared in the
1942
+ source (seen from the directive, ProgramTree.attribute: not a named
1943
+ constant, not supplied by a USE) that is listed in no data-sharing
1944
+ attribute clause of the directive and has no predetermined attribute
1945
+ the scanner can see: DO variables of loops in the construct are
1946
+ predetermined private, threadprivate variables are predetermined (5.2
1947
+ §5.1.1, 6.0 §7.1.1). References in nested
1948
+ directives' clauses are not counted (lenient), nor names a USE may
1949
+ supply."""
1950
+ if getattr(tree, "lang", "f90") == "c":
1951
+ return _c_default_none(tree, version)
1952
+ out = []
1953
+ # threadprivate variables are predetermined (5.2 §5.1.1, 6.0 §7.1.1)
1954
+ tprivate = {_base(_norm(x)) for d in tree.directives
1955
+ if d.name == "threadprivate" for x in d.args}
1956
+ for i, d in enumerate(tree.directives):
1957
+ cl = [c for c in d.clauses if c.name == "default"]
1958
+ if not cl or not any(_norm(a).startswith("none")
1959
+ for c in cl for a in c.args):
1960
+ continue
1961
+ a = tree.assoc[i]
1962
+ if a.kind not in ("block", "loop") or a.end_line is None:
1963
+ continue
1964
+ listed = {_base(x) for x in _items(_clauses(tree, i,
1965
+ *_EXPLICIT_DSA))}
1966
+ body = [(ln, st) for ln, st, _ in tree.statements
1967
+ if a.line < ln < a.end_line
1968
+ or (a.kind == "loop" and ln == a.end_line)]
1969
+ dovars = {m.group(1) for _, st in body
1970
+ if (m := _DO_VAR.match(st))}
1971
+ if version == 50 and "teams" in leaves(d.name) and not \
1972
+ {"parallel", "task", "taskloop"} & set(leaves(d.name)):
1973
+ # 5.0 §2.19.1.1 p.271: a sequential loop's variable is private
1974
+ # only in a parallel or task generating construct (5.2 adds
1975
+ # teams), so in a teams region only the DO variables of loops
1976
+ # in a nested parallel or task construct, or of loops a loop
1977
+ # directive is associated with, are predetermined
1978
+ dovars = set(a.loop_vars or ()) if a.kind == "loop" else set()
1979
+ for j in tree.descendants(i):
1980
+ b = tree.assoc[j]
1981
+ if b.kind == "loop":
1982
+ dovars |= set(b.loop_vars or ())
1983
+ if {"parallel", "task", "taskloop"} & set(_leaves(tree, j)) \
1984
+ and b.end_line is not None:
1985
+ dovars |= {m.group(1) for ln, st, _ in tree.statements
1986
+ if b.line < ln < b.end_line
1987
+ and (m := _DO_VAR.match(st))}
1988
+ for _, st in body:
1989
+ names = set(re.findall(r"(?<![%\w])([a-z_]\w*)",
1990
+ _STRING.sub("", st)))
1991
+ hit = [x for x in names - listed - dovars - tprivate
1992
+ if tree.attribute(i, x) is not None
1993
+ and not any((sid, x) in tree.params
1994
+ for sid in a.scope)]
1995
+ if hit:
1996
+ out.append(i)
1997
+ break
1998
+ return out
1999
+
2000
+
2001
+ def _c_names(st: str) -> set:
2002
+ """Identifiers of a C statement (not member names after . or ->)."""
2003
+ st = _STRING.sub("", st)
2004
+ return set(re.findall(r"(?<![\w.>])([a-z_]\w*)", st)) - {
2005
+ "for", "if", "while", "do", "int", "bool", "double", "return",
2006
+ "struct", "const", "static", "sizeof", "true", "false", "else",
2007
+ "switch", "case", "break", "continue", "void", "char", "long",
2008
+ "short", "unsigned", "signed", "float"}
2009
+
2010
+
2011
+ def _c_outside(tree, i, scopes, name) -> bool:
2012
+ """Whether `name`, referenced in a statement with scope chain
2013
+ `scopes` inside construct i, is declared (visibly) outside the
2014
+ construct: its declaring scope, seen from the statement, is on the
2015
+ directive's own scope chain. A name declared inside the construct
2016
+ (a block or for-loop scope of its own) is private (5.2 §5.1.1: variables
2017
+ with automatic storage duration declared in a scope inside the
2018
+ construct)."""
2019
+ hit = tree._find(scopes, name, tree.decls)
2020
+ if hit is None:
2021
+ return False
2022
+ return hit[0][-1] in tree.assoc[i].scope
2023
+
2024
+
2025
+ def _c_construct_refs(tree, i) -> set:
2026
+ """(name) of the variables declared outside construct i that its
2027
+ statements reference (C); the iteration variables of the
2028
+ loops the construct is associated with are not counted (predetermined
2029
+ private, 5.2 §5.1.1)."""
2030
+ a = tree.assoc[i]
2031
+ if a.kind not in ("block", "loop") or a.end_line is None:
2032
+ return set()
2033
+ loopv = set(a.loop_vars or ())
2034
+ out = set()
2035
+ for ln, st, scopes in tree.statements:
2036
+ if not (a.line < ln <= a.end_line):
2037
+ continue
2038
+ for x in _c_names(st):
2039
+ if x in loopv or not _c_outside(tree, i, scopes, x):
2040
+ continue
2041
+ if any((s, x) in tree.params for s in scopes):
2042
+ continue
2043
+ key = tree._find(scopes, x, tree.decls)
2044
+ # a function name is not a variable
2045
+ if key is not None and tree.units and any(
2046
+ n == x for _, n in tree.units.values()) and \
2047
+ key[0][-1] == 0:
2048
+ continue
2049
+ out.add(x)
2050
+ return out
2051
+
2052
+
2053
+ def _c_default_none(tree, version):
2054
+ """default(none) in C: a variable declared outside the
2055
+ construct that its statements reference, not listed in a data-sharing
2056
+ attribute clause, not a loop iteration variable of the construct, not
2057
+ threadprivate, not a named (enumeration) constant."""
2058
+ out = []
2059
+ tprivate = {_base(_norm(x)) for d in tree.directives
2060
+ if d.name == "threadprivate" for x in d.args}
2061
+ for i, d in enumerate(tree.directives):
2062
+ cl = [c for c in d.clauses if c.name == "default"]
2063
+ if not cl or not any(_norm(a).startswith("none")
2064
+ for c in cl for a in c.args):
2065
+ continue
2066
+ listed = {_base(x) for x in _items(_clauses(tree, i,
2067
+ *_EXPLICIT_DSA))}
2068
+ if _c_construct_refs(tree, i) - listed - tprivate:
2069
+ out.append(i)
2070
+ return out
2071
+
2072
+
2073
+ # =============================================================================
2074
+ # The remaining static restrictions
2075
+ # =============================================================================
2076
+
2077
+ def _decl_scope(tree: ProgramTree, i: int, name: str):
2078
+ """The scoping unit that declares `name` as seen from directive i, or
2079
+ None (not declared, or a USE may supply it)."""
2080
+ hit = tree._find(tree.assoc[i].scope, name, tree.decls)
2081
+ return None if hit is None else hit[0][-1]
2082
+
2083
+
2084
+ def _vtype(tree: ProgramTree, i: int, name: str) -> str | None:
2085
+ s = _decl_scope(tree, i, name)
2086
+ return None if s is None else tree.vtypes.get((s, name))
2087
+
2088
+
2089
+ def _is_array(tree: ProgramTree, i: int, name: str) -> bool | None:
2090
+ s = _decl_scope(tree, i, name)
2091
+ return None if s is None else (s, name) in tree.arrays
2092
+
2093
+
2094
+ def _outer_leaf_slots(tree: ProgramTree, i: int):
2095
+ """(directive index, leaf position, leaf) of the enclosing constructs,
2096
+ innermost first."""
2097
+ for p in _ancestors(tree, i):
2098
+ ls = _leaves(tree, p)
2099
+ for k in range(len(ls) - 1, -1, -1):
2100
+ yield p, k, ls[k]
2101
+
2102
+
2103
+ def _on_leaf(tree, i, k, version, names) -> set:
2104
+ """List items of directive i that the clause split puts on its leaf k
2105
+ in one of the clauses `names`."""
2106
+ split = _dsa_split(tree, i, version)
2107
+ return {x for x, cs in split[k].items() if set(cs) & set(names)} \
2108
+ if k < len(split) - 1 else set()
2109
+
2110
+
2111
+ _PRIVATIZING = ("private", "firstprivate", "lastprivate", "linear")
2112
+ _WS_LEAVES = {"do", "for", "sections", "single", "workshare", "scope"}
2113
+
2114
+
2115
+ def _ws_leaf(tree, i, leaf) -> bool:
2116
+ """A worksharing leaf (5.2 §11: do, for, sections, single, workshare,
2117
+ scope; loop only with bind(parallel), when its binding region is the
2118
+ parallel region)."""
2119
+ return leaf in _WS_LEAVES or (
2120
+ leaf == "loop" and "parallel" in _items(_clauses(tree, i, "bind")))
2121
+
2122
+
2123
+ # --- nesting of regions --------------------------------------------------------
2124
+
2125
+ @predicate
2126
+ def worksharing_closely_nested(tree, version):
2127
+ """A construct whose outermost leaf is a worksharing construct and whose
2128
+ closest enclosing construct, up to the nearest parallel, teams or target
2129
+ leaf, is a worksharing, task, taskloop, critical, ordered, atomic or
2130
+ masked construct (taskgroup and other constructs are passed through)."""
2131
+ bad_outer = {"task", "taskloop", "critical", "ordered (Block)", "atomic",
2132
+ "masked", "master"} | ({"loop"} if version == 50 else set())
2133
+
2134
+ def bad(i):
2135
+ if not _ws_leaf(tree, i, _leaves(tree, i)[0]):
2136
+ return False
2137
+ for p, leaf in _outer_leaves(tree, i):
2138
+ if leaf in ("parallel", "teams", "target"):
2139
+ return False
2140
+ if leaf in bad_outer or _ws_leaf(tree, p, leaf):
2141
+ return True
2142
+ return False
2143
+ return _each(tree, bad)
2144
+
2145
+
2146
+ def _simdizable(tree, c, version) -> bool:
2147
+ name = tree.directives[c].name
2148
+ if not _is_construct(tree, c) or name in ("simd", "atomic"):
2149
+ return True
2150
+ if version >= 60 and name in _LOOP_TRANSFORMS:
2151
+ return True
2152
+ if name == "loop": # SIMDizable iff its binding region is not defined
2153
+ return not set(_items(_clauses(tree, c, "bind"))) & {"teams",
2154
+ "parallel"}
2155
+ if name == "ordered (Block)":
2156
+ return bool(_clauses(tree, c, "simd"))
2157
+ return False
2158
+
2159
+
2160
+ @predicate
2161
+ def simd_simdizable_only(tree, version):
2162
+ """A construct lexically inside a construct with a simd leaf that is not
2163
+ SIMDizable: simd, atomic, loop without bind(teams/parallel), ordered
2164
+ with the simd clause (6.0: and loop-transforming constructs)."""
2165
+ return sorted({c for i in range(len(tree.directives))
2166
+ if "simd" in _leaves(tree, i)
2167
+ for c in tree.descendants(i)
2168
+ if not _simdizable(tree, c, version)})
2169
+
2170
+
2171
+ @predicate
2172
+ def order_concurrent_no_api_calls(tree, version):
2173
+ """A construct with order(concurrent) (a loop leaf without an order
2174
+ clause counts) whose lexical extent calls an OpenMP runtime routine
2175
+ (5.2: none may be called; 6.0: only order-concurrent-nestable routines,
2176
+ and no routine has that property)."""
2177
+ return _each(tree, lambda i: _order_concurrent(tree, i) and any(
2178
+ _api_calls(st) for _, st in _body(tree, i)))
2179
+
2180
+
2181
+ def _ordered_parent(tree, i):
2182
+ for p in _ancestors(tree, i):
2183
+ if tree.directives[p].name != "taskgroup":
2184
+ return p
2185
+ return None
2186
+
2187
+
2188
+ def _block_ordered(tree, i, simd: bool) -> bool:
2189
+ return tree.directives[i].name == "ordered (Block)" and (
2190
+ bool(_clauses(tree, i, "simd")) == simd)
2191
+
2192
+
2193
+ @predicate
2194
+ def ordered_threads_in_worksharing_loop(tree, version):
2195
+ """A block-associated ordered construct without the simd clause whose
2196
+ closest enclosing construct (taskgroup passed through) has no do leaf.
2197
+ An orphaned ordered construct is not flagged."""
2198
+ def bad(i):
2199
+ p = _ordered_parent(tree, i) if _block_ordered(tree, i, False) \
2200
+ else None
2201
+ return p is not None and not set(_leaves(tree, p)) & {"do", "for"}
2202
+ return _each(tree, bad)
2203
+
2204
+
2205
+ @predicate
2206
+ def ordered_simd_in_simd(tree, version):
2207
+ """A block-associated ordered construct with the simd clause whose
2208
+ closest enclosing construct has no simd leaf (orphans not flagged)."""
2209
+ def bad(i):
2210
+ p = tree.parent(i) if _block_ordered(tree, i, True) else None
2211
+ return p is not None and "simd" not in _leaves(tree, p)
2212
+ return _each(tree, bad)
2213
+
2214
+
2215
+ @predicate
2216
+ def ordered_binding_has_ordered_clause(tree, version):
2217
+ """A block-associated ordered construct without the simd clause closely
2218
+ nested in a construct with a do leaf that has no ordered clause, or an
2219
+ ordered(n) whose literal n is not 1 (5.2) / not the collapse value
2220
+ (6.0)."""
2221
+ def bad(i):
2222
+ p = _ordered_parent(tree, i) if _block_ordered(tree, i, False) \
2223
+ else None
2224
+ if p is None or not set(_leaves(tree, p)) & {"do", "for"}:
2225
+ return False
2226
+ cl = _clauses(tree, p, "ordered")
2227
+ if not cl:
2228
+ return True
2229
+ n = _int(cl[0].args[0]) if cl[0].args else None
2230
+ want = 1 if version < 60 else _collapse(tree, p)
2231
+ return n is not None and want is not None and n != want
2232
+ return _each(tree, bad)
2233
+
2234
+
2235
+ @predicate
2236
+ def workdistribute_in_teams(tree, version):
2237
+ """A workdistribute constituent whose immediately enclosing construct is
2238
+ not a teams construct (orphaned: flagged, the restriction names the
2239
+ construct)."""
2240
+ return _each(tree, lambda i: "workdistribute" in _leaves(tree, i)
2241
+ and _enclosing_leaf(tree, i, "workdistribute") != "teams")
2242
+
2243
+
2244
+ @predicate
2245
+ def workdistribute_no_nested_region(tree, version):
2246
+ """A construct lexically inside a workdistribute construct."""
2247
+ return sorted({c for i in range(len(tree.directives))
2248
+ if "workdistribute" in _leaves(tree, i)
2249
+ for c in tree.descendants(i) if _is_construct(tree, c)})
2250
+
2251
+
2252
+ _ASSIGNMENT = re.compile(r"^[a-z_]\w*\s*(?:\([^=]*\))?(?:\s*%\s*[a-z_]\w*\s*"
2253
+ r"(?:\([^=]*\))?)*\s*=(?!=)")
2254
+
2255
+
2256
+ @predicate
2257
+ def workdistribute_statements(tree, version):
2258
+ """A base-language statement in a workdistribute construct that is not
2259
+ an assignment or a CALL (BLOCK / END BLOCK delimiters aside); whether a
2260
+ called procedure is pure and elemental is not checked."""
2261
+ def bad(i):
2262
+ return "workdistribute" in _leaves(tree, i) and any(
2263
+ not (_ASSIGNMENT.match(st) or re.match(r"^call\b", st)
2264
+ or _BLOCK.match(st) or _END_BLOCK.match(st))
2265
+ for _, st in _body(tree, i))
2266
+ return _each(tree, bad)
2267
+
2268
+
2269
+ # --- compound constructs and clause values --------------------------------------
2270
+
2271
+ def _assoc_vars(tree, i) -> tuple | None:
2272
+ """Iteration variables of the loops associated with loop directive i,
2273
+ or None when unknown."""
2274
+ a = tree.assoc[i]
2275
+ n = _assoc_count(tree, i)
2276
+ if a.kind != "loop" or not a.loop_depth or n is None:
2277
+ return None
2278
+ if n > a.loop_depth and a.depth_known:
2279
+ return None # collapse-loop-001 reports it
2280
+ return a.loop_vars[:n] if n <= a.loop_depth else None
2281
+
2282
+
2283
+ @predicate
2284
+ def distribute_linear_loop_vars(tree, version):
2285
+ """A compound with a distribute leaf and a linear list item that is not
2286
+ the iteration variable of an associated loop."""
2287
+ def bad(i):
2288
+ ls = _leaves(tree, i)
2289
+ vs = _assoc_vars(tree, i)
2290
+ return "distribute" in ls and len(ls) > 1 and vs is not None and any(
2291
+ _base(x) not in vs for x in _items(_clauses(tree, i, "linear")))
2292
+ return _each(tree, bad)
2293
+
2294
+
2295
+ @predicate
2296
+ def schedule_runtime_auto_no_chunk(tree, version):
2297
+ """schedule(runtime, chunk) or schedule(auto, chunk)."""
2298
+ return _each(tree, lambda i: any(
2299
+ c.args and _norm(c.args[0]) in ("runtime", "auto") and len(c.args) > 1
2300
+ for c in _clauses(tree, i, "schedule")))
2301
+
2302
+
2303
+ @predicate
2304
+ def schedule_nonmonotonic_no_ordered(tree, version):
2305
+ """schedule with the nonmonotonic modifier and an ordered clause on the
2306
+ same directive."""
2307
+ return _each(tree, lambda i: any(
2308
+ "nonmonotonic" in c.modifiers for c in _clauses(tree, i, "schedule"))
2309
+ and bool(_clauses(tree, i, "ordered")))
2310
+
2311
+
2312
+ def _lit(tree, i, name) -> int | None:
2313
+ cs = _clauses(tree, i, name)
2314
+ return _int(cs[0].args[0]) if cs and cs[0].args else None
2315
+
2316
+
2317
+ @predicate
2318
+ def simdlen_le_safelen(tree, version):
2319
+ """simdlen and safelen with integer literals, simdlen > safelen."""
2320
+ def bad(i):
2321
+ a, b = _lit(tree, i, "simdlen"), _lit(tree, i, "safelen")
2322
+ return a is not None and b is not None and a > b
2323
+ return _each(tree, bad)
2324
+
2325
+
2326
+ @predicate
2327
+ def simd_order_concurrent_no_safelen(tree, version):
2328
+ """A directive with a simd leaf, order(concurrent) and safelen."""
2329
+ return _each(tree, lambda i: "simd" in _leaves(tree, i)
2330
+ and "concurrent" in _items(_clauses(tree, i, "order"))
2331
+ and bool(_clauses(tree, i, "safelen")))
2332
+
2333
+
2334
+ @predicate
2335
+ def positive_arguments(tree, version):
2336
+ """An integer literal less than one as an argument with the positive
2337
+ property: num_threads, safelen, simdlen, the schedule chunk_size, the
2338
+ ordered parameter, the aligned alignment (thread_limit, num_teams and
2339
+ dist_schedule have their own rules)."""
2340
+ def values(i):
2341
+ out = []
2342
+ for c in _clauses(tree, i, "num_threads", "safelen", "simdlen",
2343
+ "ordered"):
2344
+ out += c.args if c.name == "num_threads" else c.args[:1]
2345
+ for c in _clauses(tree, i, "schedule"):
2346
+ out += c.args[1:2]
2347
+ for c in _clauses(tree, i, "aligned"):
2348
+ out += [m for m in c.modifiers if _compact(m) not in _names()]
2349
+ return out
2350
+ return _each(tree, lambda i: _not_positive(values(i)))
2351
+
2352
+
2353
+ @predicate
2354
+ def constant_arguments(tree, version):
2355
+ """safelen, simdlen or ordered(n) whose argument is a variable declared
2356
+ in the source (not a named constant)."""
2357
+ def bad(i):
2358
+ for c in _clauses(tree, i, "safelen", "simdlen", "ordered"):
2359
+ x = _norm(c.args[0]) if c.args else ""
2360
+ if re.fullmatch(r"[a-z_]\w*", x) \
2361
+ and tree.attribute(i, x) is not None \
2362
+ and not any((s, x) in tree.params
2363
+ for s in tree.assoc[i].scope):
2364
+ return True
2365
+ return False
2366
+ return _each(tree, bad)
2367
+
2368
+
2369
+ # clause sets (5.2 §3.2, 6.0 §5.2) the skeleton does not enforce
2370
+ _REQUIRED_SETS = {
2371
+ 52: {"target update": {"to", "from"}, "scan": {"inclusive", "exclusive"},
2372
+ "depobj": {"depend", "destroy", "update"}},
2373
+ 60: {"target update": {"to", "from"},
2374
+ "scan": {"inclusive", "exclusive", "init_complete"},
2375
+ "depobj": {"destroy", "init", "update", "depend"}},
2376
+ }
2377
+ _EXCLUSIVE_SETS = {
2378
+ 52: {"scan": [{"inclusive", "exclusive"}],
2379
+ "depobj": [{"depend", "destroy", "update"}],
2380
+ "task": [{"detach", "mergeable"}],
2381
+ "taskloop": [{"nogroup", "reduction"}, {"grainsize", "num_tasks"}]},
2382
+ 60: {"scan": [{"inclusive", "exclusive", "init_complete"}],
2383
+ "single": [{"copyprivate", "nowait"}],
2384
+ "task": [{"detach", "mergeable"}],
2385
+ "taskloop": [{"nogroup", "reduction"}, {"grainsize", "num_tasks"}]},
2386
+ }
2387
+
2388
+
2389
+ @predicate
2390
+ def clause_set_required(tree, version):
2391
+ """target update without to/from, scan without inclusive/exclusive,
2392
+ depobj without depend/destroy/update (6.0: init/destroy/update)."""
2393
+ sets = _REQUIRED_SETS[52 if version < 60 else 60]
2394
+ return _each(tree, lambda i: tree.directives[i].name in sets and not (
2395
+ {c.name for c in _clauses(tree, i, end=True)}
2396
+ & sets[tree.directives[i].name]))
2397
+
2398
+
2399
+ @predicate
2400
+ def clause_set_exclusive(tree, version):
2401
+ """Two clauses with different names from one exclusive clause set on a
2402
+ directive (scan, depobj (5.2), single (6.0), task, taskloop)."""
2403
+ sets = _EXCLUSIVE_SETS[52 if version < 60 else 60]
2404
+ return _each(tree, lambda i: any(
2405
+ len({c.name for c in _clauses(tree, i, end=True)} & s) > 1
2406
+ for s in sets.get(tree.directives[i].name, ())))
2407
+
2408
+
2409
+ @predicate
2410
+ def single_copyprivate_nowait(tree, version):
2411
+ """single with copyprivate and nowait (on the directive or its end
2412
+ directive)."""
2413
+ return _each(tree, lambda i: tree.directives[i].name == "single"
2414
+ and bool(_clauses(tree, i, "copyprivate", end=True))
2415
+ and bool(_clauses(tree, i, "nowait", end=True)))
2416
+
2417
+
2418
+ _UNIQUE_LISTS = set(_DSA) | {"to", "from", "enter", "link", "local",
2419
+ "aligned", "nontemporal", "copyin",
2420
+ "copyprivate", "allocate", "inclusive",
2421
+ "exclusive"}
2422
+
2423
+
2424
+ @predicate
2425
+ def list_item_directive_wide_unique(tree, version):
2426
+ """A list item twice in one clause, in two clauses of the same name
2427
+ (not data-sharing clauses, which datasharing-dsa-001 checks after the
2428
+ split; not map or depend, where the spec allows repeats), in both to
2429
+ and from, or in two of enter, link and local, of one directive."""
2430
+ def bad(i):
2431
+ cs = [c for c in _clauses(tree, i, end=True)
2432
+ if c.name in _UNIQUE_LISTS]
2433
+ if tree.directives[i].name == "declare target" \
2434
+ and len(set(map(_norm, tree.directives[i].args))) \
2435
+ < len(tree.directives[i].args):
2436
+ return True
2437
+ by = {}
2438
+ for c in cs:
2439
+ xs = _items([c])
2440
+ if len(xs) != len(set(xs)):
2441
+ return True
2442
+ by.setdefault(c.name, []).extend(set(xs))
2443
+ if any(len(xs) != len(set(xs)) for n, xs in by.items()
2444
+ if n not in _DSA):
2445
+ return True
2446
+ pairs = [("to", "from"), ("enter", "link"), ("enter", "local"),
2447
+ ("link", "local"), ("to", "link"), ("to", "local")]
2448
+ return any(set(by.get(a, ())) & set(by.get(b, ())) for a, b in pairs)
2449
+ return _each(tree, bad)
2450
+
2451
+
2452
+ @predicate
2453
+ def linear_inscan_loop_vars(tree, version):
2454
+ """reduction(inscan, ...) and a linear list item that is not the
2455
+ iteration variable of an associated loop, on one directive."""
2456
+ def bad(i):
2457
+ vs = _assoc_vars(tree, i)
2458
+ return bool(_reductions(tree, i, "inscan")) and vs is not None and any(
2459
+ _base(x) not in vs for x in _items(_clauses(tree, i, "linear")))
2460
+ return _each(tree, bad)
2461
+
2462
+
2463
+ def _linear_ref_uval(c) -> bool:
2464
+ return any(re.match(r"^(?:ref|uval)\s*\(", _norm(a)) for a in c.args) \
2465
+ or bool({"ref", "uval"} & set(map(_norm, c.modifiers)))
2466
+
2467
+
2468
+ @predicate
2469
+ def linear_ref_uval_declare_simd(tree, version):
2470
+ """A linear clause with the ref or uval modifier on a directive other
2471
+ than declare simd (5.0: any modifier, val included, 5.0 §2.9.2 p.107,
2472
+ §2.9.3.1 p.113)."""
2473
+ def has_mod(c):
2474
+ if version == 50:
2475
+ return bool({"ref", "uval", "val"} & set(map(_norm, c.modifiers)))
2476
+ return _linear_ref_uval(c)
2477
+ return _each(tree, lambda i: tree.directives[i].name != "declare simd"
2478
+ and any(has_mod(c) for c in _clauses(tree, i, "linear")))
2479
+
2480
+
2481
+ def _linear_names(c) -> list[str]:
2482
+ return [_base(re.sub(r"^(?:ref|uval|val)\((.*)\)$", r"\1", _norm(a)))
2483
+ for a in c.args]
2484
+
2485
+
2486
+ @predicate
2487
+ def linear_not_pointer(tree, version):
2488
+ """A linear list item declared with the POINTER attribute."""
2489
+ return _each(tree, lambda i: any(
2490
+ tree.attribute(i, x) == "pointer"
2491
+ for c in _clauses(tree, i, "linear") for x in _linear_names(c)))
2492
+
2493
+
2494
+ @predicate
2495
+ def linear_integer(tree, version):
2496
+ """A linear list item (without ref) declared with an intrinsic type
2497
+ other than integer, or a derived type."""
2498
+ return _each(tree, lambda i: any(
2499
+ _vtype(tree, i, x) not in (None, "integer")
2500
+ for c in _clauses(tree, i, "linear") if not _linear_ref_uval(c)
2501
+ for x in _linear_names(c)))
2502
+
2503
+
2504
+ @predicate
2505
+ def loop_var_predetermined_dsa(tree, version):
2506
+ """After the clause split, the iteration variable of an associated loop
2507
+ in a data-sharing clause on a loop leaf (do, simd, distribute, loop,
2508
+ taskloop) other than private or lastprivate (or linear on a simd leaf
2509
+ with one associated loop)."""
2510
+ loop_leaves = {"do", "for", "simd", "distribute", "loop", "taskloop"}
2511
+
2512
+ def bad(i):
2513
+ vs = _assoc_vars(tree, i)
2514
+ if not vs:
2515
+ return False
2516
+ ls = _rule_leaves(tree.directives[i].name, version)
2517
+ split = _dsa_split(tree, i, version)
2518
+ for k, leaf in enumerate(ls):
2519
+ if leaf not in loop_leaves:
2520
+ continue
2521
+ for x, names in split[k].items():
2522
+ if x not in vs:
2523
+ continue
2524
+ for nm in names:
2525
+ if nm in ("private", "lastprivate") or (
2526
+ nm == "linear" and leaf == "simd"
2527
+ and len(vs) == 1):
2528
+ continue
2529
+ return True
2530
+ return False
2531
+ return _each(tree, bad)
2532
+
2533
+
2534
+ # --- data-sharing across nested constructs ---------------------------------------
2535
+
2536
+ def _binding(tree, i, kinds, stop) -> tuple | None:
2537
+ """(directive, leaf position, leaf) of the closest enclosing leaf in
2538
+ `kinds`, if no leaf in `stop` comes first."""
2539
+ for p, k, leaf in _outer_leaf_slots(tree, i):
2540
+ if leaf in kinds:
2541
+ return p, k, leaf
2542
+ if leaf in stop:
2543
+ return None
2544
+ return None
2545
+
2546
+
2547
+ _GENERATING = {"parallel", "teams", "target", "task", "taskloop"}
2548
+
2549
+
2550
+ def _items_on(tree, i, version, leaf, names) -> set:
2551
+ ls = _rule_leaves(tree.directives[i].name, version)
2552
+ return {_base(x) for k, lf in enumerate(ls) if lf == leaf
2553
+ for x in _on_leaf(tree, i, k, version, names)}
2554
+
2555
+
2556
+ def _private_in(tree, p, k, version, names=_PRIVATIZING) -> set:
2557
+ return {_base(x) for x in _on_leaf(tree, p, k, version, names)}
2558
+
2559
+
2560
+ def _nested_dsa_rule(inner_leaves, inner_clauses, outer, outer_clauses,
2561
+ doc):
2562
+ """A predicate: an item in `inner_clauses` on an inner leaf in
2563
+ `inner_leaves` (worksharing leaves for "ws") that is in `outer_clauses`
2564
+ on the closest enclosing `outer` leaf (no other generating leaf
2565
+ between)."""
2566
+ def fn(tree, version):
2567
+ def bad(i):
2568
+ ls = _rule_leaves(tree.directives[i].name, version)
2569
+ if ls[0] not in inner_leaves and not (
2570
+ "ws" in inner_leaves and _ws_leaf(tree, i, ls[0])):
2571
+ return False
2572
+ items = _items_on(tree, i, version, ls[0], inner_clauses)
2573
+ if not items:
2574
+ return False
2575
+ hit = _binding(tree, i, {outer}, _GENERATING - {outer})
2576
+ if hit is None:
2577
+ return False
2578
+ p, k, _ = hit
2579
+ return bool(items & _private_in(tree, p, k, version,
2580
+ outer_clauses))
2581
+ return _each(tree, bad)
2582
+ fn.__doc__ = doc
2583
+ return fn
2584
+
2585
+
2586
+ def _register(name, fn):
2587
+ fn.__name__ = name
2588
+ return predicate(fn)
2589
+
2590
+
2591
+ _register("firstprivate_private_in_parallel", _nested_dsa_rule(
2592
+ {"ws"}, ("firstprivate",), "parallel", _PRIVATIZING,
2593
+ """firstprivate on a worksharing construct of an item that is private
2594
+ (private, firstprivate, lastprivate, linear) on the closest enclosing
2595
+ parallel leaf."""))
2596
+ _register("firstprivate_private_in_teams", _nested_dsa_rule(
2597
+ {"distribute"}, ("firstprivate",), "teams", _PRIVATIZING,
2598
+ """firstprivate on a distribute construct of an item that is private on
2599
+ the closest enclosing teams leaf."""))
2600
+ _register("firstprivate_reduction_in_parallel_ws", _nested_dsa_rule(
2601
+ {"ws", "task", "taskloop"}, ("firstprivate",), "parallel",
2602
+ ("reduction",),
2603
+ """firstprivate on a worksharing, task or taskloop construct of an item
2604
+ in a reduction clause of the closest enclosing parallel leaf (5.2)."""))
2605
+ _register("firstprivate_reduction_in_parallel_task", _nested_dsa_rule(
2606
+ {"task", "taskloop"}, ("firstprivate",), "parallel", ("reduction",),
2607
+ """firstprivate on a task or taskloop construct of an item in a
2608
+ reduction clause of the closest enclosing parallel leaf (6.0)."""))
2609
+ _register("firstprivate_reduction_in_teams", _nested_dsa_rule(
2610
+ {"distribute"}, ("firstprivate",), "teams", ("reduction",),
2611
+ """firstprivate on a distribute construct of an item in a reduction
2612
+ clause of the closest enclosing teams leaf (5.2)."""))
2613
+ _register("reduction_shared_in_parallel", _nested_dsa_rule(
2614
+ {"ws"}, ("reduction",), "parallel", _PRIVATIZING + ("reduction",),
2615
+ """reduction on a worksharing construct of an item that is private
2616
+ (private, firstprivate, lastprivate, linear, reduction) on the closest
2617
+ enclosing parallel leaf."""))
2618
+ _register("reduction_shared_in_teams", _nested_dsa_rule(
2619
+ {"distribute", "loop"}, ("reduction",), "teams",
2620
+ _PRIVATIZING + ("reduction",),
2621
+ """reduction on a distribute or loop construct of an item that is
2622
+ private on the closest enclosing teams leaf (6.0)."""))
2623
+
2624
+
2625
+ @predicate
2626
+ def lastprivate_private_in_binding(tree, version):
2627
+ """lastprivate on a work-distribution leaf (worksharing leaves,
2628
+ distribute, loop) of an item that is private on the closest enclosing
2629
+ parallel (distribute: teams) leaf."""
2630
+ def bad(i):
2631
+ ls = _rule_leaves(tree.directives[i].name, version)
2632
+ first = ls[0]
2633
+ if first == "distribute":
2634
+ outer = "teams"
2635
+ elif _ws_leaf(tree, i, first) or first == "loop":
2636
+ outer = "parallel"
2637
+ else:
2638
+ return False
2639
+ items = _items_on(tree, i, version, first, ("lastprivate",))
2640
+ hit = _binding(tree, i, {outer}, _GENERATING - {outer}) \
2641
+ if items else None
2642
+ return hit is not None and bool(
2643
+ items & _private_in(tree, hit[0], hit[1], version))
2644
+ return _each(tree, bad)
2645
+
2646
+
2647
+ @predicate
2648
+ def lastprivate_conditional_scalar(tree, version):
2649
+ """lastprivate(conditional: x) with x declared as an array."""
2650
+ return _each(tree, lambda i: any(
2651
+ _is_array(tree, i, _base(x))
2652
+ for c in _clauses(tree, i, "lastprivate")
2653
+ if "conditional" in c.modifiers for x in _items([c])))
2654
+
2655
+
2656
+ def _tp_keys(tree) -> set:
2657
+ """(declaring scope, name) of threadprivate variables."""
2658
+ out = set()
2659
+ for j, d in enumerate(tree.directives):
2660
+ if d.name == "threadprivate":
2661
+ for x in d.args:
2662
+ x = _norm(x)
2663
+ if not x.startswith("/"):
2664
+ s = _decl_scope(tree, j, x)
2665
+ out.add((s if s is not None else
2666
+ tree.assoc[j].scope[-1], x))
2667
+ return out
2668
+
2669
+
2670
+ def _key(tree, i, x) -> tuple:
2671
+ s = _decl_scope(tree, i, x)
2672
+ return (s if s is not None else tree.assoc[i].scope[-1], x)
2673
+
2674
+
2675
+ @predicate
2676
+ def copyin_threadprivate(tree, version):
2677
+ """A copyin list item (not a common block) declared in the source,
2678
+ visible from the directive, that no threadprivate directive names."""
2679
+ def bad(i):
2680
+ tp = _tp_keys(tree)
2681
+ return any(not x.startswith("/") and tree.attribute(i, x) is not None
2682
+ and _key(tree, i, x) not in tp
2683
+ for x in map(_base, _items(_clauses(tree, i, "copyin"))))
2684
+ return _each(tree, bad)
2685
+
2686
+
2687
+ @predicate
2688
+ def threadprivate_only_copyin(tree, version):
2689
+ """A threadprivate variable as the base of a list item of a clause
2690
+ other than copyin and copyprivate."""
2691
+ def bad(i):
2692
+ tp = _tp_keys(tree)
2693
+ return tree.directives[i].name != "threadprivate" and any(
2694
+ _key(tree, i, _base(x)) in tp
2695
+ for c in _clauses(tree, i, end=True)
2696
+ if c.name not in ("copyin", "copyprivate")
2697
+ and c.name in _UNIQUE_LISTS | {"map", "depend"}
2698
+ for x in _items([c]))
2699
+ return _each(tree, bad)
2700
+
2701
+
2702
+ @predicate
2703
+ def threadprivate_in_specification_part(tree, version):
2704
+ """A threadprivate directive lexically inside a construct (an execution
2705
+ part)."""
2706
+ return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
2707
+ and tree.parent(i) is not None)
2708
+
2709
+
2710
+ def _foreign_scope(tree, i, x) -> bool:
2711
+ """x is declared visibly from directive i, in another scoping unit."""
2712
+ s = _decl_scope(tree, i, _norm(x))
2713
+ return s is not None and s != tree.assoc[i].scope[-1]
2714
+
2715
+
2716
+ @predicate
2717
+ def threadprivate_declaring_scope(tree, version):
2718
+ """A threadprivate variable declared (visibly) in another scoping unit
2719
+ than the directive's."""
2720
+ return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
2721
+ and any(_foreign_scope(tree, i, x) for x in
2722
+ tree.directives[i].args
2723
+ if not _norm(x).startswith("/")))
2724
+
2725
+
2726
+ def _not_saved(tree, i, x) -> bool:
2727
+ """x is declared in a subprogram (not a program, module or submodule)
2728
+ without SAVE (attribute, statement, bare SAVE, or initializer); a
2729
+ procedure of the source (a function's name is also its result
2730
+ variable's) is not a variable."""
2731
+ s = _decl_scope(tree, i, x)
2732
+ if s is None or s == 0 or any(n == x for _, n in tree.units.values()):
2733
+ return False
2734
+ if getattr(tree, "lang", "f90") == "c":
2735
+ # C: static storage duration = file scope or static
2736
+ return (s, x) not in tree.saved
2737
+ kind = tree.units.get(s, ("", None))[0]
2738
+ if kind in ("program", "module", "submodule", "blockdata", ""):
2739
+ return False
2740
+ return (s, x) not in tree.saved and (s, "*") not in tree.saved
2741
+
2742
+
2743
+ @predicate
2744
+ def threadprivate_saved(tree, version):
2745
+ """A threadprivate variable declared in a subprogram without the SAVE
2746
+ attribute."""
2747
+ return _each(tree, lambda i: tree.directives[i].name == "threadprivate"
2748
+ and any(_not_saved(tree, i, _norm(x))
2749
+ for x in tree.directives[i].args
2750
+ if not _norm(x).startswith("/")))
2751
+
2752
+
2753
+ def _dt_items(tree, i, *names) -> list[str]:
2754
+ d = tree.directives[i]
2755
+ xs = _items(_clauses(tree, i, *names))
2756
+ if "enter" in names:
2757
+ xs += [_norm(a) for a in d.args]
2758
+ return [x for x in xs if not x.startswith("/")]
2759
+
2760
+
2761
+ @predicate
2762
+ def declare_target_enter_saved(tree, version):
2763
+ """A declare target variable (5.2: any list item; 6.0: in enter or
2764
+ the extended list) declared in a subprogram without SAVE."""
2765
+ names = ("enter", "to", "link", "local") if version < 60 else (
2766
+ "enter", "to")
2767
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
2768
+ and any(_not_saved(tree, i, x)
2769
+ for x in _dt_items(tree, i, *names)))
2770
+
2771
+
2772
+ @predicate
2773
+ def declare_target_link_saved(tree, version):
2774
+ """A link list item declared in a subprogram without SAVE (6.0)."""
2775
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
2776
+ and any(_not_saved(tree, i, x)
2777
+ for x in _dt_items(tree, i, "link")))
2778
+
2779
+
2780
+ @predicate
2781
+ def declare_target_declaring_scope(tree, version):
2782
+ """A declare target variable declared (visibly) in another scoping
2783
+ unit than the directive's."""
2784
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
2785
+ and any(_foreign_scope(tree, i, x) for x in _dt_items(
2786
+ tree, i, "enter", "to", "link", "local")))
2787
+
2788
+
2789
+ @predicate
2790
+ def declare_target_procedure_placement(tree, version):
2791
+ """A declare target without an extended list and without enter, to,
2792
+ link or local clauses (5.2: no clauses or only device_type/indirect;
2793
+ 6.0: a declaration-associated directive; 5.0: no clauses at all, 5.0
2794
+ §2.12.7 p.184) in a program, module or submodule scoping unit."""
2795
+ def bad(i):
2796
+ d = tree.directives[i]
2797
+ if d.name != "declare target" or d.args or _clauses(
2798
+ tree, i, "enter", "to", "link", "local"):
2799
+ return False
2800
+ if version == 50 and d.clauses:
2801
+ return False
2802
+ kind = tree.units.get(tree.assoc[i].scope[-1], ("", None))[0]
2803
+ return kind in ("program", "module", "submodule", "blockdata")
2804
+ return _each(tree, bad)
2805
+
2806
+
2807
+ def _cross_directive(tree, first: tuple, second: tuple) -> list[int]:
2808
+ """declare target directives naming an item in a clause of `second`
2809
+ that another declare target directive names in `first` (the extended
2810
+ list counts as enter)."""
2811
+ seen = {}
2812
+ for i, d in enumerate(tree.directives):
2813
+ if d.name != "declare target":
2814
+ continue
2815
+ for kind, names in (("a", first), ("b", second)):
2816
+ for x in _dt_items(tree, i, *names) if "enter" in names else [
2817
+ x for x in _items(_clauses(tree, i, *names))]:
2818
+ seen.setdefault(_key(tree, i, _base(x)), []).append((kind, i))
2819
+ out = set()
2820
+ for hits in seen.values():
2821
+ a = {i for k, i in hits if k == "a"}
2822
+ b = {i for k, i in hits if k == "b"}
2823
+ if any(x != y for x in a for y in b):
2824
+ out.add(max(a | b))
2825
+ return sorted(out)
2826
+
2827
+
2828
+ @predicate
2829
+ def declare_target_enter_link(tree, version):
2830
+ """The same item in enter (or to, or the extended list) on one declare
2831
+ target directive and in link (6.0: or local) on another."""
2832
+ second = ("link",) if version < 60 else ("link", "local")
2833
+ return _cross_directive(tree, ("enter", "to"), second)
2834
+
2835
+
2836
+ @predicate
2837
+ def declare_target_link_local(tree, version):
2838
+ """The same item in link on one declare target directive and local on
2839
+ another (6.0)."""
2840
+ return _cross_directive(tree, ("link",), ("local",))
2841
+
2842
+
2843
+ @predicate
2844
+ def indirect_device_type_any(tree, version):
2845
+ """indirect (or indirect(.true.)) with device_type other than any."""
2846
+ def bad(i):
2847
+ ind = _clauses(tree, i, "indirect")
2848
+ on = ind and (not ind[0].args or _norm(ind[0].args[0]) == ".true.")
2849
+ return bool(on) and any(x != "any" for x in _items(
2850
+ _clauses(tree, i, "device_type")))
2851
+ return _each(tree, bad)
2852
+
2853
+
2854
+ @predicate
2855
+ def map_device_local(tree, version):
2856
+ """A map list item that a declare target local clause of the source
2857
+ names (a device-local variable, 6.0)."""
2858
+ def bad(i):
2859
+ local = {_key(tree, j, _base(x)) for j, d in enumerate(tree.directives)
2860
+ if d.name == "declare target"
2861
+ for x in _items(_clauses(tree, j, "local"))}
2862
+ return _map_construct(tree, i) and any(
2863
+ _key(tree, i, _base(x)) in local
2864
+ for x in _items(_clauses(tree, i, "map")))
2865
+ return _each(tree, bad)
2866
+
2867
+
2868
+ @predicate
2869
+ def target_map_structure_elements(tree, version):
2870
+ """On a construct with a target leaf that maps s%c (and not s), a
2871
+ statement in the construct referencing another component s%d that no
2872
+ map clause names."""
2873
+ def bad(i):
2874
+ if "target" not in _leaves(tree, i):
2875
+ return False
2876
+ # C structure elements s.c / p->c read as s%c
2877
+ maps = {re.sub(r"\([^()]*\)|\[[^\[\]]*\]", "",
2878
+ x.replace("->", "%").replace(".", "%"))
2879
+ for x in _items(_clauses(tree, i, "map"))}
2880
+ out = False
2881
+ for x in maps:
2882
+ if "%" not in x:
2883
+ continue
2884
+ s = x.split("%")[0]
2885
+ if s in maps:
2886
+ continue
2887
+ for _, st in _body(tree, i):
2888
+ st = re.sub(r"->|\.(?=[a-z_])", "%", st)
2889
+ for comp in re.findall(rf"(?<![%\w]){s}\s*(?:\([^()]*\)|"
2890
+ r"\[[^\[\]]*\])?\s*"
2891
+ r"%\s*([a-z_]\w*)", st):
2892
+ el = f"{s}%{comp}"
2893
+ # mapped: the element itself or a part of it (s%c%d)
2894
+ if not any(m == el or m.startswith(el + "%")
2895
+ for m in maps):
2896
+ out = True
2897
+ return out
2898
+ return _each(tree, bad)
2899
+
2900
+
2901
+ @predicate
2902
+ def defaultmap_none_listed(tree, version):
2903
+ """A construct with defaultmap(none) (or defaultmap(none: all)) whose
2904
+ lexical extent references a variable declared in the source that is
2905
+ not a named constant, not a DO variable of a loop in the construct,
2906
+ not threadprivate, not in a declare target enter/to/link clause and
2907
+ not listed in a data-environment attribute clause of the directive."""
2908
+ attr_clauses = _EXPLICIT_DSA + ("map",)
2909
+ dt = None
2910
+ out = []
2911
+ for i, d in enumerate(tree.directives):
2912
+ cl = [c for c in _clauses(tree, i, "defaultmap")
2913
+ if _items([c])[:1] == ["none"]
2914
+ and (not c.modifiers or _norm(c.modifiers[0]) == "all")]
2915
+ if not cl:
2916
+ continue
2917
+ if dt is None:
2918
+ dt = {_key(tree, j, _base(x)) for j, e in enumerate(
2919
+ tree.directives) if e.name == "declare target"
2920
+ for x in _dt_items(tree, j, "enter", "to", "link")}
2921
+ dt |= _tp_keys(tree)
2922
+ listed = {_base(x) for x in _items(_clauses(tree, i, *attr_clauses))}
2923
+ if getattr(tree, "lang", "f90") == "c":
2924
+ if any(_key(tree, i, x) not in dt
2925
+ for x in _c_construct_refs(tree, i) - listed):
2926
+ out.append(i)
2927
+ continue
2928
+ body = _body(tree, i)
2929
+ dovars = {m.group(1) for _, st in body if (m := _DO_VAR.match(st))}
2930
+ for _, st in body:
2931
+ names = set(re.findall(r"(?<![%\w])([a-z_]\w*)",
2932
+ _STRING.sub("", st)))
2933
+ if any(tree.attribute(i, x) is not None
2934
+ and not any((s, x) in tree.params
2935
+ for s in tree.assoc[i].scope)
2936
+ and _key(tree, i, x) not in dt
2937
+ for x in names - listed - dovars):
2938
+ out.append(i)
2939
+ break
2940
+ return out
2941
+
2942
+
2943
+ _ALLOC_PRIVATE = ("private", "firstprivate", "lastprivate", "linear",
2944
+ "reduction", "in_reduction", "induction")
2945
+
2946
+
2947
+ @predicate
2948
+ def allocate_needs_private_clause(tree, version):
2949
+ """An allocate list item (not on allocators) that no private,
2950
+ firstprivate, lastprivate, linear, reduction, in_reduction or induction
2951
+ clause of the directive names."""
2952
+ def bad(i):
2953
+ if tree.directives[i].name == "allocators":
2954
+ return False
2955
+ priv = {_base(x) for x in _items(_clauses(tree, i, *_ALLOC_PRIVATE))}
2956
+ return any(_base(x) not in priv
2957
+ for x in _items(_clauses(tree, i, "allocate")))
2958
+ return _each(tree, bad)
2959
+
2960
+
2961
+ def _has_requires(tree, i, clause) -> bool:
2962
+ unit = tree.assoc[i].unit
2963
+ return any(d.name == "requires" and tree.assoc[j].unit == unit
2964
+ and _clauses(tree, j, clause)
2965
+ for j, d in enumerate(tree.directives))
2966
+
2967
+
2968
+ @predicate
2969
+ def allocate_target_allocator(tree, version):
2970
+ """An allocate clause without an allocator modifier on a construct with
2971
+ a target leaf or inside one, without requires dynamic_allocators in
2972
+ the program unit."""
2973
+ def bad(i):
2974
+ cs = [c for c in _clauses(tree, i, "allocate")
2975
+ if not [m for m in c.modifiers
2976
+ if not re.match(r"^align\s*\(", _norm(m))]]
2977
+ in_target = "target" in _leaves(tree, i) or any(
2978
+ "target" in _leaves(tree, a) for a in _ancestors(tree, i))
2979
+ return bool(cs) and in_target and not _has_requires(
2980
+ tree, i, "dynamic_allocators")
2981
+ return _each(tree, bad)
2982
+
2983
+
2984
+ _PREDEFINED_ALLOCATORS = {
2985
+ "omp_default_mem_alloc", "omp_large_cap_mem_alloc", "omp_const_mem_alloc",
2986
+ "omp_high_bw_mem_alloc", "omp_low_lat_mem_alloc", "omp_cgroup_mem_alloc",
2987
+ "omp_pteam_mem_alloc", "omp_thread_mem_alloc", "omp_null_allocator"}
2988
+ _PREDEFINED_MEMSPACES = {
2989
+ "omp_default_mem_space", "omp_large_cap_mem_space",
2990
+ "omp_const_mem_space", "omp_high_bw_mem_space", "omp_low_lat_mem_space",
2991
+ "omp_null_mem_space"}
2992
+
2993
+
2994
+ @predicate
2995
+ def uses_allocators_not_in_other_clauses(tree, version):
2996
+ """A uses_allocators allocator that is also in a data-sharing or map
2997
+ clause of the directive."""
2998
+ def bad(i):
2999
+ al = {_base(x) for x in _items(_clauses(tree, i, "uses_allocators"))}
3000
+ return bool(al & {_base(x) for x in _items(
3001
+ _clauses(tree, i, *(_DSA + ("map",))))})
3002
+ return _each(tree, bad)
3003
+
3004
+
3005
+ def _modifiers(c) -> list[str]:
3006
+ """The modifiers of a clause other than directive-name modifiers."""
3007
+ return [m for m in c.modifiers if _compact(_norm(m)) not in _names()]
3008
+
3009
+
3010
+ @predicate
3011
+ def uses_allocators_predefined_no_modifiers(tree, version):
3012
+ """uses_allocators of a predefined allocator with modifiers (a
3013
+ directive-name modifier aside)."""
3014
+ return _each(tree, lambda i: any(
3015
+ _modifiers(c) and set(_items([c])) & _PREDEFINED_ALLOCATORS
3016
+ for c in _clauses(tree, i, "uses_allocators")))
3017
+
3018
+
3019
+ @predicate
3020
+ def uses_allocators_predefined_memspace(tree, version):
3021
+ """A memspace(...) modifier whose argument is not a predefined memory
3022
+ space name."""
3023
+ def bad(i):
3024
+ for c in _clauses(tree, i, "uses_allocators"):
3025
+ for m in c.modifiers:
3026
+ mm = re.fullmatch(r"memspace\s*\((.*)\)", _norm(m))
3027
+ if mm and mm.group(1) not in _PREDEFINED_MEMSPACES:
3028
+ return True
3029
+ return False
3030
+ return _each(tree, bad)
3031
+
3032
+
3033
+ @predicate
3034
+ def uses_allocators_traits_constant(tree, version):
3035
+ """A traits(t) modifier (5.0: an allocator(t) item) whose t is declared
3036
+ in the source as a variable that is not a named constant."""
3037
+ def bad(i):
3038
+ for c in _clauses(tree, i, "uses_allocators"):
3039
+ for m in c.modifiers:
3040
+ mm = re.fullmatch(r"traits\s*\(([a-z_]\w*)\)", _norm(m)) \
3041
+ or (version == 50 and re.fullmatch(
3042
+ r"[a-z_]\w*\(([a-z_]\w*)\)", _norm(m)))
3043
+ if mm and tree.attribute(i, mm.group(1)) is not None and \
3044
+ not any((s, mm.group(1)) in tree.params
3045
+ for s in tree.assoc[i].scope) and \
3046
+ not _c_const(tree, i, mm.group(1)):
3047
+ return True
3048
+ return False
3049
+ return _each(tree, bad)
3050
+
3051
+
3052
+ def _c_const(tree, i, name) -> bool:
3053
+ """A C variable declared const (a constant array of the
3054
+ uses_allocators traits)."""
3055
+ s = _decl_scope(tree, i, name)
3056
+ return s is not None and "const" in tree.extra.get("ctypes", {}).get(
3057
+ (s, name), "").split()
3058
+
3059
+
3060
+ _MEMORY_ORDER = ("seq_cst", "acq_rel", "release", "acquire", "relaxed")
3061
+
3062
+
3063
+ @predicate
3064
+ def requires_mem_order_before_atomic(tree, version):
3065
+ """requires atomic_default_mem_order after an atomic construct without
3066
+ a memory-order clause in the same program unit."""
3067
+ def bad(i):
3068
+ if tree.directives[i].name != "requires" or not _clauses(
3069
+ tree, i, "atomic_default_mem_order"):
3070
+ return False
3071
+ return any(tree.directives[j].name == "atomic"
3072
+ and tree.assoc[j].unit == tree.assoc[i].unit
3073
+ and not _clauses(tree, j, *_MEMORY_ORDER)
3074
+ for j in range(i))
3075
+ return _each(tree, bad)
3076
+
3077
+
3078
+ _DEVICE_GLOBAL = {52: ("reverse_offload", "unified_address",
3079
+ "unified_shared_memory"),
3080
+ 60: ("reverse_offload", "unified_address",
3081
+ "unified_shared_memory", "self_maps")}
3082
+
3083
+
3084
+ @predicate
3085
+ def requires_same_in_all_units(tree, version):
3086
+ """Program units of the source that contain declare target directives
3087
+ or device constructs whose device requirements differ (5.2: some
3088
+ specify reverse_offload, unified_address or unified_shared_memory,
3089
+ some none; 6.0: different sets, self_maps included). A unit inherits
3090
+ the requirements of the modules of the source it uses."""
3091
+ if getattr(tree, "lang", "f90") == "c":
3092
+ return [] # C: one source is one translation unit (5.2 §1.2.2)
3093
+ req_names = _DEVICE_GLOBAL[52 if version < 60 else 60]
3094
+ units, names, reqs, uses = {}, {}, {}, {}
3095
+ for ln, st, scopes in tree.statements:
3096
+ if len(scopes) > 1:
3097
+ units.setdefault(scopes[1], None)
3098
+ top_of = {}
3099
+ for _, _, scopes in tree.statements:
3100
+ for s in scopes[1:]:
3101
+ top_of[s] = scopes[1] if len(scopes) > 1 else None
3102
+ for s, (kind, name) in tree.units.items():
3103
+ if top_of.get(s) == s and kind == "module":
3104
+ names[name] = s
3105
+ for s, mods in tree.use_modules.items():
3106
+ if top_of.get(s):
3107
+ uses.setdefault(top_of[s], set()).update(mods)
3108
+ device = {}
3109
+ for i, d in enumerate(tree.directives):
3110
+ sc = tree.assoc[i].scope
3111
+ top = sc[1] if len(sc) > 1 else None
3112
+ if top is None:
3113
+ continue
3114
+ if d.name == "requires":
3115
+ reqs.setdefault(top, set()).update(
3116
+ c.name for c in d.clauses if c.name in req_names)
3117
+ if d.name == "declare target" or (
3118
+ _leaves(tree, i)[0] in _DEVICE_CONSTRUCTS):
3119
+ device.setdefault(top, i)
3120
+
3121
+ def effective(top):
3122
+ out = set(reqs.get(top, ()))
3123
+ for m in uses.get(top, ()):
3124
+ if m in names:
3125
+ out |= reqs.get(names[m], set())
3126
+ return out
3127
+ sets = {top: effective(top) for top in device}
3128
+ if version < 60:
3129
+ kinds = {bool(v) for v in sets.values()}
3130
+ if len(kinds) > 1:
3131
+ return sorted(device[t] for t, v in sets.items() if not v)
3132
+ return []
3133
+ if len({frozenset(v) for v in sets.values()}) > 1:
3134
+ common = max(sets.values(), key=len)
3135
+ return sorted(device[t] for t, v in sets.items() if v != common)
3136
+ return []
3137
+
3138
+
3139
+ def _dep_type(c) -> str | None:
3140
+ kinds = {"in", "out", "inout", "mutexinoutset", "inoutset", "depobj",
3141
+ "source", "sink"}
3142
+ return next((m for m in map(_norm, c.modifiers) if m in kinds), None)
3143
+
3144
+
3145
+ @predicate
3146
+ def depend_all_memory_out_inout(tree, version):
3147
+ """omp_all_memory in a depend clause whose type is not out or inout."""
3148
+ return _each(tree, lambda i: any(
3149
+ "omp_all_memory" in _items([c]) and _dep_type(c) not in ("out",
3150
+ "inout")
3151
+ for c in _clauses(tree, i, "depend")))
3152
+
3153
+
3154
+ @predicate
3155
+ def depend_depobj_no_section(tree, version):
3156
+ """An array section in a depend clause with the depobj type."""
3157
+ return _each(tree, lambda i: any(
3158
+ _dep_type(c) == "depobj" and any(
3159
+ re.search(r"\(.*:.*\)|\[.*:.*\]", x) for x in _items([c]))
3160
+ for c in _clauses(tree, i, "depend")))
3161
+
3162
+
3163
+ @predicate
3164
+ def depend_no_common_block(tree, version):
3165
+ """A common block name (/name/) in a depend clause."""
3166
+ return _each(tree, lambda i: any(
3167
+ x.startswith("/") for x in _items(_clauses(tree, i, "depend"))))
3168
+
3169
+
3170
+ def _zero_length(item: str) -> bool:
3171
+ """An array section with integer literal bounds lb > ub in some
3172
+ dimension (or a literal length of zero is not Fortran syntax; C, Task
3173
+ 20: a literal length of 0)."""
3174
+ for dims in _c_sections(item):
3175
+ if any(len(t) >= 2 and _int(t[1] or "x") == 0 for t in dims):
3176
+ return True
3177
+ m = re.search(r"\((.*)\)$", item)
3178
+ if not m:
3179
+ return False
3180
+ for dim in _split_top(m.group(1), ","):
3181
+ parts = _split_top(dim, ":")
3182
+ if len(parts) >= 2:
3183
+ lo, hi = _int(parts[0] or "x"), _int(parts[1] or "x")
3184
+ if lo is not None and hi is not None and lo > hi:
3185
+ return True
3186
+ return False
3187
+
3188
+
3189
+ @predicate
3190
+ def depend_no_zero_length(tree, version):
3191
+ """A depend list item that is an array section with literal bounds
3192
+ lb > ub (zero length)."""
3193
+ return _each(tree, lambda i: any(
3194
+ _zero_length(x) for x in _items(_clauses(tree, i, "depend"))))
3195
+
3196
+
3197
+ @predicate
3198
+ def taskwait_no_mutexinoutset(tree, version):
3199
+ """taskwait with depend(mutexinoutset: ...)."""
3200
+ return _each(tree, lambda i: tree.directives[i].name == "taskwait" and any(
3201
+ _dep_type(c) == "mutexinoutset" for c in _clauses(tree, i, "depend")))
3202
+
3203
+
3204
+ @predicate
3205
+ def taskwait_nowait_needs_depend(tree, version):
3206
+ """taskwait with nowait and no depend clause."""
3207
+ return _each(tree, lambda i: tree.directives[i].name == "taskwait"
3208
+ and bool(_clauses(tree, i, "nowait"))
3209
+ and not _clauses(tree, i, "depend"))
3210
+
3211
+
3212
+ @predicate
3213
+ def taskwait_replayable_needs_depend(tree, version):
3214
+ """taskwait with replayable and no depend clause (6.0)."""
3215
+ return _each(tree, lambda i: tree.directives[i].name == "taskwait"
3216
+ and bool(_clauses(tree, i, "replayable"))
3217
+ and not _clauses(tree, i, "depend"))
3218
+
3219
+
3220
+ @predicate
3221
+ def depobj_one_locator(tree, version):
3222
+ """A depend clause on depobj with more than one list item."""
3223
+ return _each(tree, lambda i: tree.directives[i].name == "depobj" and any(
3224
+ len(_items([c])) > 1 for c in _clauses(tree, i, "depend")))
3225
+
3226
+
3227
+ @predicate
3228
+ def depobj_update_not_depobj(tree, version):
3229
+ """update(depobj) on a depobj directive."""
3230
+ return _each(tree, lambda i: "depobj" in _items(
3231
+ _clauses(tree, i, "update")))
3232
+
3233
+
3234
+ def _scans(tree, i) -> list[int]:
3235
+ return [c for c in tree.children(i) if tree.directives[c].name == "scan"
3236
+ and _clauses(tree, c, "inclusive", "exclusive")]
3237
+
3238
+
3239
+ @predicate
3240
+ def scan_at_most_one(tree, version):
3241
+ """A construct with more than one scan directive (with inclusive or
3242
+ exclusive) as its separating directives."""
3243
+ return sorted(s for i in range(len(tree.directives))
3244
+ for s in _scans(tree, i)[1:])
3245
+
3246
+
3247
+ @predicate
3248
+ def scan_not_orphaned(tree, version):
3249
+ """A scan directive not directly in the loop nest of a construct with a
3250
+ do or simd leaf."""
3251
+ def bad(i):
3252
+ if tree.directives[i].name != "scan":
3253
+ return False
3254
+ p = tree.parent(i)
3255
+ return p is None or not set(_leaves(tree, p)) & {"do", "for", "simd"}
3256
+ return _each(tree, bad)
3257
+
3258
+
3259
+ @predicate
3260
+ def scan_items_in_inscan_reduction(tree, version):
3261
+ """A scan inclusive/exclusive list item that is not in a
3262
+ reduction(inscan, ...) clause of the enclosing construct."""
3263
+ def bad(i):
3264
+ if tree.directives[i].name != "scan" or tree.parent(i) is None:
3265
+ return False
3266
+ red = set(_items(_reductions(tree, tree.parent(i), "inscan")))
3267
+ return any(x not in red for x in _items(
3268
+ _clauses(tree, i, "inclusive", "exclusive")))
3269
+ return _each(tree, bad)
3270
+
3271
+
3272
+ @predicate
3273
+ def inscan_items_on_scan(tree, version):
3274
+ """A reduction(inscan, ...) list item that no inclusive or exclusive
3275
+ clause of a scan directive in the construct names."""
3276
+ def bad(i):
3277
+ red = set(_items(_reductions(tree, i, "inscan")))
3278
+ if not red:
3279
+ return False
3280
+ on = set().union(*[set(_items(_clauses(tree, s, "inclusive",
3281
+ "exclusive")))
3282
+ for s in _scans(tree, i)])
3283
+ return bool(red - on)
3284
+ return _each(tree, bad)
3285
+
3286
+
3287
+ @predicate
3288
+ def scan_perfectly_nested(tree, version):
3289
+ """A construct with a scan directive whose associated loops are not
3290
+ perfectly nested (intervening statements)."""
3291
+ def bad(i):
3292
+ a, n = tree.assoc[i], _assoc_count(tree, i)
3293
+ return bool(_scans(tree, i)) and n is not None and n > 1 and any(
3294
+ a.intervening[:n - 1])
3295
+ return _each(tree, bad)
3296
+
3297
+
3298
+ @predicate
3299
+ def declare_simd_in_subprogram(tree, version):
3300
+ """declare simd outside the specification part of a subroutine or
3301
+ function (in a program, module or submodule scoping unit, or inside a
3302
+ construct)."""
3303
+ def bad(i):
3304
+ if tree.directives[i].name != "declare simd":
3305
+ return False
3306
+ kind = tree.units.get(tree.assoc[i].scope[-1], ("", None))[0]
3307
+ return tree.parent(i) is not None or kind in (
3308
+ "program", "module", "submodule", "blockdata")
3309
+ return _each(tree, bad)
3310
+
3311
+
3312
+ def _mapper_spec(d) -> tuple:
3313
+ """(mapper identifier, type, variable) of a declare mapper argument
3314
+ (Fortran ``[id:] type :: var``; C ``[id:] type var``, the
3315
+ type with its blanks removed)."""
3316
+ raw = d.args[0] if d.args else ""
3317
+ if "::" not in raw and raw:
3318
+ m = re.match(r"^\s*(?:(\w+)\s*:(?!:))?\s*(.*\S)\s+(\w+)\s*$", raw)
3319
+ if m:
3320
+ return (m.group(1) or "default").lower(), _norm(m.group(2)), \
3321
+ m.group(3).lower()
3322
+ arg = _norm(raw)
3323
+ head, _, var = arg.partition("::")
3324
+ ident, _, typ = head.rpartition(":")
3325
+ return ident or "default", typ, var
3326
+
3327
+
3328
+ @predicate
3329
+ def declare_mapper_no_release_delete(tree, version):
3330
+ """release or delete map-type on a declare mapper map clause (5.2)."""
3331
+ return _each(tree, lambda i: tree.directives[i].name == "declare mapper"
3332
+ and any(set(c.modifiers) & {"release", "delete"}
3333
+ for c in _clauses(tree, i, "map")))
3334
+
3335
+
3336
+ @predicate
3337
+ def declare_mapper_modifier_default(tree, version):
3338
+ """A mapper(m) modifier with m other than default on a declare mapper
3339
+ map clause."""
3340
+ return _each(tree, lambda i: tree.directives[i].name == "declare mapper"
3341
+ and any(_mapper(c) != "default"
3342
+ for c in _clauses(tree, i, "map")))
3343
+
3344
+
3345
+ @predicate
3346
+ def declare_mapper_unique(tree, version):
3347
+ """Two declare mapper directives in one scoping unit with the same
3348
+ mapper identifier and type."""
3349
+ seen, out = set(), []
3350
+ for i, d in enumerate(tree.directives):
3351
+ if d.name == "declare mapper":
3352
+ ident, typ, _ = _mapper_spec(d)
3353
+ key = (tree.assoc[i].scope[-1], ident, typ)
3354
+ if key in seen:
3355
+ out.append(i)
3356
+ seen.add(key)
3357
+ return out
3358
+
3359
+
3360
+ @predicate
3361
+ def declare_mapper_type_not_intrinsic(tree, version):
3362
+ """A declare mapper for an intrinsic type; for an abstract type the
3363
+ source defines (5.0 §2.19.7.3 p.328, 5.2, 6.0); (6.0) for a
3364
+ parameterized derived type the source defines."""
3365
+ def bad(i):
3366
+ if tree.directives[i].name != "declare mapper":
3367
+ return False
3368
+ typ = _mapper_spec(tree.directives[i])[1]
3369
+ if re.match(r"^(?:integer|real|doubleprecision|complex|logical|"
3370
+ r"character)\b", typ):
3371
+ return True
3372
+ if tree.lang != "f90":
3373
+ return False
3374
+ m = re.fullmatch(r"(?:type|class)\(([a-z_]\w*)\)|([a-z_]\w*)", typ)
3375
+ t = _oop(tree)["types"].get(m and (m.group(1) or m.group(2)))
3376
+ return bool(t and (t["abstract"] or (version >= 60 and t["pdt"])))
3377
+ return _each(tree, bad)
3378
+
3379
+
3380
+ @predicate
3381
+ def declare_mapper_maps_var(tree, version):
3382
+ """A declare mapper none of whose map clauses maps the variable or an
3383
+ element of it."""
3384
+ def bad(i):
3385
+ if tree.directives[i].name != "declare mapper":
3386
+ return False
3387
+ var = _mapper_spec(tree.directives[i])[2]
3388
+ return bool(var) and not any(_base(x) == var for x in _items(
3389
+ _clauses(tree, i, "map")))
3390
+ return _each(tree, bad)
3391
+
3392
+
3393
+ # --- loop nest shape --------------------------------------------------------------
3394
+
3395
+ _BRANCH = re.compile(r"^(?:if\s*\(.*\)\s*)?(exit|cycle)\b\s*([a-z_]\w*)?$")
3396
+ _DO_NAME = re.compile(r"^(?:([a-z_]\w*)\s*:\s*)?do\b")
3397
+
3398
+
3399
+ def _branches(tree, i) -> list[tuple]:
3400
+ """(exit|cycle, depth of the DO it applies to, line) in the DO nest of
3401
+ loop directive i; depth 1 is the outermost loop of the nest."""
3402
+ out, stack = [], []
3403
+ for ln, st in _body(tree, i):
3404
+ if _is_do(st):
3405
+ m = _DO_NAME.match(st)
3406
+ stack.append(m.group(1) if m else None)
3407
+ continue
3408
+ if _END_DO.match(st):
3409
+ if stack:
3410
+ stack.pop()
3411
+ continue
3412
+ m = _BRANCH.match(st)
3413
+ if m and stack:
3414
+ name = m.group(2)
3415
+ if name:
3416
+ depth = next((k + 1 for k in range(len(stack) - 1, -1, -1)
3417
+ if stack[k] == name), None)
3418
+ else:
3419
+ depth = len(stack)
3420
+ if depth:
3421
+ out.append((m.group(1), depth, ln))
3422
+ return out
3423
+
3424
+
3425
+ def _n_assoc(tree, i) -> int:
3426
+ a = tree.assoc[i]
3427
+ n = _assoc_count(tree, i) or 1
3428
+ return min(n, a.loop_depth) if a.loop_depth else n
3429
+
3430
+
3431
+ @predicate
3432
+ def exit_from_associated_loop(tree, version):
3433
+ """An EXIT statement that terminates one of the associated loops."""
3434
+ return _each(tree, lambda i: tree.assoc[i].kind == "loop" and any(
3435
+ k == "exit" and d <= _n_assoc(tree, i)
3436
+ for k, d, _ in _branches(tree, i)))
3437
+
3438
+
3439
+ @predicate
3440
+ def cycle_in_intervening_code(tree, version):
3441
+ """A CYCLE statement that applies to an associated loop other than the
3442
+ innermost one (it is in intervening code or branches out of the inner
3443
+ loop body)."""
3444
+ return _each(tree, lambda i: tree.assoc[i].kind == "loop" and any(
3445
+ k == "cycle" and d < _n_assoc(tree, i)
3446
+ for k, d, _ in _branches(tree, i)))
3447
+
3448
+
3449
+ @predicate
3450
+ def loop_var_not_modified(tree, version):
3451
+ """An assignment to the iteration variable of an associated loop, or a
3452
+ nested DO loop with that variable, in the loop nest."""
3453
+ def bad(i):
3454
+ vs = _assoc_vars(tree, i)
3455
+ if not vs:
3456
+ return False
3457
+ if getattr(tree, "lang", "f90") == "c":
3458
+ return _c_loop_var_modified(tree, i, vs)
3459
+ dos = [m.group(1) for _, st in _body(tree, i)
3460
+ if (m := _DO_VAR.match(st))]
3461
+ if any(dos.count(v) > 1 for v in vs):
3462
+ return True
3463
+ return any(re.match(rf"^{v}\s*=(?!=)", st)
3464
+ for _, st in _body(tree, i) for v in vs)
3465
+ return _each(tree, bad)
3466
+
3467
+
3468
+ def _c_loop_var_modified(tree, i, vs) -> bool:
3469
+ """C: an assignment, increment or decrement of an associated
3470
+ loop's iteration variable in the nest, or a nested for loop on it,
3471
+ other than the associated loops' own headers."""
3472
+ own = set(tree.extra["loops"].get(i, {}).get("lines", ()))
3473
+ for ln, st in _body(tree, i):
3474
+ if st.startswith("for (") and ln in own:
3475
+ continue
3476
+ for v in vs:
3477
+ if re.search(rf"(?<![\w.>*]){v}\s*(?:=(?!=)|\+\+|--|"
3478
+ r"[-+*/%&|^]=|<<=|>>=)|(?:\+\+|--)\s*"
3479
+ rf"{v}\b", st):
3480
+ return True
3481
+ return False
3482
+
3483
+
3484
+ @predicate
3485
+ def loop_var_not_threadprivate(tree, version):
3486
+ """The iteration variable of an associated loop is threadprivate."""
3487
+ def bad(i):
3488
+ vs = _assoc_vars(tree, i)
3489
+ tp = _tp_keys(tree)
3490
+ return bool(vs) and any(_key(tree, i, v) in tp for v in vs)
3491
+ return _each(tree, bad)
3492
+
3493
+
3494
+ @predicate
3495
+ def loop_var_integer(tree, version):
3496
+ """An associated loop whose iteration variable is declared with a type
3497
+ other than integer."""
3498
+ def bad(i):
3499
+ vs = _assoc_vars(tree, i)
3500
+ return bool(vs) and any(_vtype(tree, i, v) not in (None, "integer")
3501
+ for v in vs)
3502
+ return _each(tree, bad)
3503
+
3504
+
3505
+ def _refs(expr: str, names) -> set:
3506
+ return {x for x in re.findall(r"(?<![%\w])([a-z_]\w*)", expr)
3507
+ if x in names}
3508
+
3509
+
3510
+ def _nonrect(tree, i) -> list[tuple]:
3511
+ """(lb refs, ub refs) of each associated loop after the first whose
3512
+ bounds reference an outer associated loop variable."""
3513
+ vs = _assoc_vars(tree, i)
3514
+ if not vs:
3515
+ return []
3516
+ out = []
3517
+ for k in range(1, len(vs)):
3518
+ lb, ub, _ = tree.assoc[i].loop_bounds[k]
3519
+ lr, ur = _refs(lb, vs[:k]), _refs(ub, vs[:k])
3520
+ if lr or ur:
3521
+ out.append((lr, ur))
3522
+ return out
3523
+
3524
+
3525
+ @predicate
3526
+ def nonrect_same_outer_var(tree, version):
3527
+ """A non-rectangular loop whose lb and ub reference different outer
3528
+ loop variables (or more than one)."""
3529
+ return _each(tree, lambda i: any(
3530
+ len(lr | ur) > 1 for lr, ur in _nonrect(tree, i)))
3531
+
3532
+
3533
+ def _nonrect_with(*clauses):
3534
+ def fn(tree, version):
3535
+ return _each(tree, lambda i: bool(_clauses(tree, i, *clauses))
3536
+ and bool(_nonrect(tree, i)))
3537
+ fn.__doc__ = (f"A {' or '.join(clauses)} clause on a construct whose "
3538
+ "associated loops include a non-rectangular loop (a bound "
3539
+ "referencing an outer associated loop variable).")
3540
+ return fn
3541
+
3542
+
3543
+ _register("schedule_rectangular", _nonrect_with("schedule"))
3544
+ _register("dist_schedule_rectangular", _nonrect_with("dist_schedule"))
3545
+ _register("ordered_rectangular", _nonrect_with("ordered"))
3546
+ _register("linear_rectangular", _nonrect_with("linear"))
3547
+ _register("induction_rectangular", _nonrect_with("induction"))
3548
+
3549
+
3550
+ @predicate
3551
+ def ordered_n_within_depth(tree, version):
3552
+ """ordered(n) with an integer literal n greater than the depth of the
3553
+ DO nest that follows."""
3554
+ def bad(i):
3555
+ a, n = tree.assoc[i], _lit(tree, i, "ordered")
3556
+ return a.kind == "loop" and a.loop_depth > 0 and a.depth_known \
3557
+ and n is not None and n > a.loop_depth
3558
+ return _each(tree, bad)
3559
+
3560
+
3561
+ @predicate
3562
+ def ordered_n_ge_collapse(tree, version):
3563
+ """ordered(n) and collapse(m) with integer literals, n < m."""
3564
+ def bad(i):
3565
+ n = _lit(tree, i, "ordered")
3566
+ m = _lit(tree, i, "collapse")
3567
+ return n is not None and m is not None and n < m
3568
+ return _each(tree, bad)
3569
+
3570
+
3571
+ @predicate
3572
+ def ordered_n_no_linear(tree, version):
3573
+ """ordered(n) and a linear clause on one directive (5.2)."""
3574
+ return _each(tree, lambda i: any(c.args for c in _clauses(
3575
+ tree, i, "ordered")) and bool(_clauses(tree, i, "linear")))
3576
+
3577
+
3578
+ @predicate
3579
+ def ordered_n_perfectly_nested(tree, version):
3580
+ """ordered(n) with the n associated loops not perfectly nested
3581
+ (intervening statements; 5.2; 5.0: any ordered clause, the loops the
3582
+ construct is associated with, 5.0 §2.9.2 p.107)."""
3583
+ def bad(i):
3584
+ a, n = tree.assoc[i], _lit(tree, i, "ordered")
3585
+ if version == 50 and n is None and _clauses(tree, i, "ordered"):
3586
+ n = _lit(tree, i, "collapse") or 1
3587
+ return n is not None and a.kind == "loop" and len(a.intervening) \
3588
+ >= n and any(a.intervening[:n - 1])
3589
+ return _each(tree, bad)
3590
+
3591
+
3592
+ @predicate
3593
+ def loop_do_concurrent_clauses(tree, version):
3594
+ """A construct with a loop leaf followed by a DO CONCURRENT loop with a
3595
+ data-sharing clause or collapse (6.0)."""
3596
+ def bad(i):
3597
+ if "loop" not in _leaves(tree, i):
3598
+ return False
3599
+ first = next((st for _, st in _body(tree, i) if _is_do(st)), "")
3600
+ return bool(re.match(r"^(?:[a-z_]\w*\s*:\s*)?do\s*concurrent\b",
3601
+ first)) and bool(_clauses(
3602
+ tree, i, "private", "lastprivate",
3603
+ "reduction", "collapse"))
3604
+ return _each(tree, bad)
3605
+
3606
+
3607
+ @predicate
3608
+ def reduction_section_base(tree, version):
3609
+ """A reduction, in_reduction or induction list item that is an array
3610
+ section whose base is not a name, or with literal zero length (5.2:
3611
+ or a literal stride other than 1)."""
3612
+ def bad_item(x):
3613
+ if _c_sections(x): # C: b[lb:len[:stride]]
3614
+ base = _section_base(x)
3615
+ if base is None or not re.fullmatch(r"[a-z_]\w*", base):
3616
+ return True
3617
+ if _zero_length(x):
3618
+ return True
3619
+ return version < 60 and any(
3620
+ len(t) == 3 and _int(t[2] or "1") not in (None, 1)
3621
+ for dims in _c_sections(x) for t in dims)
3622
+ if not re.search(r"\(.*:.*\)$", x):
3623
+ return False
3624
+ if not re.fullmatch(r"[a-z_]\w*", x.split("(")[0]):
3625
+ return True
3626
+ if _zero_length(x):
3627
+ return True
3628
+ if version < 60:
3629
+ inner = re.search(r"\((.*)\)$", x).group(1)
3630
+ return any(len(p) == 3 and _int(p[2] or "1") not in (None, 1)
3631
+ for p in (_split_top(d, ":")
3632
+ for d in _split_top(inner, ",")))
3633
+ return False
3634
+ return _each(tree, lambda i: any(bad_item(x) for x in _items(
3635
+ _clauses(tree, i, "reduction", "in_reduction", "induction"))))
3636
+
3637
+
3638
+ @predicate
3639
+ def aligned_pointer_or_allocatable(tree, version):
3640
+ """An aligned list item declared without POINTER or ALLOCATABLE and not
3641
+ of type(c_ptr) (5.2)."""
3642
+ def bad(i):
3643
+ for x in map(_base, _items(_clauses(tree, i, "aligned"))):
3644
+ s = _decl_scope(tree, i, x)
3645
+ if s is not None and tree.attribute(i, x) == "plain" \
3646
+ and tree.vartypes.get((s, x)) != "c_ptr":
3647
+ return True
3648
+ return False
3649
+ return _each(tree, bad)
3650
+
3651
+
3652
+ @predicate
3653
+ def aligned_array(tree, version):
3654
+ """An aligned list item declared as a scalar (6.0)."""
3655
+ return _each(tree, lambda i: any(
3656
+ _is_array(tree, i, _base(x)) is False
3657
+ for x in _items(_clauses(tree, i, "aligned"))))
3658
+
3659
+
3660
+ @predicate
3661
+ def in_reduction_matching_item(tree, version):
3662
+ """An in_reduction list item whose closest enclosing construct with the
3663
+ item in task_reduction or reduction(task, ...) uses another reduction
3664
+ identifier, or (in a main program, where no caller can supply one)
3665
+ that no enclosing construct has."""
3666
+ def ident(c):
3667
+ return next((_norm(m) for m in _modifiers(c)
3668
+ if _norm(m) not in ("task", "inscan", "default")), None)
3669
+
3670
+ def bad(i):
3671
+ for c in _clauses(tree, i, "in_reduction"):
3672
+ for x in _items([c]):
3673
+ found = None
3674
+ for p in _ancestors(tree, i):
3675
+ for oc in _clauses(tree, p, "task_reduction",
3676
+ "reduction"):
3677
+ if (oc.name == "task_reduction"
3678
+ or "task" in oc.modifiers) \
3679
+ and x in _items([oc]):
3680
+ found = oc
3681
+ if found:
3682
+ break
3683
+ if found is not None and ident(found) != ident(c):
3684
+ return True
3685
+ kind = tree.units.get(tree.assoc[i].scope[-1],
3686
+ ("", None))[0]
3687
+ if found is None and kind == "program":
3688
+ return True
3689
+ return False
3690
+ return _each(tree, bad)
3691
+
3692
+
3693
+ # --- Second pass ------------------------------------------------------------
3694
+
3695
+ _LIST_CLAUSES = set(_UNIQUE_LISTS) | {"map", "depend", "use_device_ptr",
3696
+ "use_device_addr", "is_device_ptr",
3697
+ "has_device_addr", "in_reduction",
3698
+ "task_reduction", "induction"}
3699
+
3700
+
3701
+ def _c_sections(x: str) -> list[list[list[str]]]:
3702
+ """C array sections: per [..] subscript with a ':', its
3703
+ [lb, length[, stride]] texts, as one dimension list."""
3704
+ dims = [_split_top(m.group(1), ":")
3705
+ for m in re.finditer(r"\[([^\[\]]*)\]", x)]
3706
+ return [dims] if any(len(d) > 1 for d in dims) else []
3707
+
3708
+
3709
+ def _sections(x: str) -> list[list[str]]:
3710
+ """The subscript triplets (lb, ub[, stride]) of the parenthesised parts
3711
+ of a list item that contain a ':'."""
3712
+ out = []
3713
+ for m in re.finditer(r"\(([^()]*(?:\([^()]*\)[^()]*)*)\)", x):
3714
+ dims = _split_top(m.group(1), ",")
3715
+ if any(":" in d for d in dims):
3716
+ out.append([_split_top(d, ":") for d in dims])
3717
+ return out
3718
+
3719
+
3720
+ @predicate
3721
+ def section_stride_positive(tree, version):
3722
+ """An array section with an integer literal stride less than one."""
3723
+ return _each(tree, lambda i: any(
3724
+ len(t) == 3 and _int(t[2] or "1") is not None and _int(t[2]) < 1
3725
+ for c in _clauses(tree, i, end=True) if c.name in _LIST_CLAUSES
3726
+ for x in _items([c]) for dims in _sections(x) for t in dims))
3727
+
3728
+
3729
+ @predicate
3730
+ def section_last_part_ref(tree, version):
3731
+ """A list item with a section subscript in a part-ref other than the
3732
+ last (a(1:n)%c)."""
3733
+ return _each(tree, lambda i: any(
3734
+ re.search(r"\([^()]*:[^()]*\)\s*%", x)
3735
+ for c in _clauses(tree, i, end=True) if c.name in _LIST_CLAUSES
3736
+ for x in _items([c])))
3737
+
3738
+
3739
+ @predicate
3740
+ def map_section_contiguous(tree, version):
3741
+ """A map list item that is an array section with an integer literal
3742
+ stride other than 1 (not contiguous)."""
3743
+ return _each(tree, lambda i: _map_construct(tree, i) and any(
3744
+ len(t) == 3 and _int(t[2] or "1") not in (None, 1)
3745
+ for x in _items(_clauses(tree, i, "map"))
3746
+ for dims in _sections(x) + _c_sections(x) for t in dims))
3747
+
3748
+
3749
+ def _iterator_values(spec: str, c_lang: bool, consts: dict | None = None
3750
+ ) -> tuple[str, int | None]:
3751
+ """(identifier, number of values) of one iterator-specifier
3752
+ ``[type] id = begin:end[:step]`` (None when a bound is not an integer
3753
+ literal). C/C++ ranges exclude the end, Fortran ranges include it
3754
+ (5.2 §3.2.6 p.67)."""
3755
+ m = re.match(r"\s*(?:[\w\s:*]*?(?:::)?\s+)?([a-z_]\w*)\s*=\s*(.+)$",
3756
+ spec.strip(), re.I)
3757
+ if not m:
3758
+ return "", None
3759
+ parts = _split_top(m.group(2), ":")
3760
+ nums = [_int(x) if _int(x) is not None else
3761
+ (consts or {}).get(x.strip()) for x in parts]
3762
+ if len(parts) not in (2, 3) or any(n is None for n in nums):
3763
+ return m.group(1).lower(), None
3764
+ b, e = nums[0], nums[1]
3765
+ s = nums[2] if len(nums) == 3 else 1
3766
+ if s == 0:
3767
+ return m.group(1).lower(), None
3768
+ if c_lang:
3769
+ n = max(0, -(-(e - b) // s)) if s > 0 else max(0, -(-(b - e) // -s))
3770
+ else:
3771
+ n = max(0, (e - b) // s + 1) if s > 0 else max(0, (b - e) // -s + 1)
3772
+ return m.group(1).lower(), n
3773
+
3774
+
3775
+ @predicate
3776
+ def map_iterator_shared_containing_array(tree, version):
3777
+ """A map clause whose iterator modifier gives two or more values (integer
3778
+ literal bounds) to an identifier used only in the subscript applied to a
3779
+ named variable: every instance has that variable as containing array
3780
+ (or base pointer), 5.2 §5.8.3 p.156."""
3781
+ def bad(i):
3782
+ for c in _clauses(tree, i, "map"):
3783
+ for mod in c.modifiers:
3784
+ m = re.match(r"\s*iterator\s*\((.*)\)\s*$", mod, re.I | re.S)
3785
+ if not m:
3786
+ continue
3787
+ c_lang = getattr(tree, "lang", "") == "c"
3788
+ multi = set()
3789
+ for spec in _split_top(m.group(1), ","):
3790
+ macros = tree.extra.get("macros") or {}
3791
+ ident, n = _iterator_values(
3792
+ spec, c_lang, {**{k.lower(): v for k, v in
3793
+ macros.items()}, **macros})
3794
+ if ident and n is not None and n >= 2:
3795
+ multi.add(ident)
3796
+ for x in c.args:
3797
+ x = _norm(x)
3798
+ mm = (re.fullmatch(r"([a-z_]\w*)\s*\[([^\[\]]*)\]", x)
3799
+ or re.fullmatch(r"([a-z_]\w*)\s*\(([^()]*)\)", x))
3800
+ if mm and multi & set(re.findall(r"[a-z_]\w*",
3801
+ mm.group(2))):
3802
+ return True
3803
+ return False
3804
+ return _each(tree, bad)
3805
+
3806
+
3807
+ @predicate
3808
+ def task_reduction_item_needs_in_reduction(tree, version):
3809
+ """A task construct whose body references an item of a reduction clause
3810
+ of the innermost enclosing worksharing or parallel construct (a leaf
3811
+ of it) without naming it in an in_reduction clause."""
3812
+ def bad(i):
3813
+ if tree.directives[i].name != "task":
3814
+ return False
3815
+ hit = None
3816
+ for p, k, leaf in _outer_leaf_slots(tree, i):
3817
+ if leaf == "parallel" or _ws_leaf(tree, p, leaf):
3818
+ hit = (p, k)
3819
+ break
3820
+ if leaf in ("teams", "target"):
3821
+ return False
3822
+ if hit is None:
3823
+ return False
3824
+ red = {_base(x) for x in _on_leaf(tree, hit[0], hit[1], version,
3825
+ ("reduction",))}
3826
+ red -= {_base(x) for x in _items(_clauses(tree, i, "in_reduction"))}
3827
+ if not red:
3828
+ return False
3829
+ names = set()
3830
+ for _, st in _body(tree, i):
3831
+ names |= set(re.findall(r"(?<![%\w])([a-z_]\w*)",
3832
+ _STRING.sub("", st)))
3833
+ names |= {_base(x) for x in _items(_clauses(tree, i))}
3834
+ return bool(red & names)
3835
+ return _each(tree, bad)
3836
+
3837
+
3838
+ @predicate
3839
+ def ordered_simd_threads_in_worksharing_simd(tree, version):
3840
+ """A block-associated ordered construct with both simd and threads
3841
+ whose closest enclosing construct has no simd leaf, or has one but no
3842
+ do leaf of its own or of its closest enclosing construct (5.2)."""
3843
+ def bad(i):
3844
+ d = tree.directives[i]
3845
+ if d.name != "ordered (Block)" or not (
3846
+ _clauses(tree, i, "simd") and _clauses(tree, i, "threads")):
3847
+ return False
3848
+ p = tree.parent(i)
3849
+ if p is None:
3850
+ return False
3851
+ ls = set(_leaves(tree, p))
3852
+ if "simd" not in ls:
3853
+ return True
3854
+ if ls & {"do", "for"}:
3855
+ return False
3856
+ q = tree.parent(p)
3857
+ return q is None or not set(_leaves(tree, q)) & {"do", "for"}
3858
+ return _each(tree, bad)
3859
+
3860
+
3861
+ _DECLARATIVE = {"declare target", "declare simd", "declare mapper",
3862
+ "declare reduction", "declare_induction", "declare variant",
3863
+ "requires", "threadprivate", "groupprivate", "allocate"}
3864
+ _USE_IMPLICIT = re.compile(r"^(?:use\b|import\b|implicit\b)")
3865
+
3866
+
3867
+ @predicate
3868
+ def declarative_after_use(tree, version):
3869
+ """A declarative directive (5.2: requires) before a USE, IMPORT or
3870
+ IMPLICIT statement of its own scoping unit."""
3871
+ names = _DECLARATIVE if version >= 60 else {"requires"}
3872
+
3873
+ def bad(i):
3874
+ if tree.directives[i].name not in names:
3875
+ return False
3876
+ a = tree.assoc[i]
3877
+ return any(ln > a.line and sc == a.scope and _USE_IMPLICIT.match(st)
3878
+ for ln, st, sc in tree.statements)
3879
+ return _each(tree, bad)
3880
+
3881
+
3882
+ @predicate
3883
+ def named_constant_list_item(tree, version):
3884
+ """A named constant (PARAMETER, visible from the directive) as a list
3885
+ item of a clause other than shared or firstprivate (5.2: of a
3886
+ data-sharing attribute clause; 6.0: of any clause)."""
3887
+ clauses = set(_DSA) if version < 60 else _LIST_CLAUSES
3888
+
3889
+ def bad(i):
3890
+ for c in _clauses(tree, i, end=True):
3891
+ if c.name not in clauses or c.name in ("shared", "firstprivate"):
3892
+ continue
3893
+ for x in map(_base, _items([c])):
3894
+ s = _decl_scope(tree, i, x)
3895
+ if s is not None and (s, x) in tree.params:
3896
+ return True
3897
+ return False
3898
+ return _each(tree, bad)
3899
+
3900
+
3901
+ # --- Fortran derived types and type-bound procedures (fortran-oop) ---------------
3902
+ # The scanner's facts (``types``,
3903
+ # ``vartypes``, ``vtypes``, ``units``, ``statements``) plus a pass over the
3904
+ # statements of each derived-type definition: its parent (EXTENDS), whether
3905
+ # it is abstract or parameterized, and its type-bound procedure part
3906
+ # (specific bindings, generic bindings, final subroutines). Types are
3907
+ # matched by name across the source (the programs judged here define each
3908
+ # type once); a binding whose procedure is not defined in the source is
3909
+ # never judged.
3910
+
3911
+ _TYPE_HEAD = re.compile(r"^type\b(?!\s*\()(?!\s*is\b)\s*(,[^:]*)?(?:::)?\s*"
3912
+ r"([a-z_]\w*)\s*(\(.*\))?$")
3913
+ _TBP_SPECIFIC = re.compile(r"^procedure\b\s*(?:\(\s*\w*\s*\))?\s*"
3914
+ r"(?:,([^:]*))?(?:::)?\s*(.*)$")
3915
+ _TBP_GENERIC = re.compile(r"^generic\b\s*(?:,[^:]*)?::\s*(.+?)\s*=>\s*(.*)$")
3916
+ _TBP_FINAL = re.compile(r"^final\b\s*(?:::)?\s*(.*)$")
3917
+ _GENERIC_IFACE = re.compile(r"^interface\s+([a-z_]\w*)\s*$")
3918
+ _PROC_POINTER = re.compile(r"^procedure\s*\([^)]*\)\s*,([^:]*)::\s*(.*)$")
3919
+ _CHAIN = r"[a-z_]\w*(?:\s*\([^()]*\))?(?:\s*%\s*[a-z_]\w*(?:\s*\([^()]*\))?)*"
3920
+ _TBP_REF = re.compile(rf"({_CHAIN})\s*%\s*([a-z_]\w*)\s*(?=\(|$)")
3921
+ _ASSIGN_TO = re.compile(rf"^({_CHAIN})\s*=(?![=>])")
3922
+ _ANY_CHAIN = re.compile(_CHAIN)
3923
+ _LOCAL_DECL = re.compile(r"^(type|class)\s*\(\s*([a-z_]\w*)\s*\)(.*)$")
3924
+
3925
+
3926
+ def _oop(tree: ProgramTree) -> dict:
3927
+ """{type name: {parent, abstract, pdt, comps, bindings, generics,
3928
+ finals}}, procedure names defined in the source, generic interface
3929
+ names and procedure pointer names (cached in tree.extra)."""
3930
+ if "oop" in tree.extra:
3931
+ return tree.extra["oop"]
3932
+ types, generic_names, proc_ptrs = {}, set(), set()
3933
+ cur, tbp = None, False
3934
+ for _, st, _sc in tree.statements:
3935
+ m = _TYPE_HEAD.match(st)
3936
+ if cur is None and m:
3937
+ attrs = m.group(1) or ""
3938
+ par = re.search(r"\bextends\s*\(\s*([a-z_]\w*)\s*\)", attrs)
3939
+ cur, tbp = m.group(2), False
3940
+ types[cur] = {"parent": par.group(1) if par else None,
3941
+ "abstract": bool(re.search(r"\babstract\b",
3942
+ attrs)),
3943
+ "pdt": bool(m.group(3)), "comps": {},
3944
+ "bindings": {}, "generics": {}, "finals": []}
3945
+ continue
3946
+ if cur is not None:
3947
+ if _END_TYPE.match(st):
3948
+ cur = None
3949
+ elif st == "contains":
3950
+ tbp = True
3951
+ elif tbp and (g := _TBP_GENERIC.match(st)):
3952
+ key = _norm(g.group(1))
3953
+ types[cur]["generics"].setdefault(key, []).extend(
3954
+ _norm(x) for x in _split_statements_commas(g.group(2)))
3955
+ elif tbp and (f := _TBP_FINAL.match(st)):
3956
+ types[cur]["finals"] += [_norm(x) for x in
3957
+ _split_statements_commas(f.group(1))]
3958
+ elif tbp and (p := _TBP_SPECIFIC.match(st)):
3959
+ deferred = "deferred" in (p.group(1) or "")
3960
+ for ent in _split_statements_commas(p.group(2)):
3961
+ b, _, impl = ent.partition("=>")
3962
+ b, impl = _norm(b), _norm(impl) or _norm(b)
3963
+ if b:
3964
+ types[cur]["bindings"][b] = None if deferred \
3965
+ else impl
3966
+ continue
3967
+ g = _GENERIC_IFACE.match(st)
3968
+ if g:
3969
+ generic_names.add(g.group(1))
3970
+ pp = _PROC_POINTER.match(st)
3971
+ if pp and re.search(r"\bpointer\b", pp.group(1)):
3972
+ proc_ptrs |= set(_entities(pp.group(2)))
3973
+ for (_scope, tname), comps in tree.types.items():
3974
+ if tname in types:
3975
+ types[tname]["comps"].update(comps)
3976
+ procs = {}
3977
+ for uid, (kind, name) in tree.units.items():
3978
+ if kind in ("subroutine", "function") and name:
3979
+ procs.setdefault(name, []).append(uid)
3980
+ out = {"types": types, "procs": procs, "generic_names": generic_names,
3981
+ "proc_pointers": proc_ptrs}
3982
+ tree.extra["oop"] = out
3983
+ return out
3984
+
3985
+
3986
+ def _ancestors_of(types: dict, t: str) -> list[str]:
3987
+ out = []
3988
+ while t in types and t not in out:
3989
+ out.append(t)
3990
+ t = types[t]["parent"]
3991
+ return out
3992
+
3993
+
3994
+ def _descendants_of(types: dict, t: str) -> list[str]:
3995
+ return [d for d in types if d != t and t in _ancestors_of(types, d)]
3996
+
3997
+
3998
+ def _scoped(table: dict, scopes: tuple, name: str):
3999
+ """The entry of `name` declared in the innermost of `scopes` that
4000
+ declares it (a local declaration; no USE shadowing check: a name
4001
+ declared in a scoping unit cannot also be use-associated there)."""
4002
+ for sid in reversed(scopes):
4003
+ if (sid, name) in table:
4004
+ return table[(sid, name)]
4005
+ return None
4006
+
4007
+
4008
+ def _chain_type(tree, oop, scopes, chain: str):
4009
+ """(type name, polymorphic) of a designator `a%b(i)%c`, None when a
4010
+ name on the path is not a derived-type entity the source defines."""
4011
+ parts = [re.sub(r"\(.*", "", x).strip()
4012
+ for x in re.sub(r"\([^()]*\)", "", chain).split("%")]
4013
+ t = _scoped(tree.vartypes, scopes, parts[0])
4014
+ poly = _scoped(tree.vtypes, scopes, parts[0]) == "class"
4015
+ for comp in parts[1:]:
4016
+ found = None
4017
+ for a in _ancestors_of(oop["types"], t or ""):
4018
+ if comp in oop["types"][a]["comps"]:
4019
+ found = oop["types"][a]["comps"][comp]
4020
+ break
4021
+ if found is None:
4022
+ return None
4023
+ t, poly = found[1], False
4024
+ return (t, poly) if t in oop["types"] else None
4025
+
4026
+
4027
+ def _binding_procs(oop, t: str, b: str, poly: bool, seen=None) -> set:
4028
+ """Procedures a reference to binding (or generic) `b` of an object of
4029
+ declared type `t` may invoke: the inherited or own binding, and, for a
4030
+ polymorphic object, every override in an extension of `t`."""
4031
+ seen = set() if seen is None else seen
4032
+ if (t, b) in seen:
4033
+ return set()
4034
+ seen.add((t, b))
4035
+ types, out = oop["types"], set()
4036
+ for a in _ancestors_of(types, t):
4037
+ if b in types[a]["bindings"]:
4038
+ if types[a]["bindings"][b]:
4039
+ out.add(types[a]["bindings"][b])
4040
+ break
4041
+ if b in types[a]["generics"]:
4042
+ for sp in types[a]["generics"][b]:
4043
+ out |= _binding_procs(oop, t, sp, poly, seen)
4044
+ break
4045
+ if poly:
4046
+ for d in _descendants_of(types, t):
4047
+ impl = types[d]["bindings"].get(b)
4048
+ if impl:
4049
+ out.add(impl)
4050
+ return out
4051
+
4052
+
4053
+ def _finals(oop, t: str) -> set:
4054
+ return {f for a in _ancestors_of(oop["types"], t)
4055
+ for f in oop["types"][a]["finals"]}
4056
+
4057
+
4058
+ _OPERATOR = re.compile(r"^operator\((.+)\)$")
4059
+
4060
+
4061
+ def _stmt_accesses(tree, oop, st: str, scopes: tuple) -> set:
4062
+ """Type-bound procedures a statement accesses: binding references,
4063
+ a defined assignment to a derived-type designator, a defined
4064
+ operator on one, and the final subroutines of a local derived-type
4065
+ object it declares."""
4066
+ out = set()
4067
+ for m in _TBP_REF.finditer(st):
4068
+ ty = _chain_type(tree, oop, scopes, m.group(1))
4069
+ if ty:
4070
+ out |= _binding_procs(oop, ty[0], m.group(2), ty[1])
4071
+ a = _ASSIGN_TO.match(st)
4072
+ if a:
4073
+ ty = _chain_type(tree, oop, scopes, a.group(1))
4074
+ if ty:
4075
+ out |= _binding_procs(oop, ty[0], "assignment(=)", ty[1])
4076
+ for m in _ANY_CHAIN.finditer(st):
4077
+ ty = _chain_type(tree, oop, scopes, m.group(0))
4078
+ if not ty:
4079
+ continue
4080
+ for a_ in _ancestors_of(oop["types"], ty[0]):
4081
+ for g in oop["types"][a_]["generics"]:
4082
+ op = _OPERATOR.match(g)
4083
+ if op and op.group(1) in st.replace(m.group(0), " "):
4084
+ out |= _binding_procs(oop, ty[0], g, ty[1])
4085
+ d = _LOCAL_DECL.match(st)
4086
+ attrs = _norm(d.group(3).split("::")[0]) if d else ""
4087
+ if d and "pointer" not in attrs and not re.search(
4088
+ r"intent\(in(?:out)?\)", attrs):
4089
+ # a local object or an INTENT(OUT) dummy is finalized (F2018
4090
+ # 7.5.6.3); an INTENT(IN)/(INOUT) dummy or a pointer is not
4091
+ out |= _finals(oop, d.group(2))
4092
+ return out
4093
+
4094
+
4095
+ def _declared_target_procs(tree) -> set:
4096
+ """Procedure names that appear in a declare target directive: the
4097
+ procedure a declaration-associated directive (no list, no enter/to
4098
+ clause) sits in, and the names of its extended list or enter/to
4099
+ clauses."""
4100
+ out = set()
4101
+ for i, d in enumerate(tree.directives):
4102
+ if d.name != "declare target":
4103
+ continue
4104
+ listed = _dt_items(tree, i, "enter", "to")
4105
+ if listed:
4106
+ out |= {_base(x) for x in listed}
4107
+ else:
4108
+ kind, name = tree.units.get(tree.assoc[i].scope[-1], ("", None))
4109
+ if kind in ("subroutine", "function") and name:
4110
+ out.add(name)
4111
+ return out
4112
+
4113
+
4114
+ _NOT_REGION = {"target data", "target enter data", "target exit data",
4115
+ "target update"}
4116
+
4117
+
4118
+ @predicate
4119
+ def type_bound_declare_target(tree, version):
4120
+ """A type-bound procedure (a specific binding, a generic binding's
4121
+ specific, a defined assignment or operator, a final subroutine)
4122
+ accessed in a target region whose procedure, defined in the source,
4123
+ is in no declare target directive. The region: the statements of
4124
+ target constructs and of the procedures that run on the device (in a
4125
+ declare target directive, referenced from the region, or reached as
4126
+ a type-bound procedure); firstprivate and private items of target
4127
+ constructs reach defined assignments and final subroutines."""
4128
+ if tree.lang != "f90":
4129
+ return []
4130
+ oop = _oop(tree)
4131
+ if not any(t["bindings"] or t["generics"] or t["finals"]
4132
+ for t in oop["types"].values()):
4133
+ return []
4134
+ declared = _declared_target_procs(tree)
4135
+ procs = oop["procs"]
4136
+ by_unit = {}
4137
+ for ln, st, sc in tree.statements:
4138
+ if sc:
4139
+ by_unit.setdefault(sc[-1], []).append((ln, st, sc))
4140
+ roots = [] # (origin directive, statements)
4141
+ for i, d in enumerate(tree.directives):
4142
+ if d.name.split()[0] == "target" and d.name not in _NOT_REGION:
4143
+ body = [(ln, st, sc) for ln, st, sc in tree.statements
4144
+ if _extent(tree, i)[0] < ln <= _extent(tree, i)[1]]
4145
+ roots.append((i, body))
4146
+ for c in _clauses(tree, i, "private", "firstprivate"):
4147
+ for x in _items([c]):
4148
+ ty = _chain_type(tree, oop, tree.assoc[i].scope, x)
4149
+ if ty:
4150
+ acc = _finals(oop, ty[0])
4151
+ if c.name == "firstprivate":
4152
+ acc |= _binding_procs(oop, ty[0],
4153
+ "assignment(=)", ty[1])
4154
+ roots.append((i, [], acc))
4155
+ elif d.name == "declare target":
4156
+ for name in _declared_target_procs_at(tree, i):
4157
+ for uid in procs.get(name, []):
4158
+ roots.append((i, by_unit.get(uid, [])))
4159
+ bad, done = set(), set()
4160
+ work = [(r[0], r[1], r[2] if len(r) > 2 else set()) for r in roots]
4161
+ while work:
4162
+ origin, stmts, extra = work.pop()
4163
+ reached = set(extra)
4164
+ for _ln, st, sc in stmts:
4165
+ reached |= _stmt_accesses(tree, oop, st, sc)
4166
+ for name in re.findall(r"\b([a-z_]\w*)\s*(?=\(|$)", st):
4167
+ if name in procs and not st.startswith(("subroutine",
4168
+ "function")):
4169
+ reached.add(("call", name))
4170
+ for r in reached:
4171
+ called = isinstance(r, tuple)
4172
+ name = r[1] if called else r
4173
+ if not called and name in procs and name not in declared:
4174
+ bad.add(origin)
4175
+ if name in procs and name not in done:
4176
+ done.add(name)
4177
+ for uid in procs[name]:
4178
+ work.append((origin, by_unit.get(uid, []), set()))
4179
+ return sorted(bad)
4180
+
4181
+
4182
+ def _declared_target_procs_at(tree, i: int) -> set:
4183
+ """The procedure names declare target directive i declares (as
4184
+ _declared_target_procs, for one directive)."""
4185
+ listed = _dt_items(tree, i, "enter", "to")
4186
+ if listed:
4187
+ return {_base(x) for x in listed}
4188
+ kind, name = tree.units.get(tree.assoc[i].scope[-1], ("", None))
4189
+ return {name} if kind in ("subroutine", "function") and name else set()
4190
+
4191
+
4192
+ def _type_name_bad(oop, spec: str) -> bool:
4193
+ """A type-name list item that is CLASS(*), TYPE(*) or an abstract
4194
+ type defined in the source."""
4195
+ spec = _norm(spec)
4196
+ if spec in ("class(*)", "type(*)"):
4197
+ return True
4198
+ m = re.fullmatch(r"(?:type|class)\(([a-z_]\w*)\)|([a-z_]\w*)", spec)
4199
+ name = m and (m.group(1) or m.group(2))
4200
+ t = oop["types"].get(name) if name else None
4201
+ return bool(t and t["abstract"])
4202
+
4203
+
4204
+ def _reduction_types(d) -> list[str]:
4205
+ """The type-name list of a declare reduction argument
4206
+ ``id : types : combiner``."""
4207
+ raw = d.args[0] if d.args else ""
4208
+ parts, cur, depth = [], "", 0
4209
+ for ch in raw:
4210
+ if ch == "(":
4211
+ depth += 1
4212
+ elif ch == ")":
4213
+ depth -= 1
4214
+ if ch == ":" and depth == 0 and len(parts) < 2:
4215
+ parts.append(cur)
4216
+ cur = ""
4217
+ continue
4218
+ cur += ch
4219
+ parts.append(cur)
4220
+ return _split_statements_commas(parts[1]) if len(parts) == 3 else []
4221
+
4222
+
4223
+ @predicate
4224
+ def type_name_list_item(tree, version):
4225
+ """A declare reduction type-name list item (and a declare mapper type)
4226
+ that is CLASS(*) or (6.0) TYPE(*), or an abstract type; the abstract
4227
+ type of a declare mapper is declaremapper-clause-004's."""
4228
+ if tree.lang != "f90":
4229
+ return []
4230
+ oop = _oop(tree)
4231
+
4232
+ def bad(i):
4233
+ d = tree.directives[i]
4234
+ if d.name == "declare reduction":
4235
+ return any(_type_name_bad(oop, t) for t in _reduction_types(d))
4236
+ if d.name == "declare mapper":
4237
+ return _norm(_mapper_spec(d)[1]) in ("class(*)", "type(*)")
4238
+ return False
4239
+ return _each(tree, bad)
4240
+
4241
+
4242
+ @predicate
4243
+ def declare_target_procedure_name(tree, version):
4244
+ """A declare target list item that names a generic interface or a
4245
+ procedure pointer of the source."""
4246
+ if tree.lang != "f90":
4247
+ return []
4248
+ oop = _oop(tree)
4249
+ names = oop["generic_names"] | oop["proc_pointers"]
4250
+ return _each(tree, lambda i: tree.directives[i].name == "declare target"
4251
+ and any(_base(x) in names for x in _dt_items(
4252
+ tree, i, "enter", "to", "link", "local")))
4253
+
4254
+
4255
+ # The C rules (catalog/omp/rules.yaml entries with lang: c)
4256
+ from offload.engine import c_rules # noqa: E402,F401