[clang] [OpenACC] Implement `loop` restrictions on `for` loops. (PR #115370)

via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 7 12:35:30 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

OpenACC restricts the contents of a 'for' loop affected by a 'loop' construct without a 'seq'. The loop variable must be integer, pointer, or random-access-iterator, it must monotonically increase/decrease, and the trip count must be computable at runtime before the function.

This patch tries to implement some of these limitations to the best of our ability, though it causes us to be perhaps overly restrictive at the moment. I expect we'll revisit some of these rules/add additional supported forms of loop-variable and 'monotonically increasing' here, but the currently enforced rules are heavily inspired by the OMP implementation here.

---

Patch is 348.94 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/115370.diff


50 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+13) 
- (modified) clang/include/clang/Sema/SemaOpenACC.h (+59-3) 
- (modified) clang/lib/Parse/ParseOpenACC.cpp (+4-3) 
- (modified) clang/lib/Parse/ParseStmt.cpp (+5-1) 
- (modified) clang/lib/Sema/SemaOpenACC.cpp (+447-11) 
- (modified) clang/lib/Sema/TreeTransform.h (+11-9) 
- (modified) clang/test/AST/ast-print-openacc-loop-construct.cpp (+99-99) 
- (modified) clang/test/ParserOpenACC/parse-clauses.c (+240-240) 
- (modified) clang/test/ParserOpenACC/parse-clauses.cpp (+12-12) 
- (modified) clang/test/ParserOpenACC/parse-constructs.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-async-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-attach-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-copy-clause.c (+3-3) 
- (modified) clang/test/SemaOpenACC/compute-construct-copyin-clause.c (+3-3) 
- (modified) clang/test/SemaOpenACC/compute-construct-copyout-clause.c (+3-3) 
- (modified) clang/test/SemaOpenACC/compute-construct-create-clause.c (+3-3) 
- (modified) clang/test/SemaOpenACC/compute-construct-default-clause.c (+2-2) 
- (modified) clang/test/SemaOpenACC/compute-construct-deviceptr-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-firstprivate-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-if-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-no_create-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-num_gangs-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-num_workers-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-present-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-self-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-vector_length-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/compute-construct-wait-clause.c (+1-1) 
- (modified) clang/test/SemaOpenACC/loop-ast.cpp (+59-23) 
- (modified) clang/test/SemaOpenACC/loop-construct-auto_seq_independent-ast.cpp (+64-28) 
- (modified) clang/test/SemaOpenACC/loop-construct-auto_seq_independent-clauses.c (+295-295) 
- (modified) clang/test/SemaOpenACC/loop-construct-collapse-ast.cpp (+106-46) 
- (modified) clang/test/SemaOpenACC/loop-construct-collapse-clause.cpp (+133-115) 
- (modified) clang/test/SemaOpenACC/loop-construct-device_type-ast.cpp (+87-39) 
- (modified) clang/test/SemaOpenACC/loop-construct-device_type-clause.c (+59-59) 
- (modified) clang/test/SemaOpenACC/loop-construct-device_type-clause.cpp (+7-7) 
- (modified) clang/test/SemaOpenACC/loop-construct-gang-ast.cpp (+224-98) 
- (modified) clang/test/SemaOpenACC/loop-construct-gang-clause.cpp (+62-62) 
- (modified) clang/test/SemaOpenACC/loop-construct-private-clause.c (+42-42) 
- (modified) clang/test/SemaOpenACC/loop-construct-private-clause.cpp (+35-35) 
- (modified) clang/test/SemaOpenACC/loop-construct-reduction-ast.cpp (+288-126) 
- (modified) clang/test/SemaOpenACC/loop-construct-reduction-clause.cpp (+42-42) 
- (modified) clang/test/SemaOpenACC/loop-construct-tile-ast.cpp (+170-74) 
- (modified) clang/test/SemaOpenACC/loop-construct-tile-clause.cpp (+105-85) 
- (modified) clang/test/SemaOpenACC/loop-construct-vector-ast.cpp (+214-82) 
- (modified) clang/test/SemaOpenACC/loop-construct-vector-clause.cpp (+34-34) 
- (modified) clang/test/SemaOpenACC/loop-construct-worker-ast.cpp (+181-79) 
- (modified) clang/test/SemaOpenACC/loop-construct-worker-clause.cpp (+42-42) 
- (added) clang/test/SemaOpenACC/loop-construct.cpp (+384) 
- (modified) clang/test/SemaOpenACC/loop-loc-and-stmt.c (+1-1) 
- (modified) clang/test/SemaOpenACC/loop-loc-and-stmt.cpp (+2-2) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c96a3f6d6e157f..952214ddcd0cdc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12735,6 +12735,19 @@ def err_acc_gang_reduction_numgangs_conflict
 def err_reduction_op_mismatch
     : Error<"OpenACC 'reduction' variable must have the same operator in all "
             "nested constructs (%0 vs %1)">;
