[clang] 2c75bda - [OpenACC] Split up SemaOpenACC.cpp

via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 15 13:51:15 PST 2025


Author: erichkeane
Date: 2025-01-15T13:51:05-08:00
New Revision: 2c75bda42605b620f8450e44a7b6f2db3adc21cb

URL: https://github.com/llvm/llvm-project/commit/2c75bda42605b620f8450e44a7b6f2db3adc21cb
DIFF: https://github.com/llvm/llvm-project/commit/2c75bda42605b620f8450e44a7b6f2db3adc21cb.diff

LOG: [OpenACC] Split up SemaOpenACC.cpp

This file is getting quite large, so this patch splits the 'clause'
specific parts off into its own file to keep them better organized.

Added: 
    clang/lib/Sema/SemaOpenACCClause.cpp

Modified: 
    clang/lib/Sema/CMakeLists.txt
    clang/lib/Sema/SemaOpenACC.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 3241cb53f004ce..a656b5062391ab 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -70,6 +70,7 @@ add_clang_library(clangSema
   SemaObjC.cpp
   SemaObjCProperty.cpp
   SemaOpenACC.cpp
+  SemaOpenACCClause.cpp
   SemaOpenCL.cpp
   SemaOpenMP.cpp
   SemaOverload.cpp

diff  --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 9ded913638fb3e..f5edc0ed36a9a0 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -6,1920 +6,47 @@
 //
 //===----------------------------------------------------------------------===//
 /// \file
-/// This file implements semantic analysis for OpenACC constructs and
-/// clauses.
+/// This file implements semantic analysis for OpenACC constructs, and things
+/// that are not clause specific.
 ///
 //===----------------------------------------------------------------------===//
 
-#include "clang/Sema/SemaOpenACC.h"
-#include "clang/AST/StmtOpenACC.h"
-#include "clang/Basic/DiagnosticSema.h"
-#include "clang/Basic/OpenACCKinds.h"
-#include "clang/Sema/Sema.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-using namespace clang;
-
-namespace {
-bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K,
-                                    SourceLocation StartLoc, bool IsStmt) {
-  switch (K) {
-  default:
-  case OpenACCDirectiveKind::Invalid:
-    // Nothing to do here, both invalid and unimplemented don't really need to
-    // do anything.
-    break;
-  case OpenACCDirectiveKind::ParallelLoop:
-  case OpenACCDirectiveKind::SerialLoop:
-  case OpenACCDirectiveKind::KernelsLoop:
-  case OpenACCDirectiveKind::Parallel:
-  case OpenACCDirectiveKind::Serial:
-  case OpenACCDirectiveKind::Kernels:
-  case OpenACCDirectiveKind::Loop:
-  case OpenACCDirectiveKind::Data:
-  case OpenACCDirectiveKind::EnterData:
-  case OpenACCDirectiveKind::ExitData:
-  case OpenACCDirectiveKind::HostData:
-  case OpenACCDirectiveKind::Wait:
-    if (!IsStmt)
-      return S.Diag(StartLoc, diag::err_acc_construct_appertainment) << K;
-    break;
-  }
-  return false;
-}
-
-bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind,
-                                OpenACCClauseKind ClauseKind) {
-  switch (ClauseKind) {
-    // FIXME: For each clause as we implement them, we can add the
-    // 'legalization' list here.
-  case OpenACCClauseKind::Default:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-    case OpenACCDirectiveKind::Data:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::If:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::ExitData:
-    case OpenACCDirectiveKind::HostData:
-    case OpenACCDirectiveKind::Init:
-    case OpenACCDirectiveKind::Shutdown:
-    case OpenACCDirectiveKind::Set:
-    case OpenACCDirectiveKind::Update:
-    case OpenACCDirectiveKind::Wait:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Self:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Update:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::NumGangs:
-  case OpenACCClauseKind::NumWorkers:
-  case OpenACCClauseKind::VectorLength:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::FirstPrivate:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Private:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::NoCreate:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Present:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::Declare:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Copy:
-  case OpenACCClauseKind::PCopy:
-  case OpenACCClauseKind::PresentOrCopy:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::Declare:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::CopyIn:
-  case OpenACCClauseKind::PCopyIn:
-  case OpenACCClauseKind::PresentOrCopyIn:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::Declare:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::CopyOut:
-  case OpenACCClauseKind::PCopyOut:
-  case OpenACCClauseKind::PresentOrCopyOut:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::ExitData:
-    case OpenACCDirectiveKind::Declare:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Create:
-  case OpenACCClauseKind::PCreate:
-  case OpenACCClauseKind::PresentOrCreate:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Attach:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::DevicePtr:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::Declare:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Async:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::ExitData:
-    case OpenACCDirectiveKind::Set:
-    case OpenACCDirectiveKind::Update:
-    case OpenACCDirectiveKind::Wait:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Wait:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::EnterData:
-    case OpenACCDirectiveKind::ExitData:
-    case OpenACCDirectiveKind::Update:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Seq:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::Routine:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Independent:
-  case OpenACCClauseKind::Auto:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Reduction:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::DeviceType:
-  case OpenACCClauseKind::DType:
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::Serial:
-    case OpenACCDirectiveKind::Kernels:
-    case OpenACCDirectiveKind::Data:
-    case OpenACCDirectiveKind::Init:
-    case OpenACCDirectiveKind::Shutdown:
-    case OpenACCDirectiveKind::Set:
-    case OpenACCDirectiveKind::Update:
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::Routine:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-
-  case OpenACCClauseKind::Collapse: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Tile: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  case OpenACCClauseKind::Gang: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-    case OpenACCDirectiveKind::Routine:
-      return true;
-    default:
-      return false;
-    }
-  case OpenACCClauseKind::Worker: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-    case OpenACCDirectiveKind::Routine:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Vector: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Loop:
-    case OpenACCDirectiveKind::ParallelLoop:
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::KernelsLoop:
-    case OpenACCDirectiveKind::Routine:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Finalize: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::ExitData:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::IfPresent: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::HostData:
-    case OpenACCDirectiveKind::Update:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Delete: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::ExitData:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  case OpenACCClauseKind::Detach: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::ExitData:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  case OpenACCClauseKind::DeviceNum: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Init:
-    case OpenACCDirectiveKind::Shutdown:
-    case OpenACCDirectiveKind::Set:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  case OpenACCClauseKind::UseDevice: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::HostData:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::DefaultAsync: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Set:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Device: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Update:
-      return true;
-    default:
-      return false;
-    }
-  }
-  case OpenACCClauseKind::Host: {
-    switch (DirectiveKind) {
-    case OpenACCDirectiveKind::Update:
-      return true;
-    default:
-      return false;
-    }
-  }
-  }
-
-  default:
-    // Do nothing so we can go to the 'unimplemented' diagnostic instead.
-    return true;
-  }
-  llvm_unreachable("Invalid clause kind");
-}
-
-bool checkAlreadyHasClauseOfKind(
-    SemaOpenACC &S, ArrayRef<const OpenACCClause *> ExistingClauses,
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  const auto *Itr = llvm::find_if(ExistingClauses, [&](const OpenACCClause *C) {
-    return C->getClauseKind() == Clause.getClauseKind();
-  });
-  if (Itr != ExistingClauses.end()) {
-    S.Diag(Clause.getBeginLoc(), diag::err_acc_duplicate_clause_disallowed)
-        << Clause.getDirectiveKind() << Clause.getClauseKind();
-    S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    return true;
-  }
-  return false;
-}
-
-bool checkValidAfterDeviceType(
-    SemaOpenACC &S, const OpenACCDeviceTypeClause &DeviceTypeClause,
-    const SemaOpenACC::OpenACCParsedClause &NewClause) {
-  // This is implemented for everything but 'routine', so treat as 'fine' for
-  // that.
-  if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Routine)
-    return false;
-
-  // OpenACC3.3: Section 2.4: Clauses that precede any device_type clause are
-  // default clauses.  Clauses that follow a device_type clause up to the end of
-  // the directive or up to the next device_type clause are device-specific
-  // clauses for the device types specified in the device_type argument.
-  //
-  // The above implies that despite what the individual text says, these are
-  // valid.
-  if (NewClause.getClauseKind() == OpenACCClauseKind::DType ||
-      NewClause.getClauseKind() == OpenACCClauseKind::DeviceType)
-    return false;
-
-  // Implement check from OpenACC3.3: section 2.5.4:
-  // Only the async, wait, num_gangs, num_workers, and vector_length clauses may
-  // follow a device_type clause.
-  if (isOpenACCComputeDirectiveKind(NewClause.getDirectiveKind())) {
-    switch (NewClause.getClauseKind()) {
-    case OpenACCClauseKind::Async:
-    case OpenACCClauseKind::Wait:
-    case OpenACCClauseKind::NumGangs:
-    case OpenACCClauseKind::NumWorkers:
-    case OpenACCClauseKind::VectorLength:
-      return false;
-    default:
-      break;
-    }
-  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
-    // Implement check from OpenACC3.3: section 2.9:
-    // Only the collapse, gang, worker, vector, seq, independent, auto, and tile
-    // clauses may follow a device_type clause.
-    switch (NewClause.getClauseKind()) {
-    case OpenACCClauseKind::Collapse:
-    case OpenACCClauseKind::Gang:
-    case OpenACCClauseKind::Worker:
-    case OpenACCClauseKind::Vector:
-    case OpenACCClauseKind::Seq:
-    case OpenACCClauseKind::Independent:
-    case OpenACCClauseKind::Auto:
-    case OpenACCClauseKind::Tile:
-      return false;
-    default:
-      break;
-    }
-  } else if (isOpenACCCombinedDirectiveKind(NewClause.getDirectiveKind())) {
-    // This seems like it should be the union of 2.9 and 2.5.4 from above.
-    switch (NewClause.getClauseKind()) {
-    case OpenACCClauseKind::Async:
-    case OpenACCClauseKind::Wait:
-    case OpenACCClauseKind::NumGangs:
-    case OpenACCClauseKind::NumWorkers:
-    case OpenACCClauseKind::VectorLength:
-    case OpenACCClauseKind::Collapse:
-    case OpenACCClauseKind::Gang:
-    case OpenACCClauseKind::Worker:
-    case OpenACCClauseKind::Vector:
-    case OpenACCClauseKind::Seq:
-    case OpenACCClauseKind::Independent:
-    case OpenACCClauseKind::Auto:
-    case OpenACCClauseKind::Tile:
-      return false;
-    default:
-      break;
-    }
-  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Data) {
-    // OpenACC3.3 section 2.6.5: Only the async and wait clauses may follow a
-    // device_type clause.
-    switch (NewClause.getClauseKind()) {
-    case OpenACCClauseKind::Async:
-    case OpenACCClauseKind::Wait:
-      return false;
-    default:
-      break;
-    }
-  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Set ||
-             NewClause.getDirectiveKind() == OpenACCDirectiveKind::Init ||
-             NewClause.getDirectiveKind() == OpenACCDirectiveKind::Shutdown) {
-    // There are no restrictions on 'set', 'init', or 'shutdown'.
-    return false;
-  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Update) {
-    // OpenACC3.3 section 2.14.4: Only the async and wait clauses may follow a
-    // device_type clause.
-    switch (NewClause.getClauseKind()) {
-    case OpenACCClauseKind::Async:
-    case OpenACCClauseKind::Wait:
-      return false;
-    default:
-      break;
-    }
-  }
-  S.Diag(NewClause.getBeginLoc(), diag::err_acc_clause_after_device_type)
-      << NewClause.getClauseKind() << DeviceTypeClause.getClauseKind()
-      << NewClause.getDirectiveKind();
-  S.Diag(DeviceTypeClause.getBeginLoc(), diag::note_acc_previous_clause_here);
-  return true;
-}
-
-// A temporary function that helps implement the 'not implemented' check at the
-// top of each clause checking function. This should only be used in conjunction
-// with the one being currently implemented/only updated after the entire
-// construct has been implemented.
-bool isDirectiveKindImplemented(OpenACCDirectiveKind DK) {
-  return isOpenACCComputeDirectiveKind(DK) ||
-         isOpenACCCombinedDirectiveKind(DK) || isOpenACCDataDirectiveKind(DK) ||
-         DK == OpenACCDirectiveKind::Loop || DK == OpenACCDirectiveKind::Wait ||
-         DK == OpenACCDirectiveKind::Init ||
-         DK == OpenACCDirectiveKind::Shutdown ||
-         DK == OpenACCDirectiveKind::Set;
-}
-
-class SemaOpenACCClauseVisitor {
-  SemaOpenACC &SemaRef;
-  ASTContext &Ctx;
-  ArrayRef<const OpenACCClause *> ExistingClauses;
-  bool NotImplemented = false;
-
-  OpenACCClause *isNotImplemented() {
-    NotImplemented = true;
-    return nullptr;
-  }
-
-  // OpenACC 3.3 2.9:
-  // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
-  // appears.
-  bool DiagIfSeqClause(SemaOpenACC::OpenACCParsedClause &Clause) {
-    const auto *Itr =
-        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSeqClause>);
-
-    if (Itr != ExistingClauses.end()) {
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
-          << Clause.getClauseKind() << (*Itr)->getClauseKind()
-          << Clause.getDirectiveKind();
-      SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-
-      return true;
-    }
-    return false;
-  }
-
-public:
-  SemaOpenACCClauseVisitor(SemaOpenACC &S,
-                           ArrayRef<const OpenACCClause *> ExistingClauses)
-      : SemaRef(S), Ctx(S.getASTContext()), ExistingClauses(ExistingClauses) {}
-  // Once we've implemented everything, we shouldn't need this infrastructure.
-  // But in the meantime, we use this to help decide whether the clause was
-  // handled for this directive.
-  bool diagNotImplemented() { return NotImplemented; }
-
-  OpenACCClause *Visit(SemaOpenACC::OpenACCParsedClause &Clause) {
-    switch (Clause.getClauseKind()) {
-#define VISIT_CLAUSE(CLAUSE_NAME)                                              \
-  case OpenACCClauseKind::CLAUSE_NAME:                                         \
-    return Visit##CLAUSE_NAME##Clause(Clause);
-#define CLAUSE_ALIAS(ALIAS, CLAUSE_NAME, DEPRECATED)                           \
-  case OpenACCClauseKind::ALIAS:                                               \
-  if (DEPRECATED)                                                              \
-    SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_deprecated_alias_name)   \
-        << Clause.getClauseKind() << OpenACCClauseKind::CLAUSE_NAME;           \
-  return Visit##CLAUSE_NAME##Clause(Clause);
-#include "clang/Basic/OpenACCClauses.def"
-    default:
-      return isNotImplemented();
-    }
-    llvm_unreachable("Invalid clause kind");
-  }
-
-#define VISIT_CLAUSE(CLAUSE_NAME)                                              \
-  OpenACCClause *Visit##CLAUSE_NAME##Clause(                                   \
-      SemaOpenACC::OpenACCParsedClause &Clause);
-#include "clang/Basic/OpenACCClauses.def"
-};
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Don't add an invalid clause to the AST.
-  if (Clause.getDefaultClauseKind() == OpenACCDefaultClauseKind::Invalid)
-    return nullptr;
-
-  // OpenACC 3.3, Section 2.5.4:
-  // At most one 'default' clause may appear, and it must have a value of
-  // either 'none' or 'present'.
-  // Second half of the sentence is diagnosed during parsing.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  return OpenACCDefaultClause::Create(
-      Ctx, Clause.getDefaultClauseKind(), Clause.getBeginLoc(),
-      Clause.getLParenLoc(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitTileClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-
-  // Duplicates here are not really sensible.  We could possible permit
-  // multiples if they all had the same value, but there isn't really a good
-  // reason to do so. Also, this simplifies the suppression of duplicates, in
-  // that we know if we 'find' one after instantiation, that it is the same
-  // clause, which simplifies instantiation/checking/etc.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  llvm::SmallVector<Expr *> NewSizeExprs;
-
-  // Make sure these are all positive constant expressions or *.
-  for (Expr *E : Clause.getIntExprs()) {
-    ExprResult Res = SemaRef.CheckTileSizeExpr(E);
-
-    if (!Res.isUsable())
-      return nullptr;
-
-    NewSizeExprs.push_back(Res.get());
-  }
-
-  return OpenACCTileClause::Create(Ctx, Clause.getBeginLoc(),
-                                   Clause.getLParenLoc(), NewSizeExprs,
-                                   Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitIfClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense. Prose DOES exist for 'data' and 'host_data', 'set', 'enter data' and
-  // 'exit data' both don't, but other implmementations do this.  OpenACC issue
-  // 519 filed for the latter two. Prose also exists for 'update'.
-  // GCC allows this on init/shutdown, presumably for good reason, so we do too.
-  if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Init &&
-      Clause.getDirectiveKind() != OpenACCDirectiveKind::Shutdown &&
-      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // The parser has ensured that we have a proper condition expr, so there
-  // isn't really much to do here.
-
-  // If the 'if' clause is true, it makes the 'self' clause have no effect,
-  // diagnose that here.  This only applies on compute/combined constructs.
-  if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Update) {
-    const auto *Itr =
-        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSelfClause>);
-    if (Itr != ExistingClauses.end()) {
-      SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
-      SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    }
-  }
-
-  return OpenACCIfClause::Create(Ctx, Clause.getBeginLoc(),
-                                 Clause.getLParenLoc(),
-                                 Clause.getConditionExpr(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitSelfClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // If the 'if' clause is true, it makes the 'self' clause have no effect,
-  // diagnose that here.  This only applies on compute/combined constructs.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Update)
-    return OpenACCSelfClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), Clause.getVarList(),
-                                     Clause.getEndLoc());
-
-  const auto *Itr =
-      llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCIfClause>);
-  if (Itr != ExistingClauses.end()) {
-    SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
-    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-  }
-  return OpenACCSelfClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.getConditionExpr(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitNumGangsClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // num_gangs requires at least 1 int expr in all forms.  Diagnose here, but
-  // allow us to continue, an empty clause might be useful for future
-  // diagnostics.
-  if (Clause.getIntExprs().empty())
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
-        << /*NoArgs=*/0;
-
-  unsigned MaxArgs =
-      (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
-       Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop)
-          ? 3
-          : 1;
-  // The max number of args 
diff ers between parallel and other constructs.
-  // Again, allow us to continue for the purposes of future diagnostics.
-  if (Clause.getIntExprs().size() > MaxArgs)
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
-        << /*NoArgs=*/1 << Clause.getDirectiveKind() << MaxArgs
-        << Clause.getIntExprs().size();
-
-  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
-  // directive that has a gang clause and is within a compute construct that has
-  // a num_gangs clause with more than one explicit argument.
-  if (Clause.getIntExprs().size() > 1 &&
-      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    auto *GangClauseItr =
-        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
-    auto *ReductionClauseItr =
-        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
-
-    if (GangClauseItr != ExistingClauses.end() &&
-        ReductionClauseItr != ExistingClauses.end()) {
-      SemaRef.Diag(Clause.getBeginLoc(),
-                   diag::err_acc_gang_reduction_numgangs_conflict)
-          << OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
-          << Clause.getDirectiveKind() << /*is on combined directive=*/1;
-      SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
-                   diag::note_acc_previous_clause_here);
-      SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-  }
-
-  // OpenACC 3.3 Section 2.5.4:
-  // A reduction clause may not appear on a parallel construct with a
-  // num_gangs clause that has more than one argument.
-  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
-       Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) &&
-      Clause.getIntExprs().size() > 1) {
-    auto *Parallel =
-        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
-
-    if (Parallel != ExistingClauses.end()) {
-      SemaRef.Diag(Clause.getBeginLoc(),
-                   diag::err_acc_reduction_num_gangs_conflict)
-          << /*>1 arg in first loc=*/1 << Clause.getClauseKind()
-          << Clause.getDirectiveKind() << OpenACCClauseKind::Reduction;
-      SemaRef.Diag((*Parallel)->getBeginLoc(),
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-  }
-
-  // OpenACC 3.3 Section 2.9.2:
-  // An argument with no keyword or with the 'num' keyword is allowed only when
-  // the 'num_gangs' does not appear on the 'kernel' construct.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
-    auto GangClauses = llvm::make_filter_range(
-        ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
-
-    for (auto *GC : GangClauses) {
-      if (cast<OpenACCGangClause>(GC)->hasExprOfKind(OpenACCGangKind::Num)) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_num_arg_conflict_reverse)
-            << OpenACCClauseKind::NumGangs << OpenACCClauseKind::Gang
-            << /*Num argument*/ 1;
-        SemaRef.Diag(GC->getBeginLoc(), diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  return OpenACCNumGangsClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs(),
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitNumWorkersClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // OpenACC 3.3 Section 2.9.2:
-  // An argument is allowed only when the 'num_workers' does not appear on the
-  // kernels construct.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
-    auto WorkerClauses = llvm::make_filter_range(
-        ExistingClauses, llvm::IsaPred<OpenACCWorkerClause>);
-
-    for (auto *WC : WorkerClauses) {
-      if (cast<OpenACCWorkerClause>(WC)->hasIntExpr()) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_num_arg_conflict_reverse)
-            << OpenACCClauseKind::NumWorkers << OpenACCClauseKind::Worker
-            << /*num argument*/ 0;
-        SemaRef.Diag(WC->getBeginLoc(), diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  assert(Clause.getIntExprs().size() == 1 &&
-         "Invalid number of expressions for NumWorkers");
-  return OpenACCNumWorkersClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorLengthClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // OpenACC 3.3 Section 2.9.4:
-  // An argument is allowed only when the 'vector_length' does not appear on the
-  // 'kernels' construct.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
-    auto VectorClauses = llvm::make_filter_range(
-        ExistingClauses, llvm::IsaPred<OpenACCVectorClause>);
-
-    for (auto *VC : VectorClauses) {
-      if (cast<OpenACCVectorClause>(VC)->hasIntExpr()) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_num_arg_conflict_reverse)
-            << OpenACCClauseKind::VectorLength << OpenACCClauseKind::Vector
-            << /*num argument*/ 0;
-        SemaRef.Diag(VC->getBeginLoc(), diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  assert(Clause.getIntExprs().size() == 1 &&
-         "Invalid number of expressions for NumWorkers");
-  return OpenACCVectorLengthClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitAsyncClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There is no prose in the standard that says duplicates aren't allowed,
-  // but this diagnostic is present in other compilers, as well as makes
-  // sense.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  assert(Clause.getNumIntExprs() < 2 &&
-         "Invalid number of expressions for Async");
-  return OpenACCAsyncClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr,
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceNumClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on certain constructs, so skip/treat
-  // as unimplemented in those cases.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
-  // same directive.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
-      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  assert(Clause.getNumIntExprs() == 1 &&
-         "Invalid number of expressions for device_num");
-  return OpenACCDeviceNumClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultAsyncClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
-  // same directive.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  assert(Clause.getNumIntExprs() == 1 &&
-         "Invalid number of expressions for default_async");
-  return OpenACCDefaultAsyncClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitPrivateClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCPrivateClause::Create(Ctx, Clause.getBeginLoc(),
-                                      Clause.getLParenLoc(),
-                                      Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCFirstPrivateClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitNoCreateClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCNoCreateClause::Create(Ctx, Clause.getBeginLoc(),
-                                       Clause.getLParenLoc(),
-                                       Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitPresentClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'compute'/'combined'/'data'
-  // constructs, and 'compute'/'combined'/'data' constructs are the only
-  // construct that can do anything with this yet, so skip/treat as
-  // unimplemented in this case.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCPresentClause::Create(Ctx, Clause.getBeginLoc(),
-                                      Clause.getLParenLoc(),
-                                      Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitHostClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCHostClause::Create(Ctx, Clause.getBeginLoc(),
-                                   Clause.getLParenLoc(), Clause.getVarList(),
-                                   Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCDeviceClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), Clause.getVarList(),
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'compute'/'combined'/'data'
-  // constructs, and 'compute'/'combined'/'data' constructs are the only
-  // construct that can do anything with this yet, so skip/treat as
-  // unimplemented in this case.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCCopyClause::Create(
-      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyInClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'compute'/'combined'/'data'
-  // constructs, and 'compute'/'combined'/'data' constructs are the only
-  // construct that can do anything with this yet, so skip/treat as
-  // unimplemented in this case.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCCopyInClause::Create(
-      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isReadOnly(), Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'compute'/'combined'/'data'
-  // constructs, and 'compute'/'combined'/'data' constructs are the only
-  // construct that can do anything with this yet, so skip/treat as
-  // unimplemented in this case.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCCopyOutClause::Create(
-      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-
-  return OpenACCCreateClause::Create(
-      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, but we
-  // still have to make sure it is a pointer type.
-  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
-  llvm::erase_if(VarList, [&](Expr *E) {
-    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Attach, E);
-  });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
-  return OpenACCAttachClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), Clause.getVarList(),
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDetachClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, but we
-  // still have to make sure it is a pointer type.
-  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
-  llvm::erase_if(VarList, [&](Expr *E) {
-    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Detach, E);
-  });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
-  return OpenACCDetachClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), Clause.getVarList(),
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDeleteClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable reference, so there
-  // really isn't anything to do here. GCC does some duplicate-finding, though
-  // it isn't apparent in the standard where this is justified.
-  return OpenACCDeleteClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), Clause.getVarList(),
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitUseDeviceClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // ActOnVar ensured that everything is a valid variable or array, so nothing
-  // left to do here.
-  return OpenACCUseDeviceClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'compute'/'combined'/'data'
-  // constructs, and 'compute'/'combined'/'data' constructs are the only
-  // construct that can do anything with this yet, so skip/treat as
-  // unimplemented in this case.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  // ActOnVar ensured that everything is a valid variable reference, but we
-  // still have to make sure it is a pointer type.
-  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
-  llvm::erase_if(VarList, [&](Expr *E) {
-    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::DevicePtr, E);
-  });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
-
-  return OpenACCDevicePtrClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  return OpenACCWaitClause::Create(
-      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getDevNumExpr(),
-      Clause.getQueuesLoc(), Clause.getQueueIdExprs(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceTypeClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions implemented properly on everything except 'routine'.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine)
-    return isNotImplemented();
-
-  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
-  // same directive.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
-      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  // TODO OpenACC: Once we get enough of the CodeGen implemented that we have
-  // a source for the list of valid architectures, we need to warn on unknown
-  // identifiers here.
-
-  return OpenACCDeviceTypeClause::Create(
-      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.getDeviceTypeArchitectures(), Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitAutoClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // OpenACC 3.3 2.9:
-  // Only one of the seq, independent, and auto clauses may appear.
-  const auto *Itr =
-      llvm::find_if(ExistingClauses,
-                    llvm::IsaPred<OpenACCIndependentClause, OpenACCSeqClause>);
-  if (Itr != ExistingClauses.end()) {
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
-        << Clause.getClauseKind() << Clause.getDirectiveKind();
-    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    return nullptr;
-  }
-
-  return OpenACCAutoClause::Create(Ctx, Clause.getBeginLoc(),
-                                   Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitIndependentClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // OpenACC 3.3 2.9:
-  // Only one of the seq, independent, and auto clauses may appear.
-  const auto *Itr = llvm::find_if(
-      ExistingClauses, llvm::IsaPred<OpenACCAutoClause, OpenACCSeqClause>);
-  if (Itr != ExistingClauses.end()) {
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
-        << Clause.getClauseKind() << Clause.getDirectiveKind();
-    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    return nullptr;
-  }
-
-  return OpenACCIndependentClause::Create(Ctx, Clause.getBeginLoc(),
-                                          Clause.getEndLoc());
-}
-
-ExprResult CheckGangStaticExpr(SemaOpenACC &S, Expr *E) {
-  if (isa<OpenACCAsteriskSizeExpr>(E))
-    return E;
-  return S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
-                        E->getBeginLoc(), E);
-}
-
-bool IsOrphanLoop(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
-  return DK == OpenACCDirectiveKind::Loop &&
-         AssocKind == OpenACCDirectiveKind::Invalid;
-}
-
-bool HasAssocKind(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
-  return DK == OpenACCDirectiveKind::Loop &&
-         AssocKind != OpenACCDirectiveKind::Invalid;
-}
-
-ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, OpenACCGangKind GK,
-                             OpenACCClauseKind CK, OpenACCDirectiveKind DK,
-                             OpenACCDirectiveKind AssocKind) {
-  S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
-      << GK << CK << IsOrphanLoop(DK, AssocKind) << DK
-      << HasAssocKind(DK, AssocKind) << AssocKind;
-  return ExprError();
-}
-ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, StringRef TagKind,
-                             OpenACCClauseKind CK, OpenACCDirectiveKind DK,
-                             OpenACCDirectiveKind AssocKind) {
-  S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
-      << TagKind << CK << IsOrphanLoop(DK, AssocKind) << DK
-      << HasAssocKind(DK, AssocKind) << AssocKind;
-  return ExprError();
-}
-
-ExprResult CheckGangParallelExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
-                                 OpenACCDirectiveKind AssocKind,
-                                 OpenACCGangKind GK, Expr *E) {
-  switch (GK) {
-  case OpenACCGangKind::Static:
-    return CheckGangStaticExpr(S, E);
-  case OpenACCGangKind::Num:
-    // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
-    // construct, or an orphaned loop construct, the gang clause behaves as
-    // follows. ... The num argument is not allowed.
-    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
-  case OpenACCGangKind::Dim: {
-    // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
-    // construct, or an orphaned loop construct, the gang clause behaves as
-    // follows. ... The dim argument must be a constant positive integer value
-    // 1, 2, or 3.
-    if (!E)
-      return ExprError();
-    ExprResult Res =
-        S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
-                       E->getBeginLoc(), E);
-
-    if (!Res.isUsable())
-      return Res;
-
-    if (Res.get()->isInstantiationDependent())
-      return Res;
-
-    std::optional<llvm::APSInt> ICE =
-        Res.get()->getIntegerConstantExpr(S.getASTContext());
-
-    if (!ICE || *ICE <= 0 || ICE > 3) {
-      S.Diag(Res.get()->getBeginLoc(), diag::err_acc_gang_dim_value)
-          << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
-      return ExprError();
-    }
-
-    return ExprResult{
-        ConstantExpr::Create(S.getASTContext(), Res.get(), APValue{*ICE})};
-  }
-  }
-  llvm_unreachable("Unknown gang kind in gang parallel check");
-}
-
-ExprResult CheckGangKernelsExpr(SemaOpenACC &S,
-                                ArrayRef<const OpenACCClause *> ExistingClauses,
-                                OpenACCDirectiveKind DK,
-                                OpenACCDirectiveKind AssocKind,
-                                OpenACCGangKind GK, Expr *E) {
-  switch (GK) {
-  // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
-  // construct, the gang clause behaves as follows. ... The dim argument is
-  // not allowed.
-  case OpenACCGangKind::Dim:
-    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
-  case OpenACCGangKind::Num: {
-    // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
-    // construct, the gang clause behaves as follows. ... An argument with no
-    // keyword or with num keyword is only allowed when num_gangs does not
-    // appear on the kernels construct. ... The region of a loop with the gang
-    // clause may not contain another loop with a gang clause unless within a
-    // nested compute region.
-
-    // If this is a 'combined' construct, search the list of existing clauses.
-    // Else we need to search the containing 'kernel'.
-    auto Collection = isOpenACCCombinedDirectiveKind(DK)
-                          ? ExistingClauses
-                          : S.getActiveComputeConstructInfo().Clauses;
-
-    const auto *Itr =
-        llvm::find_if(Collection, llvm::IsaPred<OpenACCNumGangsClause>);
-
-    if (Itr != Collection.end()) {
-      S.Diag(E->getBeginLoc(), diag::err_acc_num_arg_conflict)
-          << "num" << OpenACCClauseKind::Gang << DK
-          << HasAssocKind(DK, AssocKind) << AssocKind
-          << OpenACCClauseKind::NumGangs;
-
-      S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-      return ExprError();
-    }
-    return ExprResult{E};
-  }
-  case OpenACCGangKind::Static:
-    return CheckGangStaticExpr(S, E);
-    return ExprError();
-  }
-  llvm_unreachable("Unknown gang kind in gang kernels check");
-}
-
-ExprResult CheckGangSerialExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
-                               OpenACCDirectiveKind AssocKind,
-                               OpenACCGangKind GK, Expr *E) {
-  switch (GK) {
-  // 'dim' and 'num' don't really make sense on serial, and GCC rejects them
-  // too, so we disallow them too.
-  case OpenACCGangKind::Dim:
-  case OpenACCGangKind::Num:
-    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
-  case OpenACCGangKind::Static:
-    return CheckGangStaticExpr(S, E);
-  }
-  llvm_unreachable("Unknown gang kind in gang serial check");
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  if (DiagIfSeqClause(Clause))
-    return nullptr;
-
-  // Restrictions only properly implemented on 'loop'/'combined' constructs, and
-  // it is the only construct that can do anything with this, so skip/treat as
-  // unimplemented for the routine constructs.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  Expr *IntExpr =
-      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
-  if (IntExpr) {
-    if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-      switch (SemaRef.getActiveComputeConstructInfo().Kind) {
-      case OpenACCDirectiveKind::Invalid:
-      case OpenACCDirectiveKind::Parallel:
-        // No restriction on when 'parallel' can contain an argument.
-        break;
-      case OpenACCDirectiveKind::Serial:
-        // GCC disallows this, and there is no real good reason for us to permit
-        // it, so disallow until we come up with a use case that makes sense.
-        DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
-                          Clause.getDirectiveKind(),
-                          SemaRef.getActiveComputeConstructInfo().Kind);
-        IntExpr = nullptr;
-        break;
-      case OpenACCDirectiveKind::Kernels: {
-        const auto *Itr =
-            llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
-                          llvm::IsaPred<OpenACCVectorLengthClause>);
-        if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
-          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
-              << "length" << OpenACCClauseKind::Vector
-              << Clause.getDirectiveKind()
-              << HasAssocKind(Clause.getDirectiveKind(),
-                              SemaRef.getActiveComputeConstructInfo().Kind)
-              << SemaRef.getActiveComputeConstructInfo().Kind
-              << OpenACCClauseKind::VectorLength;
-          SemaRef.Diag((*Itr)->getBeginLoc(),
-                       diag::note_acc_previous_clause_here);
-
-          IntExpr = nullptr;
-        }
-        break;
-      }
-      default:
-        llvm_unreachable("Non compute construct in active compute construct");
-      }
-    } else {
-      if (Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
-        DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
-                          Clause.getDirectiveKind(),
-                          SemaRef.getActiveComputeConstructInfo().Kind);
-        IntExpr = nullptr;
-      } else if (Clause.getDirectiveKind() ==
-                 OpenACCDirectiveKind::KernelsLoop) {
-        const auto *Itr = llvm::find_if(
-            ExistingClauses, llvm::IsaPred<OpenACCVectorLengthClause>);
-        if (Itr != ExistingClauses.end()) {
-          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
-              << "length" << OpenACCClauseKind::Vector
-              << Clause.getDirectiveKind()
-              << HasAssocKind(Clause.getDirectiveKind(),
-                              SemaRef.getActiveComputeConstructInfo().Kind)
-              << SemaRef.getActiveComputeConstructInfo().Kind
-              << OpenACCClauseKind::VectorLength;
-          SemaRef.Diag((*Itr)->getBeginLoc(),
-                       diag::note_acc_previous_clause_here);
-
-          IntExpr = nullptr;
-        }
-      }
-    }
-  }
-
-  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
-    // contain a loop with a gang, worker, or vector clause unless within a
-    // nested compute region.
-    if (SemaRef.LoopVectorClauseLoc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Vector << OpenACCClauseKind::Vector
-          << /*skip kernels construct info*/ 0;
-      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-  }
-
-  return OpenACCVectorClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), IntExpr,
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitWorkerClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  if (DiagIfSeqClause(Clause))
-    return nullptr;
-
-  // Restrictions only properly implemented on 'loop'/'combined' constructs, and
-  // it is the only construct that can do anything with this, so skip/treat as
-  // unimplemented for the routine constructs.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  Expr *IntExpr =
-      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
-
-  if (IntExpr) {
-    if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-      switch (SemaRef.getActiveComputeConstructInfo().Kind) {
-      case OpenACCDirectiveKind::Invalid:
-      case OpenACCDirectiveKind::ParallelLoop:
-      case OpenACCDirectiveKind::SerialLoop:
-      case OpenACCDirectiveKind::Parallel:
-      case OpenACCDirectiveKind::Serial:
-        DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
-                          OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
-                          SemaRef.getActiveComputeConstructInfo().Kind);
-        IntExpr = nullptr;
-        break;
-      case OpenACCDirectiveKind::KernelsLoop:
-      case OpenACCDirectiveKind::Kernels: {
-        const auto *Itr =
-            llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
-                          llvm::IsaPred<OpenACCNumWorkersClause>);
-        if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
-          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
-              << "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
-              << HasAssocKind(Clause.getDirectiveKind(),
-                              SemaRef.getActiveComputeConstructInfo().Kind)
-              << SemaRef.getActiveComputeConstructInfo().Kind
-              << OpenACCClauseKind::NumWorkers;
-          SemaRef.Diag((*Itr)->getBeginLoc(),
-                       diag::note_acc_previous_clause_here);
-
-          IntExpr = nullptr;
-        }
-        break;
-      }
-      default:
-        llvm_unreachable("Non compute construct in active compute construct");
-      }
-    } else {
-      if (Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop ||
-          Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
-        DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
-                          OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
-                          SemaRef.getActiveComputeConstructInfo().Kind);
-        IntExpr = nullptr;
-      } else {
-        assert(Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop &&
-               "Unknown combined directive kind?");
-        const auto *Itr = llvm::find_if(ExistingClauses,
-                                        llvm::IsaPred<OpenACCNumWorkersClause>);
-        if (Itr != ExistingClauses.end()) {
-          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
-              << "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
-              << HasAssocKind(Clause.getDirectiveKind(),
-                              SemaRef.getActiveComputeConstructInfo().Kind)
-              << SemaRef.getActiveComputeConstructInfo().Kind
-              << OpenACCClauseKind::NumWorkers;
-          SemaRef.Diag((*Itr)->getBeginLoc(),
-                       diag::note_acc_previous_clause_here);
-
-          IntExpr = nullptr;
-        }
-      }
-    }
-  }
-
-  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    // OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
-    // contain a loop with a gang or worker clause unless within a nested
-    // compute region.
-    if (SemaRef.LoopWorkerClauseLoc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Worker << OpenACCClauseKind::Worker
-          << /*skip kernels construct info*/ 0;
-      SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-
-    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
-    // contain a loop with a gang, worker, or vector clause unless within a
-    // nested compute region.
-    if (SemaRef.LoopVectorClauseLoc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Worker << OpenACCClauseKind::Vector
-          << /*skip kernels construct info*/ 0;
-      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-  }
-
-  return OpenACCWorkerClause::Create(Ctx, Clause.getBeginLoc(),
-                                     Clause.getLParenLoc(), IntExpr,
-                                     Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitGangClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  if (DiagIfSeqClause(Clause))
-    return nullptr;
-
-  // Restrictions only properly implemented on 'loop' constructs, and it is
-  // the only construct that can do anything with this, so skip/treat as
-  // unimplemented for the combined constructs.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
-  // directive that has a gang clause and is within a compute construct that has
-  // a num_gangs clause with more than one explicit argument.
-  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
-       SemaRef.getActiveComputeConstructInfo().Kind !=
-           OpenACCDirectiveKind::Invalid) ||
-      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    // num_gangs clause on the active compute construct.
-    auto ActiveComputeConstructContainer =
-        isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
-            ? ExistingClauses
-            : SemaRef.getActiveComputeConstructInfo().Clauses;
-    auto *NumGangsClauseItr = llvm::find_if(
-        ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
-
-    if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
-        cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
-            1) {
-      auto *ReductionClauseItr =
-          llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
-
-      if (ReductionClauseItr != ExistingClauses.end()) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_gang_reduction_numgangs_conflict)
-            << OpenACCClauseKind::Gang << OpenACCClauseKind::Reduction
-            << Clause.getDirectiveKind()
-            << isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
-        SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
-                     diag::note_acc_previous_clause_here);
-        SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
-                     diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  llvm::SmallVector<OpenACCGangKind> GangKinds;
-  llvm::SmallVector<Expr *> IntExprs;
-
-  // Store the existing locations, so we can do duplicate checking.  Index is
-  // the int-value of the OpenACCGangKind enum.
-  SourceLocation ExistingElemLoc[3];
-
-  for (unsigned I = 0; I < Clause.getIntExprs().size(); ++I) {
-    OpenACCGangKind GK = Clause.getGangKinds()[I];
-    ExprResult ER =
-        SemaRef.CheckGangExpr(ExistingClauses, Clause.getDirectiveKind(), GK,
-                              Clause.getIntExprs()[I]);
-
-    if (!ER.isUsable())
-      continue;
-
-    // OpenACC 3.3 2.9: 'gang-arg-list' may have at most one num, one dim, and
-    // one static argument.
-    if (ExistingElemLoc[static_cast<unsigned>(GK)].isValid()) {
-      SemaRef.Diag(ER.get()->getBeginLoc(), diag::err_acc_gang_multiple_elt)
-          << static_cast<unsigned>(GK);
-      SemaRef.Diag(ExistingElemLoc[static_cast<unsigned>(GK)],
-                   diag::note_acc_previous_expr_here);
-      continue;
-    }
-
-    ExistingElemLoc[static_cast<unsigned>(GK)] = ER.get()->getBeginLoc();
-    GangKinds.push_back(GK);
-    IntExprs.push_back(ER.get());
-  }
-
-  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    // 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
-    // within a nested compute region.
-    if (SemaRef.LoopGangClauseOnKernel.Loc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Gang << OpenACCClauseKind::Gang
-          << /*kernels construct info*/ 1
-          << SemaRef.LoopGangClauseOnKernel.DirKind;
-      SemaRef.Diag(SemaRef.LoopGangClauseOnKernel.Loc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-
-    // OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
-    // contain a loop with a gang or worker clause unless within a nested
-    // compute region.
-    if (SemaRef.LoopWorkerClauseLoc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Gang << OpenACCClauseKind::Worker
-          << /*!kernels construct info*/ 0;
-      SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-
-    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
-    // contain a loop with a gang, worker, or vector clause unless within a
-    // nested compute region.
-    if (SemaRef.LoopVectorClauseLoc.isValid()) {
-      // This handles the 'inner loop' diagnostic, but we cannot set that we're
-      // on one of these until we get to the end of the construct.
-      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
-          << OpenACCClauseKind::Gang << OpenACCClauseKind::Vector
-          << /*!kernels construct info*/ 0;
-      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
-                   diag::note_acc_previous_clause_here);
-      return nullptr;
-    }
-  }
-
-  return SemaRef.CheckGangClause(Clause.getDirectiveKind(), ExistingClauses,
-                                 Clause.getBeginLoc(), Clause.getLParenLoc(),
-                                 GangKinds, IntExprs, Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitFinalizeClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There isn't anything to do here, this is only valid on one construct, and
-  // has no associated rules.
-  return OpenACCFinalizeClause::Create(Ctx, Clause.getBeginLoc(),
-                                       Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitIfPresentClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // There isn't anything to do here, this is only valid on one construct, and
-  // has no associated rules.
-  return OpenACCIfPresentClause::Create(Ctx, Clause.getBeginLoc(),
-                                        Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitSeqClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Restrictions only properly implemented on 'loop' constructs and combined ,
-  // and it is the only construct that can do anything with this, so skip/treat
-  // as unimplemented for the routine constructs.
-  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
-    return isNotImplemented();
-
-  // OpenACC 3.3 2.9:
-  // Only one of the seq, independent, and auto clauses may appear.
-  const auto *Itr =
-      llvm::find_if(ExistingClauses,
-                    llvm::IsaPred<OpenACCAutoClause, OpenACCIndependentClause>);
-  if (Itr != ExistingClauses.end()) {
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
-        << Clause.getClauseKind() << Clause.getDirectiveKind();
-    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    return nullptr;
-  }
-
-  // OpenACC 3.3 2.9:
-  // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
-  // appears.
-  Itr = llvm::find_if(ExistingClauses,
-                      llvm::IsaPred<OpenACCGangClause, OpenACCWorkerClause,
-                                    OpenACCVectorClause>);
-
-  if (Itr != ExistingClauses.end()) {
-    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
-        << Clause.getClauseKind() << (*Itr)->getClauseKind()
-        << Clause.getDirectiveKind();
-    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
-    return nullptr;
-  }
-
-  return OpenACCSeqClause::Create(Ctx, Clause.getBeginLoc(),
-                                  Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
-  // directive that has a gang clause and is within a compute construct that has
-  // a num_gangs clause with more than one explicit argument.
-  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
-       SemaRef.getActiveComputeConstructInfo().Kind !=
-           OpenACCDirectiveKind::Invalid) ||
-      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
-    // num_gangs clause on the active compute construct.
-    auto ActiveComputeConstructContainer =
-        isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
-            ? ExistingClauses
-            : SemaRef.getActiveComputeConstructInfo().Clauses;
-    auto *NumGangsClauseItr = llvm::find_if(
-        ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
-
-    if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
-        cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
-            1) {
-      auto *GangClauseItr =
-          llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
-
-      if (GangClauseItr != ExistingClauses.end()) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_gang_reduction_numgangs_conflict)
-            << OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
-            << Clause.getDirectiveKind()
-            << isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
-        SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
-                     diag::note_acc_previous_clause_here);
-        SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
-                     diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  // OpenACC3.3 Section 2.9.11: If a variable is involved in a reduction that
-  // spans multiple nested loops where two or more of those loops have
-  // associated loop directives, a reduction clause containing that variable
-  // must appear on each of those loop directives.
-  //
-  // This can't really be implemented in the CFE, as this requires a level of
-  // rechability/useage analysis that we're not really wanting to get into.
-  // Additionally, I'm alerted that this restriction is one that the middle-end
-  // can just 'figure out' as an extension and isn't really necessary.
-  //
-  // OpenACC3.3 Section 2.9.11: Every 'var' in a reduction clause appearing on
-  // an orphaned loop construct must be private.
-  //
-  // This again is something we cannot really diagnose, as it requires we see
-  // all the uses/scopes of all variables referenced.  The middle end/MLIR might
-  // be able to diagnose this.
-
-  // OpenACC 3.3 Section 2.5.4:
-  // A reduction clause may not appear on a parallel construct with a
-  // num_gangs clause that has more than one argument.
-  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
-      Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) {
-    auto NumGangsClauses = llvm::make_filter_range(
-        ExistingClauses, llvm::IsaPred<OpenACCNumGangsClause>);
-
-    for (auto *NGC : NumGangsClauses) {
-      unsigned NumExprs =
-          cast<OpenACCNumGangsClause>(NGC)->getIntExprs().size();
-
-      if (NumExprs > 1) {
-        SemaRef.Diag(Clause.getBeginLoc(),
-                     diag::err_acc_reduction_num_gangs_conflict)
-            << /*>1 arg in first loc=*/0 << Clause.getClauseKind()
-            << Clause.getDirectiveKind() << OpenACCClauseKind::NumGangs;
-        SemaRef.Diag(NGC->getBeginLoc(), diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  SmallVector<Expr *> ValidVars;
+#include "clang/AST/StmtOpenACC.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/OpenACCKinds.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaOpenACC.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
 
-  for (Expr *Var : Clause.getVarList()) {
-    ExprResult Res = SemaRef.CheckReductionVar(Clause.getDirectiveKind(),
-                                               Clause.getReductionOp(), Var);
+using namespace clang;
 
-    if (Res.isUsable())
-      ValidVars.push_back(Res.get());
+namespace {
+bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K,
+                                    SourceLocation StartLoc, bool IsStmt) {
+  switch (K) {
+  default:
+  case OpenACCDirectiveKind::Invalid:
+    // Nothing to do here, both invalid and unimplemented don't really need to
+    // do anything.
+    break;
+  case OpenACCDirectiveKind::ParallelLoop:
+  case OpenACCDirectiveKind::SerialLoop:
+  case OpenACCDirectiveKind::KernelsLoop:
+  case OpenACCDirectiveKind::Parallel:
+  case OpenACCDirectiveKind::Serial:
+  case OpenACCDirectiveKind::Kernels:
+  case OpenACCDirectiveKind::Loop:
+  case OpenACCDirectiveKind::Data:
+  case OpenACCDirectiveKind::EnterData:
+  case OpenACCDirectiveKind::ExitData:
+  case OpenACCDirectiveKind::HostData:
+  case OpenACCDirectiveKind::Wait:
+    if (!IsStmt)
+      return S.Diag(StartLoc, diag::err_acc_construct_appertainment) << K;
+    break;
   }
-
-  return SemaRef.CheckReductionClause(
-      ExistingClauses, Clause.getDirectiveKind(), Clause.getBeginLoc(),
-      Clause.getLParenLoc(), Clause.getReductionOp(), ValidVars,
-      Clause.getEndLoc());
-}
-
-OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause(
-    SemaOpenACC::OpenACCParsedClause &Clause) {
-  // Duplicates here are not really sensible.  We could possible permit
-  // multiples if they all had the same value, but there isn't really a good
-  // reason to do so. Also, this simplifies the suppression of duplicates, in
-  // that we know if we 'find' one after instantiation, that it is the same
-  // clause, which simplifies instantiation/checking/etc.
-  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
-    return nullptr;
-
-  ExprResult LoopCount = SemaRef.CheckCollapseLoopCount(Clause.getLoopCount());
-
-  if (!LoopCount.isUsable())
-    return nullptr;
-
-  return OpenACCCollapseClause::Create(Ctx, Clause.getBeginLoc(),
-                                       Clause.getLParenLoc(), Clause.isForce(),
-                                       LoopCount.get(), Clause.getEndLoc());
+  return false;
 }
 
 void CollectActiveReductionClauses(
@@ -2172,190 +299,6 @@ SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
   }
 }
 
-OpenACCClause *
-SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
-                         OpenACCParsedClause &Clause) {
-  if (Clause.getClauseKind() == OpenACCClauseKind::Invalid)
-    return nullptr;
-
-  // Diagnose that we don't support this clause on this directive.
-  if (!doesClauseApplyToDirective(Clause.getDirectiveKind(),
-                                  Clause.getClauseKind())) {
-    Diag(Clause.getBeginLoc(), diag::err_acc_clause_appertainment)
-        << Clause.getDirectiveKind() << Clause.getClauseKind();
-    return nullptr;
-  }
-
-  if (const auto *DevTypeClause =
-          llvm::find_if(ExistingClauses,
-                        [&](const OpenACCClause *C) {
-                          return isa<OpenACCDeviceTypeClause>(C);
-                        });
-      DevTypeClause != ExistingClauses.end()) {
-    if (checkValidAfterDeviceType(
-            *this, *cast<OpenACCDeviceTypeClause>(*DevTypeClause), Clause))
-      return nullptr;
-  }
-
-  SemaOpenACCClauseVisitor Visitor{*this, ExistingClauses};
-  OpenACCClause *Result = Visitor.Visit(Clause);
-  assert((!Result || Result->getClauseKind() == Clause.getClauseKind()) &&
-         "Created wrong clause?");
-
-  if (Visitor.diagNotImplemented())
-    Diag(Clause.getBeginLoc(), diag::warn_acc_clause_unimplemented)
-        << Clause.getClauseKind();
-
-  return Result;
-
-}
-
-namespace {
-// Return true if the two vars refer to the same variable, for the purposes of
-// equality checking.
-bool areVarsEqual(Expr *VarExpr1, Expr *VarExpr2) {
-  if (VarExpr1->isInstantiationDependent() ||
-      VarExpr2->isInstantiationDependent())
-    return false;
-
-  VarExpr1 = VarExpr1->IgnoreParenCasts();
-  VarExpr2 = VarExpr2->IgnoreParenCasts();
-
-  // Legal expressions can be: Scalar variable reference, sub-array, array
-  // element, or composite variable member.
-
-  // Sub-array.
-  if (isa<ArraySectionExpr>(VarExpr1)) {
-    auto *Expr2AS = dyn_cast<ArraySectionExpr>(VarExpr2);
-    if (!Expr2AS)
-      return false;
-
-    auto *Expr1AS = cast<ArraySectionExpr>(VarExpr1);
-
-    if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
-      return false;
-    // We could possibly check to see if the ranges aren't overlapping, but it
-    // isn't clear that the rules allow this.
-    return true;
-  }
-
-  // Array-element.
-  if (isa<ArraySubscriptExpr>(VarExpr1)) {
-    auto *Expr2AS = dyn_cast<ArraySubscriptExpr>(VarExpr2);
-    if (!Expr2AS)
-      return false;
-
-    auto *Expr1AS = cast<ArraySubscriptExpr>(VarExpr1);
-
-    if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
-      return false;
-
-    // We could possibly check to see if the elements referenced aren't the
-    // same, but it isn't clear by reading of the standard that this is allowed
-    // (and that the 'var' refered to isn't the array).
-    return true;
-  }
-
-  // Scalar variable reference, or composite variable.
-  if (isa<DeclRefExpr>(VarExpr1)) {
-    auto *Expr2DRE = dyn_cast<DeclRefExpr>(VarExpr2);
-    if (!Expr2DRE)
-      return false;
-
-    auto *Expr1DRE = cast<DeclRefExpr>(VarExpr1);
-
-    return Expr1DRE->getDecl()->getMostRecentDecl() ==
-           Expr2DRE->getDecl()->getMostRecentDecl();
-  }
-
-  llvm_unreachable("Unknown variable type encountered");
-}
-} // namespace
-
-/// OpenACC 3.3 section 2.5.15:
-/// At a mininmum, the supported data types include ... the numerical data types
-/// in C, C++, and Fortran.
-///
-/// If the reduction var is a composite variable, each
-/// member of the composite variable must be a supported datatype for the
-/// reduction operation.
-ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind,
-                                          OpenACCReductionOperator ReductionOp,
-                                          Expr *VarExpr) {
-  VarExpr = VarExpr->IgnoreParenCasts();
-
-  auto TypeIsValid = [](QualType Ty) {
-    return Ty->isDependentType() || Ty->isScalarType();
-  };
-
-  if (isa<ArraySectionExpr>(VarExpr)) {
-    Expr *ASExpr = VarExpr;
-    QualType BaseTy = ArraySectionExpr::getBaseOriginalType(ASExpr);
-    QualType EltTy = getASTContext().getBaseElementType(BaseTy);
-
-    if (!TypeIsValid(EltTy)) {
-      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
-          << EltTy << /*Sub array base type*/ 1;
-      return ExprError();
-    }
-  } else if (auto *RD = VarExpr->getType()->getAsRecordDecl()) {
-    if (!RD->isStruct() && !RD->isClass()) {
-      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
-          << /*not class or struct*/ 0 << VarExpr->getType();
-      return ExprError();
-    }
-
-    if (!RD->isCompleteDefinition()) {
-      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
-          << /*incomplete*/ 1 << VarExpr->getType();
-      return ExprError();
-    }
-    if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
-        CXXRD && !CXXRD->isAggregate()) {
-      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
-          << /*aggregate*/ 2 << VarExpr->getType();
-      return ExprError();
-    }
-
-    for (FieldDecl *FD : RD->fields()) {
-      if (!TypeIsValid(FD->getType())) {
-        Diag(VarExpr->getExprLoc(),
-             diag::err_acc_reduction_composite_member_type);
-        Diag(FD->getLocation(), diag::note_acc_reduction_composite_member_loc);
-        return ExprError();
-      }
-    }
-  } else if (!TypeIsValid(VarExpr->getType())) {
-    Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
-        << VarExpr->getType() << /*Sub array base type*/ 0;
-    return ExprError();
-  }
-
-  // OpenACC3.3: 2.9.11: Reduction clauses on nested constructs for the same
-  // reduction 'var' must have the same reduction operator.
-  if (!VarExpr->isInstantiationDependent()) {
-
-    for (const OpenACCReductionClause *RClause : ActiveReductionClauses) {
-      if (RClause->getReductionOp() == ReductionOp)
-        break;
-
-      for (Expr *OldVarExpr : RClause->getVarList()) {
-        if (OldVarExpr->isInstantiationDependent())
-          continue;
-
-        if (areVarsEqual(VarExpr, OldVarExpr)) {
-          Diag(VarExpr->getExprLoc(), diag::err_reduction_op_mismatch)
-              << ReductionOp << RClause->getReductionOp();
-          Diag(OldVarExpr->getExprLoc(), diag::note_acc_previous_clause_here);
-          return ExprError();
-        }
-      }
-    }
-  }
-
-  return VarExpr;
-}
-
 void SemaOpenACC::ActOnConstruct(OpenACCDirectiveKind K,
                                  SourceLocation DirLoc) {
   // Start an evaluation context to parse the clause arguments on.
@@ -2809,181 +752,6 @@ ExprResult SemaOpenACC::ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc,
                        OK_Ordinary, ColonLoc, RBLoc);
 }
 
-ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) {
-  if (!LoopCount)
-    return ExprError();
-
-  assert((LoopCount->isInstantiationDependent() ||
-          LoopCount->getType()->isIntegerType()) &&
-         "Loop argument non integer?");
-
-  // If this is dependent, there really isn't anything we can check.
-  if (LoopCount->isInstantiationDependent())
-    return ExprResult{LoopCount};
-
-  std::optional<llvm::APSInt> ICE =
-      LoopCount->getIntegerConstantExpr(getASTContext());
-
-  // OpenACC 3.3: 2.9.1
-  // The argument to the collapse clause must be a constant positive integer
-  // expression.
-  if (!ICE || *ICE <= 0) {
-    Diag(LoopCount->getBeginLoc(), diag::err_acc_collapse_loop_count)
-        << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
-    return ExprError();
-  }
-
-  return ExprResult{
-      ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})};
-}
-
-ExprResult
-SemaOpenACC::CheckGangExpr(ArrayRef<const OpenACCClause *> ExistingClauses,
-                           OpenACCDirectiveKind DK, OpenACCGangKind GK,
-                           Expr *E) {
-  // There are two cases for the enforcement here: the 'current' directive is a
-  // 'loop', where we need to check the active compute construct kind, or the
-  // current directive is a 'combined' construct, where we have to check the
-  // current one.
-  switch (DK) {
-  case OpenACCDirectiveKind::ParallelLoop:
-    return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
-                                 E);
-  case OpenACCDirectiveKind::SerialLoop:
-    return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
-                               E);
-  case OpenACCDirectiveKind::KernelsLoop:
-    return CheckGangKernelsExpr(*this, ExistingClauses, DK,
-                                ActiveComputeConstructInfo.Kind, GK, E);
-  case OpenACCDirectiveKind::Loop:
-    switch (ActiveComputeConstructInfo.Kind) {
-    case OpenACCDirectiveKind::Invalid:
-    case OpenACCDirectiveKind::Parallel:
-    case OpenACCDirectiveKind::ParallelLoop:
-      return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind,
-                                   GK, E);
-    case OpenACCDirectiveKind::SerialLoop:
-    case OpenACCDirectiveKind::Serial:
-      return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
-                                 E);
-    case OpenACCDirectiveKind::KernelsLoop:
-    case OpenACCDirectiveKind::Kernels:
-      return CheckGangKernelsExpr(*this, ExistingClauses, DK,
-                                  ActiveComputeConstructInfo.Kind, GK, E);
-    default:
-      llvm_unreachable("Non compute construct in active compute construct?");
-    }
-  default:
-    // TODO: OpenACC: when we implement this on 'routine', we'll have to
-    // implement its checking here.
-    llvm_unreachable("Invalid directive kind for a Gang clause");
-  }
-  llvm_unreachable("Compute construct directive not handled?");
-}
-
-OpenACCClause *
-SemaOpenACC::CheckGangClause(OpenACCDirectiveKind DirKind,
-                             ArrayRef<const OpenACCClause *> ExistingClauses,
-                             SourceLocation BeginLoc, SourceLocation LParenLoc,
-                             ArrayRef<OpenACCGangKind> GangKinds,
-                             ArrayRef<Expr *> IntExprs, SourceLocation EndLoc) {
-  // OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
-  // that has a gang clause with a dim: argument whose value is greater than 1.
-
-  const auto *ReductionItr =
-      llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
-
-  if (ReductionItr != ExistingClauses.end()) {
-    const auto GangZip = llvm::zip_equal(GangKinds, IntExprs);
-    const auto GangItr = llvm::find_if(GangZip, [](const auto &Tuple) {
-      return std::get<0>(Tuple) == OpenACCGangKind::Dim;
-    });
-
-    if (GangItr != GangZip.end()) {
-      const Expr *DimExpr = std::get<1>(*GangItr);
-
-      assert(
-          (DimExpr->isInstantiationDependent() || isa<ConstantExpr>(DimExpr)) &&
-          "Improperly formed gang argument");
-      if (const auto *DimVal = dyn_cast<ConstantExpr>(DimExpr);
-          DimVal && DimVal->getResultAsAPSInt() > 1) {
-        Diag(DimVal->getBeginLoc(), diag::err_acc_gang_reduction_conflict)
-            << /*gang/reduction=*/0 << DirKind;
-        Diag((*ReductionItr)->getBeginLoc(),
-             diag::note_acc_previous_clause_here);
-        return nullptr;
-      }
-    }
-  }
-
-  return OpenACCGangClause::Create(getASTContext(), BeginLoc, LParenLoc,
-                                   GangKinds, IntExprs, EndLoc);
-}
-
-OpenACCClause *SemaOpenACC::CheckReductionClause(
-    ArrayRef<const OpenACCClause *> ExistingClauses,
-    OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
-    SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
-    ArrayRef<Expr *> Vars, SourceLocation EndLoc) {
-  if (DirectiveKind == OpenACCDirectiveKind::Loop ||
-      isOpenACCCombinedDirectiveKind(DirectiveKind)) {
-    // OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
-    // that has a gang clause with a dim: argument whose value is greater
-    // than 1.
-    const auto GangClauses = llvm::make_filter_range(
-        ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
-
-    for (auto *GC : GangClauses) {
-      const auto *GangClause = cast<OpenACCGangClause>(GC);
-      for (unsigned I = 0; I < GangClause->getNumExprs(); ++I) {
-        std::pair<OpenACCGangKind, const Expr *> EPair = GangClause->getExpr(I);
-        if (EPair.first != OpenACCGangKind::Dim)
-          continue;
-
-        if (const auto *DimVal = dyn_cast<ConstantExpr>(EPair.second);
-            DimVal && DimVal->getResultAsAPSInt() > 1) {
-          Diag(BeginLoc, diag::err_acc_gang_reduction_conflict)
-              << /*reduction/gang=*/1 << DirectiveKind;
-          Diag(GangClause->getBeginLoc(), diag::note_acc_previous_clause_here);
-          return nullptr;
-        }
-      }
-    }
-  }
-
-  auto *Ret = OpenACCReductionClause::Create(
-      getASTContext(), BeginLoc, LParenLoc, ReductionOp, Vars, EndLoc);
-  return Ret;
-}
-
-ExprResult SemaOpenACC::CheckTileSizeExpr(Expr *SizeExpr) {
-  if (!SizeExpr)
-    return ExprError();
-
-  assert((SizeExpr->isInstantiationDependent() ||
-          SizeExpr->getType()->isIntegerType()) &&
-         "size argument non integer?");
-
-  // If dependent, or an asterisk, the expression is fine.
-  if (SizeExpr->isInstantiationDependent() ||
-      isa<OpenACCAsteriskSizeExpr>(SizeExpr))
-    return ExprResult{SizeExpr};
-
-  std::optional<llvm::APSInt> ICE =
-      SizeExpr->getIntegerConstantExpr(getASTContext());
-
-  // OpenACC 3.3 2.9.8
-  // where each tile size is a constant positive integer expression or asterisk.
-  if (!ICE || *ICE <= 0) {
-    Diag(SizeExpr->getBeginLoc(), diag::err_acc_size_expr_value)
-        << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
-    return ExprError();
-  }
-
-  return ExprResult{
-      ConstantExpr::Create(getASTContext(), SizeExpr, APValue{*ICE})};
-}
-
 void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) {
   if (!getLangOpts().OpenACC)
     return;

diff  --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp
new file mode 100644
index 00000000000000..27da14de4c04f5
--- /dev/null
+++ b/clang/lib/Sema/SemaOpenACCClause.cpp
@@ -0,0 +1,2247 @@
+//===--- SemaOpenACCClause.cpp - Semantic Analysis for OpenACC clause -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements semantic analysis for OpenACC clauses.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/OpenACCClause.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/OpenACCKinds.h"
+#include "clang/Sema/SemaOpenACC.h"
+
+using namespace clang;
+
+namespace {
+bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind,
+                                OpenACCClauseKind ClauseKind) {
+  switch (ClauseKind) {
+    // FIXME: For each clause as we implement them, we can add the
+    // 'legalization' list here.
+  case OpenACCClauseKind::Default:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+    case OpenACCDirectiveKind::Data:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::If:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::ExitData:
+    case OpenACCDirectiveKind::HostData:
+    case OpenACCDirectiveKind::Init:
+    case OpenACCDirectiveKind::Shutdown:
+    case OpenACCDirectiveKind::Set:
+    case OpenACCDirectiveKind::Update:
+    case OpenACCDirectiveKind::Wait:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Self:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Update:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::NumGangs:
+  case OpenACCClauseKind::NumWorkers:
+  case OpenACCClauseKind::VectorLength:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::FirstPrivate:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Private:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::NoCreate:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Present:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::Declare:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Copy:
+  case OpenACCClauseKind::PCopy:
+  case OpenACCClauseKind::PresentOrCopy:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::Declare:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::CopyIn:
+  case OpenACCClauseKind::PCopyIn:
+  case OpenACCClauseKind::PresentOrCopyIn:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::Declare:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::CopyOut:
+  case OpenACCClauseKind::PCopyOut:
+  case OpenACCClauseKind::PresentOrCopyOut:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::ExitData:
+    case OpenACCDirectiveKind::Declare:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Create:
+  case OpenACCClauseKind::PCreate:
+  case OpenACCClauseKind::PresentOrCreate:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Attach:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::DevicePtr:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::Declare:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Async:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::ExitData:
+    case OpenACCDirectiveKind::Set:
+    case OpenACCDirectiveKind::Update:
+    case OpenACCDirectiveKind::Wait:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Wait:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::EnterData:
+    case OpenACCDirectiveKind::ExitData:
+    case OpenACCDirectiveKind::Update:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Seq:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::Routine:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Independent:
+  case OpenACCClauseKind::Auto:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Reduction:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::DeviceType:
+  case OpenACCClauseKind::DType:
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::Serial:
+    case OpenACCDirectiveKind::Kernels:
+    case OpenACCDirectiveKind::Data:
+    case OpenACCDirectiveKind::Init:
+    case OpenACCDirectiveKind::Shutdown:
+    case OpenACCDirectiveKind::Set:
+    case OpenACCDirectiveKind::Update:
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::Routine:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+
+  case OpenACCClauseKind::Collapse: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Tile: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  case OpenACCClauseKind::Gang: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+    case OpenACCDirectiveKind::Routine:
+      return true;
+    default:
+      return false;
+    }
+  case OpenACCClauseKind::Worker: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+    case OpenACCDirectiveKind::Routine:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Vector: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Loop:
+    case OpenACCDirectiveKind::ParallelLoop:
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::KernelsLoop:
+    case OpenACCDirectiveKind::Routine:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Finalize: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::ExitData:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::IfPresent: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::HostData:
+    case OpenACCDirectiveKind::Update:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Delete: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::ExitData:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  case OpenACCClauseKind::Detach: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::ExitData:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  case OpenACCClauseKind::DeviceNum: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Init:
+    case OpenACCDirectiveKind::Shutdown:
+    case OpenACCDirectiveKind::Set:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  case OpenACCClauseKind::UseDevice: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::HostData:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::DefaultAsync: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Set:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Device: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Update:
+      return true;
+    default:
+      return false;
+    }
+  }
+  case OpenACCClauseKind::Host: {
+    switch (DirectiveKind) {
+    case OpenACCDirectiveKind::Update:
+      return true;
+    default:
+      return false;
+    }
+  }
+  }
+
+  default:
+    // Do nothing so we can go to the 'unimplemented' diagnostic instead.
+    return true;
+  }
+  llvm_unreachable("Invalid clause kind");
+}
+
+bool checkAlreadyHasClauseOfKind(
+    SemaOpenACC &S, ArrayRef<const OpenACCClause *> ExistingClauses,
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  const auto *Itr = llvm::find_if(ExistingClauses, [&](const OpenACCClause *C) {
+    return C->getClauseKind() == Clause.getClauseKind();
+  });
+  if (Itr != ExistingClauses.end()) {
+    S.Diag(Clause.getBeginLoc(), diag::err_acc_duplicate_clause_disallowed)
+        << Clause.getDirectiveKind() << Clause.getClauseKind();
+    S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    return true;
+  }
+  return false;
+}
+bool checkValidAfterDeviceType(
+    SemaOpenACC &S, const OpenACCDeviceTypeClause &DeviceTypeClause,
+    const SemaOpenACC::OpenACCParsedClause &NewClause) {
+  // This is implemented for everything but 'routine', so treat as 'fine' for
+  // that.
+  if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Routine)
+    return false;
+
+  // OpenACC3.3: Section 2.4: Clauses that precede any device_type clause are
+  // default clauses.  Clauses that follow a device_type clause up to the end of
+  // the directive or up to the next device_type clause are device-specific
+  // clauses for the device types specified in the device_type argument.
+  //
+  // The above implies that despite what the individual text says, these are
+  // valid.
+  if (NewClause.getClauseKind() == OpenACCClauseKind::DType ||
+      NewClause.getClauseKind() == OpenACCClauseKind::DeviceType)
+    return false;
+
+  // Implement check from OpenACC3.3: section 2.5.4:
+  // Only the async, wait, num_gangs, num_workers, and vector_length clauses may
+  // follow a device_type clause.
+  if (isOpenACCComputeDirectiveKind(NewClause.getDirectiveKind())) {
+    switch (NewClause.getClauseKind()) {
+    case OpenACCClauseKind::Async:
+    case OpenACCClauseKind::Wait:
+    case OpenACCClauseKind::NumGangs:
+    case OpenACCClauseKind::NumWorkers:
+    case OpenACCClauseKind::VectorLength:
+      return false;
+    default:
+      break;
+    }
+  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
+    // Implement check from OpenACC3.3: section 2.9:
+    // Only the collapse, gang, worker, vector, seq, independent, auto, and tile
+    // clauses may follow a device_type clause.
+    switch (NewClause.getClauseKind()) {
+    case OpenACCClauseKind::Collapse:
+    case OpenACCClauseKind::Gang:
+    case OpenACCClauseKind::Worker:
+    case OpenACCClauseKind::Vector:
+    case OpenACCClauseKind::Seq:
+    case OpenACCClauseKind::Independent:
+    case OpenACCClauseKind::Auto:
+    case OpenACCClauseKind::Tile:
+      return false;
+    default:
+      break;
+    }
+  } else if (isOpenACCCombinedDirectiveKind(NewClause.getDirectiveKind())) {
+    // This seems like it should be the union of 2.9 and 2.5.4 from above.
+    switch (NewClause.getClauseKind()) {
+    case OpenACCClauseKind::Async:
+    case OpenACCClauseKind::Wait:
+    case OpenACCClauseKind::NumGangs:
+    case OpenACCClauseKind::NumWorkers:
+    case OpenACCClauseKind::VectorLength:
+    case OpenACCClauseKind::Collapse:
+    case OpenACCClauseKind::Gang:
+    case OpenACCClauseKind::Worker:
+    case OpenACCClauseKind::Vector:
+    case OpenACCClauseKind::Seq:
+    case OpenACCClauseKind::Independent:
+    case OpenACCClauseKind::Auto:
+    case OpenACCClauseKind::Tile:
+      return false;
+    default:
+      break;
+    }
+  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Data) {
+    // OpenACC3.3 section 2.6.5: Only the async and wait clauses may follow a
+    // device_type clause.
+    switch (NewClause.getClauseKind()) {
+    case OpenACCClauseKind::Async:
+    case OpenACCClauseKind::Wait:
+      return false;
+    default:
+      break;
+    }
+  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Set ||
+             NewClause.getDirectiveKind() == OpenACCDirectiveKind::Init ||
+             NewClause.getDirectiveKind() == OpenACCDirectiveKind::Shutdown) {
+    // There are no restrictions on 'set', 'init', or 'shutdown'.
+    return false;
+  } else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Update) {
+    // OpenACC3.3 section 2.14.4: Only the async and wait clauses may follow a
+    // device_type clause.
+    switch (NewClause.getClauseKind()) {
+    case OpenACCClauseKind::Async:
+    case OpenACCClauseKind::Wait:
+      return false;
+    default:
+      break;
+    }
+  }
+  S.Diag(NewClause.getBeginLoc(), diag::err_acc_clause_after_device_type)
+      << NewClause.getClauseKind() << DeviceTypeClause.getClauseKind()
+      << NewClause.getDirectiveKind();
+  S.Diag(DeviceTypeClause.getBeginLoc(), diag::note_acc_previous_clause_here);
+  return true;
+}
+
+// A temporary function that helps implement the 'not implemented' check at the
+// top of each clause checking function. This should only be used in conjunction
+// with the one being currently implemented/only updated after the entire
+// construct has been implemented.
+bool isDirectiveKindImplemented(OpenACCDirectiveKind DK) {
+  return DK != OpenACCDirectiveKind::Declare &&
+         DK != OpenACCDirectiveKind::Atomic &&
+         DK != OpenACCDirectiveKind::Routine;
+}
+
+class SemaOpenACCClauseVisitor {
+  SemaOpenACC &SemaRef;
+  ASTContext &Ctx;
+  ArrayRef<const OpenACCClause *> ExistingClauses;
+  bool NotImplemented = false;
+
+  OpenACCClause *isNotImplemented() {
+    NotImplemented = true;
+    return nullptr;
+  }
+
+  // OpenACC 3.3 2.9:
+  // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
+  // appears.
+  bool DiagIfSeqClause(SemaOpenACC::OpenACCParsedClause &Clause) {
+    const auto *Itr =
+        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSeqClause>);
+
+    if (Itr != ExistingClauses.end()) {
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
+          << Clause.getClauseKind() << (*Itr)->getClauseKind()
+          << Clause.getDirectiveKind();
+      SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+
+      return true;
+    }
+    return false;
+  }
+
+public:
+  SemaOpenACCClauseVisitor(SemaOpenACC &S,
+                           ArrayRef<const OpenACCClause *> ExistingClauses)
+      : SemaRef(S), Ctx(S.getASTContext()), ExistingClauses(ExistingClauses) {}
+  // Once we've implemented everything, we shouldn't need this infrastructure.
+  // But in the meantime, we use this to help decide whether the clause was
+  // handled for this directive.
+  bool diagNotImplemented() { return NotImplemented; }
+
+  OpenACCClause *Visit(SemaOpenACC::OpenACCParsedClause &Clause) {
+    switch (Clause.getClauseKind()) {
+#define VISIT_CLAUSE(CLAUSE_NAME)                                              \
+  case OpenACCClauseKind::CLAUSE_NAME:                                         \
+    return Visit##CLAUSE_NAME##Clause(Clause);
+#define CLAUSE_ALIAS(ALIAS, CLAUSE_NAME, DEPRECATED)                           \
+  case OpenACCClauseKind::ALIAS:                                               \
+  if (DEPRECATED)                                                              \
+    SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_deprecated_alias_name)   \
+        << Clause.getClauseKind() << OpenACCClauseKind::CLAUSE_NAME;           \
+  return Visit##CLAUSE_NAME##Clause(Clause);
+#include "clang/Basic/OpenACCClauses.def"
+    default:
+      return isNotImplemented();
+    }
+    llvm_unreachable("Invalid clause kind");
+  }
+
+#define VISIT_CLAUSE(CLAUSE_NAME)                                              \
+  OpenACCClause *Visit##CLAUSE_NAME##Clause(                                   \
+      SemaOpenACC::OpenACCParsedClause &Clause);
+#include "clang/Basic/OpenACCClauses.def"
+};
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Don't add an invalid clause to the AST.
+  if (Clause.getDefaultClauseKind() == OpenACCDefaultClauseKind::Invalid)
+    return nullptr;
+
+  // OpenACC 3.3, Section 2.5.4:
+  // At most one 'default' clause may appear, and it must have a value of
+  // either 'none' or 'present'.
+  // Second half of the sentence is diagnosed during parsing.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  return OpenACCDefaultClause::Create(
+      Ctx, Clause.getDefaultClauseKind(), Clause.getBeginLoc(),
+      Clause.getLParenLoc(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitTileClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+
+  // Duplicates here are not really sensible.  We could possible permit
+  // multiples if they all had the same value, but there isn't really a good
+  // reason to do so. Also, this simplifies the suppression of duplicates, in
+  // that we know if we 'find' one after instantiation, that it is the same
+  // clause, which simplifies instantiation/checking/etc.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  llvm::SmallVector<Expr *> NewSizeExprs;
+
+  // Make sure these are all positive constant expressions or *.
+  for (Expr *E : Clause.getIntExprs()) {
+    ExprResult Res = SemaRef.CheckTileSizeExpr(E);
+
+    if (!Res.isUsable())
+      return nullptr;
+
+    NewSizeExprs.push_back(Res.get());
+  }
+
+  return OpenACCTileClause::Create(Ctx, Clause.getBeginLoc(),
+                                   Clause.getLParenLoc(), NewSizeExprs,
+                                   Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitIfClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense. Prose DOES exist for 'data' and 'host_data', 'set', 'enter data' and
+  // 'exit data' both don't, but other implmementations do this.  OpenACC issue
+  // 519 filed for the latter two. Prose also exists for 'update'.
+  // GCC allows this on init/shutdown, presumably for good reason, so we do too.
+  if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Init &&
+      Clause.getDirectiveKind() != OpenACCDirectiveKind::Shutdown &&
+      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // The parser has ensured that we have a proper condition expr, so there
+  // isn't really much to do here.
+
+  // If the 'if' clause is true, it makes the 'self' clause have no effect,
+  // diagnose that here.  This only applies on compute/combined constructs.
+  if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Update) {
+    const auto *Itr =
+        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSelfClause>);
+    if (Itr != ExistingClauses.end()) {
+      SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
+      SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    }
+  }
+
+  return OpenACCIfClause::Create(Ctx, Clause.getBeginLoc(),
+                                 Clause.getLParenLoc(),
+                                 Clause.getConditionExpr(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitSelfClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // If the 'if' clause is true, it makes the 'self' clause have no effect,
+  // diagnose that here.  This only applies on compute/combined constructs.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Update)
+    return OpenACCSelfClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), Clause.getVarList(),
+                                     Clause.getEndLoc());
+
+  const auto *Itr =
+      llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCIfClause>);
+  if (Itr != ExistingClauses.end()) {
+    SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
+    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+  }
+  return OpenACCSelfClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.getConditionExpr(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitNumGangsClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // num_gangs requires at least 1 int expr in all forms.  Diagnose here, but
+  // allow us to continue, an empty clause might be useful for future
+  // diagnostics.
+  if (Clause.getIntExprs().empty())
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
+        << /*NoArgs=*/0;
+
+  unsigned MaxArgs =
+      (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
+       Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop)
+          ? 3
+          : 1;
+  // The max number of args 
diff ers between parallel and other constructs.
+  // Again, allow us to continue for the purposes of future diagnostics.
+  if (Clause.getIntExprs().size() > MaxArgs)
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
+        << /*NoArgs=*/1 << Clause.getDirectiveKind() << MaxArgs
+        << Clause.getIntExprs().size();
+
+  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
+  // directive that has a gang clause and is within a compute construct that has
+  // a num_gangs clause with more than one explicit argument.
+  if (Clause.getIntExprs().size() > 1 &&
+      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    auto *GangClauseItr =
+        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
+    auto *ReductionClauseItr =
+        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
+
+    if (GangClauseItr != ExistingClauses.end() &&
+        ReductionClauseItr != ExistingClauses.end()) {
+      SemaRef.Diag(Clause.getBeginLoc(),
+                   diag::err_acc_gang_reduction_numgangs_conflict)
+          << OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
+          << Clause.getDirectiveKind() << /*is on combined directive=*/1;
+      SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
+                   diag::note_acc_previous_clause_here);
+      SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+  }
+
+  // OpenACC 3.3 Section 2.5.4:
+  // A reduction clause may not appear on a parallel construct with a
+  // num_gangs clause that has more than one argument.
+  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
+       Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) &&
+      Clause.getIntExprs().size() > 1) {
+    auto *Parallel =
+        llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
+
+    if (Parallel != ExistingClauses.end()) {
+      SemaRef.Diag(Clause.getBeginLoc(),
+                   diag::err_acc_reduction_num_gangs_conflict)
+          << /*>1 arg in first loc=*/1 << Clause.getClauseKind()
+          << Clause.getDirectiveKind() << OpenACCClauseKind::Reduction;
+      SemaRef.Diag((*Parallel)->getBeginLoc(),
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+  }
+
+  // OpenACC 3.3 Section 2.9.2:
+  // An argument with no keyword or with the 'num' keyword is allowed only when
+  // the 'num_gangs' does not appear on the 'kernel' construct.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
+    auto GangClauses = llvm::make_filter_range(
+        ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
+
+    for (auto *GC : GangClauses) {
+      if (cast<OpenACCGangClause>(GC)->hasExprOfKind(OpenACCGangKind::Num)) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_num_arg_conflict_reverse)
+            << OpenACCClauseKind::NumGangs << OpenACCClauseKind::Gang
+            << /*Num argument*/ 1;
+        SemaRef.Diag(GC->getBeginLoc(), diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  return OpenACCNumGangsClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs(),
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitNumWorkersClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // OpenACC 3.3 Section 2.9.2:
+  // An argument is allowed only when the 'num_workers' does not appear on the
+  // kernels construct.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
+    auto WorkerClauses = llvm::make_filter_range(
+        ExistingClauses, llvm::IsaPred<OpenACCWorkerClause>);
+
+    for (auto *WC : WorkerClauses) {
+      if (cast<OpenACCWorkerClause>(WC)->hasIntExpr()) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_num_arg_conflict_reverse)
+            << OpenACCClauseKind::NumWorkers << OpenACCClauseKind::Worker
+            << /*num argument*/ 0;
+        SemaRef.Diag(WC->getBeginLoc(), diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  assert(Clause.getIntExprs().size() == 1 &&
+         "Invalid number of expressions for NumWorkers");
+  return OpenACCNumWorkersClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorLengthClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // OpenACC 3.3 Section 2.9.4:
+  // An argument is allowed only when the 'vector_length' does not appear on the
+  // 'kernels' construct.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
+    auto VectorClauses = llvm::make_filter_range(
+        ExistingClauses, llvm::IsaPred<OpenACCVectorClause>);
+
+    for (auto *VC : VectorClauses) {
+      if (cast<OpenACCVectorClause>(VC)->hasIntExpr()) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_num_arg_conflict_reverse)
+            << OpenACCClauseKind::VectorLength << OpenACCClauseKind::Vector
+            << /*num argument*/ 0;
+        SemaRef.Diag(VC->getBeginLoc(), diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  assert(Clause.getIntExprs().size() == 1 &&
+         "Invalid number of expressions for NumWorkers");
+  return OpenACCVectorLengthClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitAsyncClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There is no prose in the standard that says duplicates aren't allowed,
+  // but this diagnostic is present in other compilers, as well as makes
+  // sense.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  assert(Clause.getNumIntExprs() < 2 &&
+         "Invalid number of expressions for Async");
+  return OpenACCAsyncClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr,
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceNumClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on certain constructs, so skip/treat
+  // as unimplemented in those cases.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
+  // same directive.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
+      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  assert(Clause.getNumIntExprs() == 1 &&
+         "Invalid number of expressions for device_num");
+  return OpenACCDeviceNumClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultAsyncClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
+  // same directive.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  assert(Clause.getNumIntExprs() == 1 &&
+         "Invalid number of expressions for default_async");
+  return OpenACCDefaultAsyncClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitPrivateClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCPrivateClause::Create(Ctx, Clause.getBeginLoc(),
+                                      Clause.getLParenLoc(),
+                                      Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCFirstPrivateClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitNoCreateClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCNoCreateClause::Create(Ctx, Clause.getBeginLoc(),
+                                       Clause.getLParenLoc(),
+                                       Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitPresentClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'compute'/'combined'/'data'
+  // constructs, and 'compute'/'combined'/'data' constructs are the only
+  // construct that can do anything with this yet, so skip/treat as
+  // unimplemented in this case.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCPresentClause::Create(Ctx, Clause.getBeginLoc(),
+                                      Clause.getLParenLoc(),
+                                      Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitHostClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCHostClause::Create(Ctx, Clause.getBeginLoc(),
+                                   Clause.getLParenLoc(), Clause.getVarList(),
+                                   Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCDeviceClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), Clause.getVarList(),
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'compute'/'combined'/'data'
+  // constructs, and 'compute'/'combined'/'data' constructs are the only
+  // construct that can do anything with this yet, so skip/treat as
+  // unimplemented in this case.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCCopyClause::Create(
+      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyInClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'compute'/'combined'/'data'
+  // constructs, and 'compute'/'combined'/'data' constructs are the only
+  // construct that can do anything with this yet, so skip/treat as
+  // unimplemented in this case.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCCopyInClause::Create(
+      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.isReadOnly(), Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'compute'/'combined'/'data'
+  // constructs, and 'compute'/'combined'/'data' constructs are the only
+  // construct that can do anything with this yet, so skip/treat as
+  // unimplemented in this case.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCCopyOutClause::Create(
+      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+
+  return OpenACCCreateClause::Create(
+      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, but we
+  // still have to make sure it is a pointer type.
+  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
+  llvm::erase_if(VarList, [&](Expr *E) {
+    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Attach, E);
+  });
+  Clause.setVarListDetails(VarList,
+                           /*IsReadOnly=*/false, /*IsZero=*/false);
+  return OpenACCAttachClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), Clause.getVarList(),
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDetachClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, but we
+  // still have to make sure it is a pointer type.
+  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
+  llvm::erase_if(VarList, [&](Expr *E) {
+    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Detach, E);
+  });
+  Clause.setVarListDetails(VarList,
+                           /*IsReadOnly=*/false, /*IsZero=*/false);
+  return OpenACCDetachClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), Clause.getVarList(),
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDeleteClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable reference, so there
+  // really isn't anything to do here. GCC does some duplicate-finding, though
+  // it isn't apparent in the standard where this is justified.
+  return OpenACCDeleteClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), Clause.getVarList(),
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitUseDeviceClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // ActOnVar ensured that everything is a valid variable or array, so nothing
+  // left to do here.
+  return OpenACCUseDeviceClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'compute'/'combined'/'data'
+  // constructs, and 'compute'/'combined'/'data' constructs are the only
+  // construct that can do anything with this yet, so skip/treat as
+  // unimplemented in this case.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  // ActOnVar ensured that everything is a valid variable reference, but we
+  // still have to make sure it is a pointer type.
+  llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
+  llvm::erase_if(VarList, [&](Expr *E) {
+    return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::DevicePtr, E);
+  });
+  Clause.setVarListDetails(VarList,
+                           /*IsReadOnly=*/false, /*IsZero=*/false);
+
+  return OpenACCDevicePtrClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  return OpenACCWaitClause::Create(
+      Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getDevNumExpr(),
+      Clause.getQueuesLoc(), Clause.getQueueIdExprs(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceTypeClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions implemented properly on everything except 'routine'.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine)
+    return isNotImplemented();
+
+  // OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
+  // same directive.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
+      checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  // TODO OpenACC: Once we get enough of the CodeGen implemented that we have
+  // a source for the list of valid architectures, we need to warn on unknown
+  // identifiers here.
+
+  return OpenACCDeviceTypeClause::Create(
+      Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
+      Clause.getDeviceTypeArchitectures(), Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitAutoClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // OpenACC 3.3 2.9:
+  // Only one of the seq, independent, and auto clauses may appear.
+  const auto *Itr =
+      llvm::find_if(ExistingClauses,
+                    llvm::IsaPred<OpenACCIndependentClause, OpenACCSeqClause>);
+  if (Itr != ExistingClauses.end()) {
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
+        << Clause.getClauseKind() << Clause.getDirectiveKind();
+    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    return nullptr;
+  }
+
+  return OpenACCAutoClause::Create(Ctx, Clause.getBeginLoc(),
+                                   Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitIndependentClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // OpenACC 3.3 2.9:
+  // Only one of the seq, independent, and auto clauses may appear.
+  const auto *Itr = llvm::find_if(
+      ExistingClauses, llvm::IsaPred<OpenACCAutoClause, OpenACCSeqClause>);
+  if (Itr != ExistingClauses.end()) {
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
+        << Clause.getClauseKind() << Clause.getDirectiveKind();
+    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    return nullptr;
+  }
+
+  return OpenACCIndependentClause::Create(Ctx, Clause.getBeginLoc(),
+                                          Clause.getEndLoc());
+}
+
+ExprResult CheckGangStaticExpr(SemaOpenACC &S, Expr *E) {
+  if (isa<OpenACCAsteriskSizeExpr>(E))
+    return E;
+  return S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
+                        E->getBeginLoc(), E);
+}
+
+bool IsOrphanLoop(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
+  return DK == OpenACCDirectiveKind::Loop &&
+         AssocKind == OpenACCDirectiveKind::Invalid;
+}
+
+bool HasAssocKind(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
+  return DK == OpenACCDirectiveKind::Loop &&
+         AssocKind != OpenACCDirectiveKind::Invalid;
+}
+
+ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, OpenACCGangKind GK,
+                             OpenACCClauseKind CK, OpenACCDirectiveKind DK,
+                             OpenACCDirectiveKind AssocKind) {
+  S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
+      << GK << CK << IsOrphanLoop(DK, AssocKind) << DK
+      << HasAssocKind(DK, AssocKind) << AssocKind;
+  return ExprError();
+}
+ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, StringRef TagKind,
+                             OpenACCClauseKind CK, OpenACCDirectiveKind DK,
+                             OpenACCDirectiveKind AssocKind) {
+  S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
+      << TagKind << CK << IsOrphanLoop(DK, AssocKind) << DK
+      << HasAssocKind(DK, AssocKind) << AssocKind;
+  return ExprError();
+}
+
+ExprResult CheckGangParallelExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
+                                 OpenACCDirectiveKind AssocKind,
+                                 OpenACCGangKind GK, Expr *E) {
+  switch (GK) {
+  case OpenACCGangKind::Static:
+    return CheckGangStaticExpr(S, E);
+  case OpenACCGangKind::Num:
+    // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
+    // construct, or an orphaned loop construct, the gang clause behaves as
+    // follows. ... The num argument is not allowed.
+    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
+  case OpenACCGangKind::Dim: {
+    // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
+    // construct, or an orphaned loop construct, the gang clause behaves as
+    // follows. ... The dim argument must be a constant positive integer value
+    // 1, 2, or 3.
+    if (!E)
+      return ExprError();
+    ExprResult Res =
+        S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
+                       E->getBeginLoc(), E);
+
+    if (!Res.isUsable())
+      return Res;
+
+    if (Res.get()->isInstantiationDependent())
+      return Res;
+
+    std::optional<llvm::APSInt> ICE =
+        Res.get()->getIntegerConstantExpr(S.getASTContext());
+
+    if (!ICE || *ICE <= 0 || ICE > 3) {
+      S.Diag(Res.get()->getBeginLoc(), diag::err_acc_gang_dim_value)
+          << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
+      return ExprError();
+    }
+
+    return ExprResult{
+        ConstantExpr::Create(S.getASTContext(), Res.get(), APValue{*ICE})};
+  }
+  }
+  llvm_unreachable("Unknown gang kind in gang parallel check");
+}
+
+ExprResult CheckGangKernelsExpr(SemaOpenACC &S,
+                                ArrayRef<const OpenACCClause *> ExistingClauses,
+                                OpenACCDirectiveKind DK,
+                                OpenACCDirectiveKind AssocKind,
+                                OpenACCGangKind GK, Expr *E) {
+  switch (GK) {
+  // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
+  // construct, the gang clause behaves as follows. ... The dim argument is
+  // not allowed.
+  case OpenACCGangKind::Dim:
+    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
+  case OpenACCGangKind::Num: {
+    // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
+    // construct, the gang clause behaves as follows. ... An argument with no
+    // keyword or with num keyword is only allowed when num_gangs does not
+    // appear on the kernels construct. ... The region of a loop with the gang
+    // clause may not contain another loop with a gang clause unless within a
+    // nested compute region.
+
+    // If this is a 'combined' construct, search the list of existing clauses.
+    // Else we need to search the containing 'kernel'.
+    auto Collection = isOpenACCCombinedDirectiveKind(DK)
+                          ? ExistingClauses
+                          : S.getActiveComputeConstructInfo().Clauses;
+
+    const auto *Itr =
+        llvm::find_if(Collection, llvm::IsaPred<OpenACCNumGangsClause>);
+
+    if (Itr != Collection.end()) {
+      S.Diag(E->getBeginLoc(), diag::err_acc_num_arg_conflict)
+          << "num" << OpenACCClauseKind::Gang << DK
+          << HasAssocKind(DK, AssocKind) << AssocKind
+          << OpenACCClauseKind::NumGangs;
+
+      S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+      return ExprError();
+    }
+    return ExprResult{E};
+  }
+  case OpenACCGangKind::Static:
+    return CheckGangStaticExpr(S, E);
+    return ExprError();
+  }
+  llvm_unreachable("Unknown gang kind in gang kernels check");
+}
+
+ExprResult CheckGangSerialExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
+                               OpenACCDirectiveKind AssocKind,
+                               OpenACCGangKind GK, Expr *E) {
+  switch (GK) {
+  // 'dim' and 'num' don't really make sense on serial, and GCC rejects them
+  // too, so we disallow them too.
+  case OpenACCGangKind::Dim:
+  case OpenACCGangKind::Num:
+    return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
+  case OpenACCGangKind::Static:
+    return CheckGangStaticExpr(S, E);
+  }
+  llvm_unreachable("Unknown gang kind in gang serial check");
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  if (DiagIfSeqClause(Clause))
+    return nullptr;
+
+  // Restrictions only properly implemented on 'loop'/'combined' constructs, and
+  // it is the only construct that can do anything with this, so skip/treat as
+  // unimplemented for the routine constructs.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  Expr *IntExpr =
+      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
+  if (IntExpr) {
+    if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+      switch (SemaRef.getActiveComputeConstructInfo().Kind) {
+      case OpenACCDirectiveKind::Invalid:
+      case OpenACCDirectiveKind::Parallel:
+        // No restriction on when 'parallel' can contain an argument.
+        break;
+      case OpenACCDirectiveKind::Serial:
+        // GCC disallows this, and there is no real good reason for us to permit
+        // it, so disallow until we come up with a use case that makes sense.
+        DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
+                          Clause.getDirectiveKind(),
+                          SemaRef.getActiveComputeConstructInfo().Kind);
+        IntExpr = nullptr;
+        break;
+      case OpenACCDirectiveKind::Kernels: {
+        const auto *Itr =
+            llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
+                          llvm::IsaPred<OpenACCVectorLengthClause>);
+        if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
+          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
+              << "length" << OpenACCClauseKind::Vector
+              << Clause.getDirectiveKind()
+              << HasAssocKind(Clause.getDirectiveKind(),
+                              SemaRef.getActiveComputeConstructInfo().Kind)
+              << SemaRef.getActiveComputeConstructInfo().Kind
+              << OpenACCClauseKind::VectorLength;
+          SemaRef.Diag((*Itr)->getBeginLoc(),
+                       diag::note_acc_previous_clause_here);
+
+          IntExpr = nullptr;
+        }
+        break;
+      }
+      default:
+        llvm_unreachable("Non compute construct in active compute construct");
+      }
+    } else {
+      if (Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
+        DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
+                          Clause.getDirectiveKind(),
+                          SemaRef.getActiveComputeConstructInfo().Kind);
+        IntExpr = nullptr;
+      } else if (Clause.getDirectiveKind() ==
+                 OpenACCDirectiveKind::KernelsLoop) {
+        const auto *Itr = llvm::find_if(
+            ExistingClauses, llvm::IsaPred<OpenACCVectorLengthClause>);
+        if (Itr != ExistingClauses.end()) {
+          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
+              << "length" << OpenACCClauseKind::Vector
+              << Clause.getDirectiveKind()
+              << HasAssocKind(Clause.getDirectiveKind(),
+                              SemaRef.getActiveComputeConstructInfo().Kind)
+              << SemaRef.getActiveComputeConstructInfo().Kind
+              << OpenACCClauseKind::VectorLength;
+          SemaRef.Diag((*Itr)->getBeginLoc(),
+                       diag::note_acc_previous_clause_here);
+
+          IntExpr = nullptr;
+        }
+      }
+    }
+  }
+
+  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
+    // contain a loop with a gang, worker, or vector clause unless within a
+    // nested compute region.
+    if (SemaRef.LoopVectorClauseLoc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Vector << OpenACCClauseKind::Vector
+          << /*skip kernels construct info*/ 0;
+      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+  }
+
+  return OpenACCVectorClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), IntExpr,
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitWorkerClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  if (DiagIfSeqClause(Clause))
+    return nullptr;
+
+  // Restrictions only properly implemented on 'loop'/'combined' constructs, and
+  // it is the only construct that can do anything with this, so skip/treat as
+  // unimplemented for the routine constructs.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  Expr *IntExpr =
+      Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
+
+  if (IntExpr) {
+    if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+      switch (SemaRef.getActiveComputeConstructInfo().Kind) {
+      case OpenACCDirectiveKind::Invalid:
+      case OpenACCDirectiveKind::ParallelLoop:
+      case OpenACCDirectiveKind::SerialLoop:
+      case OpenACCDirectiveKind::Parallel:
+      case OpenACCDirectiveKind::Serial:
+        DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
+                          OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
+                          SemaRef.getActiveComputeConstructInfo().Kind);
+        IntExpr = nullptr;
+        break;
+      case OpenACCDirectiveKind::KernelsLoop:
+      case OpenACCDirectiveKind::Kernels: {
+        const auto *Itr =
+            llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
+                          llvm::IsaPred<OpenACCNumWorkersClause>);
+        if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
+          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
+              << "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
+              << HasAssocKind(Clause.getDirectiveKind(),
+                              SemaRef.getActiveComputeConstructInfo().Kind)
+              << SemaRef.getActiveComputeConstructInfo().Kind
+              << OpenACCClauseKind::NumWorkers;
+          SemaRef.Diag((*Itr)->getBeginLoc(),
+                       diag::note_acc_previous_clause_here);
+
+          IntExpr = nullptr;
+        }
+        break;
+      }
+      default:
+        llvm_unreachable("Non compute construct in active compute construct");
+      }
+    } else {
+      if (Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop ||
+          Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
+        DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
+                          OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
+                          SemaRef.getActiveComputeConstructInfo().Kind);
+        IntExpr = nullptr;
+      } else {
+        assert(Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop &&
+               "Unknown combined directive kind?");
+        const auto *Itr = llvm::find_if(ExistingClauses,
+                                        llvm::IsaPred<OpenACCNumWorkersClause>);
+        if (Itr != ExistingClauses.end()) {
+          SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
+              << "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
+              << HasAssocKind(Clause.getDirectiveKind(),
+                              SemaRef.getActiveComputeConstructInfo().Kind)
+              << SemaRef.getActiveComputeConstructInfo().Kind
+              << OpenACCClauseKind::NumWorkers;
+          SemaRef.Diag((*Itr)->getBeginLoc(),
+                       diag::note_acc_previous_clause_here);
+
+          IntExpr = nullptr;
+        }
+      }
+    }
+  }
+
+  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    // OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
+    // contain a loop with a gang or worker clause unless within a nested
+    // compute region.
+    if (SemaRef.LoopWorkerClauseLoc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Worker << OpenACCClauseKind::Worker
+          << /*skip kernels construct info*/ 0;
+      SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+
+    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
+    // contain a loop with a gang, worker, or vector clause unless within a
+    // nested compute region.
+    if (SemaRef.LoopVectorClauseLoc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Worker << OpenACCClauseKind::Vector
+          << /*skip kernels construct info*/ 0;
+      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+  }
+
+  return OpenACCWorkerClause::Create(Ctx, Clause.getBeginLoc(),
+                                     Clause.getLParenLoc(), IntExpr,
+                                     Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitGangClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  if (DiagIfSeqClause(Clause))
+    return nullptr;
+
+  // Restrictions only properly implemented on 'loop' constructs, and it is
+  // the only construct that can do anything with this, so skip/treat as
+  // unimplemented for the combined constructs.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
+  // directive that has a gang clause and is within a compute construct that has
+  // a num_gangs clause with more than one explicit argument.
+  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
+       SemaRef.getActiveComputeConstructInfo().Kind !=
+           OpenACCDirectiveKind::Invalid) ||
+      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    // num_gangs clause on the active compute construct.
+    auto ActiveComputeConstructContainer =
+        isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
+            ? ExistingClauses
+            : SemaRef.getActiveComputeConstructInfo().Clauses;
+    auto *NumGangsClauseItr = llvm::find_if(
+        ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
+
+    if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
+        cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
+            1) {
+      auto *ReductionClauseItr =
+          llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
+
+      if (ReductionClauseItr != ExistingClauses.end()) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_gang_reduction_numgangs_conflict)
+            << OpenACCClauseKind::Gang << OpenACCClauseKind::Reduction
+            << Clause.getDirectiveKind()
+            << isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
+        SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
+                     diag::note_acc_previous_clause_here);
+        SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
+                     diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  llvm::SmallVector<OpenACCGangKind> GangKinds;
+  llvm::SmallVector<Expr *> IntExprs;
+
+  // Store the existing locations, so we can do duplicate checking.  Index is
+  // the int-value of the OpenACCGangKind enum.
+  SourceLocation ExistingElemLoc[3];
+
+  for (unsigned I = 0; I < Clause.getIntExprs().size(); ++I) {
+    OpenACCGangKind GK = Clause.getGangKinds()[I];
+    ExprResult ER =
+        SemaRef.CheckGangExpr(ExistingClauses, Clause.getDirectiveKind(), GK,
+                              Clause.getIntExprs()[I]);
+
+    if (!ER.isUsable())
+      continue;
+
+    // OpenACC 3.3 2.9: 'gang-arg-list' may have at most one num, one dim, and
+    // one static argument.
+    if (ExistingElemLoc[static_cast<unsigned>(GK)].isValid()) {
+      SemaRef.Diag(ER.get()->getBeginLoc(), diag::err_acc_gang_multiple_elt)
+          << static_cast<unsigned>(GK);
+      SemaRef.Diag(ExistingElemLoc[static_cast<unsigned>(GK)],
+                   diag::note_acc_previous_expr_here);
+      continue;
+    }
+
+    ExistingElemLoc[static_cast<unsigned>(GK)] = ER.get()->getBeginLoc();
+    GangKinds.push_back(GK);
+    IntExprs.push_back(ER.get());
+  }
+
+  if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    // 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
+    // within a nested compute region.
+    if (SemaRef.LoopGangClauseOnKernel.Loc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Gang << OpenACCClauseKind::Gang
+          << /*kernels construct info*/ 1
+          << SemaRef.LoopGangClauseOnKernel.DirKind;
+      SemaRef.Diag(SemaRef.LoopGangClauseOnKernel.Loc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+
+    // OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
+    // contain a loop with a gang or worker clause unless within a nested
+    // compute region.
+    if (SemaRef.LoopWorkerClauseLoc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Gang << OpenACCClauseKind::Worker
+          << /*!kernels construct info*/ 0;
+      SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+
+    // OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
+    // contain a loop with a gang, worker, or vector clause unless within a
+    // nested compute region.
+    if (SemaRef.LoopVectorClauseLoc.isValid()) {
+      // This handles the 'inner loop' diagnostic, but we cannot set that we're
+      // on one of these until we get to the end of the construct.
+      SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
+          << OpenACCClauseKind::Gang << OpenACCClauseKind::Vector
+          << /*!kernels construct info*/ 0;
+      SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
+                   diag::note_acc_previous_clause_here);
+      return nullptr;
+    }
+  }
+
+  return SemaRef.CheckGangClause(Clause.getDirectiveKind(), ExistingClauses,
+                                 Clause.getBeginLoc(), Clause.getLParenLoc(),
+                                 GangKinds, IntExprs, Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitFinalizeClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There isn't anything to do here, this is only valid on one construct, and
+  // has no associated rules.
+  return OpenACCFinalizeClause::Create(Ctx, Clause.getBeginLoc(),
+                                       Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitIfPresentClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // There isn't anything to do here, this is only valid on one construct, and
+  // has no associated rules.
+  return OpenACCIfPresentClause::Create(Ctx, Clause.getBeginLoc(),
+                                        Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitSeqClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Restrictions only properly implemented on 'loop' constructs and combined ,
+  // and it is the only construct that can do anything with this, so skip/treat
+  // as unimplemented for the routine constructs.
+  if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
+    return isNotImplemented();
+
+  // OpenACC 3.3 2.9:
+  // Only one of the seq, independent, and auto clauses may appear.
+  const auto *Itr =
+      llvm::find_if(ExistingClauses,
+                    llvm::IsaPred<OpenACCAutoClause, OpenACCIndependentClause>);
+  if (Itr != ExistingClauses.end()) {
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
+        << Clause.getClauseKind() << Clause.getDirectiveKind();
+    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    return nullptr;
+  }
+
+  // OpenACC 3.3 2.9:
+  // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
+  // appears.
+  Itr = llvm::find_if(ExistingClauses,
+                      llvm::IsaPred<OpenACCGangClause, OpenACCWorkerClause,
+                                    OpenACCVectorClause>);
+
+  if (Itr != ExistingClauses.end()) {
+    SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
+        << Clause.getClauseKind() << (*Itr)->getClauseKind()
+        << Clause.getDirectiveKind();
+    SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
+    return nullptr;
+  }
+
+  return OpenACCSeqClause::Create(Ctx, Clause.getBeginLoc(),
+                                  Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
+  // directive that has a gang clause and is within a compute construct that has
+  // a num_gangs clause with more than one explicit argument.
+  if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
+       SemaRef.getActiveComputeConstructInfo().Kind !=
+           OpenACCDirectiveKind::Invalid) ||
+      isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
+    // num_gangs clause on the active compute construct.
+    auto ActiveComputeConstructContainer =
+        isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
+            ? ExistingClauses
+            : SemaRef.getActiveComputeConstructInfo().Clauses;
+    auto *NumGangsClauseItr = llvm::find_if(
+        ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
+
+    if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
+        cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
+            1) {
+      auto *GangClauseItr =
+          llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
+
+      if (GangClauseItr != ExistingClauses.end()) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_gang_reduction_numgangs_conflict)
+            << OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
+            << Clause.getDirectiveKind()
+            << isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
+        SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
+                     diag::note_acc_previous_clause_here);
+        SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
+                     diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  // OpenACC3.3 Section 2.9.11: If a variable is involved in a reduction that
+  // spans multiple nested loops where two or more of those loops have
+  // associated loop directives, a reduction clause containing that variable
+  // must appear on each of those loop directives.
+  //
+  // This can't really be implemented in the CFE, as this requires a level of
+  // rechability/useage analysis that we're not really wanting to get into.
+  // Additionally, I'm alerted that this restriction is one that the middle-end
+  // can just 'figure out' as an extension and isn't really necessary.
+  //
+  // OpenACC3.3 Section 2.9.11: Every 'var' in a reduction clause appearing on
+  // an orphaned loop construct must be private.
+  //
+  // This again is something we cannot really diagnose, as it requires we see
+  // all the uses/scopes of all variables referenced.  The middle end/MLIR might
+  // be able to diagnose this.
+
+  // OpenACC 3.3 Section 2.5.4:
+  // A reduction clause may not appear on a parallel construct with a
+  // num_gangs clause that has more than one argument.
+  if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
+      Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) {
+    auto NumGangsClauses = llvm::make_filter_range(
+        ExistingClauses, llvm::IsaPred<OpenACCNumGangsClause>);
+
+    for (auto *NGC : NumGangsClauses) {
+      unsigned NumExprs =
+          cast<OpenACCNumGangsClause>(NGC)->getIntExprs().size();
+
+      if (NumExprs > 1) {
+        SemaRef.Diag(Clause.getBeginLoc(),
+                     diag::err_acc_reduction_num_gangs_conflict)
+            << /*>1 arg in first loc=*/0 << Clause.getClauseKind()
+            << Clause.getDirectiveKind() << OpenACCClauseKind::NumGangs;
+        SemaRef.Diag(NGC->getBeginLoc(), diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  SmallVector<Expr *> ValidVars;
+
+  for (Expr *Var : Clause.getVarList()) {
+    ExprResult Res = SemaRef.CheckReductionVar(Clause.getDirectiveKind(),
+                                               Clause.getReductionOp(), Var);
+
+    if (Res.isUsable())
+      ValidVars.push_back(Res.get());
+  }
+
+  return SemaRef.CheckReductionClause(
+      ExistingClauses, Clause.getDirectiveKind(), Clause.getBeginLoc(),
+      Clause.getLParenLoc(), Clause.getReductionOp(), ValidVars,
+      Clause.getEndLoc());
+}
+
+OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause(
+    SemaOpenACC::OpenACCParsedClause &Clause) {
+  // Duplicates here are not really sensible.  We could possible permit
+  // multiples if they all had the same value, but there isn't really a good
+  // reason to do so. Also, this simplifies the suppression of duplicates, in
+  // that we know if we 'find' one after instantiation, that it is the same
+  // clause, which simplifies instantiation/checking/etc.
+  if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
+    return nullptr;
+
+  ExprResult LoopCount = SemaRef.CheckCollapseLoopCount(Clause.getLoopCount());
+
+  if (!LoopCount.isUsable())
+    return nullptr;
+
+  return OpenACCCollapseClause::Create(Ctx, Clause.getBeginLoc(),
+                                       Clause.getLParenLoc(), Clause.isForce(),
+                                       LoopCount.get(), Clause.getEndLoc());
+}
+
+// Return true if the two vars refer to the same variable, for the purposes of
+// equality checking.
+bool areVarsEqual(Expr *VarExpr1, Expr *VarExpr2) {
+  if (VarExpr1->isInstantiationDependent() ||
+      VarExpr2->isInstantiationDependent())
+    return false;
+
+  VarExpr1 = VarExpr1->IgnoreParenCasts();
+  VarExpr2 = VarExpr2->IgnoreParenCasts();
+
+  // Legal expressions can be: Scalar variable reference, sub-array, array
+  // element, or composite variable member.
+
+  // Sub-array.
+  if (isa<ArraySectionExpr>(VarExpr1)) {
+    auto *Expr2AS = dyn_cast<ArraySectionExpr>(VarExpr2);
+    if (!Expr2AS)
+      return false;
+
+    auto *Expr1AS = cast<ArraySectionExpr>(VarExpr1);
+
+    if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
+      return false;
+    // We could possibly check to see if the ranges aren't overlapping, but it
+    // isn't clear that the rules allow this.
+    return true;
+  }
+
+  // Array-element.
+  if (isa<ArraySubscriptExpr>(VarExpr1)) {
+    auto *Expr2AS = dyn_cast<ArraySubscriptExpr>(VarExpr2);
+    if (!Expr2AS)
+      return false;
+
+    auto *Expr1AS = cast<ArraySubscriptExpr>(VarExpr1);
+
+    if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
+      return false;
+
+    // We could possibly check to see if the elements referenced aren't the
+    // same, but it isn't clear by reading of the standard that this is allowed
+    // (and that the 'var' refered to isn't the array).
+    return true;
+  }
+
+  // Scalar variable reference, or composite variable.
+  if (isa<DeclRefExpr>(VarExpr1)) {
+    auto *Expr2DRE = dyn_cast<DeclRefExpr>(VarExpr2);
+    if (!Expr2DRE)
+      return false;
+
+    auto *Expr1DRE = cast<DeclRefExpr>(VarExpr1);
+
+    return Expr1DRE->getDecl()->getMostRecentDecl() ==
+           Expr2DRE->getDecl()->getMostRecentDecl();
+  }
+
+  llvm_unreachable("Unknown variable type encountered");
+}
+} // namespace
+
+OpenACCClause *
+SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
+                         OpenACCParsedClause &Clause) {
+  if (Clause.getClauseKind() == OpenACCClauseKind::Invalid)
+    return nullptr;
+
+  // Diagnose that we don't support this clause on this directive.
+  if (!doesClauseApplyToDirective(Clause.getDirectiveKind(),
+                                  Clause.getClauseKind())) {
+    Diag(Clause.getBeginLoc(), diag::err_acc_clause_appertainment)
+        << Clause.getDirectiveKind() << Clause.getClauseKind();
+    return nullptr;
+  }
+
+  if (const auto *DevTypeClause =
+          llvm::find_if(ExistingClauses,
+                        [&](const OpenACCClause *C) {
+                          return isa<OpenACCDeviceTypeClause>(C);
+                        });
+      DevTypeClause != ExistingClauses.end()) {
+    if (checkValidAfterDeviceType(
+            *this, *cast<OpenACCDeviceTypeClause>(*DevTypeClause), Clause))
+      return nullptr;
+  }
+
+  SemaOpenACCClauseVisitor Visitor{*this, ExistingClauses};
+  OpenACCClause *Result = Visitor.Visit(Clause);
+  assert((!Result || Result->getClauseKind() == Clause.getClauseKind()) &&
+         "Created wrong clause?");
+
+  if (Visitor.diagNotImplemented())
+    Diag(Clause.getBeginLoc(), diag::warn_acc_clause_unimplemented)
+        << Clause.getClauseKind();
+
+  return Result;
+
+}
+
+/// OpenACC 3.3 section 2.5.15:
+/// At a mininmum, the supported data types include ... the numerical data types
+/// in C, C++, and Fortran.
+///
+/// If the reduction var is a composite variable, each
+/// member of the composite variable must be a supported datatype for the
+/// reduction operation.
+ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind,
+                                          OpenACCReductionOperator ReductionOp,
+                                          Expr *VarExpr) {
+  VarExpr = VarExpr->IgnoreParenCasts();
+
+  auto TypeIsValid = [](QualType Ty) {
+    return Ty->isDependentType() || Ty->isScalarType();
+  };
+
+  if (isa<ArraySectionExpr>(VarExpr)) {
+    Expr *ASExpr = VarExpr;
+    QualType BaseTy = ArraySectionExpr::getBaseOriginalType(ASExpr);
+    QualType EltTy = getASTContext().getBaseElementType(BaseTy);
+
+    if (!TypeIsValid(EltTy)) {
+      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
+          << EltTy << /*Sub array base type*/ 1;
+      return ExprError();
+    }
+  } else if (auto *RD = VarExpr->getType()->getAsRecordDecl()) {
+    if (!RD->isStruct() && !RD->isClass()) {
+      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
+          << /*not class or struct*/ 0 << VarExpr->getType();
+      return ExprError();
+    }
+
+    if (!RD->isCompleteDefinition()) {
+      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
+          << /*incomplete*/ 1 << VarExpr->getType();
+      return ExprError();
+    }
+    if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
+        CXXRD && !CXXRD->isAggregate()) {
+      Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
+          << /*aggregate*/ 2 << VarExpr->getType();
+      return ExprError();
+    }
+
+    for (FieldDecl *FD : RD->fields()) {
+      if (!TypeIsValid(FD->getType())) {
+        Diag(VarExpr->getExprLoc(),
+             diag::err_acc_reduction_composite_member_type);
+        Diag(FD->getLocation(), diag::note_acc_reduction_composite_member_loc);
+        return ExprError();
+      }
+    }
+  } else if (!TypeIsValid(VarExpr->getType())) {
+    Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
+        << VarExpr->getType() << /*Sub array base type*/ 0;
+    return ExprError();
+  }
+
+  // OpenACC3.3: 2.9.11: Reduction clauses on nested constructs for the same
+  // reduction 'var' must have the same reduction operator.
+  if (!VarExpr->isInstantiationDependent()) {
+
+    for (const OpenACCReductionClause *RClause : ActiveReductionClauses) {
+      if (RClause->getReductionOp() == ReductionOp)
+        break;
+
+      for (Expr *OldVarExpr : RClause->getVarList()) {
+        if (OldVarExpr->isInstantiationDependent())
+          continue;
+
+        if (areVarsEqual(VarExpr, OldVarExpr)) {
+          Diag(VarExpr->getExprLoc(), diag::err_reduction_op_mismatch)
+              << ReductionOp << RClause->getReductionOp();
+          Diag(OldVarExpr->getExprLoc(), diag::note_acc_previous_clause_here);
+          return ExprError();
+        }
+      }
+    }
+  }
+
+  return VarExpr;
+}
+
+ExprResult SemaOpenACC::CheckTileSizeExpr(Expr *SizeExpr) {
+  if (!SizeExpr)
+    return ExprError();
+
+  assert((SizeExpr->isInstantiationDependent() ||
+          SizeExpr->getType()->isIntegerType()) &&
+         "size argument non integer?");
+
+  // If dependent, or an asterisk, the expression is fine.
+  if (SizeExpr->isInstantiationDependent() ||
+      isa<OpenACCAsteriskSizeExpr>(SizeExpr))
+    return ExprResult{SizeExpr};
+
+  std::optional<llvm::APSInt> ICE =
+      SizeExpr->getIntegerConstantExpr(getASTContext());
+
+  // OpenACC 3.3 2.9.8
+  // where each tile size is a constant positive integer expression or asterisk.
+  if (!ICE || *ICE <= 0) {
+    Diag(SizeExpr->getBeginLoc(), diag::err_acc_size_expr_value)
+        << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
+    return ExprError();
+  }
+
+  return ExprResult{
+      ConstantExpr::Create(getASTContext(), SizeExpr, APValue{*ICE})};
+}
+
+ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) {
+  if (!LoopCount)
+    return ExprError();
+
+  assert((LoopCount->isInstantiationDependent() ||
+          LoopCount->getType()->isIntegerType()) &&
+         "Loop argument non integer?");
+
+  // If this is dependent, there really isn't anything we can check.
+  if (LoopCount->isInstantiationDependent())
+    return ExprResult{LoopCount};
+
+  std::optional<llvm::APSInt> ICE =
+      LoopCount->getIntegerConstantExpr(getASTContext());
+
+  // OpenACC 3.3: 2.9.1
+  // The argument to the collapse clause must be a constant positive integer
+  // expression.
+  if (!ICE || *ICE <= 0) {
+    Diag(LoopCount->getBeginLoc(), diag::err_acc_collapse_loop_count)
+        << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
+    return ExprError();
+  }
+
+  return ExprResult{
+      ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})};
+}
+
+ExprResult
+SemaOpenACC::CheckGangExpr(ArrayRef<const OpenACCClause *> ExistingClauses,
+                           OpenACCDirectiveKind DK, OpenACCGangKind GK,
+                           Expr *E) {
+  // There are two cases for the enforcement here: the 'current' directive is a
+  // 'loop', where we need to check the active compute construct kind, or the
+  // current directive is a 'combined' construct, where we have to check the
+  // current one.
+  switch (DK) {
+  case OpenACCDirectiveKind::ParallelLoop:
+    return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
+                                 E);
+  case OpenACCDirectiveKind::SerialLoop:
+    return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
+                               E);
+  case OpenACCDirectiveKind::KernelsLoop:
+    return CheckGangKernelsExpr(*this, ExistingClauses, DK,
+                                ActiveComputeConstructInfo.Kind, GK, E);
+  case OpenACCDirectiveKind::Loop:
+    switch (ActiveComputeConstructInfo.Kind) {
+    case OpenACCDirectiveKind::Invalid:
+    case OpenACCDirectiveKind::Parallel:
+    case OpenACCDirectiveKind::ParallelLoop:
+      return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind,
+                                   GK, E);
+    case OpenACCDirectiveKind::SerialLoop:
+    case OpenACCDirectiveKind::Serial:
+      return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
+                                 E);
+    case OpenACCDirectiveKind::KernelsLoop:
+    case OpenACCDirectiveKind::Kernels:
+      return CheckGangKernelsExpr(*this, ExistingClauses, DK,
+                                  ActiveComputeConstructInfo.Kind, GK, E);
+    default:
+      llvm_unreachable("Non compute construct in active compute construct?");
+    }
+  default:
+    // TODO: OpenACC: when we implement this on 'routine', we'll have to
+    // implement its checking here.
+    llvm_unreachable("Invalid directive kind for a Gang clause");
+  }
+  llvm_unreachable("Compute construct directive not handled?");
+}
+
+OpenACCClause *
+SemaOpenACC::CheckGangClause(OpenACCDirectiveKind DirKind,
+                             ArrayRef<const OpenACCClause *> ExistingClauses,
+                             SourceLocation BeginLoc, SourceLocation LParenLoc,
+                             ArrayRef<OpenACCGangKind> GangKinds,
+                             ArrayRef<Expr *> IntExprs, SourceLocation EndLoc) {
+  // OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
+  // that has a gang clause with a dim: argument whose value is greater than 1.
+
+  const auto *ReductionItr =
+      llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
+
+  if (ReductionItr != ExistingClauses.end()) {
+    const auto GangZip = llvm::zip_equal(GangKinds, IntExprs);
+    const auto GangItr = llvm::find_if(GangZip, [](const auto &Tuple) {
+      return std::get<0>(Tuple) == OpenACCGangKind::Dim;
+    });
+
+    if (GangItr != GangZip.end()) {
+      const Expr *DimExpr = std::get<1>(*GangItr);
+
+      assert(
+          (DimExpr->isInstantiationDependent() || isa<ConstantExpr>(DimExpr)) &&
+          "Improperly formed gang argument");
+      if (const auto *DimVal = dyn_cast<ConstantExpr>(DimExpr);
+          DimVal && DimVal->getResultAsAPSInt() > 1) {
+        Diag(DimVal->getBeginLoc(), diag::err_acc_gang_reduction_conflict)
+            << /*gang/reduction=*/0 << DirKind;
+        Diag((*ReductionItr)->getBeginLoc(),
+             diag::note_acc_previous_clause_here);
+        return nullptr;
+      }
+    }
+  }
+
+  return OpenACCGangClause::Create(getASTContext(), BeginLoc, LParenLoc,
+                                   GangKinds, IntExprs, EndLoc);
+}
+
+OpenACCClause *SemaOpenACC::CheckReductionClause(
+    ArrayRef<const OpenACCClause *> ExistingClauses,
+    OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
+    SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
+    ArrayRef<Expr *> Vars, SourceLocation EndLoc) {
+  if (DirectiveKind == OpenACCDirectiveKind::Loop ||
+      isOpenACCCombinedDirectiveKind(DirectiveKind)) {
+    // OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
+    // that has a gang clause with a dim: argument whose value is greater
+    // than 1.
+    const auto GangClauses = llvm::make_filter_range(
+        ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
+
+    for (auto *GC : GangClauses) {
+      const auto *GangClause = cast<OpenACCGangClause>(GC);
+      for (unsigned I = 0; I < GangClause->getNumExprs(); ++I) {
+        std::pair<OpenACCGangKind, const Expr *> EPair = GangClause->getExpr(I);
+        if (EPair.first != OpenACCGangKind::Dim)
+          continue;
+
+        if (const auto *DimVal = dyn_cast<ConstantExpr>(EPair.second);
+            DimVal && DimVal->getResultAsAPSInt() > 1) {
+          Diag(BeginLoc, diag::err_acc_gang_reduction_conflict)
+              << /*reduction/gang=*/1 << DirectiveKind;
+          Diag(GangClause->getBeginLoc(), diag::note_acc_previous_clause_here);
+          return nullptr;
+        }
+      }
+    }
+  }
+
+  auto *Ret = OpenACCReductionClause::Create(
+      getASTContext(), BeginLoc, LParenLoc, ReductionOp, Vars, EndLoc);
+  return Ret;
+}


        


More information about the cfe-commits mailing list