https://github.com/ykhatav updated 
https://github.com/llvm/llvm-project/pull/212307

>From 15554886936818af13a7e44cd247ddb6ee7912ab Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Mon, 27 Jul 2026 10:29:02 -0700
Subject: [PATCH 1/6] Validate prefer_type fr()/attr() arguments in append_args
 clause

---
 clang/lib/Parse/ParseOpenMP.cpp               |  5 +-
 clang/lib/Sema/SemaOpenMP.cpp                 | 90 +++++++++++--------
 ...riant_append_args_prefer_type_messages.cpp | 59 ++++++++++++
 3 files changed, 114 insertions(+), 40 deletions(-)
 create mode 100644 
clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp

diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index dd2dc60a99156..6b218b8ce84c8 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3765,8 +3765,9 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo 
&InteropInfo,
   bool IsTargetSync = false;
 
   while (Tok.is(tok::identifier)) {
-    // Currently prefer_type is only allowed with 'init' and it must be first.
-    bool PreferTypeAllowed = Kind == OMPC_init && InteropInfo.Prefs.empty() &&
+    // prefer_type is allowed with 'init' and 'append_args' and must be first.
+    bool PreferTypeAllowed = (Kind == OMPC_init || Kind == OMPC_append_args) &&
+                             InteropInfo.Prefs.empty() &&
                              !IsTarget && !IsTargetSync;
     if (Tok.getIdentifierInfo()->isStr("target")) {
       // OpenMP 5.1 [2.15.1, interop Construct, Restrictions]
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 76bb0d38d428f..fc46ac7273f32 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7842,6 +7842,49 @@ 
SemaOpenMP::checkOpenMPDeclareVariantFunction(SemaOpenMP::DeclGroupPtrTy DG,
   return std::make_pair(FD, cast<Expr>(DRE));
 }
 
+/// Check prefer_type fr()/attr() arguments in an OMPInteropInfo for validity.
+/// Returns true if all arguments are valid; emits a diagnostic and returns
+/// false on the first invalid argument.
+static bool checkPreferTypeArgs(SemaOpenMP &S, const OMPInteropInfo &Info) {
+  for (const OMPInteropPref &P : Info.Prefs) {
+    const Expr *E = P.Fr;
+    if (!E) {
+      assert(Info.HasPreferAttrs && "null Fr requires OMP 6.0 syntax");
+    } else if (!E->isValueDependent() && !E->isTypeDependent() &&
+               !E->isInstantiationDependent() &&
+               !E->containsUnexpandedParameterPack()) {
+      if (!E->isIntegerConstantExpr(S.getASTContext()) &&
+          !isa<StringLiteral>(E)) {
+        S.Diag(E->getExprLoc(), diag::err_omp_interop_prefer_type);
+        return false;
+      }
+    }
+    for (const Expr *A : P.Attrs) {
+      if (A->isValueDependent() || A->isTypeDependent() ||
+          A->isInstantiationDependent() ||
+          A->containsUnexpandedParameterPack())
+        continue;
+      const auto *SL = dyn_cast<StringLiteral>(A);
+      if (!SL) {
+        S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_not_string);
+        return false;
+      }
+      if (!SL->getString().starts_with("ompx_")) {
+        S.Diag(A->getExprLoc(),
+               diag::err_omp_interop_attr_missing_ompx_prefix)
+            << SL->getString();
+        return false;
+      }
+      if (SL->getString().contains(',')) {
+        S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_contains_comma)
+            << SL->getString();
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 void SemaOpenMP::ActOnOpenMPDeclareVariantDirective(
     FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI,
     ArrayRef<Expr *> AdjustArgsNothing,
@@ -7921,6 +7964,13 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective(
     }
   }
 
+  // OpenMP 6.0 [16.1.3] Check prefer_type fr()/attr() arguments in
+  // append_args.
+  for (const OMPInteropInfo &Info : AppendArgs) {
+    if (!checkPreferTypeArgs(*this, Info))
+      return;
+  }
+
   auto *NewAttr = OMPDeclareVariantAttr::CreateImplicit(
       getASTContext(), VariantRef, &TI,
       const_cast<Expr **>(AdjustArgsNothing.data()), AdjustArgsNothing.size(),
@@ -19086,44 +19136,8 @@ OMPClause *SemaOpenMP::ActOnOpenMPInitClause(
   if (!isValidInteropVariable(SemaRef, InteropVar, VarLoc, OMPC_init))
     return nullptr;
 
-  // Check prefer_type values. fr() arguments are either string literals or
-  // constant integral expressions; null Fr is only valid in OMP 6.0.
-  // attr() arguments must be ext-string-literals with the 'ompx_' prefix
-  // (OpenMP 6.0 spec, section 16.1.3).
-  for (const OMPInteropPref &P : InteropInfo.Prefs) {
-    const Expr *E = P.Fr;
-    if (!E) {
-      assert(InteropInfo.HasPreferAttrs && "null Fr requires OMP 6.0 syntax");
-    } else if (!E->isValueDependent() && !E->isTypeDependent() &&
-               !E->isInstantiationDependent() &&
-               !E->containsUnexpandedParameterPack()) {
-      if (!E->isIntegerConstantExpr(getASTContext()) &&
-          !isa<StringLiteral>(E)) {
-        Diag(E->getExprLoc(), diag::err_omp_interop_prefer_type);
-        return nullptr;
-      }
-    }
-    for (const Expr *A : P.Attrs) {
-      if (A->isValueDependent() || A->isTypeDependent() ||
-          A->isInstantiationDependent() || 
A->containsUnexpandedParameterPack())
-        continue;
-      const auto *SL = dyn_cast<StringLiteral>(A);
-      if (!SL) {
-        Diag(A->getExprLoc(), diag::err_omp_interop_attr_not_string);
-        return nullptr;
-      }
-      if (!SL->getString().starts_with("ompx_")) {
-        Diag(A->getExprLoc(), diag::err_omp_interop_attr_missing_ompx_prefix)
-            << SL->getString();
-        return nullptr;
-      }
-      if (SL->getString().contains(',')) {
-        Diag(A->getExprLoc(), diag::err_omp_interop_attr_contains_comma)
-            << SL->getString();
-        return nullptr;
-      }
-    }
-  }
+  if (!checkPreferTypeArgs(*this, InteropInfo))
+    return nullptr;
 
   return OMPInitClause::Create(getASTContext(), InteropVar, InteropInfo,
                                StartLoc, LParenLoc, VarLoc, EndLoc);
diff --git 
a/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp 
b/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp
new file mode 100644
index 0000000000000..0fb9566faf3a3
--- /dev/null
+++ b/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -std=c++11 -o - %s
+
+typedef void *omp_interop_t;
+
+void foo_v1(float *A, float *B, omp_interop_t IOp);
+
+// expected-error@+2 {{prefer_list item must be a string literal or constant 
integral expression}}
+#pragma omp declare variant(foo_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(1.0)}), target))
+void foo_fr_float(float *A, float *B) {}
+
+void bar_v1(float *A, omp_interop_t IOp);
+
+// expected-error@+2 {{attr() argument must be a string literal}}
+#pragma omp declare variant(bar_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr(1)}), target))
+void bar_attr_int(float *A) {}
+
+void baz_v1(float *A, omp_interop_t IOp);
+
+// expected-error@+2 {{attr() argument 'cuda_prop' must start with the 'ompx_' 
prefix}}
+#pragma omp declare variant(baz_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("cuda_prop")}), target))
+void baz_attr_no_prefix(float *A) {}
+
+void qux_v1(float *A, omp_interop_t IOp);
+
+// expected-error@+2 {{attr() argument 'ompx_a,b' must not contain a comma}}
+#pragma omp declare variant(qux_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_a,b")}), target))
+void qux_attr_comma(float *A) {}
+
+// Valid cases -- no diagnostics expected.
+void valid_v1(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr("cuda")}), target))
+void valid_fr_string(float *A) {}
+
+void valid_v2(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v2) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(1)}), target))
+void valid_fr_int(float *A) {}
+
+void valid_v3(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v3) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_myattr")}), target))
+void valid_attr(float *A) {}
+
+// Template case: fr() argument becomes invalid at instantiation.
+template <typename T>
+void tmpl_v1(T *A, omp_interop_t IOp);
+
+// expected-error@+2 {{prefer_list item must be a string literal or constant 
integral expression}}
+#pragma omp declare variant(tmpl_v1<int>) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(1.5)}), target))
+void tmpl_fr_invalid(int *A) {}