+def err_acc_loop_variable_type
+    : Error<"loop variable of loop associated with an OpenACC 'loop' construct "
+            "must be of integer, pointer, or random-access-iterator type (is "
+            "%0)">;
+def err_acc_loop_variable
+    : Error<"OpenACC 'loop' construct must have initialization clause in "
+            "canonical form ('var = init' or 'T var = init')">;
+def err_acc_loop_terminating_condition
+    : Error<"OpenACC 'loop' construct must have a terminating condition">;
+def err_acc_loop_not_monotonic
+    : Error<"OpenACC 'loop' variable must monotonically increase or decrease "
+            "('++', '--', or compound assignment)">;
+
 // AMDGCN builtins diagnostics
 def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;
 def note_amdgcn_global_load_lds_size_valid_value : Note<"size must be 1, 2, or 4">;
diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h
index cc5b0c4ece1ebe..53a4a6991ad67a 100644
--- a/clang/include/clang/Sema/SemaOpenACC.h
+++ b/clang/include/clang/Sema/SemaOpenACC.h
@@ -118,6 +118,43 @@ class SemaOpenACC : public SemaBase {
   /// 'loop' clause enforcement, where this is 'blocked' by a compute construct.
   llvm::SmallVector<OpenACCReductionClause *> ActiveReductionClauses;
 
+  // Type to check the info about the 'for stmt'.
+  struct ForStmtBeginChecker {
+    SemaOpenACC &SemaRef;
+    SourceLocation ForLoc;
+    bool IsRangeFor = false;
+    std::optional<const CXXForRangeStmt *> RangeFor = nullptr;
+    const Stmt *Init = nullptr;
+    bool InitChanged = false;
+    std::optional<const Stmt *> Cond = nullptr;
+    std::optional<const Stmt *> Inc = nullptr;
+    // Prevent us from checking 2x, which can happen with collapse & tile.
+    bool AlreadyChecked = false;
+
+    ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc,
+                        std::optional<const CXXForRangeStmt *> S)
+        : SemaRef(SemaRef), ForLoc(ForLoc), IsRangeFor(true), RangeFor(S) {}
+
+    ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc,
+                        const Stmt *I, bool InitChanged,
+                        std::optional<const Stmt *> C,
+                        std::optional<const Stmt *> Inc)
+        : SemaRef(SemaRef), ForLoc(ForLoc), IsRangeFor(false), Init(I),
+          InitChanged(InitChanged), Cond(C), Inc(Inc) {}
+    // Do the checking for the For/Range-For. Currently this implements the 'not
+    // seq' restrictions only, and should be called either if we know we are a
+    // top-level 'for' (the one associated via associated-stmt), or extended via
+    // 'collapse'.
+    void check();
+
+    const ValueDecl *checkInit();
+    void checkCond();
+    void checkInc(const ValueDecl *Init);
+  };
+
+  /// Helper function for checking the 'for' and 'range for' stmts.
+  void ForStmtBeginHelper(SourceLocation ForLoc, ForStmtBeginChecker &C);
+
 public:
   ComputeConstructInfo &getActiveComputeConstructInfo() {
     return ActiveComputeConstructInfo;
@@ -137,6 +174,11 @@ class SemaOpenACC : public SemaBase {
   /// permits us to implement the restriction of no further 'gang', 'vector', or
   /// 'worker' clauses.
   SourceLocation LoopVectorClauseLoc;
+  /// If there is a current 'active' loop construct that does NOT have a 'seq'
+  /// clause on it, this has that source location. This permits us to implement
+  /// the 'loop' restrictions on the loop variable. This can be extended via
+  /// 'collapse', so we need to keep this around for a while.
+  SourceLocation LoopWithoutSeqLoc;
 
   // Redeclaration of the version in OpenACCClause.h.
   using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
@@ -568,8 +610,19 @@ class SemaOpenACC : public SemaBase {
   void ActOnWhileStmt(SourceLocation WhileLoc);
   // Called when we encounter a 'do' statement, before looking at its 'body'.
   void ActOnDoStmt(SourceLocation DoLoc);
+  // Called when we encounter a 'for' statement, before looking at its 'body',
+  // for the 'range-for'. 'ActOnForStmtEnd' is used after the body.
+  void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *OldRangeFor,
+                              const Stmt *RangeFor);
+  void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *RangeFor);
   // Called when we encounter a 'for' statement, before looking at its 'body'.
-  void ActOnForStmtBegin(SourceLocation ForLoc);
+  // 'ActOnForStmtEnd' is used after the body.
+  void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *First,
+                         const Stmt *Second, const Stmt *Third);
+  void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *OldFirst,
+                         const Stmt *First, const Stmt *OldSecond,
+                         const Stmt *Second, const Stmt *OldThird,
+                         const Stmt *Third);
   // Called when we encounter a 'for' statement, after we've consumed/checked
   // the body. This is necessary for a number of checks on the contents of the
   // 'for' statement.
