[clang] 6263de9 - [OpenACC] Implement 'modifier-list' sema/AST

via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 4 12:32:39 PDT 2025


Author: erichkeane
Date: 2025-04-04T12:32:33-07:00
New Revision: 6263de90df7f58c8b98475024d5eef102e10a372

URL: https://github.com/llvm/llvm-project/commit/6263de90df7f58c8b98475024d5eef102e10a372
DIFF: https://github.com/llvm/llvm-project/commit/6263de90df7f58c8b98475024d5eef102e10a372.diff

LOG: [OpenACC] Implement 'modifier-list' sema/AST

OpenACC 3.3-NEXT has changed the way tags for copy, copyin, copyout, and
create clauses are specified, and end up adding a few extras, and
permits them as a list.  This patch encodes these as bitmask enum so
they can be stored succinctly, but still diagnose reasonably.

Added: 
    

Modified: 
    clang/include/clang/AST/OpenACCClause.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/OpenACCKinds.h
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/SemaOpenACC.h
    clang/lib/AST/OpenACCClause.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/Parse/ParseOpenACC.cpp
    clang/lib/Sema/SemaOpenACC.cpp
    clang/lib/Sema/SemaOpenACCClause.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReader.cpp
    clang/lib/Serialization/ASTWriter.cpp
    clang/test/AST/ast-print-openacc-combined-construct.cpp
    clang/test/AST/ast-print-openacc-compute-construct.cpp
    clang/test/AST/ast-print-openacc-data-construct.cpp
    clang/test/AST/ast-print-openacc-declare-construct.cpp
    clang/test/ParserOpenACC/parse-clauses.c
    clang/test/SemaOpenACC/combined-construct-copy-ast.cpp
    clang/test/SemaOpenACC/combined-construct-copy-clause.c
    clang/test/SemaOpenACC/combined-construct-copyin-ast.cpp
    clang/test/SemaOpenACC/combined-construct-copyin-clause.c
    clang/test/SemaOpenACC/combined-construct-copyout-ast.cpp
    clang/test/SemaOpenACC/combined-construct-copyout-clause.c
    clang/test/SemaOpenACC/combined-construct-create-ast.cpp
    clang/test/SemaOpenACC/combined-construct-create-clause.c
    clang/test/SemaOpenACC/compute-construct-copy-clause.c
    clang/test/SemaOpenACC/compute-construct-copyin-clause.c
    clang/test/SemaOpenACC/compute-construct-copyout-clause.c
    clang/test/SemaOpenACC/compute-construct-create-clause.c
    clang/test/SemaOpenACC/compute-construct-varlist-ast.cpp
    clang/test/SemaOpenACC/data-construct-copy-ast.cpp
    clang/test/SemaOpenACC/data-construct-copy-clause.c
    clang/test/SemaOpenACC/data-construct-copyin-ast.cpp
    clang/test/SemaOpenACC/data-construct-copyin-clause.c
    clang/test/SemaOpenACC/data-construct-copyout-ast.cpp
    clang/test/SemaOpenACC/data-construct-copyout-clause.c
    clang/test/SemaOpenACC/data-construct-create-ast.cpp
    clang/test/SemaOpenACC/data-construct-create-clause.c
    clang/test/SemaOpenACC/declare-construct-ast.cpp
    clang/test/SemaOpenACC/declare-construct.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h
index 4c5fe03a34361..b3a5746af7cb0 100644
--- a/clang/include/clang/AST/OpenACCClause.h
+++ b/clang/include/clang/AST/OpenACCClause.h
@@ -1087,11 +1087,13 @@ class OpenACCCopyClause final
     : public OpenACCClauseWithVarList,
       private llvm::TrailingObjects<OpenACCCopyClause, Expr *> {
   friend TrailingObjects;
+  OpenACCModifierKind Modifiers;
 
   OpenACCCopyClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc,
-                    SourceLocation LParenLoc, ArrayRef<Expr *> VarList,
-                    SourceLocation EndLoc)
-      : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc) {
+                    SourceLocation LParenLoc, OpenACCModifierKind Mods,
+                    ArrayRef<Expr *> VarList, SourceLocation EndLoc)
+      : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc),
+        Modifiers(Mods) {
     assert((Spelling == OpenACCClauseKind::Copy ||
             Spelling == OpenACCClauseKind::PCopy ||
             Spelling == OpenACCClauseKind::PresentOrCopy) &&
@@ -1110,20 +1112,23 @@ class OpenACCCopyClause final
   static OpenACCCopyClause *
   Create(const ASTContext &C, OpenACCClauseKind Spelling,
          SourceLocation BeginLoc, SourceLocation LParenLoc,
-         ArrayRef<Expr *> VarList, SourceLocation EndLoc);
+         OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
+         SourceLocation EndLoc);
+
+  OpenACCModifierKind getModifierList() const { return Modifiers; }
 };
 
 class OpenACCCopyInClause final
     : public OpenACCClauseWithVarList,
       private llvm::TrailingObjects<OpenACCCopyInClause, Expr *> {
   friend TrailingObjects;
-  bool IsReadOnly;
+  OpenACCModifierKind Modifiers;
 
   OpenACCCopyInClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc,
-                      SourceLocation LParenLoc, bool IsReadOnly,
+                      SourceLocation LParenLoc, OpenACCModifierKind Mods,
                       ArrayRef<Expr *> VarList, SourceLocation EndLoc)
       : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc),
-        IsReadOnly(IsReadOnly) {
+        Modifiers(Mods) {
     assert((Spelling == OpenACCClauseKind::CopyIn ||
             Spelling == OpenACCClauseKind::PCopyIn ||
             Spelling == OpenACCClauseKind::PresentOrCopyIn) &&
@@ -1139,24 +1144,25 @@ class OpenACCCopyInClause final
            C->getClauseKind() == OpenACCClauseKind::PCopyIn ||
            C->getClauseKind() == OpenACCClauseKind::PresentOrCopyIn;
   }
-  bool isReadOnly() const { return IsReadOnly; }
+  OpenACCModifierKind getModifierList() const { return Modifiers; }
   static OpenACCCopyInClause *
   Create(const ASTContext &C, OpenACCClauseKind Spelling,
-         SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsReadOnly,
-         ArrayRef<Expr *> VarList, SourceLocation EndLoc);
+         SourceLocation BeginLoc, SourceLocation LParenLoc,
+         OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
+         SourceLocation EndLoc);
 };
 
 class OpenACCCopyOutClause final
     : public OpenACCClauseWithVarList,
       private llvm::TrailingObjects<OpenACCCopyOutClause, Expr *> {
   friend TrailingObjects;
-  bool IsZero;
+  OpenACCModifierKind Modifiers;
 
   OpenACCCopyOutClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc,
-                       SourceLocation LParenLoc, bool IsZero,
+                       SourceLocation LParenLoc, OpenACCModifierKind Mods,
                        ArrayRef<Expr *> VarList, SourceLocation EndLoc)
       : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc),
-        IsZero(IsZero) {
+        Modifiers(Mods) {
     assert((Spelling == OpenACCClauseKind::CopyOut ||
             Spelling == OpenACCClauseKind::PCopyOut ||
             Spelling == OpenACCClauseKind::PresentOrCopyOut) &&
@@ -1172,24 +1178,25 @@ class OpenACCCopyOutClause final
            C->getClauseKind() == OpenACCClauseKind::PCopyOut ||
            C->getClauseKind() == OpenACCClauseKind::PresentOrCopyOut;
   }
-  bool isZero() const { return IsZero; }
+  OpenACCModifierKind getModifierList() const { return Modifiers; }
   static OpenACCCopyOutClause *
   Create(const ASTContext &C, OpenACCClauseKind Spelling,
-         SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsZero,
-         ArrayRef<Expr *> VarList, SourceLocation EndLoc);
+         SourceLocation BeginLoc, SourceLocation LParenLoc,
+         OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
+         SourceLocation EndLoc);
 };
 
 class OpenACCCreateClause final
     : public OpenACCClauseWithVarList,
       private llvm::TrailingObjects<OpenACCCreateClause, Expr *> {
   friend TrailingObjects;
-  bool IsZero;
+  OpenACCModifierKind Modifiers;
 
   OpenACCCreateClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc,
-                      SourceLocation LParenLoc, bool IsZero,
+                      SourceLocation LParenLoc, OpenACCModifierKind Mods,
                       ArrayRef<Expr *> VarList, SourceLocation EndLoc)
       : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc),
-        IsZero(IsZero) {
+        Modifiers(Mods) {
     assert((Spelling == OpenACCClauseKind::Create ||
             Spelling == OpenACCClauseKind::PCreate ||
             Spelling == OpenACCClauseKind::PresentOrCreate) &&
@@ -1205,11 +1212,12 @@ class OpenACCCreateClause final
            C->getClauseKind() == OpenACCClauseKind::PCreate ||
            C->getClauseKind() == OpenACCClauseKind::PresentOrCreate;
   }
-  bool isZero() const { return IsZero; }
+  OpenACCModifierKind getModifierList() const { return Modifiers; }
   static OpenACCCreateClause *
   Create(const ASTContext &C, OpenACCClauseKind Spelling,
-         SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsZero,
-         ArrayRef<Expr *> VarList, SourceLocation EndLoc);
+         SourceLocation BeginLoc, SourceLocation LParenLoc,
+         OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
+         SourceLocation EndLoc);
 };
 
 class OpenACCReductionClause final

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 954f538e15026..f46e7fed28794 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1458,6 +1458,9 @@ def err_acc_invalid_reduction_operator
     : Error<"invalid reduction operator,  expected '+', '*', 'max', 'min', "
             "'&', '|', '^', '&&', or '||'">;
 def err_acc_incorrect_bind_arg : Error<"expected identifier or string literal">;
+def err_acc_modifier
+    : Error<"%enum_select<ACCModifier>{%Unknown{unknown}|%Duplicate{duplicate}}"
+            "0 modifier %1 in OpenACC modifier-list on '%2' clause">;
 
 // OpenMP support.
 def warn_pragma_omp_ignored : Warning<

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dc98ceadd23ca..393bfecf9a36b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13055,6 +13055,8 @@ def warn_acc_confusing_routine_name
       InGroup<DiagGroup<"openacc-confusing-routine-name">>;
 def err_acc_decl_for_routine
     : Error<"expected function or lambda declaration for 'routine' construct">;
+def err_acc_invalid_modifier
+    : Error<"OpenACC '%0' modifier not valid on '%1' clause">;
 
 // AMDGCN builtins diagnostics
 def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">;

diff  --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h
index c2d7732123ef2..652831e23a758 100644
--- a/clang/include/clang/Basic/OpenACCKinds.h
+++ b/clang/include/clang/Basic/OpenACCKinds.h
@@ -15,10 +15,13 @@
 #define LLVM_CLANG_BASIC_OPENACCKINDS_H
 
 #include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
 // Represents the Construct/Directive kind of a pragma directive. Note the
 // OpenACC standard is inconsistent between calling these Construct vs
 // Directive, but we're calling it a Directive to be consistent with OpenMP.
@@ -619,6 +622,74 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out,
                                      OpenACCGangKind Op) {
   return printOpenACCGangKind(Out, Op);
 }