>From 0f1069f81eb594a7947d5d02e2fca61c6e5580a9 Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Mon, 27 Jul 2026 11:37:10 -0700
Subject: [PATCH 2/6] Fix formatting

---
 clang/lib/Parse/ParseOpenMP.cpp | 4 ++--
 clang/lib/Sema/SemaOpenMP.cpp   | 6 ++----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 6b218b8ce84c8..ab46d81402d7d 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3767,8 +3767,8 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo 
&InteropInfo,
   while (Tok.is(tok::identifier)) {
     // prefer_type is allowed with 'init' and 'append_args' and must be first.
     bool PreferTypeAllowed = (Kind == OMPC_init || Kind == OMPC_append_args) &&
-                             InteropInfo.Prefs.empty() &&
-                             !IsTarget && !IsTargetSync;
+                             InteropInfo.Prefs.empty() && !IsTarget &&
+                             !IsTargetSync;
     if (Tok.getIdentifierInfo()->isStr("target")) {
       // OpenMP 5.1 [2.15.1, interop Construct, Restrictions]
       // Each interop-type may be specified on an action-clause at most
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index fc46ac7273f32..15b4969f1f3a9 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7861,8 +7861,7 @@ static bool checkPreferTypeArgs(SemaOpenMP &S, const 
OMPInteropInfo &Info) {
     }
     for (const Expr *A : P.Attrs) {
       if (A->isValueDependent() || A->isTypeDependent() ||
-          A->isInstantiationDependent() ||
-          A->containsUnexpandedParameterPack())
+          A->isInstantiationDependent() || 
A->containsUnexpandedParameterPack())
         continue;
       const auto *SL = dyn_cast<StringLiteral>(A);
       if (!SL) {
@@ -7870,8 +7869,7 @@ static bool checkPreferTypeArgs(SemaOpenMP &S, const 
OMPInteropInfo &Info) {
         return false;
       }
       if (!SL->getString().starts_with("ompx_")) {
-        S.Diag(A->getExprLoc(),
-               diag::err_omp_interop_attr_missing_ompx_prefix)
+        S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_missing_ompx_prefix)
             << SL->getString();
         return false;
       }

>From 10cfffe38d7d37ca99cc1b20925959c30ab41115 Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Wed, 29 Jul 2026 10:11:11 -0700
Subject: [PATCH 3/6] Address review comments

---
 clang/lib/Sema/SemaOpenMP.cpp                 | 28 +++++++------
 ...riant_append_args_prefer_type_messages.cpp | 39 +++++++++++++++++++
 2 files changed, 55 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 15b4969f1f3a9..4d723cde7de7d 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7842,17 +7842,21 @@ 
SemaOpenMP::checkOpenMPDeclareVariantFunction(SemaOpenMP::DeclGroupPtrTy DG,
   return std::make_pair(FD, cast<Expr>(DRE));
 }
 
-/// Check prefer_type fr()/attr() arguments in an OMPInteropInfo for validity.
-/// Returns true if all arguments are valid; emits a diagnostic and returns
-/// false on the first invalid argument.
+/// Validate prefer_type fr() and attr() arguments in an OMPInteropInfo.
+/// fr() must be a string literal or constant integer expression.
+/// attr() must be a string literal starting with "ompx_" and containing no 
commas.
+/// Returns true if valid; emits diagnostic and returns false on first error.
 static bool checkPreferTypeArgs(SemaOpenMP &S, const OMPInteropInfo &Info) {
+  auto isDependent = [](const Expr *E) {
+    return E->isValueDependent() || E->isTypeDependent() ||
+           E->isInstantiationDependent() ||
+           E->containsUnexpandedParameterPack();
+  };
   for (const OMPInteropPref &P : Info.Prefs) {
     const Expr *E = P.Fr;
     if (!E) {
       assert(Info.HasPreferAttrs && "null Fr requires OMP 6.0 syntax");
-    } else if (!E->isValueDependent() && !E->isTypeDependent() &&
-               !E->isInstantiationDependent() &&
-               !E->containsUnexpandedParameterPack()) {
+    } else if (!isDependent(E)) {
       if (!E->isIntegerConstantExpr(S.getASTContext()) &&
           !isa<StringLiteral>(E)) {
         S.Diag(E->getExprLoc(), diag::err_omp_interop_prefer_type);
@@ -7860,22 +7864,22 @@ static bool checkPreferTypeArgs(SemaOpenMP &S, const 
OMPInteropInfo &Info) {
       }
     }
     for (const Expr *A : P.Attrs) {
-      if (A->isValueDependent() || A->isTypeDependent() ||
-          A->isInstantiationDependent() || 
A->containsUnexpandedParameterPack())
+      if (isDependent(A))
         continue;
       const auto *SL = dyn_cast<StringLiteral>(A);
       if (!SL) {
         S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_not_string);
         return false;
       }
-      if (!SL->getString().starts_with("ompx_")) {
+      StringRef Str = SL->getString();
+      if (!Str.starts_with("ompx_")) {
         S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_missing_ompx_prefix)
-            << SL->getString();
+            << Str;
         return false;
       }
-      if (SL->getString().contains(',')) {
+      if (Str.contains(',')) {
         S.Diag(A->getExprLoc(), diag::err_omp_interop_attr_contains_comma)
-            << SL->getString();
+            << Str;
         return false;
       }
     }
diff --git 
a/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp 
b/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp
index 0fb9566faf3a3..7c514a1e24395 100644
--- a/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp
+++ b/clang/test/OpenMP/declare_variant_append_args_prefer_type_messages.cpp
@@ -30,6 +30,21 @@ void qux_v1(float *A, omp_interop_t IOp);
   append_args(interop(prefer_type({attr("ompx_a,b")}), target))
 void qux_attr_comma(float *A) {}
 
+// Edge cases for attr() and fr().
+void edge_v1(float *A, omp_interop_t IOp);
+
+// expected-error@+2 {{attr() argument 'ompx_a,b,c' must not contain a comma}}
+#pragma omp declare variant(edge_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_a,b,c")}), target))
+void edge_attr_multi_commas(float *A) {}
+
+void edge_v2(float *A, omp_interop_t IOp);
+
+// expected-error@+2 {{attr() argument '' must start with the 'ompx_' prefix}}
+#pragma omp declare variant(edge_v2) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("")}), target))
+void edge_attr_empty(float *A) {}
+
 // Valid cases -- no diagnostics expected.
 void valid_v1(float *A, omp_interop_t IOp);
 
@@ -49,6 +64,30 @@ void valid_v3(float *A, omp_interop_t IOp);
   append_args(interop(prefer_type({attr("ompx_myattr")}), target))
 void valid_attr(float *A) {}
 
+void valid_v4(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v4) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_prop")}), target))
+void valid_attr_only(float *A) {}
+
+void valid_v5(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v5) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(1), attr("ompx_prop")}), target))
+void valid_combined(float *A) {}
+
+void valid_v6(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v6) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_")}), target))
+void valid_attr_prefix_only(float *A) {}
+
+void valid_v7(float *A, omp_interop_t IOp);
+
+#pragma omp declare variant(valid_v7) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr("")}), target))
+void valid_fr_empty_string(float *A) {}
+
 // Template case: fr() argument becomes invalid at instantiation.
 template <typename T>
 void tmpl_v1(T *A, omp_interop_t IOp);

>From 61910553632373f7f52e4e40b59235f6e69fb81f Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Wed, 29 Jul 2026 13:08:36 -0700
Subject: [PATCH 4/6] Apply clang-format

---
 clang/lib/Sema/SemaOpenMP.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 4d723cde7de7d..51164744b345a 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7844,8 +7844,9 @@ 
SemaOpenMP::checkOpenMPDeclareVariantFunction(SemaOpenMP::DeclGroupPtrTy DG,
 
 /// Validate prefer_type fr() and attr() arguments in an OMPInteropInfo.
 /// fr() must be a string literal or constant integer expression.
-/// attr() must be a string literal starting with "ompx_" and containing no 
commas.
-/// Returns true if valid; emits diagnostic and returns false on first error.
+/// attr() must be a string literal starting with "ompx_" and containing no
+/// commas. Returns true if valid; emits diagnostic and returns false on first
+/// error.
 static bool checkPreferTypeArgs(SemaOpenMP &S, const OMPInteropInfo &Info) {
   auto isDependent = [](const Expr *E) {
     return E->isValueDependent() || E->isTypeDependent() ||

>From abfece960f19dbcf9d4eea242f7507ab28a930bb Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Tue, 4 Aug 2026 11:45:27 -0700
Subject: [PATCH 5/6] Add missing template instantiation and PCH code

---
 clang/lib/AST/AttrImpl.cpp                    | 35 +++++++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 21 ++++-
 ...iant_append_args_prefer_type_ast_print.cpp | 79 +++++++++++++++++++
 clang/utils/TableGen/ClangAttrEmitter.cpp     | 53 ++++++++++++-
 4 files changed, 183 insertions(+), 5 deletions(-)
 create mode 100644 
clang/test/OpenMP/declare_variant_append_args_prefer_type_ast_print.cpp

diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp
index cfd47e82b04b5..4fd7549e08468 100644
--- a/clang/lib/AST/AttrImpl.cpp
+++ b/clang/lib/AST/AttrImpl.cpp
@@ -233,11 +233,44 @@ void OMPDeclareVariantAttr::printPrettyPragma(
     OS << ")";
   }
 
-  auto PrintInteropInfo = [&OS](OMPInteropInfo *Begin, OMPInteropInfo *End) {
+  auto PrintInteropInfo = [&OS, &Policy](OMPInteropInfo *Begin,
+                                         OMPInteropInfo *End) {
     for (OMPInteropInfo *I = Begin; I != End; ++I) {
       if (I != Begin)
         OS << ", ";
       OS << "interop(";
+      if (!I->Prefs.empty()) {
+        OS << "prefer_type(";
+        if (I->HasPreferAttrs) {
+          StringRef Sep = "";
+          for (const auto &P : I->Prefs) {
+            OS << Sep << "{";
+            if (P.Fr) {
+              OS << "fr(";
+              P.Fr->printPretty(OS, nullptr, Policy);
+              OS << ")";
+            }
+            for (Expr *A : P.Attrs) {
+              if (P.Fr)
+                OS << ",";
+              OS << "attr(";
+              A->printPretty(OS, nullptr, Policy);
+              OS << ")";
+            }
+            OS << "}";
+            Sep = ",";
+          }
+        } else {
+          StringRef Sep = "";
+          for (const auto &P : I->Prefs) {
+            OS << Sep;
+            if (P.Fr)
+              P.Fr->printPretty(OS, nullptr, Policy);
+            Sep = ",";
+          }
+        }
+        OS << "),";
+      }
       OS << getInteropTypeString(I);
       OS << ")";
     }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f1f97ca125f46..9eece865e9c11 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -578,8 +578,25 @@ static void instantiateOMPDeclareVariantAttr(
     NeedDeviceAddrExprs.push_back(ER.get());
   }
   for (OMPInteropInfo &II : Attr.appendArgs()) {
-    // When prefer_type is implemented for append_args handle them here too.
-    AppendArgs.emplace_back(II.IsTarget, II.IsTargetSync);
+    OMPInteropInfo Info(II.IsTarget, II.IsTargetSync);
+    Info.HasPreferAttrs = II.HasPreferAttrs;
+    for (const OMPInteropPref &P : II.Prefs) {
+      Expr *SubstFr = nullptr;
+      if (P.Fr) {
+        ExprResult ER = Subst(P.Fr);
+        if (ER.isInvalid())
+          continue;
+        SubstFr = ER.get();
+      }
+      llvm::SmallVector<Expr *, 2> SubstAttrs;
+      for (Expr *A : P.Attrs) {
+        ExprResult ER = Subst(A);
+        if (!ER.isInvalid())
+          SubstAttrs.push_back(ER.get());
+      }
+      Info.Prefs.emplace_back(SubstFr, std::move(SubstAttrs));
+    }
+    AppendArgs.push_back(Info);
   }
 
   S.OpenMP().ActOnOpenMPDeclareVariantDirective(
diff --git 
a/clang/test/OpenMP/declare_variant_append_args_prefer_type_ast_print.cpp 
b/clang/test/OpenMP/declare_variant_append_args_prefer_type_ast_print.cpp
new file mode 100644
index 0000000000000..b8ffe7c23d516
--- /dev/null
+++ b/clang/test/OpenMP/declare_variant_append_args_prefer_type_ast_print.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -ast-print %s | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -emit-pch -o %t %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -include-pch %t -ast-print %s \
+// RUN:   | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -ast-dump %s \
+// RUN:   | FileCheck %s --check-prefix=DUMP
+
+#ifndef HEADER
+#define HEADER
+
+typedef void *omp_interop_t;
+
+// Basic append_args with prefer_type (non-template).
+void foo_v1(float *A, omp_interop_t IOp);
+
+// PRINT: #pragma omp declare variant(foo_v1) match(construct={dispatch}) 
append_args(interop(prefer_type({fr("cuda")}),target))
+#pragma omp declare variant(foo_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr("cuda")}), target))
+void foo(float *A) {}
+
+// append_args with prefer_type containing fr() + attr().
+void bar_v1(float *A, omp_interop_t IOp);
+
+// PRINT: #pragma omp declare variant(bar_v1) match(construct={dispatch}) 
append_args(interop(prefer_type({fr("sycl"),attr("ompx_gpu")}),targetsync))
+#pragma omp declare variant(bar_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr("sycl"), attr("ompx_gpu")}), targetsync))
+void bar(float *A) {}
+
+// Template: prefer_type with integer expression in fr().
+template <typename T>
+void tmpl_v1(T *A, omp_interop_t IOp);
+
+template <typename T>
+void tmpl_bar(T *A);
+
+// PRINT: #pragma omp declare variant(tmpl_v1<int>) 
match(construct={dispatch}) append_args(interop(prefer_type({fr(1)}),target))
+#pragma omp declare variant(tmpl_v1<int>) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(1)}), target))
+void tmpl_bar(int *A) {}
+
+// Template with dependent expression in fr().
+template <int N>
+void dep_v1(float *A, omp_interop_t IOp);
+
+template <int N>
+void dep_bar(float *A);
+
+// PRINT: #pragma omp declare variant(dep_v1<N>) match(construct={dispatch}) 
append_args(interop(prefer_type({fr(N)}),target))
+#pragma omp declare variant(dep_v1<N>) match(construct={dispatch}) \
+  append_args(interop(prefer_type({fr(N)}), target))
+template <int N>
+void dep_bar(float *A) {}
+
+// DUMP: FunctionDecl{{.*}}dep_bar 'void (float *)' 
explicit_instantiation_definition
+// DUMP: OMPDeclareVariantAttr
+// DUMP: IntegerLiteral{{.*}}'int' 4
+template void dep_bar<4>(float *);
+
+// Multiple prefer_type entries with attr() only.
+void multi_v1(float *A, omp_interop_t IOp);
+
+// PRINT: #pragma omp declare variant(multi_v1) match(construct={dispatch}) 
append_args(interop(prefer_type({attr("ompx_propA")},{fr(2),attr("ompx_propB")}),target))
+#pragma omp declare variant(multi_v1) match(construct={dispatch}) \
+  append_args(interop(prefer_type({attr("ompx_propA")}, {fr(2), 
attr("ompx_propB")}), target))
+void multi(float *A) {}
+
+#endif // HEADER
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 5987cb4a0f16b..a875f7f44c00d 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -905,8 +905,21 @@ namespace {
       OS << "I != E; ++I) {\n";
       OS << "      bool IsTarget = Record.readBool();\n";
       OS << "      bool IsTargetSync = Record.readBool();\n";
-      OS << "      " << getLowerName()
-         << ".emplace_back(IsTarget, IsTargetSync);\n";
+      OS << "      OMPInteropInfo Info(IsTarget, IsTargetSync);\n";
+      OS << "      Info.HasPreferAttrs = Record.readBool();\n";
+      OS << "      unsigned prefsSize = Record.readInt();\n";
+      OS << "      Info.Prefs.reserve(prefsSize);\n";
+      OS << "      for (unsigned J = 0; J < prefsSize; ++J) {\n";
+      OS << "        bool hasFr = Record.readBool();\n";
+      OS << "        Expr *Fr = hasFr ? Record.readExpr() : nullptr;\n";
+      OS << "        unsigned attrsSize = Record.readInt();\n";
+      OS << "        llvm::SmallVector<Expr *, 2> Attrs;\n";
+      OS << "        Attrs.reserve(attrsSize);\n";
+      OS << "        for (unsigned K = 0; K < attrsSize; ++K)\n";
+      OS << "          Attrs.push_back(Record.readExpr());\n";
+      OS << "        Info.Prefs.emplace_back(Fr, std::move(Attrs));\n";
+      OS << "      }\n";
+      OS << "      " << getLowerName() << ".push_back(Info);\n";
       OS << "    }\n";
     }
 
@@ -917,6 +930,42 @@ namespace {
          << getLowerName() << "_end(); I != E; ++I) {\n";
       OS << "      Record.writeBool(I->IsTarget);\n";
       OS << "      Record.writeBool(I->IsTargetSync);\n";
+      OS << "      Record.writeBool(I->HasPreferAttrs);\n";
+      OS << "      Record.push_back(I->Prefs.size());\n";
+      OS << "      for (auto &P : I->Prefs) {\n";
+      OS << "        Record.writeBool(P.Fr != nullptr);\n";
+      OS << "        if (P.Fr) Record.AddStmt(P.Fr);\n";
+      OS << "        Record.push_back(P.Attrs.size());\n";
+      OS << "        for (Expr *A : P.Attrs) Record.AddStmt(A);\n";
+      OS << "      }\n";
+      OS << "    }\n";
+    }
+
+    void writeASTVisitorTraversal(raw_ostream &OS) const override {
+      OS << "  {\n";
+      OS << "    OMPInteropInfo *I = A->" << getLowerName() << "_begin();\n";
+      OS << "    " << getType() << " *E = A->" << getLowerName()
+         << "_end();\n";
+      OS << "    for (; I != E; ++I) {\n";
+      OS << "      for (auto &P : I->Prefs) {\n";
+      OS << "        if (P.Fr && !getDerived().TraverseStmt(P.Fr))\n";
+      OS << "          return false;\n";
+      OS << "        for (Expr *A : P.Attrs)\n";
+      OS << "          if (!getDerived().TraverseStmt(A))\n";
+      OS << "            return false;\n";
+      OS << "      }\n";
+      OS << "    }\n";
+      OS << "  }\n";
+    }
+
+    void writeDumpChildren(raw_ostream &OS) const override {
+      OS << "    for (" << getAttrName() << "Attr::" << getLowerName()
+         << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->"
+         << getLowerName() << "_end(); I != E; ++I) {\n";
+      OS << "      for (auto &P : I->Prefs) {\n";
+      OS << "        if (P.Fr) Visit(P.Fr);\n";
+      OS << "        for (Expr *A : P.Attrs) Visit(A);\n";
+      OS << "      }\n";
       OS << "    }\n";
     }
   };

>From f5a68295dd7e43c1504a559047794f6a4ba11a98 Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Tue, 4 Aug 2026 11:57:51 -0700
Subject: [PATCH 6/6] Fix formatting

---
 clang/utils/TableGen/ClangAttrEmitter.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index a875f7f44c00d..65a65f90969dd 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -944,8 +944,7 @@ namespace {
     void writeASTVisitorTraversal(raw_ostream &OS) const override {
       OS << "  {\n";
       OS << "    OMPInteropInfo *I = A->" << getLowerName() << "_begin();\n";
-      OS << "    " << getType() << " *E = A->" << getLowerName()
-         << "_end();\n";
+      OS << "    " << getType() << " *E = A->" << getLowerName() << 
"_end();\n";
       OS << "    for (; I != E; ++I) {\n";
       OS << "      for (auto &P : I->Prefs) {\n";
       OS << "        if (P.Fr && !getDerived().TraverseStmt(P.Fr))\n";

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to