@@ -598,7 +651,9 @@ class SemaOpenACC : public SemaBase {
   /// Called when we encounter an associated statement for our construct, this
   /// should check legality of the statement as it appertains to this Construct.
   StmtResult ActOnAssociatedStmt(SourceLocation DirectiveLoc,
-                                 OpenACCDirectiveKind K, StmtResult AssocStmt);
+                                 OpenACCDirectiveKind K,
+                                 ArrayRef<const OpenACCClause *> Clauses,
+                                 StmtResult AssocStmt);
 
   /// Called after the directive has been completely parsed, including the
   /// declaration group or associated statement.
@@ -712,12 +767,13 @@ class SemaOpenACC : public SemaBase {
     SourceLocation OldLoopGangClauseOnKernelLoc;
     SourceLocation OldLoopWorkerClauseLoc;
     SourceLocation OldLoopVectorClauseLoc;
+    SourceLocation OldLoopWithoutSeqLoc;
     llvm::SmallVector<OpenACCLoopConstruct *> ParentlessLoopConstructs;
     llvm::SmallVector<OpenACCReductionClause *> ActiveReductionClauses;
     LoopInConstructRAII LoopRAII;
 
   public:
-    AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind,
+    AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind, SourceLocation,
                        ArrayRef<const OpenACCClause *>,
                        ArrayRef<OpenACCClause *>);
     void SetCollapseInfoBeforeAssociatedStmt(
diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp
index 51d4dc38c17f67..c29779cc917312 100644
--- a/clang/lib/Parse/ParseOpenACC.cpp
+++ b/clang/lib/Parse/ParseOpenACC.cpp
@@ -1498,14 +1498,15 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() {
     return StmtError();
 
   StmtResult AssocStmt;
-  SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(
-      getActions().OpenACC(), DirInfo.DirKind, {}, DirInfo.Clauses);
+  SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getActions().OpenACC(),
+                                                DirInfo.DirKind, DirInfo.DirLoc,
+                                                {}, DirInfo.Clauses);
   if (doesDirectiveHaveAssociatedStmt(DirInfo.DirKind)) {
     ParsingOpenACCDirectiveRAII DirScope(*this, /*Value=*/false);
     ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind));
 
     AssocStmt = getActions().OpenACC().ActOnAssociatedStmt(
-        DirInfo.StartLoc, DirInfo.DirKind, ParseStatement());
+        DirInfo.StartLoc, DirInfo.DirKind, DirInfo.Clauses, ParseStatement());
   }
 
   return getActions().OpenACC().ActOnEndStmtDirective(
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 9ba3b112254933..f6d787a0c88319 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2360,7 +2360,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
   // OpenACC Restricts a for-loop inside of certain construct/clause
   // combinations, so diagnose that here in OpenACC mode.
   SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
-  getActions().OpenACC().ActOnForStmtBegin(ForLoc);
+  if (ForRangeInfo.ParsedForRangeDecl())
+    getActions().OpenACC().ActOnRangeForStmtBegin(ForLoc, ForRangeStmt.get());
+  else
+    getActions().OpenACC().ActOnForStmtBegin(
+        ForLoc, FirstPart.get(), SecondPart.get().second, ThirdPart.get());
 
   // C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
   // there is no compound stmt.  C90 does not have this clause.  We only do this
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 3073e87af90fa7..b9706400f07202 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -1520,13 +1520,14 @@ void CollectActiveReductionClauses(
 SemaOpenACC::SemaOpenACC(Sema &S) : SemaBase(S) {}
 
 SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
-    SemaOpenACC &S, OpenACCDirectiveKind DK,
+    SemaOpenACC &S, OpenACCDirectiveKind DK, SourceLocation DirLoc,
     ArrayRef<const OpenACCClause *> UnInstClauses,
     ArrayRef<OpenACCClause *> Clauses)
     : SemaRef(S), OldActiveComputeConstructInfo(S.ActiveComputeConstructInfo),
       DirKind(DK), OldLoopGangClauseOnKernelLoc(S.LoopGangClauseOnKernelLoc),
       OldLoopWorkerClauseLoc(S.LoopWorkerClauseLoc),
       OldLoopVectorClauseLoc(S.LoopVectorClauseLoc),
+      OldLoopWithoutSeqLoc(S.LoopWithoutSeqLoc),
       ActiveReductionClauses(S.ActiveReductionClauses),
       LoopRAII(SemaRef, /*PreserveDepth=*/false) {
 
@@ -1548,11 +1549,19 @@ SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
     SemaRef.LoopGangClauseOnKernelLoc = {};
     SemaRef.LoopWorkerClauseLoc = {};
     SemaRef.LoopVectorClauseLoc = {};
+    SemaRef.LoopWithoutSeqLoc = {};
   } else if (DirKind == OpenACCDirectiveKind::Loop) {
     CollectActiveReductionClauses(S.ActiveReductionClauses, Clauses);
     SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
     SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
 
+    // Set the active 'loop' location if there isn't a 'seq' on it, so we can
+    // diagnose the for loops.
+    SemaRef.LoopWithoutSeqLoc = {};
+    if (Clauses.end() ==
+        llvm::find_if(Clauses, llvm::IsaPred<OpenACCSeqClause>))
+      SemaRef.LoopWithoutSeqLoc = DirLoc;
+
     // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
     // construct, the gang clause behaves as follows. ... The region of a loop
     // with a gang clause may not contain another loop with a gang clause unless
@@ -1653,6 +1662,7 @@ SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
   SemaRef.LoopGangClauseOnKernelLoc = OldLoopGangClauseOnKernelLoc;
   SemaRef.LoopWorkerClauseLoc = OldLoopWorkerClauseLoc;
   SemaRef.LoopVectorClauseLoc = OldLoopVectorClauseLoc;
+  SemaRef.LoopWithoutSeqLoc = OldLoopWithoutSeqLoc;
   SemaRef.ActiveReductionClauses.swap(ActiveReductionClauses);
 
   if (DirKind == OpenACCDirectiveKind::Parallel ||
@@ -2573,14 +2583,15 @@ void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) {
   }
 }
 
-void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
-  if (!getLangOpts().OpenACC)
-    return;
+void SemaOpenACC::ForStmtBeginHelper(SourceLocation ForLoc,
+                                     ForStmtBeginChecker &C) {
+  assert(getLangOpts().OpenACC && "Check enabled when not OpenACC?");
 
   // Enable the while/do-while checking.
   LoopInfo.TopLevelLoopSeen = true;
 
   if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
+    C.check();
 
     // OpenACC 3.3 2.9.1:
     // Each associated loop, except the innermost, must contain exactly one loop
@@ -2604,6 +2615,8 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
   }
 
   if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
+    C.check();
+
     if (LoopInfo.CurLevelHasLoopAlready) {
       Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Tile*/ 1;
       assert(TileInfo.ActiveTile && "No tile object?");
@@ -2624,6 +2637,431 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
   LoopInfo.CurLevelHasLoopAlready = false;
 }
 
+namespace {
+bool isValidLoopVariableType(QualType LoopVarTy) {
+  // Just skip if it is dependent, it could be any of the below.
+  if (LoopVarTy->isDependentType())
+    return true;
+
+  // The loop variable must be of integer,
+  if (LoopVarTy->isIntegerType())
+    return true;
+
+  // C/C++ pointer,
+  if (LoopVarTy->isPointerType())
+    return true;
+
+  // or C++ random-access iterator type.
+  if (const auto *RD = LoopVarTy->getAsCXXRecordDecl()) {
+    // Note: Only do CXXRecordDecl because RecordDecl can't be a random access
+    // iterator type!
+
+    // We could either do a lot of work to see if this matches
+    // random-access-iterator, but it seems that just checking that the
+    // 'iterator_category' typedef is more than sufficient. If programmers are
+    // willing to lie about this, we can let them.
+
+    for (const auto *TD :
+         llvm::make_filter_range(RD->decls(), llvm::IsaPred<TypedefNameDecl>)) {
+      const auto *TDND = cast<TypedefNameDecl>(TD)->getCanonicalDecl();
+
+      if (TDND->getName() != "iterator_category")
+        continue;
+
+      // If there is no type for this decl, return false.
+      if (TDND->getUnderlyingType().isNull())
+        return false;
+
+      const CXXRecordDecl *ItrCategoryDecl =
+          TDND->getUnderlyingType()->getAsCXXRecordDecl();
+
+      // If the category isn't a record decl, it isn't the tag type.
+      if (!ItrCategoryDecl)
+        return false;
+
+      auto IsRandomAccessIteratorTag = [](const CXXRecordDecl *RD) {
+        if (RD->getName() != "random_access_iterator_tag")
+          return false;
+        // Checks just for std::random_access_iterator_tag.
+        return RD->getEnclosingNamespaceContext()->isStdNamespace();
+      };
+
+      if (IsRandomAccessIteratorTag(ItrCategoryDecl))
+        return true;
+
+      // We can also support types inherited from the
+      // random_access_iterator_tag.
+      for (CXXBaseSpecifier BS : ItrCategoryDecl->bases()) {
+
+        if (IsRandomAccessIteratorTag(BS.getType()->getAsCXXRecordDecl()))
+          return true;
+      }
+
+      return false;
+    }
+  }
+
+  return false;
+}
+
+} // namespace
+
+void SemaOpenACC::ForStmtBeginChecker::check() {
+  if (!SemaRef.LoopWithoutSeqLoc.isValid())
+    return;
+
+  if (AlreadyChecked)
+    return;
+  AlreadyChecked = true;
+
+  // OpenACC3.3 2.1:
+  // A loop associated with a loop construct that does not have a seq clause
+  // must be written to meet all the following conditions:
+  // - The loop variable must be of integer, C/C++ pointer, or C++ random-access
+  // iterator type.
+  // - The loop variable must monotonically increase or decrease in the
+  // direction of its termination condition.
+  // - The loop trip count must be computable in constant time when entering the
+  // loop construct.
+  //
+  // For a C++ range-based for loop, the loop variable
+  // identified by the above conditions is the internal iterator, such as a
+  // pointer, that the compiler generates to iterate the range.  it is not the
+  // variable declared by the for loop.
+
+  if (IsRangeFor) {
+    // If the range-for is being instantiated and didn't change, don't
+    // re-diagnose.
+    if (!RangeFor.has_value())
+      return;
+    // For a range-for, we can assume everything is 'corect' other than the type
+    // of the iterator, so check that.
+    const DeclStmt *RangeStmt = (*RangeFor)->getBeginStmt();
+
+    // In some dependent contexts, the autogenerated range statement doesn't get
+    // included until instantiation, so skip for now.
+    if (!RangeStmt)
+      return;
+
+    const ValueDecl *InitVar = cast<ValueDecl>(RangeStmt->getSingleDecl());
+    QualType VarType = InitVar->getType().getNonReferenceType();
+    if (!isValidLoopVariableType(VarType)) {
+      SemaRef.Diag(InitVar->getBeginLoc(), diag::err_acc_loop_variable_type)
+          << VarType;
+      SemaRef.Diag(SemaRef.LoopWithoutSeqLoc, diag::note_acc_construct_here)
+          << "loop";
+    }
+    return;
+  }
+
+  // Else we are in normal 'ForStmt', so we can diagnose everything.
+  // We only have to check cond/inc if they have changed, but 'init' needs to
+  // just suppress its diagnostics if it hasn't changed.
+  const ValueDecl *InitVar = checkInit();
+  if (Cond.has_value())
+    checkCond();
+  if (Inc.has_value())
+    checkInc(InitVar);
+}
+const ValueDecl *SemaOpenACC::ForStmtBeginChecker::checkInit() {
+  if (!Init) {
+    if (InitChanged) {
+      SemaRef.Diag(ForLoc, diag::err_acc_loop_variable);
+      SemaRef.Diag(SemaRef.LoopWithoutSeqLoc, diag::note_acc_construct_here)
+          << "loop";
+    }
+    return nullptr;
+  }
+
+  auto DiagLoopVar = [&]() {
+    if (InitChanged) {
+      SemaRef.Diag(Init->getBeginLoc(), diag::err_acc_loop_variable);
+      SemaRef.Diag(SemaRef.LoopWithoutSeqLoc, diag::note_acc_construct_here)
+          << "loop";
+    }
+    return nullptr;
+  };
+
+  if (const auto *ExprTemp = dyn_cast<ExprWithCleanups>(Init))
+    Init = ExprTemp->getSubExpr();
+  if (const auto *E = dyn_cast<Expr>(Init))
+    Init = E->IgnoreParenImpCasts();
+
+  const ValueDecl *InitVar = nullptr;
+
+  if (const auto *BO = dyn_cast<BinaryOperator>(Init)) {
+    // Allow assignment operator here.
+
+    if (!BO->isAssignmentOp())
+      return DiagLoopVar();
+
+    const Expr *LHS = BO->getLHS()->IgnoreParenImpCasts();
+
+    if (const auto *DRE = dyn_cast<DeclRefExpr>(LHS))
+      InitVar = DRE->getDecl();
+  } else if (const auto *DS = dyn_cast<DeclStmt>(Init)) {
+    // Allow T t = <whatever>
+    if (!DS->isSingleDecl())
+      return DiagLoopVar();
+
+    InitVar = dyn_cast<ValueDecl>(DS->getSingleDecl());
+
+    // Ensure we have an initializer, unless this is a record/dependent type.
+
+    if (InitVar) {
+      if (!isa<VarDecl>(InitVar))
+        return DiagLoopVar();
+
+      if (!InitVar->getType()->isRecordType() &&
+          !InitVar->getType()->isDependentType() &&
+          !cast<VarDecl>(InitVar)->hasInit())
+        return DiagLoopVar();
+    }
+  } else if (auto *CE = dyn_cast<CXXOperatorCallExpr>(Init)) {
+    // Allow assignment operator call.
+    if (CE->getOperator() != OO_Equal)
+      return DiagLoopVar();
+
+    const Expr *LHS = CE->getArg(0)->IgnoreParenImpCasts();
+
+    if (auto *DRE = dyn_cast<DeclRefExpr>(LHS)) {
+      InitVar = DRE->getDecl();
+    } else if (auto *ME = dyn_cast<MemberExpr>(LHS)) {
+      if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts()))
+        InitVar = ME->getMemberDecl();
+    }
+  }
+
+  if (!InitVar)
+    return DiagLoopVar();
+
+  InitVar = cast<ValueDecl>(InitVar->getCanonicalDecl());
+  QualType VarType = InitVar->getType().getNonReferenceType();
+
+  // Since we have one, all we need to do is ensure it is the right type.
+  if (!isValidLoopVariableType(VarType)) {
+    if (InitChanged) {
+      SemaRef.Diag(InitVar->getBeginLoc(), diag::err_acc_loop_variable_type)
+          << VarType;
+      SemaRef.Diag(SemaRef.LoopWithoutSeqLoc, diag::note_acc_construct_here)
+          << "loop";
+    }
+    return nullptr;
+  }
+
+  return InitVar;
+}
+void SemaOpenACC::ForStmtBeginChecker::checkCond() {
+  if (!*Cond) {
+    SemaRef.Diag(ForLoc, diag::err_acc_loop_terminating_condition);
+    SemaRef.Diag(SemaRef.LoopWithoutSeqLoc, diag::note_acc_construct_here)
+        << "loop";
+  }
+  // Nothing else to do here.  we could probably do some additional work to look
+  // into the termination condition, but that error-prone.  For now, we don't
+  // implement anything other than 'there is a termination condition', and if
+  // codegen/MLIR comes up with some necessary restrictions, we can implement
+  // them here.
+}
+
+void SemaOpenACC::ForStmtBeginChecker::checkInc(const ValueDecl *Init) {
+
+  if (!*Inc) {
+    SemaRef.Diag(ForLoc, diag...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/115370


More information about the cfe-commits mailing list