+
+// Represents the 'modifier' of a 'modifier-list', as applied to copy, copyin,
+// copyout, and create. Implemented as a 'bitmask'
+enum class OpenACCModifierKind : uint8_t {
+  Invalid = 0,
+  Always = 1 << 0,
+  AlwaysIn = 1 << 1,
+  AlwaysOut = 1 << 2,
+  Readonly = 1 << 3,
+  Zero = 1 << 4,
+  LLVM_MARK_AS_BITMASK_ENUM(Zero)
+};
+
+inline bool isOpenACCModifierBitSet(OpenACCModifierKind List,
+                                    OpenACCModifierKind Bit) {
+  return (List & Bit) != OpenACCModifierKind::Invalid;
+}
+
+template <typename StreamTy>
+inline StreamTy &printOpenACCModifierKind(StreamTy &Out,
+                                          OpenACCModifierKind Mods) {
+  if (Mods == OpenACCModifierKind::Invalid)
+    return Out << "<invalid>";
+
+  bool First = true;
+
+  if (isOpenACCModifierBitSet(Mods, OpenACCModifierKind::Always)) {
+    Out << "always";
+    First = false;
+  }
+
+  if (isOpenACCModifierBitSet(Mods, OpenACCModifierKind::AlwaysIn)) {
+    if (!First)
+      Out << ", ";
+    Out << "alwaysin";
+    First = false;
+  }
+
+  if (isOpenACCModifierBitSet(Mods, OpenACCModifierKind::AlwaysOut)) {
+    if (!First)
+      Out << ", ";
+    Out << "alwaysout";
+    First = false;
+  }
+
+  if (isOpenACCModifierBitSet(Mods, OpenACCModifierKind::Readonly)) {
+    if (!First)
+      Out << ", ";
+    Out << "readonly";
+    First = false;
+  }
+
+  if (isOpenACCModifierBitSet(Mods, OpenACCModifierKind::Zero)) {
+    if (!First)
+      Out << ", ";
+    Out << "zero";
+    First = false;
+  }
+  return Out;
+}
+inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &Out,
+                                             OpenACCModifierKind Op) {
+  return printOpenACCModifierKind(Out, Op);
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out,
+                                     OpenACCModifierKind Op) {
+  return printOpenACCModifierKind(Out, Op);
+}
 } // namespace clang
 
 #endif // LLVM_CLANG_BASIC_OPENACCKINDS_H

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 5770692c42f13..53da6269a3b11 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3769,6 +3769,8 @@ class Parser : public CodeCompletionHandler {
   ExprResult ParseOpenACCIDExpression();
   /// Parses the variable list for the `cache` construct.
   OpenACCCacheParseInfo ParseOpenACCCacheVarList();
+  /// Parses the 'modifier-list' for copy, copyin, copyout, create.
+  OpenACCModifierKind tryParseModifierList(OpenACCClauseKind CK);
 
   using OpenACCVarParseResult = std::pair<ExprResult, OpenACCParseCanContinue>;
   /// Parses a single variable in a variable list for OpenACC.

diff  --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h
index 18d92e62d71a7..4c3a13a3b044f 100644
--- a/clang/include/clang/Sema/SemaOpenACC.h
+++ b/clang/include/clang/Sema/SemaOpenACC.h
@@ -238,8 +238,7 @@ class SemaOpenACC : public SemaBase {
 
     struct VarListDetails {
       SmallVector<Expr *> VarList;
-      bool IsReadOnly;
-      bool IsZero;
+      OpenACCModifierKind ModifierKind;
     };
 
     struct WaitDetails {
@@ -451,12 +450,10 @@ class SemaOpenACC : public SemaBase {
       return const_cast<OpenACCParsedClause *>(this)->getVarList();
     }
 
-    bool isReadOnly() const {
-      return std::get<VarListDetails>(Details).IsReadOnly;
+    OpenACCModifierKind getModifierList() const {
+      return std::get<VarListDetails>(Details).ModifierKind;
     }
 
-    bool isZero() const { return std::get<VarListDetails>(Details).IsZero; }
-
     bool isForce() const {
       assert(ClauseKind == OpenACCClauseKind::Collapse &&
              "Only 'collapse' has a force tag");
@@ -552,8 +549,8 @@ class SemaOpenACC : public SemaBase {
       Details = GangDetails{std::move(GKs), std::move(IntExprs)};
     }
 
-    void setVarListDetails(ArrayRef<Expr *> VarList, bool IsReadOnly,
-                           bool IsZero) {
+    void setVarListDetails(ArrayRef<Expr *> VarList,
+                           OpenACCModifierKind ModKind) {
       assert((ClauseKind == OpenACCClauseKind::Private ||
               ClauseKind == OpenACCClauseKind::NoCreate ||
               ClauseKind == OpenACCClauseKind::Present ||
@@ -582,23 +579,25 @@ class SemaOpenACC : public SemaBase {
                DirKind == OpenACCDirectiveKind::Update) ||
               ClauseKind == OpenACCClauseKind::FirstPrivate) &&
              "Parsed clause kind does not have a var-list");
-      assert((!IsReadOnly || ClauseKind == OpenACCClauseKind::CopyIn ||
+      assert((ModKind == OpenACCModifierKind::Invalid ||
+              ClauseKind == OpenACCClauseKind::Copy ||
+              ClauseKind == OpenACCClauseKind::PCopy ||
+              ClauseKind == OpenACCClauseKind::PresentOrCopy ||
+              ClauseKind == OpenACCClauseKind::CopyIn ||
               ClauseKind == OpenACCClauseKind::PCopyIn ||
-              ClauseKind == OpenACCClauseKind::PresentOrCopyIn) &&
-             "readonly: tag only valid on copyin");
-      assert((!IsZero || ClauseKind == OpenACCClauseKind::CopyOut ||
+              ClauseKind == OpenACCClauseKind::PresentOrCopyIn ||
+              ClauseKind == OpenACCClauseKind::CopyOut ||
               ClauseKind == OpenACCClauseKind::PCopyOut ||
               ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
               ClauseKind == OpenACCClauseKind::Create ||
               ClauseKind == OpenACCClauseKind::PCreate ||
               ClauseKind == OpenACCClauseKind::PresentOrCreate) &&
-             "zero: tag only valid on copyout/create");
-      Details =
-          VarListDetails{{VarList.begin(), VarList.end()}, IsReadOnly, IsZero};
+             "Modifier Kind only valid on copy, copyin, copyout, create");
+      Details = VarListDetails{{VarList.begin(), VarList.end()}, ModKind};
     }
 
-    void setVarListDetails(llvm::SmallVector<Expr *> &&VarList, bool IsReadOnly,
-                           bool IsZero) {
+    void setVarListDetails(llvm::SmallVector<Expr *> &&VarList,
+                           OpenACCModifierKind ModKind) {
       assert((ClauseKind == OpenACCClauseKind::Private ||
               ClauseKind == OpenACCClauseKind::NoCreate ||
               ClauseKind == OpenACCClauseKind::Present ||
@@ -627,18 +626,21 @@ class SemaOpenACC : public SemaBase {
                DirKind == OpenACCDirectiveKind::Update) ||
               ClauseKind == OpenACCClauseKind::FirstPrivate) &&
              "Parsed clause kind does not have a var-list");
-      assert((!IsReadOnly || ClauseKind == OpenACCClauseKind::CopyIn ||
+      assert((ModKind == OpenACCModifierKind::Invalid ||
+              ClauseKind == OpenACCClauseKind::Copy ||
+              ClauseKind == OpenACCClauseKind::PCopy ||
+              ClauseKind == OpenACCClauseKind::PresentOrCopy ||
+              ClauseKind == OpenACCClauseKind::CopyIn ||
               ClauseKind == OpenACCClauseKind::PCopyIn ||
-              ClauseKind == OpenACCClauseKind::PresentOrCopyIn) &&
-             "readonly: tag only valid on copyin");
-      assert((!IsZero || ClauseKind == OpenACCClauseKind::CopyOut ||
+              ClauseKind == OpenACCClauseKind::PresentOrCopyIn ||
+              ClauseKind == OpenACCClauseKind::CopyOut ||
               ClauseKind == OpenACCClauseKind::PCopyOut ||
               ClauseKind == OpenACCClauseKind::PresentOrCopyOut ||
               ClauseKind == OpenACCClauseKind::Create ||
               ClauseKind == OpenACCClauseKind::PCreate ||
               ClauseKind == OpenACCClauseKind::PresentOrCreate) &&
-             "zero: tag only valid on copyout/create");
-      Details = VarListDetails{std::move(VarList), IsReadOnly, IsZero};
+             "Modifier Kind only valid on copy, copyin, copyout, create");
+      Details = VarListDetails{std::move(VarList), ModKind};
     }
 
     void setReductionDetails(OpenACCReductionOperator Op,
@@ -826,7 +828,8 @@ class SemaOpenACC : public SemaBase {
 
   // Checking for the arguments specific to the declare-clause that need to be
   // checked during both phases of template translation.
-  bool CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause);
+  bool CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause,
+                          OpenACCModifierKind Mods);
 
   ExprResult ActOnRoutineName(Expr *RoutineName);
 

diff  --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp
index ab76e6dffa0ff..d7cbb51335359 100644
--- a/clang/lib/AST/OpenACCClause.cpp
+++ b/clang/lib/AST/OpenACCClause.cpp
@@ -434,11 +434,12 @@ OpenACCDeviceClause *OpenACCDeviceClause::Create(const ASTContext &C,
 OpenACCCopyClause *
 OpenACCCopyClause::Create(const ASTContext &C, OpenACCClauseKind Spelling,
                           SourceLocation BeginLoc, SourceLocation LParenLoc,
-                          ArrayRef<Expr *> VarList, SourceLocation EndLoc) {
+                          OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
+                          SourceLocation EndLoc) {
   void *Mem =
       C.Allocate(OpenACCCopyClause::totalSizeToAlloc<Expr *>(VarList.size()));
   return new (Mem)
-      OpenACCCopyClause(Spelling, BeginLoc, LParenLoc, VarList, EndLoc);
+      OpenACCCopyClause(Spelling, BeginLoc, LParenLoc, Mods, VarList, EndLoc);
 }
 
 OpenACCLinkClause *OpenACCLinkClause::Create(const ASTContext &C,
@@ -463,34 +464,34 @@ OpenACCDeviceResidentClause *OpenACCDeviceResidentClause::Create(
 OpenACCCopyInClause *
 OpenACCCopyInClause::Create(const ASTContext &C, OpenACCClauseKind Spelling,
                             SourceLocation BeginLoc, SourceLocation LParenLoc,
-                            bool IsReadOnly, ArrayRef<Expr *> VarList,
+                            OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
                             SourceLocation EndLoc) {
   void *Mem =
       C.Allocate(OpenACCCopyInClause::totalSizeToAlloc<Expr *>(VarList.size()));
-  return new (Mem) OpenACCCopyInClause(Spelling, BeginLoc, LParenLoc,
-                                       IsReadOnly, VarList, EndLoc);
+  return new (Mem)
+      OpenACCCopyInClause(Spelling, BeginLoc, LParenLoc, Mods, VarList, EndLoc);
 }
 
 OpenACCCopyOutClause *
 OpenACCCopyOutClause::Create(const ASTContext &C, OpenACCClauseKind Spelling,
                              SourceLocation BeginLoc, SourceLocation LParenLoc,
-                             bool IsZero, ArrayRef<Expr *> VarList,
+                             OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
                              SourceLocation EndLoc) {
   void *Mem = C.Allocate(
       OpenACCCopyOutClause::totalSizeToAlloc<Expr *>(VarList.size()));
-  return new (Mem) OpenACCCopyOutClause(Spelling, BeginLoc, LParenLoc, IsZero,
+  return new (Mem) OpenACCCopyOutClause(Spelling, BeginLoc, LParenLoc, Mods,
                                         VarList, EndLoc);
 }
 
 OpenACCCreateClause *
 OpenACCCreateClause::Create(const ASTContext &C, OpenACCClauseKind Spelling,
                             SourceLocation BeginLoc, SourceLocation LParenLoc,
-                            bool IsZero, ArrayRef<Expr *> VarList,
+                            OpenACCModifierKind Mods, ArrayRef<Expr *> VarList,
                             SourceLocation EndLoc) {
   void *Mem =
       C.Allocate(OpenACCCreateClause::totalSizeToAlloc<Expr *>(VarList.size()));
-  return new (Mem) OpenACCCreateClause(Spelling, BeginLoc, LParenLoc, IsZero,
-                                       VarList, EndLoc);
+  return new (Mem)
+      OpenACCCreateClause(Spelling, BeginLoc, LParenLoc, Mods, VarList, EndLoc);
 }
 
 OpenACCDeviceTypeClause *OpenACCDeviceTypeClause::Create(
@@ -808,6 +809,8 @@ void OpenACCClausePrinter::VisitDeviceClause(const OpenACCDeviceClause &C) {
 
 void OpenACCClausePrinter::VisitCopyClause(const OpenACCCopyClause &C) {
   OS << C.getClauseKind() << '(';
+  if (C.getModifierList() != OpenACCModifierKind::Invalid)
+    OS << C.getModifierList() << ": ";
   llvm::interleaveComma(C.getVarList(), OS,
                         [&](const Expr *E) { printExpr(E); });
   OS << ")";
@@ -830,8 +833,8 @@ void OpenACCClausePrinter::VisitDeviceResidentClause(
 
 void OpenACCClausePrinter::VisitCopyInClause(const OpenACCCopyInClause &C) {
   OS << C.getClauseKind() << '(';
-  if (C.isReadOnly())
-    OS << "readonly: ";
+  if (C.getModifierList() != OpenACCModifierKind::Invalid)
+    OS << C.getModifierList() << ": ";
   llvm::interleaveComma(C.getVarList(), OS,
                         [&](const Expr *E) { printExpr(E); });
   OS << ")";
@@ -839,8 +842,8 @@ void OpenACCClausePrinter::VisitCopyInClause(const OpenACCCopyInClause &C) {
 
 void OpenACCClausePrinter::VisitCopyOutClause(const OpenACCCopyOutClause &C) {
   OS << C.getClauseKind() << '(';
-  if (C.isZero())
-    OS << "zero: ";
+  if (C.getModifierList() != OpenACCModifierKind::Invalid)
+    OS << C.getModifierList() << ": ";
   llvm::interleaveComma(C.getVarList(), OS,
                         [&](const Expr *E) { printExpr(E); });
   OS << ")";
@@ -848,8 +851,8 @@ void OpenACCClausePrinter::VisitCopyOutClause(const OpenACCCopyOutClause &C) {
 
 void OpenACCClausePrinter::VisitCreateClause(const OpenACCCreateClause &C) {
   OS << C.getClauseKind() << '(';
-  if (C.isZero())
-    OS << "zero: ";
+  if (C.getModifierList() != OpenACCModifierKind::Invalid)
+    OS << C.getModifierList() << ": ";
   llvm::interleaveComma(C.getVarList(), OS,
                         [&](const Expr *E) { printExpr(E); });
   OS << ")";

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 05f1953aa473c..be8d609974d81 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -405,9 +405,6 @@ void TextNodeDumper::Visit(const OpenACCClause *C) {
     case OpenACCClauseKind::Async:
     case OpenACCClauseKind::Auto:
     case OpenACCClauseKind::Attach:
-    case OpenACCClauseKind::Copy:
-    case OpenACCClauseKind::PCopy:
-    case OpenACCClauseKind::PresentOrCopy:
     case OpenACCClauseKind::Host:
     case OpenACCClauseKind::If:
     case OpenACCClauseKind::IfPresent:
@@ -457,26 +454,38 @@ void TextNodeDumper::Visit(const OpenACCClause *C) {
         OS << ": force";
       break;
 
+    case OpenACCClauseKind::Copy:
+    case OpenACCClauseKind::PCopy:
+    case OpenACCClauseKind::PresentOrCopy:
+      OS << " clause";
+      if (cast<OpenACCCopyClause>(C)->getModifierList() !=
+          OpenACCModifierKind::Invalid)
+        OS << " modifiers: " << cast<OpenACCCopyClause>(C)->getModifierList();
+      break;
     case OpenACCClauseKind::CopyIn:
     case OpenACCClauseKind::PCopyIn:
     case OpenACCClauseKind::PresentOrCopyIn:
       OS << " clause";
-      if (cast<OpenACCCopyInClause>(C)->isReadOnly())
-        OS << " : readonly";
+      if (cast<OpenACCCopyInClause>(C)->getModifierList() !=
+          OpenACCModifierKind::Invalid)
+        OS << " modifiers: " << cast<OpenACCCopyInClause>(C)->getModifierList();
       break;
     case OpenACCClauseKind::CopyOut:
     case OpenACCClauseKind::PCopyOut:
     case OpenACCClauseKind::PresentOrCopyOut:
       OS << " clause";
-      if (cast<OpenACCCopyOutClause>(C)->isZero())
-        OS << " : zero";
+      if (cast<OpenACCCopyOutClause>(C)->getModifierList() !=
+          OpenACCModifierKind::Invalid)
+        OS << " modifiers: "
+           << cast<OpenACCCopyOutClause>(C)->getModifierList();
       break;
     case OpenACCClauseKind::Create:
     case OpenACCClauseKind::PCreate:
     case OpenACCClauseKind::PresentOrCreate:
       OS << " clause";
-      if (cast<OpenACCCreateClause>(C)->isZero())
-        OS << " : zero";
+      if (cast<OpenACCCreateClause>(C)->getModifierList() !=
+          OpenACCModifierKind::Invalid)
+        OS << " modifiers: " << cast<OpenACCCreateClause>(C)->getModifierList();
       break;
     case OpenACCClauseKind::Wait:
       OS << " clause";

diff  --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp
index 4f4ae362983d0..64916995907c5 100644
--- a/clang/lib/Parse/ParseOpenACC.cpp
+++ b/clang/lib/Parse/ParseOpenACC.cpp
@@ -662,6 +662,70 @@ ExprResult Parser::ParseOpenACCConditionExpr() {
   return R.isInvalid() ? ExprError() : R.get().second;
 }
 
+// Tries to parse the 'modifier-list' for a 'copy', 'copyin', 'copyout', or
+// 'create' clause.
+OpenACCModifierKind Parser::tryParseModifierList(OpenACCClauseKind CK) {
+  // Use the tentative parsing to decide whether we are a comma-delmited list of
+  // identifers ending in a colon so we can do an actual parse with diagnostics.
+  {
+    RevertingTentativeParsingAction TPA{*this};
+    // capture any <ident><comma> pairs.
+    while (isTokenIdentifierOrKeyword(*this, getCurToken()) &&
+           NextToken().is(tok::comma)) {
+      ConsumeToken();
+      ConsumeToken();
+    }
+
+    if (!isTokenIdentifierOrKeyword(*this, getCurToken()) ||
+        !NextToken().is(tok::colon)) {
+      // No modifiers as this isn't a valid modifier-list.
+      return OpenACCModifierKind::Invalid;
+    }
+  }
+
+  auto GetModKind = [](Token T) {
+    return StringSwitch<OpenACCModifierKind>(T.getIdentifierInfo()->getName())
+        .Case("always", OpenACCModifierKind::Always)
+        .Case("alwaysin", OpenACCModifierKind::AlwaysIn)
+        .Case("alwaysout", OpenACCModifierKind::AlwaysOut)
+        .Case("readonly", OpenACCModifierKind::Readonly)
+        .Case("zero", OpenACCModifierKind::Zero)
+        .Default(OpenACCModifierKind::Invalid);
+  };
+
+  OpenACCModifierKind CurModList = OpenACCModifierKind::Invalid;
+  auto ConsumeModKind = [&]() {
+    Token IdentToken = getCurToken();
+    OpenACCModifierKind NewKind = GetModKind(IdentToken);
+
+    if (NewKind == OpenACCModifierKind::Invalid)
+      Diag(IdentToken.getLocation(), diag::err_acc_modifier)
+          << diag::ACCModifier::Unknown << IdentToken.getIdentifierInfo() << CK;
+    else if ((NewKind & CurModList) != OpenACCModifierKind::Invalid)
+      Diag(IdentToken.getLocation(), diag::err_acc_modifier)
+          << diag::ACCModifier::Duplicate << IdentToken.getIdentifierInfo()
+          << CK;
+    else
+      CurModList |= NewKind;
+
+    // Consumes the identifier.
+    ConsumeToken();
+    // Consumes the comma or colon.
+    ConsumeToken();
+  };
+
+  // Inspect all but the last item. We inspected enough to know that our current
+  // token is the identifier-like thing, so just check for the comma.
+  while (NextToken().is(tok::comma))
+    ConsumeModKind();
+
+  // Above we confirmed that this should be correct/we should be on the last
+  // item.
+  ConsumeModKind();
+
+  return CurModList;
+}
+
 // OpenACC 3.3, section 1.7:
 // To simplify the specification and convey appropriate constraint information,
 // a pqr-list is a comma-separated list of pdr items. The one exception is a
@@ -981,26 +1045,21 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
 
       break;
     }
+    case OpenACCClauseKind::Copy:
+    case OpenACCClauseKind::PCopy:
+    case OpenACCClauseKind::PresentOrCopy:
     case OpenACCClauseKind::CopyIn:
     case OpenACCClauseKind::PCopyIn:
-    case OpenACCClauseKind::PresentOrCopyIn: {
-      bool IsReadOnly = tryParseAndConsumeSpecialTokenKind(
-          *this, OpenACCSpecialTokenKind::ReadOnly, ClauseKind);
-      ParsedClause.setVarListDetails(ParseOpenACCVarList(DirKind, ClauseKind),
-                                     IsReadOnly,
-                                     /*IsZero=*/false);
-      break;
-    }
-    case OpenACCClauseKind::Create:
-    case OpenACCClauseKind::PCreate:
-    case OpenACCClauseKind::PresentOrCreate:
+    case OpenACCClauseKind::PresentOrCopyIn:
     case OpenACCClauseKind::CopyOut:
     case OpenACCClauseKind::PCopyOut:
-    case OpenACCClauseKind::PresentOrCopyOut: {
-      bool IsZero = tryParseAndConsumeSpecialTokenKind(
-          *this, OpenACCSpecialTokenKind::Zero, ClauseKind);
+    case OpenACCClauseKind::PresentOrCopyOut:
+    case OpenACCClauseKind::Create:
+    case OpenACCClauseKind::PCreate:
+    case OpenACCClauseKind::PresentOrCreate: {
+      OpenACCModifierKind ModList = tryParseModifierList(ClauseKind);
       ParsedClause.setVarListDetails(ParseOpenACCVarList(DirKind, ClauseKind),
-                                     /*IsReadOnly=*/false, IsZero);
+                                     ModList);
       break;
     }
     case OpenACCClauseKind::Reduction: {
@@ -1026,15 +1085,12 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
     case OpenACCClauseKind::Detach:
     case OpenACCClauseKind::DevicePtr:
     case OpenACCClauseKind::UseDevice:
-    case OpenACCClauseKind::Copy:
-    case OpenACCClauseKind::PCopy:
-    case OpenACCClauseKind::PresentOrCopy:
     case OpenACCClauseKind::FirstPrivate:
     case OpenACCClauseKind::NoCreate:
     case OpenACCClauseKind::Present:
     case OpenACCClauseKind::Private:
       ParsedClause.setVarListDetails(ParseOpenACCVarList(DirKind, ClauseKind),
-                                     /*IsReadOnly=*/false, /*IsZero=*/false);
+                                     OpenACCModifierKind::Invalid);
       break;
     case OpenACCClauseKind::Collapse: {
       bool HasForce = tryParseAndConsumeSpecialTokenKind(

diff  --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 819fb0853e8f5..c80f5f848f60b 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -2070,7 +2070,6 @@ void SemaOpenACC::CheckRoutineDecl(SourceLocation DirLoc,
     return;
   }
 
-  // TODO ERICH: Check bind here.
   auto BindItr = llvm::find_if(Clauses, llvm::IsaPred<OpenACCBindClause>);
   for (auto *A : NextParsedFDecl->attrs()) {
     // OpenACC 3.3 2.15:

diff  --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp
index a98b6712014cd..7d10c50d404d2 100644
--- a/clang/lib/Sema/SemaOpenACCClause.cpp
+++ b/clang/lib/Sema/SemaOpenACCClause.cpp
@@ -683,6 +683,59 @@ class SemaOpenACCClauseVisitor {
     return false;
   }
 
+  OpenACCModifierKind
+  CheckModifierList(SemaOpenACC::OpenACCParsedClause &Clause,
+                    OpenACCModifierKind Mods) {
+    auto CheckSingle = [=](OpenACCModifierKind CurMods,
+                           OpenACCModifierKind ValidKinds,
+                           OpenACCModifierKind Bit) {
+      if (!isOpenACCModifierBitSet(CurMods, Bit) ||
+          isOpenACCModifierBitSet(ValidKinds, Bit))
+        return CurMods;
+
+      SemaRef.Diag(Clause.getLParenLoc(), diag::err_acc_invalid_modifier)
+          << Bit << Clause.getClauseKind();
+
+      return CurMods ^ Bit;
+    };
+    auto Check = [&](OpenACCModifierKind ValidKinds) {
+      if ((Mods | ValidKinds) == ValidKinds)
+        return Mods;
+
+      Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Always);
+      Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::AlwaysIn);
+      Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::AlwaysOut);
+      Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Readonly);
+      Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Zero);
+      return Mods;
+    };
+
+    switch (Clause.getClauseKind()) {
+    default:
+      llvm_unreachable("Only for copy, copyin, copyout, create");
+    case OpenACCClauseKind::Copy:
+    case OpenACCClauseKind::PCopy:
+    case OpenACCClauseKind::PresentOrCopy:
+      return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
+                   OpenACCModifierKind::AlwaysOut);
+    case OpenACCClauseKind::CopyIn:
+    case OpenACCClauseKind::PCopyIn:
+    case OpenACCClauseKind::PresentOrCopyIn:
+      return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
+                   OpenACCModifierKind::Readonly);
+    case OpenACCClauseKind::CopyOut:
+    case OpenACCClauseKind::PCopyOut:
+    case OpenACCClauseKind::PresentOrCopyOut:
+      return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
+                   OpenACCModifierKind::Zero);
+    case OpenACCClauseKind::Create:
+    case OpenACCClauseKind::PCreate:
+    case OpenACCClauseKind::PresentOrCreate:
+      return Check(OpenACCModifierKind::Zero);
+    }
+    llvm_unreachable("didn't return from switch above?");
+  }
+
 public:
   SemaOpenACCClauseVisitor(SemaOpenACC &S,
                            ArrayRef<const OpenACCClause *> ExistingClauses)
@@ -1070,7 +1123,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitPresentClause(
 
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
     return nullptr;
 
   return OpenACCPresentClause::Create(Ctx, Clause.getBeginLoc(),
@@ -1106,25 +1159,28 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyClause(
   // really isn't anything to do here. GCC does some duplicate-finding, though
   // it isn't apparent in the standard where this is justified.
 
+  OpenACCModifierKind NewMods =
+      CheckModifierList(Clause, Clause.getModifierList());
+
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, NewMods))
     return nullptr;
 
   return OpenACCCopyClause::Create(
       Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.getVarList(), Clause.getEndLoc());
+      Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
 }
 
 OpenACCClause *SemaOpenACCClauseVisitor::VisitLinkClause(
     SemaOpenACC::OpenACCParsedClause &Clause) {
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
     return nullptr;
 
   Clause.setVarListDetails(SemaRef.CheckLinkClauseVarList(Clause.getVarList()),
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
+                           OpenACCModifierKind::Invalid);
 
   return OpenACCLinkClause::Create(Ctx, Clause.getBeginLoc(),
                                    Clause.getLParenLoc(), Clause.getVarList(),
@@ -1135,7 +1191,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceResidentClause(
     SemaOpenACC::OpenACCParsedClause &Clause) {
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
     return nullptr;
 
   return OpenACCDeviceResidentClause::Create(
@@ -1149,14 +1205,17 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyInClause(
   // really isn't anything to do here. GCC does some duplicate-finding, though
   // it isn't apparent in the standard where this is justified.
 
+  OpenACCModifierKind NewMods =
+      CheckModifierList(Clause, Clause.getModifierList());
+
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, NewMods))
     return nullptr;
 
   return OpenACCCopyInClause::Create(
       Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isReadOnly(), Clause.getVarList(), Clause.getEndLoc());
+      Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
 }
 
 OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
@@ -1165,14 +1224,17 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
   // really isn't anything to do here. GCC does some duplicate-finding, though
   // it isn't apparent in the standard where this is justified.
 
+  OpenACCModifierKind NewMods =
+      CheckModifierList(Clause, Clause.getModifierList());
+
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, NewMods))
     return nullptr;
 
   return OpenACCCopyOutClause::Create(
       Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
+      Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
 }
 
 OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(
@@ -1181,14 +1243,17 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(
   // really isn't anything to do here. GCC does some duplicate-finding, though
   // it isn't apparent in the standard where this is justified.
 
+  OpenACCModifierKind NewMods =
+      CheckModifierList(Clause, Clause.getModifierList());
+
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, NewMods))
     return nullptr;
 
   return OpenACCCreateClause::Create(
       Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
-      Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
+      Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
 }
 
 OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
@@ -1199,8 +1264,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
   llvm::erase_if(VarList, [&](Expr *E) {
     return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Attach, E);
   });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
+  Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
   return OpenACCAttachClause::Create(Ctx, Clause.getBeginLoc(),
                                      Clause.getLParenLoc(), Clause.getVarList(),
                                      Clause.getEndLoc());
@@ -1214,8 +1278,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDetachClause(
   llvm::erase_if(VarList, [&](Expr *E) {
     return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Detach, E);
   });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
+  Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
   return OpenACCDetachClause::Create(Ctx, Clause.getBeginLoc(),
                                      Clause.getLParenLoc(), Clause.getVarList(),
                                      Clause.getEndLoc());
@@ -1248,12 +1311,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
   llvm::erase_if(VarList, [&](Expr *E) {
     return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::DevicePtr, E);
   });
-  Clause.setVarListDetails(VarList,
-                           /*IsReadOnly=*/false, /*IsZero=*/false);
+  Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
 
   // 'declare' has some restrictions that need to be enforced separately, so
   // check it here.
-  if (SemaRef.CheckDeclareClause(Clause))
+  if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
     return nullptr;
 
   return OpenACCDevicePtrClause::Create(
@@ -2396,7 +2458,8 @@ SemaOpenACC::CheckLinkClauseVarList(ArrayRef<Expr *> VarExprs) {
 
   return NewVarList;
 }
-bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause) {
+bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause,
+                                     OpenACCModifierKind Mods) {
 
   if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Declare)
     return false;
@@ -2487,7 +2550,6 @@ bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause) {
     FilteredVarList.push_back(VarExpr);
   }
 
-  Clause.setVarListDetails(FilteredVarList, Clause.isReadOnly(),
-                           Clause.isZero());
+  Clause.setVarListDetails(FilteredVarList, Mods);
   return false;
 }

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 2d6f2ca67af8a..e0f7ccc4674d8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1186,22 +1186,24 @@ void OpenACCDeclClauseInstantiator::VisitVectorClause(
 void OpenACCDeclClauseInstantiator::VisitCopyClause(
     const OpenACCCopyClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+                                 C.getModifierList());
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause, C.getModifierList()))
     return;
   NewClause = OpenACCCopyClause::Create(
       SemaRef.getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.getVarList(), ParsedClause.getEndLoc());
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
+      ParsedClause.getEndLoc());
 }
 
 void OpenACCDeclClauseInstantiator::VisitLinkClause(
     const OpenACCLinkClause &C) {
   ParsedClause.setVarListDetails(
       SemaRef.OpenACC().CheckLinkClauseVarList(VisitVarList(C.getVarList())),
-      /*IsReadOnly=*/false, /*IsZero=*/false);
+      OpenACCModifierKind::Invalid);
 
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause,
+                                           OpenACCModifierKind::Invalid))
     return;
 
   NewClause = OpenACCLinkClause::Create(
@@ -1213,8 +1215,9 @@ void OpenACCDeclClauseInstantiator::VisitLinkClause(
 void OpenACCDeclClauseInstantiator::VisitDeviceResidentClause(
     const OpenACCDeviceResidentClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+                                 OpenACCModifierKind::Invalid);
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause,
+                                           OpenACCModifierKind::Invalid))
     return;
   NewClause = OpenACCDeviceResidentClause::Create(
       SemaRef.getASTContext(), ParsedClause.getBeginLoc(),
@@ -1224,48 +1227,49 @@ void OpenACCDeclClauseInstantiator::VisitDeviceResidentClause(
 
 void OpenACCDeclClauseInstantiator::VisitCopyInClause(
     const OpenACCCopyInClause &C) {
-  ParsedClause.setVarListDetails(VisitVarList(C.getVarList()), C.isReadOnly(),
-                                 /*IsZero=*/false);
+  ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
+                                 C.getModifierList());
 
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause, C.getModifierList()))
     return;
   NewClause = OpenACCCopyInClause::Create(
       SemaRef.getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isReadOnly(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 void OpenACCDeclClauseInstantiator::VisitCopyOutClause(
     const OpenACCCopyOutClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, C.isZero());
+                                 C.getModifierList());
 
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause, C.getModifierList()))
     return;
   NewClause = OpenACCCopyOutClause::Create(
       SemaRef.getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isZero(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 void OpenACCDeclClauseInstantiator::VisitCreateClause(
     const OpenACCCreateClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, C.isZero());
+                                 C.getModifierList());
 
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause, C.getModifierList()))
     return;
   NewClause = OpenACCCreateClause::Create(
       SemaRef.getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isZero(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 void OpenACCDeclClauseInstantiator::VisitPresentClause(
     const OpenACCPresentClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+                                 OpenACCModifierKind::Invalid);
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause,
+                                           OpenACCModifierKind::Invalid))
     return;
   NewClause = OpenACCPresentClause::Create(
       SemaRef.getASTContext(), ParsedClause.getBeginLoc(),
@@ -1282,9 +1286,9 @@ void OpenACCDeclClauseInstantiator::VisitDevicePtrClause(
                                      OpenACCClauseKind::DevicePtr, E);
                                }),
                 VarList.end());
-  ParsedClause.setVarListDetails(VarList,
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
-  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause))
+  ParsedClause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
+  if (SemaRef.OpenACC().CheckDeclareClause(ParsedClause,
+                                           OpenACCModifierKind::Invalid))
     return;
   NewClause = OpenACCDevicePtrClause::Create(
       SemaRef.getASTContext(), ParsedClause.getBeginLoc(),

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 237c5a9ef501b..b9bf748a2e98e 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -11760,7 +11760,7 @@ void OpenACCClauseTransform<Derived>::VisitSelfClause(
     }
 
     ParsedClause.setVarListDetails(InstantiatedVarList,
-                                   /*IsReadOnly=*/false, /*IsZero=*/false);
+                                   OpenACCModifierKind::Invalid);
 
     NewClause = OpenACCSelfClause::Create(
         Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11818,7 +11818,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitPrivateClause(
     const OpenACCPrivateClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCPrivateClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11830,7 +11830,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitHostClause(
     const OpenACCHostClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCHostClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11842,7 +11842,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitDeviceClause(
     const OpenACCDeviceClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCDeviceClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11854,7 +11854,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitFirstPrivateClause(
     const OpenACCFirstPrivateClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCFirstPrivateClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11866,7 +11866,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitNoCreateClause(
     const OpenACCNoCreateClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCNoCreateClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11878,7 +11878,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitPresentClause(
     const OpenACCPresentClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
 
   NewClause = OpenACCPresentClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
@@ -11890,12 +11890,13 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitCopyClause(
     const OpenACCCopyClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 C.getModifierList());
 
   NewClause = OpenACCCopyClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.getVarList(), ParsedClause.getEndLoc());
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
+      ParsedClause.getEndLoc());
 }
 
 template <typename Derived>
@@ -11923,13 +11924,13 @@ void OpenACCClauseTransform<Derived>::VisitBindClause(
 template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitCopyInClause(
     const OpenACCCopyInClause &C) {
-  ParsedClause.setVarListDetails(VisitVarList(C.getVarList()), C.isReadOnly(),
-                                 /*IsZero=*/false);
+  ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
+                                 C.getModifierList());
 
   NewClause = OpenACCCopyInClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isReadOnly(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 
@@ -11937,12 +11938,12 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitCopyOutClause(
     const OpenACCCopyOutClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, C.isZero());
+                                 C.getModifierList());
 
   NewClause = OpenACCCopyOutClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isZero(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 
@@ -11950,12 +11951,12 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitCreateClause(
     const OpenACCCreateClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, C.isZero());
+                                 C.getModifierList());
 
   NewClause = OpenACCCreateClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getClauseKind(),
       ParsedClause.getBeginLoc(), ParsedClause.getLParenLoc(),
-      ParsedClause.isZero(), ParsedClause.getVarList(),
+      ParsedClause.getModifierList(), ParsedClause.getVarList(),
       ParsedClause.getEndLoc());
 }
 template <typename Derived>
@@ -11969,8 +11970,7 @@ void OpenACCClauseTransform<Derived>::VisitAttachClause(
         OpenACCClauseKind::Attach, E);
   });
 
-  ParsedClause.setVarListDetails(VarList,
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+  ParsedClause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
   NewClause = OpenACCAttachClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
       ParsedClause.getLParenLoc(), ParsedClause.getVarList(),
@@ -11991,8 +11991,7 @@ void OpenACCClauseTransform<Derived>::VisitDetachClause(
                      }),
       VarList.end());
 
-  ParsedClause.setVarListDetails(VarList,
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+  ParsedClause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
   NewClause = OpenACCDetachClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
       ParsedClause.getLParenLoc(), ParsedClause.getVarList(),
@@ -12003,7 +12002,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitDeleteClause(
     const OpenACCDeleteClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
   NewClause = OpenACCDeleteClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
       ParsedClause.getLParenLoc(), ParsedClause.getVarList(),
@@ -12014,7 +12013,7 @@ template <typename Derived>
 void OpenACCClauseTransform<Derived>::VisitUseDeviceClause(
     const OpenACCUseDeviceClause &C) {
   ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+                                 OpenACCModifierKind::Invalid);
   NewClause = OpenACCUseDeviceClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
       ParsedClause.getLParenLoc(), ParsedClause.getVarList(),
@@ -12032,8 +12031,7 @@ void OpenACCClauseTransform<Derived>::VisitDevicePtrClause(
         OpenACCClauseKind::DevicePtr, E);
   });
 
-  ParsedClause.setVarListDetails(VarList,
-                                 /*IsReadOnly=*/false, /*IsZero=*/false);
+  ParsedClause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
   NewClause = OpenACCDevicePtrClause::Create(
       Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
       ParsedClause.getLParenLoc(), ParsedClause.getVarList(),

diff  --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 38697eb835134..d8d77e7f55232 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -12715,36 +12715,37 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() {
   case OpenACCClauseKind::PresentOrCopy:
   case OpenACCClauseKind::Copy: {
     SourceLocation LParenLoc = readSourceLocation();
+    OpenACCModifierKind ModList = readEnum<OpenACCModifierKind>();
     llvm::SmallVector<Expr *> VarList = readOpenACCVarList();
     return OpenACCCopyClause::Create(getContext(), ClauseKind, BeginLoc,
-                                     LParenLoc, VarList, EndLoc);
+                                     LParenLoc, ModList, VarList, EndLoc);
   }
   case OpenACCClauseKind::CopyIn:
   case OpenACCClauseKind::PCopyIn:
   case OpenACCClauseKind::PresentOrCopyIn: {
     SourceLocation LParenLoc = readSourceLocation();
-    bool IsReadOnly = readBool();
+    OpenACCModifierKind ModList = readEnum<OpenACCModifierKind>();
     llvm::SmallVector<Expr *> VarList = readOpenACCVarList();
     return OpenACCCopyInClause::Create(getContext(), ClauseKind, BeginLoc,
-                                       LParenLoc, IsReadOnly, VarList, EndLoc);
+                                       LParenLoc, ModList, VarList, EndLoc);
   }
   case OpenACCClauseKind::CopyOut:
   case OpenACCClauseKind::PCopyOut:
   case OpenACCClauseKind::PresentOrCopyOut: {
     SourceLocation LParenLoc = readSourceLocation();
-    bool IsZero = readBool();
+    OpenACCModifierKind ModList = readEnum<OpenACCModifierKind>();
     llvm::SmallVector<Expr *> VarList = readOpenACCVarList();
     return OpenACCCopyOutClause::Create(getContext(), ClauseKind, BeginLoc,
-                                        LParenLoc, IsZero, VarList, EndLoc);
+                                        LParenLoc, ModList, VarList, EndLoc);
   }
   case OpenACCClauseKind::Create:
   case OpenACCClauseKind::PCreate:
   case OpenACCClauseKind::PresentOrCreate: {
     SourceLocation LParenLoc = readSourceLocation();
-    bool IsZero = readBool();
+    OpenACCModifierKind ModList = readEnum<OpenACCModifierKind>();
     llvm::SmallVector<Expr *> VarList = readOpenACCVarList();
     return OpenACCCreateClause::Create(getContext(), ClauseKind, BeginLoc,
-                                       LParenLoc, IsZero, VarList, EndLoc);
+                                       LParenLoc, ModList, VarList, EndLoc);
   }
   case OpenACCClauseKind::Async: {
     SourceLocation LParenLoc = readSourceLocation();

diff  --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index f27be5fb4c76c..a48c05061626a 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8715,6 +8715,7 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) {
   case OpenACCClauseKind::PresentOrCopy: {
     const auto *CC = cast<OpenACCCopyClause>(C);
     writeSourceLocation(CC->getLParenLoc());
+    writeEnum(CC->getModifierList());
     writeOpenACCVarList(CC);
     return;
   }
@@ -8723,7 +8724,7 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) {
   case OpenACCClauseKind::PresentOrCopyIn: {
     const auto *CIC = cast<OpenACCCopyInClause>(C);
     writeSourceLocation(CIC->getLParenLoc());
-    writeBool(CIC->isReadOnly());
+    writeEnum(CIC->getModifierList());
     writeOpenACCVarList(CIC);
     return;
   }
@@ -8732,7 +8733,7 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) {
   case OpenACCClauseKind::PresentOrCopyOut: {
     const auto *COC = cast<OpenACCCopyOutClause>(C);
     writeSourceLocation(COC->getLParenLoc());
-    writeBool(COC->isZero());
+    writeEnum(COC->getModifierList());
     writeOpenACCVarList(COC);
     return;
   }
@@ -8741,7 +8742,7 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) {
   case OpenACCClauseKind::PresentOrCreate: {
     const auto *CC = cast<OpenACCCreateClause>(C);
     writeSourceLocation(CC->getLParenLoc());
-    writeBool(CC->isZero());
+    writeEnum(CC->getModifierList());
     writeOpenACCVarList(CC);
     return;
   }

diff  --git a/clang/test/AST/ast-print-openacc-combined-construct.cpp b/clang/test/AST/ast-print-openacc-combined-construct.cpp
index b5afc1515aa18..be959fb2b6117 100644
--- a/clang/test/AST/ast-print-openacc-combined-construct.cpp
+++ b/clang/test/AST/ast-print-openacc-combined-construct.cpp
@@ -171,16 +171,16 @@ void foo() {
 #pragma acc parallel loop no_create(i, array[1], array, array[1:2]) present(i, array[1], array, array[1:2])
   for(int i = 0;i<5;++i);
 
-// CHECK: #pragma acc parallel loop copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
-#pragma acc parallel loop copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
+// CHECK: #pragma acc parallel loop copy(alwaysin: i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
+#pragma acc parallel loop copy(alwaysin: i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
   for(int i = 0;i<5;++i);
 
-// CHECK: #pragma acc parallel loop copyin(i, array[1], array, array[1:2]) pcopyin(readonly: i, array[1], array, array[1:2]) present_or_copyin(i, array[1], array, array[1:2])
-#pragma acc parallel loop copyin(i, array[1], array, array[1:2]) pcopyin(readonly:i, array[1], array, array[1:2]) present_or_copyin(i, array[1], array, array[1:2])
+// CHECK: #pragma acc parallel loop copyin(i, array[1], array, array[1:2]) pcopyin(readonly: i, array[1], array, array[1:2]) present_or_copyin(always, alwaysin: i, array[1], array, array[1:2])
+#pragma acc parallel loop copyin(i, array[1], array, array[1:2]) pcopyin(readonly:i, array[1], array, array[1:2]) present_or_copyin(always, alwaysin: i, array[1], array, array[1:2])
   for(int i = 0;i<5;++i);
 
-// CHECK: #pragma acc parallel loop copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(i, array[1], array, array[1:2])
-#pragma acc parallel loop copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(i, array[1], array, array[1:2])
+// CHECK: #pragma acc parallel loop copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(always, alwaysin: i, array[1], array, array[1:2])
+#pragma acc parallel loop copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(always, alwaysin: i, array[1], array, array[1:2])
   for(int i = 0;i<5;++i);
 
 // CHECK: #pragma acc parallel loop create(i, array[1], array, array[1:2]) pcreate(zero: i, array[1], array, array[1:2]) present_or_create(i, array[1], array, array[1:2])

diff  --git a/clang/test/AST/ast-print-openacc-compute-construct.cpp b/clang/test/AST/ast-print-openacc-compute-construct.cpp
index 9516bfd843000..1fbb81a220aab 100644
--- a/clang/test/AST/ast-print-openacc-compute-construct.cpp
+++ b/clang/test/AST/ast-print-openacc-compute-construct.cpp
@@ -56,12 +56,16 @@ void foo() {
 #pragma acc parallel no_create(i, array[1], array, array[1:2]) present(i, array[1], array, array[1:2])
   while(true);
 
-// CHECK: #pragma acc parallel copyin(i, array[1], array, array[1:2]) pcopyin(readonly: i, array[1], array, array[1:2]) present_or_copyin(i, array[1], array, array[1:2])
-#pragma acc parallel copyin(i, array[1], array, array[1:2]) pcopyin(readonly:i, array[1], array, array[1:2]) present_or_copyin(i, array[1], array, array[1:2])
+// CHECK: #pragma acc parallel copy(i, array[1], array, array[1:2]) pcopy(alwaysin: i, array[1], array, array[1:2]) present_or_copy(always, alwaysout: i, array[1], array, array[1:2])
+#pragma acc parallel copy(i, array[1], array, array[1:2]) pcopy(alwaysin:i, array[1], array, array[1:2]) present_or_copy(always, alwaysout: i, array[1], array, array[1:2])
   while(true);
 
-// CHECK: #pragma acc parallel copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(i, array[1], array, array[1:2])
-#pragma acc parallel copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(i, array[1], array, array[1:2])
+// CHECK: #pragma acc parallel copyin(i, array[1], array, array[1:2]) pcopyin(readonly: i, array[1], array, array[1:2]) present_or_copyin(always, readonly: i, array[1], array, array[1:2])
+#pragma acc parallel copyin(i, array[1], array, array[1:2]) pcopyin(readonly:i, array[1], array, array[1:2]) present_or_copyin(readonly, always: i, array[1], array, array[1:2])
+  while(true);
+
+// CHECK: #pragma acc parallel copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(always, zero: i, array[1], array, array[1:2])
+#pragma acc parallel copyout(i, array[1], array, array[1:2]) pcopyout(zero: i, array[1], array, array[1:2]) present_or_copyout(always, zero: i, array[1], array, array[1:2])
   while(true);
 
 // CHECK: #pragma acc parallel create(i, array[1], array, array[1:2]) pcreate(zero: i, array[1], array, array[1:2]) present_or_create(i, array[1], array, array[1:2])

diff  --git a/clang/test/AST/ast-print-openacc-data-construct.cpp b/clang/test/AST/ast-print-openacc-data-construct.cpp
index 6d6f54cb45ada..b7d2428fb605a 100644
--- a/clang/test/AST/ast-print-openacc-data-construct.cpp
+++ b/clang/test/AST/ast-print-openacc-data-construct.cpp
@@ -17,8 +17,8 @@ void foo() {
 // CHECK: #pragma acc enter data copyin(Var)
 #pragma acc enter data copyin(Var)
   ;
-// CHECK: #pragma acc exit data copyout(Var)
-#pragma acc exit data copyout(Var)
+// CHECK: #pragma acc exit data copyout(always, zero: Var)
+#pragma acc exit data copyout(zero, always: Var)
   ;
 // CHECK: #pragma acc host_data use_device(Var)
 #pragma acc host_data use_device(Var)
@@ -44,8 +44,8 @@ void foo() {
 // CHECK: #pragma acc data default(none) async(i)
 #pragma acc data default(none) async(i)
   ;
-// CHECK: #pragma acc enter data copyin(i) async(i)
-#pragma acc enter data copyin(i) async(i)
+// CHECK: #pragma acc enter data copyin(always: i) async(i)
+#pragma acc enter data copyin(always: i) async(i)
 // CHECK: #pragma acc exit data copyout(i) async
 #pragma acc exit data copyout(i) async
 
@@ -56,8 +56,8 @@ void foo() {
 // CHECK: #pragma acc enter data copyin(Var) wait
 #pragma acc enter data copyin(Var) wait
 
-// CHECK: #pragma acc exit data copyout(Var) wait(*iPtr, i)
-#pragma acc exit data copyout(Var) wait(*iPtr, i)
+// CHECK: #pragma acc exit data copyout(always, zero: Var) wait(*iPtr, i)
+#pragma acc exit data copyout(always, zero: Var) wait(*iPtr, i)
 
 // CHECK: #pragma acc data default(none) wait(queues: *iPtr, i)
 #pragma acc data default(none) wait(queues:*iPtr, i)
@@ -88,8 +88,8 @@ void foo() {
 #pragma acc data present(i, array[1], array, array[1:2])
   ;
 
-// CHECK: #pragma acc data default(none) copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
-#pragma acc data default(none) copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(i, array[1], array, array[1:2])
+// CHECK: #pragma acc data default(none) copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(alwaysin, alwaysout: i, array[1], array, array[1:2])
+#pragma acc data default(none) copy(i, array[1], array, array[1:2]) pcopy(i, array[1], array, array[1:2]) present_or_copy(alwaysin, alwaysout: i, array[1], array, array[1:2])
   ;
 
 // CHECK: #pragma acc enter data copyin(i, array[1], array, array[1:2]) pcopyin(readonly: i, array[1], array, array[1:2]) present_or_copyin(i, array[1], array, array[1:2])

diff  --git a/clang/test/AST/ast-print-openacc-declare-construct.cpp b/clang/test/AST/ast-print-openacc-declare-construct.cpp
index fce4afc6aedae..2a61b08c5500b 100644
--- a/clang/test/AST/ast-print-openacc-declare-construct.cpp
+++ b/clang/test/AST/ast-print-openacc-declare-construct.cpp
@@ -5,8 +5,8 @@ int GlobalArray[5];
 int GlobalArray2[5];
 // CHECK: #pragma acc declare deviceptr(Global) copyin(GlobalArray)
 #pragma acc declare deviceptr(Global), copyin(GlobalArray)
-// CHECK: #pragma acc declare create(Global2, GlobalArray2)
-#pragma acc declare create(Global2, GlobalArray2)
+// CHECK: #pragma acc declare create(zero: Global2, GlobalArray2)
+#pragma acc declare create(zero: Global2, GlobalArray2)
 
 namespace NS {
 int NSVar;
@@ -18,8 +18,8 @@ int NSArray[5];
 struct Struct {
   static const int StaticMem = 5;
   static const int StaticMemArray[5];
-// CHECK: #pragma acc declare copyin(StaticMem, StaticMemArray)
-#pragma acc declare copyin(StaticMem, StaticMemArray)
+// CHECK: #pragma acc declare copyin(always, alwaysin: StaticMem, StaticMemArray)
+#pragma acc declare copyin(always, alwaysin: StaticMem, StaticMemArray)
 
   void MemFunc1(int Arg) {
     int Local;

diff  --git a/clang/test/ParserOpenACC/parse-clauses.c b/clang/test/ParserOpenACC/parse-clauses.c
index e31b7492dab2c..2319b1abb9e83 100644
--- a/clang/test/ParserOpenACC/parse-clauses.c
+++ b/clang/test/ParserOpenACC/parse-clauses.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 %s -verify -fopenacc -std=c99
-// RUNX: %clang_cc1 %s -verify -fopenacc
-// RUNX: %clang_cc1 %s -verify -fopenacc -x c++
+// RUN: %clang_cc1 %s -verify=expected,c -fopenacc -std=c99
+// RUN: %clang_cc1 %s -verify=expected,c -fopenacc
+// RUN: %clang_cc1 %s -verify=expected,cpp -fopenacc -x c++
 
 void func() {
 
@@ -422,7 +422,8 @@ void VarListClauses() {
 #pragma acc serial copy(HasMem.MemArr[:]), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{expected expression}}
+  // cpp-error at +2{{expected unqualified-id}}
+  // c-error at +1{{expected expression}}
 #pragma acc serial copy(HasMem.MemArr[::]), self
   for(int i = 0; i < 5;++i) {}
 
@@ -443,6 +444,21 @@ void VarListClauses() {
 #pragma acc serial present_or_copy(HasMem.MemArr[3:])
   for(int i = 0; i < 5;++i) {}
 
+  // expected-error at +2{{unknown modifier 'foo' in OpenACC modifier-list on 'copy' clause}}
+  // expected-error at +1{{unknown modifier 'bar' in OpenACC modifier-list on 'copy' clause}}
+#pragma acc parallel copy(foo, bar: HasMem.MemArr[3:]) self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +1{{duplicate modifier 'always' in OpenACC modifier-list on 'copy' clause}}
+#pragma acc parallel copy(always, alwaysin, always: HasMem.MemArr[3:]) self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +3{{use of undeclared identifier 'always'}}
+  // expected-error at +2{{use of undeclared identifier 'alwaysin'}}
+  // expected-error at +1{{use of undeclared identifier 'always'}}
+#pragma acc parallel copy(always, alwaysin, always, HasMem.MemArr[3:]) self
+  for(int i = 0; i < 5;++i) {}
+
   // expected-error at +2 2{{OpenACC variable in 'use_device' clause is not a valid variable name or array name}}
   // expected-error at +1{{expected ','}}
 #pragma acc host_data use_device(s.array[s.value] s.array[s.value :5] ), if_present
@@ -580,15 +596,15 @@ void VarListClauses() {
 #pragma acc serial copyout(zero s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'readonly' on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
 #pragma acc serial copyout(readonly:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'copyout' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
 #pragma acc serial copyout(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'copyout' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
 #pragma acc serial copyout(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
@@ -597,6 +613,15 @@ void VarListClauses() {
 #pragma acc serial copyout(invalid s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
+  // expected-error at +1{{unknown modifier 'bar' in OpenACC modifier-list on 'copyout' clause}}
+#pragma acc serial copyout(invalid, bar: s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +1{{duplicate modifier 'zero' in OpenACC modifier-list on 'copyout' clause}}
+#pragma acc serial copyout(zero, zero, always: s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
   // expected-error at +1{{expected ','}}
 #pragma acc serial create(s.array[s.value] s.array[s.value :5] ), self
   for(int i = 0; i < 5;++i) {}
@@ -607,6 +632,20 @@ void VarListClauses() {
 #pragma acc serial create(zero:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc serial create(always, zero:s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +2{{duplicate modifier 'always' in OpenACC modifier-list on 'create' clause}}
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc serial create(always, always, zero:s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc serial create(always, invalid, zero:s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
   // expected-warning at +1{{OpenACC clause name 'pcreate' is a deprecated clause name and is now an alias for 'create'}}
 #pragma acc serial pcreate(s.array[s.value : 5], s.value)
   for(int i = 0; i < 5;++i) {}
@@ -623,15 +662,15 @@ void VarListClauses() {
 #pragma acc serial create(zero s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'readonly' on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
 #pragma acc serial create(readonly:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'create' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
 #pragma acc serial create(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'create' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
 #pragma acc serial create(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
@@ -666,15 +705,15 @@ void VarListClauses() {
 #pragma acc serial copyin(readonly s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'zero' on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
 #pragma acc serial copyin(zero :s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'copyin' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyin' clause}}
 #pragma acc serial copyin(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
-  // expected-error at +1{{invalid tag 'invalid' on 'copyin' clause}}
+  // expected-error at +1{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyin' clause}}
 #pragma acc serial copyin(invalid:s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
 
@@ -682,6 +721,15 @@ void VarListClauses() {
   // expected-error at +1{{expected ','}}
 #pragma acc serial copyin(invalid s.array[s.value : 5], s.value), self
   for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +2{{unknown modifier 'foo' in OpenACC modifier-list on 'copyin' clause}}
+  // expected-error at +1{{unknown modifier 'bar' in OpenACC modifier-list on 'copyin' clause}}
+#pragma acc serial copyin(foo, bar: s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
+
+  // expected-error at +1{{duplicate modifier 'readonly' in OpenACC modifier-list on 'copyin' clause}}
+#pragma acc serial copyin(always, readonly, readonly: s.array[s.value : 5], s.value), self
+  for(int i = 0; i < 5;++i) {}
 }
 
 void ReductionClauseParsing() {

diff  --git a/clang/test/SemaOpenACC/combined-construct-copy-ast.cpp b/clang/test/SemaOpenACC/combined-construct-copy-ast.cpp
index a696badb0847c..75e5d0283889e 100644
--- a/clang/test/SemaOpenACC/combined-construct-copy-ast.cpp
+++ b/clang/test/SemaOpenACC/combined-construct-copy-ast.cpp
@@ -14,18 +14,18 @@ void NormalUses(float *PointerParam) {
   // CHECK: ParmVarDecl
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copy(GlobalArray) pcopy(PointerParam[Global]) present_or_copy(Global)
+#pragma acc parallel loop copy(always:GlobalArray) pcopy(alwaysin:PointerParam[Global]) present_or_copy(alwaysout: Global)
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
-  // CHECK-NEXT: copy clause
+  // CHECK-NEXT: copy clause modifiers: always
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: alwaysin
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysout
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: ForStmt
   // CHECK:NullStmt
@@ -42,12 +42,12 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copy(t) pcopy(NTTP, u) present_or_copy(u[0:t])
+#pragma acc parallel loop copy(t) pcopy(always, alwaysin, alwaysout: NTTP, u) present_or_copy(u[0:t])
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: copy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always, alwaysin, alwaysout
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_copy clause
@@ -75,7 +75,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: copy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always, alwaysin, alwaysout
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/combined-construct-copy-clause.c b/clang/test/SemaOpenACC/combined-construct-copy-clause.c
index c588fd8d94987..07c412c621ff1 100644
--- a/clang/test/SemaOpenACC/combined-construct-copy-clause.c
+++ b/clang/test/SemaOpenACC/combined-construct-copy-clause.c
@@ -70,3 +70,18 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copy(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc parallel loop copy(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+#pragma acc serial loop copy(readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc kernels loop copy(zero: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc parallel loop copy(always, alwaysin, alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/combined-construct-copyin-ast.cpp b/clang/test/SemaOpenACC/combined-construct-copyin-ast.cpp
index e7ac8238a7db6..f7b5cb841b966 100644
--- a/clang/test/SemaOpenACC/combined-construct-copyin-ast.cpp
+++ b/clang/test/SemaOpenACC/combined-construct-copyin-ast.cpp
@@ -14,18 +14,18 @@ void NormalUses(float *PointerParam) {
   // CHECK: ParmVarDecl
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copyin(GlobalArray) pcopyin(readonly:PointerParam[Global]) present_or_copyin(Global)
+#pragma acc parallel loop copyin(GlobalArray) pcopyin(readonly:PointerParam[Global]) present_or_copyin(always, alwaysin: Global)
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: ForStmt
   // CHECK: NullStmt
@@ -42,12 +42,12 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copyin(t) pcopyin(readonly: NTTP, u) present_or_copyin(u[0:t])
+#pragma acc parallel loop copyin(always, readonly, alwaysin: t) pcopyin(readonly: NTTP, u) present_or_copyin(u[0:t])
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
-  // CHECK-NEXT: copyin clause
+  // CHECK-NEXT: copyin clause modifiers: always, alwaysin, readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_copyin clause
@@ -73,9 +73,9 @@ void TemplUses(T t, U u) {
 
 // #pragma acc parallel copyin(t) pcopyin(readonly: NTTP, u) present_or_copyin(u[0:t])
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
-  // CHECK-NEXT: copyin clause
+  // CHECK-NEXT: copyin clause modifiers: always, alwaysin, readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/combined-construct-copyin-clause.c b/clang/test/SemaOpenACC/combined-construct-copyin-clause.c
index 4cb635ddb2470..b4a6eafdb9ebd 100644
--- a/clang/test/SemaOpenACC/combined-construct-copyin-clause.c
+++ b/clang/test/SemaOpenACC/combined-construct-copyin-clause.c
@@ -61,7 +61,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop copyin((float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
-  // expected-error at +2{{invalid tag 'invalid' on 'copyin' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyin' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop copyin(invalid:(float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
@@ -76,3 +76,19 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copyin(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc parallel loop copyin(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+#pragma acc serial loop copyin(alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc kernels loop copyin(zero: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc parallel loop copyin(always, alwaysin, readonly: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/combined-construct-copyout-ast.cpp b/clang/test/SemaOpenACC/combined-construct-copyout-ast.cpp
index 02eda413dd53f..ec4451e9df7d3 100644
--- a/clang/test/SemaOpenACC/combined-construct-copyout-ast.cpp
+++ b/clang/test/SemaOpenACC/combined-construct-copyout-ast.cpp
@@ -14,18 +14,18 @@ void NormalUses(float *PointerParam) {
   // CHECK: ParmVarDecl
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(Global)
+#pragma acc parallel loop copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(always, alwaysin: Global)
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: For
   // CHECK: NullStmt
@@ -42,12 +42,12 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc parallel loop copyout(t) pcopyout(zero: NTTP, u) present_or_copyout(u[0:t])
+#pragma acc parallel loop copyout(always, alwaysin: t) pcopyout(zero: NTTP, u) present_or_copyout(u[0:t])
   for (unsigned i = 0; i < 5; ++i);
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
-  // CHECK-NEXT: copyout clause
+  // CHECK-NEXT: copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_copyout clause
@@ -73,9 +73,9 @@ void TemplUses(T t, U u) {
 
 // #pragma acc parallel copyout(t) pcopyout(zero: NTTP, u) present_or_copyout(u[0:t])
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
-  // CHECK-NEXT: copyout clause
+  // CHECK-NEXT: copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/combined-construct-copyout-clause.c b/clang/test/SemaOpenACC/combined-construct-copyout-clause.c
index c43f9592062fa..6621adb5c6124 100644
--- a/clang/test/SemaOpenACC/combined-construct-copyout-clause.c
+++ b/clang/test/SemaOpenACC/combined-construct-copyout-clause.c
@@ -61,7 +61,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop copyout((float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
-  // expected-error at +2{{invalid tag 'invalid' on 'copyout' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop copyout(invalid:(float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
@@ -76,3 +76,19 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copyout(LocalInt)
   for(int i = 0; i < 6;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc parallel loop copyout(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 0; i < 6;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+#pragma acc serial loop copyout(alwaysout: V1)
+  for(int i = 0; i < 6;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc kernels loop copyout(readonly: V1)
+  for(int i = 0; i < 6;++i);
+#pragma acc parallel loop copyout(always, alwaysin, zero: V1)
+  for(int i = 0; i < 6;++i);
+}
+

diff  --git a/clang/test/SemaOpenACC/combined-construct-create-ast.cpp b/clang/test/SemaOpenACC/combined-construct-create-ast.cpp
index 16268eda4e5d2..888654e690c0b 100644
--- a/clang/test/SemaOpenACC/combined-construct-create-ast.cpp
+++ b/clang/test/SemaOpenACC/combined-construct-create-ast.cpp
@@ -19,7 +19,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -47,7 +47,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_create clause
@@ -75,7 +75,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCCombinedConstruct{{.*}} parallel loop
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/combined-construct-create-clause.c b/clang/test/SemaOpenACC/combined-construct-create-clause.c
index c17e6921a7da2..bf7dfe83a0511 100644
--- a/clang/test/SemaOpenACC/combined-construct-create-clause.c
+++ b/clang/test/SemaOpenACC/combined-construct-create-clause.c
@@ -61,7 +61,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop create((float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
-  // expected-error at +2{{invalid tag 'invalid' on 'create' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel loop create(invalid:(float)ArrayParam[2])
   for(int i = 0; i < 5; ++i);
@@ -76,3 +76,27 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_create(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+
+void ModList() {
+  int V1;
+  // expected-error at +4{{OpenACC 'always' modifier not valid on 'create' clause}}
+  // expected-error at +3{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc parallel loop create(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc serial loop create(always: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+#pragma acc kernels loop create(alwaysin: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+#pragma acc parallel loop create(alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc serial loop create(readonly: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc kernels loop create(zero: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/compute-construct-copy-clause.c b/clang/test/SemaOpenACC/compute-construct-copy-clause.c
index c4a9963ef4c7c..e83bdab64c246 100644
--- a/clang/test/SemaOpenACC/compute-construct-copy-clause.c
+++ b/clang/test/SemaOpenACC/compute-construct-copy-clause.c
@@ -70,3 +70,18 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copy(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc parallel copy(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+#pragma acc serial copy(readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc kernels copy(zero: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc parallel copy(always, alwaysin, alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/compute-construct-copyin-clause.c b/clang/test/SemaOpenACC/compute-construct-copyin-clause.c
index 84b5c29f3bf67..eaa8a604df32a 100644
--- a/clang/test/SemaOpenACC/compute-construct-copyin-clause.c
+++ b/clang/test/SemaOpenACC/compute-construct-copyin-clause.c
@@ -61,7 +61,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel copyin((float)ArrayParam[2])
   while(1);
-  // expected-error at +2{{invalid tag 'invalid' on 'copyin' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyin' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel copyin(invalid:(float)ArrayParam[2])
   while(1);
@@ -76,3 +76,18 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copyin(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc parallel copyin(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+#pragma acc serial copyin(alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc kernels copyin(zero: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc parallel copyin(always, alwaysin, readonly: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/compute-construct-copyout-clause.c b/clang/test/SemaOpenACC/compute-construct-copyout-clause.c
index da64be291494e..f1ea21d0824cc 100644
--- a/clang/test/SemaOpenACC/compute-construct-copyout-clause.c
+++ b/clang/test/SemaOpenACC/compute-construct-copyout-clause.c
@@ -61,7 +61,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel copyout((float)ArrayParam[2])
   while(1);
-  // expected-error at +2{{invalid tag 'invalid' on 'copyout' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel copyout(invalid:(float)ArrayParam[2])
   while(1);
@@ -76,3 +76,18 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_copyout(LocalInt)
   for(int i = 0; i < 6;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc parallel copyout(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 0; i < 6;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+#pragma acc serial copyout(alwaysout: V1)
+  for(int i = 0; i < 6;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc kernels copyout(readonly: V1)
+  for(int i = 0; i < 6;++i);
+#pragma acc parallel copyout(always, alwaysin, zero: V1)
+  for(int i = 0; i < 6;++i);
+}

diff  --git a/clang/test/SemaOpenACC/compute-construct-create-clause.c b/clang/test/SemaOpenACC/compute-construct-create-clause.c
index d54a82a4e7dfa..926c5b88a5115 100644
--- a/clang/test/SemaOpenACC/compute-construct-create-clause.c
+++ b/clang/test/SemaOpenACC/compute-construct-create-clause.c
@@ -62,7 +62,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel create((float)ArrayParam[2])
   while(1);
-  // expected-error at +2{{invalid tag 'invalid' on 'create' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc parallel create(invalid:(float)ArrayParam[2])
   while(1);
@@ -77,3 +77,26 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc loop present_or_create(LocalInt)
   for(int i = 5; i < 10;++i);
 }
+void ModList() {
+  int V1;
+  // expected-error at +4{{OpenACC 'always' modifier not valid on 'create' clause}}
+  // expected-error at +3{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc parallel create(always, alwaysin, alwaysout, zero, readonly: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc serial create(always: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+#pragma acc kernels create(alwaysin: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+#pragma acc parallel create(alwaysout: V1)
+  for(int i = 5; i < 10;++i);
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc serial create(readonly: V1)
+  for(int i = 5; i < 10;++i);
+#pragma acc kernels loop create(zero: V1)
+  for(int i = 5; i < 10;++i);
+}

diff  --git a/clang/test/SemaOpenACC/compute-construct-varlist-ast.cpp b/clang/test/SemaOpenACC/compute-construct-varlist-ast.cpp
index 1bfd4e8af6481..df01c59349665 100644
--- a/clang/test/SemaOpenACC/compute-construct-varlist-ast.cpp
+++ b/clang/test/SemaOpenACC/compute-construct-varlist-ast.cpp
@@ -99,52 +99,52 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copy(GlobalArray) pcopy(PointerParam[Global]) present_or_copy(Global)
+#pragma acc parallel copy(GlobalArray) pcopy(always: PointerParam[Global]) present_or_copy(alwaysin, alwaysout: Global)
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
-  // CHECK-NEXT: copy clause
+  // CHECK-NEXT: copy clause 
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysin, alwaysout
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: WhileStmt
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copyin(GlobalArray) pcopyin(readonly: PointerParam[Global]) present_or_copyin(Global)
+#pragma acc parallel copyin(GlobalArray) pcopyin(readonly: PointerParam[Global]) present_or_copyin(always, alwaysin: Global)
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: WhileStmt
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(Global)
+#pragma acc parallel copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(always, alwaysin: Global)
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: WhileStmt
   // CHECK-NEXT: CXXBoolLiteralExpr
@@ -155,7 +155,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -329,15 +329,15 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copy(t) pcopy(NTTP, u) present_or_copy(u[0:t])
+#pragma acc parallel copy(t) pcopy(always: NTTP, u) present_or_copy(alwaysin, alwaysout: u[0:t])
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysin, alwaysout
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -346,15 +346,15 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copyin(t) pcopyin(readonly:NTTP, u) present_or_copyin(u[0:t])
+#pragma acc parallel copyin(t) pcopyin(readonly:NTTP, u) present_or_copyin(always, alwaysin: u[0:t])
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -363,15 +363,15 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-#pragma acc parallel copyout(t) pcopyout(zero:NTTP, u) present_or_copyout(u[0:t])
+#pragma acc parallel copyout(t) pcopyout(zero:NTTP, u) present_or_copyout(always, alwaysin: u[0:t])
   while(true);
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -385,7 +385,7 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_create clause
@@ -529,16 +529,16 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-//#pragma acc parallel copy(t) pcopy(NTTP, u) copy_or_present(u[0:t])
+//#pragma acc parallel copy(t) pcopy(always: NTTP, u) present_or_copy(alwaysin, alwaysout: u[0:t])
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysin, alwaysout
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
@@ -549,16 +549,16 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-//#pragma acc parallel copyin(t) pcopyin(readonly:NTTP, u) present_or_copyin(u[0:t])
+//#pragma acc parallel copyin(t) pcopyin(readonly:NTTP, u) present_or_copyin(always, alwaysin: u[0:t])
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
@@ -569,16 +569,16 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: CXXBoolLiteralExpr
   // CHECK-NEXT: NullStmt
 
-//#pragma acc parallel copyout(t) pcopyout(zero:NTTP, u) present_or_copyout(u[0:t])
+// #pragma acc parallel copyout(t) pcopyout(zero:NTTP, u) present_or_copyout(always, alwaysin: u[0:t])
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: always, alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
@@ -593,7 +593,7 @@ void TemplUses(T t, U u, T*PointerParam) {
   // CHECK-NEXT: OpenACCComputeConstruct{{.*}} parallel
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/data-construct-copy-ast.cpp b/clang/test/SemaOpenACC/data-construct-copy-ast.cpp
index de067f00a2b29..8c0a12f244257 100644
--- a/clang/test/SemaOpenACC/data-construct-copy-ast.cpp
+++ b/clang/test/SemaOpenACC/data-construct-copy-ast.cpp
@@ -14,18 +14,18 @@ void NormalUses(float *PointerParam) {
   // CHECK: FunctionDecl{{.*}}NormalUses
   // CHECK: ParmVarDecl
   // CHECK-NEXT: CompoundStmt
-#pragma acc data copy(GlobalArray) pcopy(PointerParam[Global]) present_or_copy(Global)
+#pragma acc data copy(GlobalArray) pcopy(always, alwaysin: PointerParam[Global]) present_or_copy(alwaysout: Global)
   ;
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopy clause
+  // CHECK-NEXT: pcopy clause modifiers: always, alwaysin
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysout
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: NullStmt
 }
@@ -40,15 +40,15 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc data copy(t) pcopy(NTTP, u) present_or_copy(u[0:t])
+#pragma acc data copy(always: t) pcopy(NTTP, u) present_or_copy(alwaysin, alwaysout: u[0:t])
   ;
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
-  // CHECK-NEXT: copy clause
+  // CHECK-NEXT: copy clause modifiers: always
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
   // CHECK-NEXT: pcopy clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysin, alwaysout
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -69,14 +69,14 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: CompoundStmt
 
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
-  // CHECK-NEXT: copy clause
+  // CHECK-NEXT: copy clause modifiers: always
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
   // CHECK-NEXT: pcopy clause
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copy clause
+  // CHECK-NEXT: present_or_copy clause modifiers: alwaysin, alwaysout
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'

diff  --git a/clang/test/SemaOpenACC/data-construct-copy-clause.c b/clang/test/SemaOpenACC/data-construct-copy-clause.c
index 882a0bc87e003..0b2b0534073ed 100644
--- a/clang/test/SemaOpenACC/data-construct-copy-clause.c
+++ b/clang/test/SemaOpenACC/data-construct-copy-clause.c
@@ -67,3 +67,19 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc host_data present_or_copy(LocalInt)
   ;
 }
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc data copy(always, alwaysin, alwaysout, zero, readonly: V1)
+  ;
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+#pragma acc data copy(readonly: V1)
+  ;
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc data copy(zero: V1)
+  ;
+#pragma acc data copy(always, alwaysin, alwaysout: V1)
+;
+}
+

diff  --git a/clang/test/SemaOpenACC/data-construct-copyin-ast.cpp b/clang/test/SemaOpenACC/data-construct-copyin-ast.cpp
index fd21d60c84431..a3c8e7a36f9f8 100644
--- a/clang/test/SemaOpenACC/data-construct-copyin-ast.cpp
+++ b/clang/test/SemaOpenACC/data-construct-copyin-ast.cpp
@@ -19,7 +19,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -29,17 +29,17 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: NullStmt
 
-#pragma acc enter data copyin(GlobalArray) pcopyin(readonly:PointerParam[Global]) present_or_copyin(Global)
+#pragma acc enter data copyin(GlobalArray) pcopyin(readonly:PointerParam[Global]) present_or_copyin(readonly, always: Global)
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, readonly 
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
 }
 
@@ -54,15 +54,15 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc data copyin(t) pcopyin(readonly: NTTP, u) present_or_copyin(u[0:t])
+#pragma acc data copyin(t) pcopyin(readonly: NTTP, u) present_or_copyin(readonly, always: u[0:t])
   ;
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, readonly
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -73,7 +73,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_copyin clause
@@ -98,12 +98,12 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copyin clause
+  // CHECK-NEXT: present_or_copyin clause modifiers: always, readonly
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
@@ -115,7 +115,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyin clause : readonly
+  // CHECK-NEXT: pcopyin clause modifiers: readonly
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/data-construct-copyin-clause.c b/clang/test/SemaOpenACC/data-construct-copyin-clause.c
index 370cc7000f8d8..edc3f0a2e91fe 100644
--- a/clang/test/SemaOpenACC/data-construct-copyin-clause.c
+++ b/clang/test/SemaOpenACC/data-construct-copyin-clause.c
@@ -58,7 +58,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data copyin((float)ArrayParam[2])
   ;
-  // expected-error at +2{{invalid tag 'invalid' on 'copyin' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyin' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data copyin(invalid:(float)ArrayParam[2])
   ;
@@ -71,3 +71,24 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc host_data pcopyin(LocalInt)
   ;
 }
+
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc data copyin(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+#pragma acc data copyin(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc data copyin(zero: V1)
+#pragma acc data copyin(always, alwaysin, readonly: V1)
+
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc enter data copyin(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+#pragma acc enter data copyin(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc enter data copyin(zero: V1)
+#pragma acc enter data copyin(always, alwaysin, readonly: V1)
+}

diff  --git a/clang/test/SemaOpenACC/data-construct-copyout-ast.cpp b/clang/test/SemaOpenACC/data-construct-copyout-ast.cpp
index 38e6e7b476fe5..069ced7de83d8 100644
--- a/clang/test/SemaOpenACC/data-construct-copyout-ast.cpp
+++ b/clang/test/SemaOpenACC/data-construct-copyout-ast.cpp
@@ -19,7 +19,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -29,17 +29,17 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
   // CHECK-NEXT: NullStmt
 
-#pragma acc exit data copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(Global)
+#pragma acc exit data copyout(GlobalArray) pcopyout(zero:PointerParam[Global]) present_or_copyout(always, zero: Global)
   // CHECK-NEXT: OpenACCExitDataConstruct{{.*}} exit data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: always, zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue Var{{.*}}'Global' 'int'
 }
 
@@ -54,15 +54,15 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: ParmVarDecl{{.*}} referenced u 'U'
   // CHECK-NEXT: CompoundStmt
 
-#pragma acc data copyout(t) pcopyout(zero: NTTP, u) present_or_copyout(u[0:t])
+#pragma acc data copyout(t) pcopyout(zero: NTTP, u) present_or_copyout(alwaysin: u[0:t])
   ;
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: IntegerLiteral{{.*}} 'int' 0
@@ -73,7 +73,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCExitDataConstruct{{.*}} exit data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_copyout clause
@@ -98,12 +98,12 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
-  // CHECK-NEXT: present_or_copyout clause
+  // CHECK-NEXT: present_or_copyout clause modifiers: alwaysin
   // CHECK-NEXT: ArraySectionExpr
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}} 'u' 'int *'
@@ -115,7 +115,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCExitDataConstruct{{.*}} exit data
   // CHECK-NEXT: copyout clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcopyout clause : zero
+  // CHECK-NEXT: pcopyout clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/data-construct-copyout-clause.c b/clang/test/SemaOpenACC/data-construct-copyout-clause.c
index 0f9d5f2ad5c83..8d137e093db0e 100644
--- a/clang/test/SemaOpenACC/data-construct-copyout-clause.c
+++ b/clang/test/SemaOpenACC/data-construct-copyout-clause.c
@@ -58,7 +58,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data copyout((float)ArrayParam[2])
   ;
-  // expected-error at +2{{invalid tag 'invalid' on 'copyout' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'copyout' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data copyout(invalid:(float)ArrayParam[2])
   ;
@@ -71,3 +71,25 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc host_data pcopyout(LocalInt)
   ;
 }
+
+void ModList() {
+  int V1;
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc data copyout(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+#pragma acc data copyout(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc data copyout(readonly: V1)
+#pragma acc data copyout(always, alwaysin, zero: V1)
+
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc exit data copyout(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+#pragma acc exit data copyout(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc exit data copyout(readonly: V1)
+#pragma acc exit data copyout(always, alwaysin, zero: V1)
+}
+

diff  --git a/clang/test/SemaOpenACC/data-construct-create-ast.cpp b/clang/test/SemaOpenACC/data-construct-create-ast.cpp
index 623d44aac43f9..a147125aff29b 100644
--- a/clang/test/SemaOpenACC/data-construct-create-ast.cpp
+++ b/clang/test/SemaOpenACC/data-construct-create-ast.cpp
@@ -19,7 +19,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -33,7 +33,7 @@ void NormalUses(float *PointerParam) {
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'short[5]' lvalue Var{{.*}}'GlobalArray' 'short[5]'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: ArraySubscriptExpr{{.*}}'float' lvalue
   // CHECK-NEXT: ImplicitCastExpr{{.*}} 'float *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr{{.*}}'float *' lvalue ParmVar{{.*}}'PointerParam' 'float *'
@@ -59,7 +59,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_create clause
@@ -73,7 +73,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'T' lvalue ParmVar{{.*}} 't' 'T'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'auto' lvalue NonTypeTemplateParm{{.*}} 'NTTP' 'auto &'
   // CHECK-NEXT: DeclRefExpr{{.*}}'U' lvalue ParmVar{{.*}} 'u' 'U'
   // CHECK-NEXT: present_or_create clause
@@ -98,7 +98,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCDataConstruct{{.*}} data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'
@@ -115,7 +115,7 @@ void TemplUses(T t, U u) {
   // CHECK-NEXT: OpenACCEnterDataConstruct{{.*}} enter data
   // CHECK-NEXT: create clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'int' lvalue ParmVar{{.*}} 't' 'int'
-  // CHECK-NEXT: pcreate clause : zero
+  // CHECK-NEXT: pcreate clause modifiers: zero
   // CHECK-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'const unsigned int' lvalue
   // CHECK-NEXT: NonTypeTemplateParmDecl{{.*}} referenced 'auto &' depth 0 index 0 NTTP
   // CHECK-NEXT: DeclRefExpr{{.*}}'const unsigned int' lvalue Var{{.*}} 'CEVar' 'const unsigned int'

diff  --git a/clang/test/SemaOpenACC/data-construct-create-clause.c b/clang/test/SemaOpenACC/data-construct-create-clause.c
index 4972bdca4b85d..e49d53b17ee82 100644
--- a/clang/test/SemaOpenACC/data-construct-create-clause.c
+++ b/clang/test/SemaOpenACC/data-construct-create-clause.c
@@ -58,7 +58,7 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data create((float)ArrayParam[2])
   ;
-  // expected-error at +2{{invalid tag 'invalid' on 'create' clause}}
+  // expected-error at +2{{unknown modifier 'invalid' in OpenACC modifier-list on 'create' clause}}
   // expected-error at +1{{OpenACC variable is not a valid variable name, sub-array, array element, member of a composite variable, or composite variable member}}
 #pragma acc data create(invalid:(float)ArrayParam[2])
   ;
@@ -71,3 +71,36 @@ void uses(int IntParam, short *PointerParam, float ArrayParam[5], Complete Compo
 #pragma acc host_data pcreate(LocalInt)
   ;
 }
+
+void ModList() {
+  int V1;
+  // expected-error at +4{{OpenACC 'always' modifier not valid on 'create' clause}}
+  // expected-error at +3{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc data create(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc data create(always: V1)
+  // expected-error at +1{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+#pragma acc data create(alwaysin: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+#pragma acc data create(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc data create(readonly: V1)
+#pragma acc data create(zero: V1)
+
+  // expected-error at +4{{OpenACC 'always' modifier not valid on 'create' clause}}
+  // expected-error at +3{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc enter data create(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc enter data create(always: V1)
+  // expected-error at +1{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+#pragma acc enter data create(alwaysin: V1)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+#pragma acc enter data create(alwaysout: V1)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc enter data create(readonly: V1)
+#pragma acc enter data create(zero: V1)
+}

diff  --git a/clang/test/SemaOpenACC/declare-construct-ast.cpp b/clang/test/SemaOpenACC/declare-construct-ast.cpp
index ab49c9e42ea4e..54b40ff6e93de 100644
--- a/clang/test/SemaOpenACC/declare-construct-ast.cpp
+++ b/clang/test/SemaOpenACC/declare-construct-ast.cpp
@@ -11,11 +11,11 @@ int *Global;
 // CHECK: VarDecl{{.*}}Global 'int *'
 int GlobalArray[5];
 // CHECK-NEXT: VarDecl{{.*}}GlobalArray 'int[5]'
-#pragma acc declare deviceptr(Global), copyin(GlobalArray)
+#pragma acc declare deviceptr(Global), copyin(readonly, always: GlobalArray)
 // CHECK-NEXT: OpenACCDeclareDecl
 // CHECK-NEXT: deviceptr clause
 // CHECK-NEXT: DeclRefExpr{{.*}}'Global' 'int *'
-// CHECK-NEXT: copyin clause
+// CHECK-NEXT: copyin clause modifiers: always, readonly
 // CHECK-NEXT: DeclRefExpr{{.*}}'GlobalArray' 'int[5]'
 
 int *Global1;
@@ -162,12 +162,12 @@ struct DependentStruct {
   static constexpr T StaticMemArray2[5] = {};
   // CHECK-NEXT: VarDecl{{.*}} StaticMemArray2 'const T[5]'
   // CHECK-NEXT: InitListExpr{{.*}}'void'
-#pragma acc declare copyin(StaticMem, StaticMemArray) create(StaticMem2, StaticMemArray2)
+#pragma acc declare copyin(StaticMem, StaticMemArray) create(zero: StaticMem2, StaticMemArray2)
   // CHECK-NEXT: OpenACCDeclareDecl
   // CHECK-NEXT: copyin clause
   // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMem' 'const T'
   // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMemArray' 'const T[5]'
-  // CHECK-NEXT: create clause
+  // CHECK-NEXT: create clause modifiers: zero
   // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMem2' 'const T'
   // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMemArray2' 'const T[5]'
 
@@ -189,14 +189,14 @@ struct DependentStruct {
     U LocalArray2[5];
     // CHECK-NEXT: DeclStmt
     // CHECK-NEXT: VarDecl{{.*}} LocalArray2 'U[5]'
-#pragma acc declare copy(Arg, Local, LocalArray) copyout(Arg2, Local2, LocalArray2)
+#pragma acc declare copy(always, alwaysin: Arg, Local, LocalArray) copyout(zero: Arg2, Local2, LocalArray2)
     // CHECK-NEXT: DeclStmt
     // CHECK-NEXT: OpenACCDeclareDecl
-    // CHECK-NEXT: copy clause
+    // CHECK-NEXT: copy clause modifiers: always, alwaysin
     // CHECK-NEXT: DeclRefExpr{{.*}}'Arg' 'U'
     // CHECK-NEXT: DeclRefExpr{{.*}}'Local' 'T'
     // CHECK-NEXT: DeclRefExpr{{.*}}'LocalArray' 'U[5]'
-    // CHECK-NEXT: copyout clause
+    // CHECK-NEXT: copyout clause modifiers: zero
     // CHECK-NEXT: DeclRefExpr{{.*}}'Arg2' 'U'
     // CHECK-NEXT: DeclRefExpr{{.*}}'Local2' 'T'
     // CHECK-NEXT: DeclRefExpr{{.*}}'LocalArray2' 'U[5]'
@@ -251,7 +251,7 @@ struct DependentStruct {
 // CHECK-NEXT: copyin clause
 // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMem' 'const int'
 // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMemArray' 'const int[5]'
-// CHECK-NEXT: create clause
+// CHECK-NEXT: create clause modifiers: zero
 // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMem2' 'const int'
 // CHECK-NEXT: DeclRefExpr{{.*}}'StaticMemArray2' 'const int[5]'
 
@@ -279,11 +279,11 @@ struct DependentStruct {
 
 // CHECK-NEXT: DeclStmt
 // CHECK-NEXT: OpenACCDeclareDecl
-// CHECK-NEXT: copy clause
+// CHECK-NEXT: copy clause modifiers: always, alwaysin
 // CHECK-NEXT: DeclRefExpr{{.*}}'Arg' 'float'
 // CHECK-NEXT: DeclRefExpr{{.*}}'Local' 'int'
 // CHECK-NEXT: DeclRefExpr{{.*}}'LocalArray' 'float[5]'
-// CHECK-NEXT: copyout clause
+// CHECK-NEXT: copyout clause modifiers: zero
 // CHECK-NEXT: DeclRefExpr{{.*}}'Arg2' 'float'
 // CHECK-NEXT: DeclRefExpr{{.*}}'Local2' 'int'
 // CHECK-NEXT: DeclRefExpr{{.*}}'LocalArray2' 'float[5]'

diff  --git a/clang/test/SemaOpenACC/declare-construct.cpp b/clang/test/SemaOpenACC/declare-construct.cpp
index a1fed096635fa..25038b5bf242c 100644
--- a/clang/test/SemaOpenACC/declare-construct.cpp
+++ b/clang/test/SemaOpenACC/declare-construct.cpp
@@ -306,3 +306,48 @@ struct Struct2 {
 }
 };
 
+void ModList() {
+  int V1, V2, V3, V4, V5, V6, V7, V8, V9, V10,
+      V11, V12, V13, V14, V15, V16, V17, V18;
+  // expected-error at +2{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc declare copy(always, alwaysin, alwaysout, zero, readonly: V1)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copy' clause}}
+#pragma acc declare copy(readonly: V2)
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copy' clause}}
+#pragma acc declare copy(zero: V3)
+#pragma acc declare copy(always, alwaysin, alwaysout: V4)
+
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc declare copyin(always, alwaysin, alwaysout, zero, readonly: V5)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyin' clause}}
+#pragma acc declare copyin(alwaysout: V6)
+  // expected-error at +1{{OpenACC 'zero' modifier not valid on 'copyin' clause}}
+#pragma acc declare copyin(zero: V7)
+#pragma acc declare copyin(always, alwaysin, readonly: V8)
+
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc declare copyout(always, alwaysin, alwaysout, zero, readonly: V9)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'copyout' clause}}
+#pragma acc declare copyout(alwaysout: V10)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'copyout' clause}}
+#pragma acc declare copyout(readonly: V11)
+#pragma acc declare copyout(always, alwaysin, zero: V12)
+
+  // expected-error at +4{{OpenACC 'always' modifier not valid on 'create' clause}}
+  // expected-error at +3{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+  // expected-error at +2{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc declare create(always, alwaysin, alwaysout, zero, readonly: V13)
+  // expected-error at +1{{OpenACC 'always' modifier not valid on 'create' clause}}
+#pragma acc declare create(always: V14)
+  // expected-error at +1{{OpenACC 'alwaysin' modifier not valid on 'create' clause}}
+#pragma acc declare create(alwaysin: V15)
+  // expected-error at +1{{OpenACC 'alwaysout' modifier not valid on 'create' clause}}
+#pragma acc declare create(alwaysout: V16)
+  // expected-error at +1{{OpenACC 'readonly' modifier not valid on 'create' clause}}
+#pragma acc declare create(readonly: V17)
+#pragma acc declare create(zero: V18)
+}


        


More information about the cfe-commits mailing list