https://github.com/ykhatav updated https://github.com/llvm/llvm-project/pull/198868
>From 6c546f7211978cb54ee0716b330fe0fa6b44012c Mon Sep 17 00:00:00 2001 From: "Khatavkar, Yashasvi" <[email protected]> Date: Wed, 20 May 2026 11:34:43 -0700 Subject: [PATCH 1/2] Add parsing support for prefer_type modifier extensions for interop object --- clang/include/clang/AST/OpenMPClause.h | 82 ++++++++++-- .../clang/Basic/DiagnosticParseKinds.td | 2 + clang/include/clang/Basic/OpenMPKinds.h | 10 +- clang/lib/AST/OpenMPClause.cpp | 98 ++++++++++++--- clang/lib/Parse/ParseOpenMP.cpp | 117 ++++++++++++++++-- clang/lib/Sema/SemaOpenMP.cpp | 11 +- clang/lib/Sema/TreeTransform.h | 27 +++- clang/lib/Serialization/ASTReader.cpp | 22 +++- clang/lib/Serialization/ASTWriter.cpp | 12 ++ .../interop_prefer_type_brace_ast_print.cpp | 71 +++++++++++ .../interop_prefer_type_brace_messages.cpp | 42 +++++++ 11 files changed, 442 insertions(+), 52 deletions(-) create mode 100644 clang/test/OpenMP/interop_prefer_type_brace_ast_print.cpp create mode 100644 clang/test/OpenMP/interop_prefer_type_brace_messages.cpp diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index ccf2c40bc5efa..629d03f9e74a6 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -8851,7 +8851,7 @@ class OMPOrderClause final : public OMPClause { /// \endcode class OMPInitClause final : public OMPVarListClause<OMPInitClause>, - private llvm::TrailingObjects<OMPInitClause, Expr *> { + private llvm::TrailingObjects<OMPInitClause, Expr *, unsigned> { friend class OMPClauseReader; friend OMPVarListClause; friend TrailingObjects; @@ -8861,6 +8861,30 @@ class OMPInitClause final bool IsTarget = false; bool IsTargetSync = false; + bool HasPreferAttrs = false; + + /// Total number of attr() exprs across all pref-specs (sum of the + /// per-pref-spec counts in the trailing unsigned[]). + unsigned NumAttrs = 0; + + /// Trailing-objects layout (single contiguous Expr* array): + /// Expr*[ varlist_size() + NumAttrs ]: + /// [0] = InteropVar + /// [1 .. NumPrefs] = Fr expr per pref-spec (null if attr-only) + /// [varlist_size() ..] = flat list of attr exprs, concatenated in + /// pref-spec order + /// unsigned[ NumPrefs ]: + /// [i] = number of attr() exprs in pref-spec i + /// + /// varlist_size() = 1 + NumPrefs, so OMPVarListClause iteration covers + /// InteropVar + the Fr block. + + size_t numTrailingObjects(OverloadToken<Expr *>) const { + return varlist_size() + NumAttrs; + } + size_t numTrailingObjects(OverloadToken<unsigned>) const { + return getNumPrefs(); + } void setInteropVar(Expr *E) { varlist_begin()[0] = E; } @@ -8868,6 +8892,10 @@ class OMPInitClause final void setIsTargetSync(bool V) { IsTargetSync = V; } + void setHasPreferAttrs(bool V) { HasPreferAttrs = V; } + + void setAttrs(ArrayRef<unsigned> Counts, ArrayRef<Expr *> Attrs); + /// Sets the location of the interop variable. void setVarLoc(SourceLocation Loc) { VarLoc = Loc; } @@ -8879,7 +8907,7 @@ class OMPInitClause final /// \param LParenLoc Location of '('. /// \param VarLoc Location of the interop variable. /// \param EndLoc Ending location of the clause. - /// \param N Number of expressions. + /// \param N Number of varlist entries (1 + NumPrefs). OMPInitClause(bool IsTarget, bool IsTargetSync, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation VarLoc, SourceLocation EndLoc, unsigned N) @@ -8894,6 +8922,14 @@ class OMPInitClause final } public: + struct PrefView { + /// Foreign-runtime-id expression. Null for attr-only specs. + Expr *Fr; + /// attr() string-literal expressions. Empty for fr-only or OMP 5.1 + /// flat specs. + ArrayRef<Expr *> Attrs; + }; + /// Creates a fully specified clause. /// /// \param C AST context. @@ -8909,11 +8945,14 @@ class OMPInitClause final SourceLocation LParenLoc, SourceLocation VarLoc, SourceLocation EndLoc); - /// Creates an empty clause with \a N expressions. + /// Creates an empty clause sized for \a NumPrefs pref-specs and \a NumAttrs + /// total attr() exprs across them. /// /// \param C AST context. - /// \param N Number of expression items. - static OMPInitClause *CreateEmpty(const ASTContext &C, unsigned N); + /// \param NumPrefs Number of pref-specs (length of the Fr block). + /// \param NumAttrs Total attr() exprs across all pref-specs. + static OMPInitClause *CreateEmpty(const ASTContext &C, unsigned NumPrefs, + unsigned NumAttrs); /// Returns the location of the interop variable. SourceLocation getVarLoc() const { return VarLoc; } @@ -8928,9 +8967,38 @@ class OMPInitClause final /// Returns true is interop-type 'targetsync' is used. bool getIsTargetSync() const { return IsTargetSync; } + /// Returns true if OMP 6.0 {fr/attr} syntax is used. + bool getHasPreferAttrs() const { return HasPreferAttrs; } + + /// Number of pref-specs in prefer_type(...). + unsigned getNumPrefs() const { return varlist_size() - 1; } + + /// Total attrs across all pref-specs. + unsigned getNumAttrs() const { return NumAttrs; } + + /// Per-pref-spec attr counts (one entry per pref-spec). + ArrayRef<unsigned> getAttrCounts() const { + return getTrailingObjects<unsigned>(getNumPrefs()); + } + + PrefView getPref(unsigned I) { + assert(I < getNumPrefs() && "pref-spec index out of range"); + Expr **E = getTrailingObjects<Expr *>(); + ArrayRef<unsigned> Counts = getAttrCounts(); + unsigned AttrStart = 0; + for (unsigned K = 0; K < I; ++K) + AttrStart += Counts[K]; + return PrefView{ + E[1 + I], ArrayRef<Expr *>(E + varlist_size() + AttrStart, Counts[I])}; + } + PrefView getPref(unsigned I) const { + return const_cast<OMPInitClause *>(this)->getPref(I); + } + child_range children() { - return child_range(reinterpret_cast<Stmt **>(varlist_begin()), - reinterpret_cast<Stmt **>(varlist_end())); + return child_range( + reinterpret_cast<Stmt **>(varlist_begin()), + reinterpret_cast<Stmt **>(varlist_begin() + varlist_size() + NumAttrs)); } const_child_range children() const { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 7bcd1870a2600..6abda0671add2 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1691,6 +1691,8 @@ def err_omp_expected_interop_type : Error< def warn_omp_more_one_interop_type : Warning<"interop type '%0' cannot be specified more than once">, InGroup<OpenMPClauses>; +def err_omp_interop_multiple_fr : Error< + "only one 'fr' selector allowed per preference-specification">; def err_expected_sequence_or_directive : Error< "expected an OpenMP 'directive' or 'sequence' attribute argument">; def ext_omp_attributes : ExtWarn< diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h index 4e83bfcd0128b..72b25e0908a34 100644 --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -292,12 +292,20 @@ static constexpr unsigned NumberOfOMPAllocateClauseModifiers = /// Contains 'interop' data for 'append_args' and 'init' clauses. class Expr; +/// One entry of a prefer_type list. Each pref-spec carries an optional fr() +/// foreign-runtime-id expression and zero or more attr() ext-string-literal +/// expressions. Fr is nullptr for attr-only specs. +struct OMPInteropPref final { + Expr *Fr = nullptr; + llvm::SmallVector<Expr *, 2> Attrs; +}; struct OMPInteropInfo final { OMPInteropInfo(bool IsTarget = false, bool IsTargetSync = false) : IsTarget(IsTarget), IsTargetSync(IsTargetSync) {} bool IsTarget; bool IsTargetSync; - llvm::SmallVector<Expr *, 4> PreferTypes; + bool HasPreferAttrs = false; + llvm::SmallVector<OMPInteropPref, 4> Prefs; }; OpenMPDefaultClauseVariableCategory diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 3a35e17aff40b..9fce666ce00d8 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -1805,19 +1805,52 @@ OMPInitClause *OMPInitClause::Create(const ASTContext &C, Expr *InteropVar, SourceLocation VarLoc, SourceLocation EndLoc) { - void *Mem = - C.Allocate(totalSizeToAlloc<Expr *>(InteropInfo.PreferTypes.size() + 1)); - auto *Clause = new (Mem) OMPInitClause( - InteropInfo.IsTarget, InteropInfo.IsTargetSync, StartLoc, LParenLoc, - VarLoc, EndLoc, InteropInfo.PreferTypes.size() + 1); - Clause->setInteropVar(InteropVar); - llvm::copy(InteropInfo.PreferTypes, Clause->getTrailingObjects() + 1); + unsigned NumPrefs = InteropInfo.Prefs.size(); + unsigned NumAttrs = 0; + for (const OMPInteropPref &P : InteropInfo.Prefs) + NumAttrs += P.Attrs.size(); + + // Trailing layout: Expr*[1 + NumPrefs + NumAttrs], unsigned[NumPrefs]. + void *Mem = C.Allocate( + totalSizeToAlloc<Expr *, unsigned>(1 + NumPrefs + NumAttrs, NumPrefs)); + auto *Clause = new (Mem) + OMPInitClause(InteropInfo.IsTarget, InteropInfo.IsTargetSync, StartLoc, + LParenLoc, VarLoc, EndLoc, /*VarListN=*/1 + NumPrefs); + Clause->NumAttrs = NumAttrs; + Clause->HasPreferAttrs = InteropInfo.HasPreferAttrs; + + Expr **E = Clause->getTrailingObjects<Expr *>(); + E[0] = InteropVar; + for (unsigned I = 0; I < NumPrefs; ++I) + E[1 + I] = InteropInfo.Prefs[I].Fr; + unsigned *Counts = Clause->getTrailingObjects<unsigned>(); + unsigned AttrPos = 1 + NumPrefs; + for (unsigned I = 0; I < NumPrefs; ++I) { + Counts[I] = InteropInfo.Prefs[I].Attrs.size(); + for (Expr *A : InteropInfo.Prefs[I].Attrs) + E[AttrPos++] = A; + } return Clause; } -OMPInitClause *OMPInitClause::CreateEmpty(const ASTContext &C, unsigned N) { - void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(N)); - return new (Mem) OMPInitClause(N); +OMPInitClause *OMPInitClause::CreateEmpty(const ASTContext &C, + unsigned NumPrefs, + unsigned NumAttrs) { + void *Mem = C.Allocate( + totalSizeToAlloc<Expr *, unsigned>(1 + NumPrefs + NumAttrs, NumPrefs)); + auto *Clause = new (Mem) OMPInitClause(/*VarListN=*/1 + NumPrefs); + Clause->NumAttrs = NumAttrs; + return Clause; +} + +void OMPInitClause::setAttrs(ArrayRef<unsigned> Counts, + ArrayRef<Expr *> Attrs) { + assert(Counts.size() == getNumPrefs() && + "attr-count vector size must match number of pref-specs"); + assert(Attrs.size() == NumAttrs && + "attr-expr count must match preallocated NumAttrs"); + llvm::copy(Counts, getTrailingObjects<unsigned>()); + llvm::copy(Attrs, getTrailingObjects<Expr *>() + varlist_size()); } OMPBindClause * @@ -2388,17 +2421,42 @@ void OMPClausePrinter::VisitOMPHintClause(OMPHintClause *Node) { void OMPClausePrinter::VisitOMPInitClause(OMPInitClause *Node) { OS << "init("; - bool First = true; - for (const Expr *E : Node->prefs()) { - if (First) - OS << "prefer_type("; - else - OS << ","; - E->printPretty(OS, nullptr, Policy); - First = false; - } - if (!First) + unsigned NumPrefs = Node->getNumPrefs(); + if (NumPrefs > 0) { + OS << "prefer_type("; + if (Node->getHasPreferAttrs()) { + // OMP 6.0 brace-grouped form + llvm::interleaveComma( + llvm::seq<unsigned>(0, NumPrefs), OS, [&](unsigned I) { + OMPInitClause::PrefView P = Node->getPref(I); + OS << "{"; + if (P.Fr) { + OS << "fr("; + P.Fr->printPretty(OS, nullptr, Policy); + OS << ")"; + if (!P.Attrs.empty()) + OS << ", "; + } + if (!P.Attrs.empty()) { + OS << "attr("; + llvm::interleaveComma(P.Attrs, OS, [&](const Expr *A) { + A->printPretty(OS, nullptr, Policy); + }); + OS << ")"; + } + OS << "}"; + }); + } else { + llvm::interleave( + llvm::seq<unsigned>(0, NumPrefs), OS, + [&](unsigned I) { + if (Expr *Fr = Node->getPref(I).Fr) + Fr->printPretty(OS, nullptr, Policy); + }, + ","); + } OS << "), "; + } if (Node->getIsTarget()) OS << "target"; if (Node->getIsTargetSync()) { diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 7f3c575fb68bb..aab5fb035b912 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3690,9 +3690,8 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, while (Tok.is(tok::identifier)) { // Currently prefer_type is only allowed with 'init' and it must be first. - bool PreferTypeAllowed = Kind == OMPC_init && - InteropInfo.PreferTypes.empty() && !IsTarget && - !IsTargetSync; + bool PreferTypeAllowed = Kind == OMPC_init && 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 @@ -3715,17 +3714,109 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, HasError = true; while (Tok.isNot(tok::r_paren)) { - SourceLocation Loc = Tok.getLocation(); - ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr); - ExprResult PTExpr = ParseRHSOfBinaryExpression(LHS, prec::Conditional); - PTExpr = Actions.ActOnFinishFullExpr(PTExpr.get(), Loc, - /*DiscardedValue=*/false); - if (PTExpr.isUsable()) { - InteropInfo.PreferTypes.push_back(PTExpr.get()); - } else { - HasError = true; - SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + // OMP 6.0: { fr(...), attr(...) } brace-grouped pref-spec + if (Tok.is(tok::l_brace)) { + BalancedDelimiterTracker BT(*this, tok::l_brace, + tok::annot_pragma_openmp_end); + BT.consumeOpen(); + Expr *FrExpr = nullptr; + SmallVector<Expr *, 2> AttrExprs; + bool SeenFr = false; + + while (Tok.isNot(tok::r_brace) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + if (Tok.is(tok::identifier) && + Tok.getIdentifierInfo()->isStr("fr")) { + if (SeenFr) { + // Diagnose and skip the duplicate fr(...) entirely + Diag(Tok, diag::err_omp_interop_multiple_fr); + HasError = true; + ConsumeToken(); // 'fr' + SkipUntil( + {tok::comma, tok::r_brace, tok::annot_pragma_openmp_end}, StopBeforeMatch); + continue; + } + SeenFr = true; + ConsumeToken(); // 'fr' + BalancedDelimiterTracker FT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (FT.expectAndConsume(diag::err_expected_lparen_after, "fr")) { + HasError = true; + SkipUntil({tok::comma, tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end}, + StopBeforeMatch); + continue; + } + SourceLocation Loc = Tok.getLocation(); + ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr); + ExprResult Arg = + ParseRHSOfBinaryExpression(LHS, prec::Conditional); + Arg = Actions.ActOnFinishFullExpr(Arg.get(), Loc, false); + if (Arg.isUsable()) + FrExpr = Arg.get(); + else + HasError = true; + FT.consumeClose(); + } else if (Tok.is(tok::identifier) && + Tok.getIdentifierInfo()->isStr("attr")) { + ConsumeToken(); // 'attr' + BalancedDelimiterTracker AT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (AT.expectAndConsume(diag::err_expected_lparen_after, + "attr")) { + HasError = true; + SkipUntil({tok::comma, tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end}, + StopBeforeMatch); + continue; + } + while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::r_brace) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + if (Tok.is(tok::string_literal)) { + ExprResult StrRes = ParseStringLiteralExpression(); + if (!StrRes.isUsable()) + HasError = true; + else + AttrExprs.push_back(StrRes.get()); + } else { + HasError = true; + Diag(Tok, diag::err_expected) << tok::string_literal; + ConsumeToken(); + } + if (Tok.is(tok::comma)) + ConsumeToken(); + } + AT.consumeClose(); + } else { + HasError = true; + Diag(Tok, diag::err_omp_expected_interop_type); + ConsumeToken(); + } + if (Tok.is(tok::comma)) + ConsumeToken(); + } + if (BT.consumeClose()) + HasError = true; + + InteropInfo.Prefs.push_back({FrExpr, std::move(AttrExprs)}); + InteropInfo.HasPreferAttrs = true; + } else { + // OMP 5.1: flat foreign-runtime-id (string or int). Stored as a + // pref-spec with Fr=expr and no attr() entries. + SourceLocation Loc = Tok.getLocation(); + ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr); + ExprResult PTExpr = + ParseRHSOfBinaryExpression(LHS, prec::Conditional); + PTExpr = Actions.ActOnFinishFullExpr(PTExpr.get(), Loc, + /*DiscardedValue=*/false); + if (PTExpr.isUsable()) { + InteropInfo.Prefs.push_back({PTExpr.get(), {}}); + } else { + HasError = true; + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } } if (Tok.is(tok::comma)) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index d6f6bc919a31b..ff3afd932bc27 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -18967,9 +18967,14 @@ OMPClause *SemaOpenMP::ActOnOpenMPInitClause( if (!isValidInteropVariable(SemaRef, InteropVar, VarLoc, OMPC_init)) return nullptr; - // Check prefer_type values. These foreign-runtime-id values are either - // string literals or constant integral expressions. - for (const Expr *E : InteropInfo.PreferTypes) { + // Check prefer_type values. fr() arguments are either string literals or + // constant integral expressions; null Fr is only valid in OMP 6.0 + for (const OMPInteropPref &P : InteropInfo.Prefs) { + const Expr *E = P.Fr; + if (!E) { + assert(InteropInfo.HasPreferAttrs && "null Fr requires OMP 6.0 syntax"); + continue; + } if (E->isValueDependent() || E->isTypeDependent() || E->isInstantiationDependent() || E->containsUnexpandedParameterPack()) continue; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 9c0479fe8aa4f..736a30fe7535e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -10997,13 +10997,28 @@ OMPClause *TreeTransform<Derived>::TransformOMPInitClause(OMPInitClause *C) { return nullptr; OMPInteropInfo InteropInfo(C->getIsTarget(), C->getIsTargetSync()); - InteropInfo.PreferTypes.reserve(C->varlist_size() - 1); - for (Expr *E : llvm::drop_begin(C->varlist())) { - ExprResult ER = getDerived().TransformExpr(cast<Expr>(E)); - if (ER.isInvalid()) - return nullptr; - InteropInfo.PreferTypes.push_back(ER.get()); + unsigned NumPrefs = C->getNumPrefs(); + InteropInfo.Prefs.reserve(NumPrefs); + for (unsigned I = 0; I < NumPrefs; ++I) { + OMPInitClause::PrefView P = C->getPref(I); + Expr *NewFr = nullptr; + if (P.Fr) { + ExprResult ER = getDerived().TransformExpr(P.Fr); + if (ER.isInvalid()) + return nullptr; + NewFr = ER.get(); + } + SmallVector<Expr *, 2> NewAttrs; + NewAttrs.reserve(P.Attrs.size()); + for (Expr *A : P.Attrs) { + ExprResult ER = getDerived().TransformExpr(A); + if (ER.isInvalid()) + return nullptr; + NewAttrs.push_back(ER.get()); + } + InteropInfo.Prefs.push_back({NewFr, std::move(NewAttrs)}); } + InteropInfo.HasPreferAttrs = C->getHasPreferAttrs(); return getDerived().RebuildOMPInitClause(IVR.get(), InteropInfo, C->getBeginLoc(), C->getLParenLoc(), C->getVarLoc(), C->getEndLoc()); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 06bc8f8c8c13e..28e0bc8d22087 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11786,9 +11786,13 @@ OMPClause *OMPClauseReader::readClause() { case llvm::omp::OMPC_order: C = new (Context) OMPOrderClause(); break; - case llvm::omp::OMPC_init: - C = OMPInitClause::CreateEmpty(Context, Record.readInt()); + case llvm::omp::OMPC_init: { + unsigned VarListSize = Record.readInt(); + unsigned NumAttrs = Record.readInt(); + C = OMPInitClause::CreateEmpty(Context, /*NumPrefs=*/VarListSize - 1, + NumAttrs); break; + } case llvm::omp::OMPC_use: C = new (Context) OMPUseClause(); break; @@ -12103,6 +12107,20 @@ void OMPClauseReader::VisitOMPInitClause(OMPInitClause *C) { C->setVarRefs(Vars); C->setIsTarget(Record.readBool()); C->setIsTargetSync(Record.readBool()); + C->setHasPreferAttrs(Record.readBool()); + + unsigned NumPrefs = C->getNumPrefs(); + SmallVector<unsigned, 4> Counts; + SmallVector<Expr *, 8> Attrs; + Counts.reserve(NumPrefs); + for (unsigned I = 0; I < NumPrefs; ++I) { + unsigned NA = Record.readInt(); + Counts.push_back(NA); + for (unsigned J = 0; J < NA; ++J) + Attrs.push_back(Record.readSubExpr()); + } + C->setAttrs(Counts, Attrs); + C->setLParenLoc(Record.readSourceLocation()); C->setVarLoc(Record.readSourceLocation()); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 074b0fccdb65d..86178c8481cdf 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -8282,11 +8282,23 @@ void OMPClauseWriter::VisitOMPSIMDClause(OMPSIMDClause *) {} void OMPClauseWriter::VisitOMPNogroupClause(OMPNogroupClause *) {} void OMPClauseWriter::VisitOMPInitClause(OMPInitClause *C) { + // Sizes for CreateEmpty on the read side: varlist_size = 1 + NumPrefs, then + // NumAttrs (total attrs across all pref-specs). Record.push_back(C->varlist_size()); + Record.push_back(C->getNumAttrs()); + // Varlist (interop var + Fr block). for (Expr *VE : C->varlist()) Record.AddStmt(VE); Record.writeBool(C->getIsTarget()); Record.writeBool(C->getIsTargetSync()); + Record.writeBool(C->getHasPreferAttrs()); + // Per-pref-spec: attr count + that many attr exprs, in order. + for (unsigned I = 0, E = C->getNumPrefs(); I < E; ++I) { + OMPInitClause::PrefView P = C->getPref(I); + Record.push_back(P.Attrs.size()); + for (Expr *A : P.Attrs) + Record.AddStmt(A); + } Record.AddSourceLocation(C->getLParenLoc()); Record.AddSourceLocation(C->getVarLoc()); } diff --git a/clang/test/OpenMP/interop_prefer_type_brace_ast_print.cpp b/clang/test/OpenMP/interop_prefer_type_brace_ast_print.cpp new file mode 100644 index 0000000000000..55936827d934e --- /dev/null +++ b/clang/test/OpenMP/interop_prefer_type_brace_ast_print.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -fsyntax-only -verify %s + +// expected-no-diagnostics + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -ast-print %s | FileCheck %s --check-prefix=PRINT + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -emit-pch -o %t %s + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -include-pch %t -ast-print %s | FileCheck %s --check-prefix=PRINT + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -ast-dump %s | FileCheck %s --check-prefix=DUMP + +#ifndef HEADER +#define HEADER + +typedef void *omp_interop_t; + +void brace_specs() { + omp_interop_t obj; + + // Single brace entry with fr() only. + // PRINT: #pragma omp interop init(prefer_type({fr("sycl")}), targetsync : obj) + #pragma omp interop init(prefer_type({fr("sycl")}), targetsync: obj) + + // Multiple brace entries, each with fr() only. + // PRINT: #pragma omp interop init(prefer_type({fr("sycl")}, {fr("level_zero")}, {fr("opencl")}), targetsync : obj) + #pragma omp interop init(prefer_type({fr("sycl")}, {fr("level_zero")}, \ + {fr("opencl")}), targetsync: obj) + + // fr() + attr() on one entry. + // PRINT: #pragma omp interop init(prefer_type({fr("sycl"), attr("ompx_propX")}), targetsync : obj) + #pragma omp interop init(prefer_type({fr("sycl"), attr("ompx_propX")}), \ + targetsync: obj) + + // attr() only (no fr()) — any runtime. + // PRINT: #pragma omp interop init(prefer_type({attr("ompx_propX", "ompx_propY")}), targetsync : obj) + #pragma omp interop init(prefer_type({attr("ompx_propX", "ompx_propY")}), \ + targetsync: obj) + + // Integer expression inside fr() on brace-grouped entry. + // PRINT: #pragma omp interop init(prefer_type({fr(4)}, {fr(6)}), targetsync : obj) + #pragma omp interop init(prefer_type({fr(4)}, {fr(6)}), targetsync: obj) + + // Mixed flat + brace entries + // PRINT: #pragma omp interop init(prefer_type({fr("sycl")}, {fr("opencl")}), targetsync : obj) + #pragma omp interop init(prefer_type("sycl", {fr("opencl")}), \ + targetsync: obj) + + // Multiple pref-specs mixing fr-only, fr+attr, and attr-only. + // PRINT: #pragma omp interop init(prefer_type({fr("sycl"), attr("ompx_propX")}, {fr("level_zero")}, {attr("ompx_propY")}), targetsync : obj) + #pragma omp interop init(prefer_type({fr("sycl"), attr("ompx_propX")}, {fr("level_zero")}, {attr("ompx_propY")}), targetsync: obj) +} + +template <int N> +void tmpl(omp_interop_t obj) { + // PRINT: #pragma omp interop init(prefer_type({fr(N), attr("ompx_propX")}), targetsync : obj) + #pragma omp interop init(prefer_type({fr(N), attr("ompx_propX")}), targetsync: obj) +} + +// DUMP: FunctionDecl {{.*}} tmpl 'void (omp_interop_t)' explicit_instantiation_definition +// DUMP: TemplateArgument integral '7' +// DUMP: OMPInitClause +// DUMP: IntegerLiteral {{.*}} 'int' 7 +// DUMP: StringLiteral {{.*}} "ompx_propX" +template void tmpl<7>(omp_interop_t); +#endif // HEADER diff --git a/clang/test/OpenMP/interop_prefer_type_brace_messages.cpp b/clang/test/OpenMP/interop_prefer_type_brace_messages.cpp new file mode 100644 index 0000000000000..2c9d7bbc1a5f1 --- /dev/null +++ b/clang/test/OpenMP/interop_prefer_type_brace_messages.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -std=c++11 -o - %s + +// Diagnostics for the OMP 6.0 brace-grouped prefer_type syntax. + +typedef void *omp_interop_t; + +static void foo() { + omp_interop_t obj; + + // expected-error@+2 {{only one 'fr' selector allowed per preference-specification}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({fr("sycl"), fr("opencl")}), targetsync: obj) + + // Non-constant variable expression inside fr(). + int x = 2; + // expected-error@+2 {{prefer_list item must be a string literal or constant integral expression}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({fr(x)}), targetsync: obj) + + // Floating-point literal inside fr(). + // expected-error@+2 {{prefer_list item must be a string literal or constant integral expression}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({fr(1.5)}), targetsync: obj) + + // Integer inside attr() — only string literals allowed. + // expected-error@+2 {{expected <string_literal>}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({attr(1)}), targetsync: obj) + + // expected-error@+2 {{expected '(' after 'fr'}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({fr "sycl"}), targetsync: obj) + + // expected-error@+2 {{expected '(' after 'attr'}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({attr "ompx_propX"}), targetsync: obj) + + // Anything that is not 'fr' or 'attr' is rejected by the brace parser. + // expected-error@+2 {{expected interop type: 'target' and/or 'targetsync'}} + // expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop init(prefer_type({foo}), targetsync: obj) +} >From 68353435bc61c1b8815d070431b1d8deed71eab4 Mon Sep 17 00:00:00 2001 From: "Khatavkar, Yashasvi" <[email protected]> Date: Fri, 22 May 2026 11:35:18 -0700 Subject: [PATCH 2/2] refactor parsing code --- clang/include/clang/Parse/Parser.h | 6 ++ clang/lib/Parse/ParseOpenMP.cpp | 109 ++++++++++++++++------------- 2 files changed, 65 insertions(+), 50 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index dc3dc8a4ae0e9..ada8ca7e7e52c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -7001,6 +7001,12 @@ class Parser : public CodeCompletionHandler { /// Parses the 'interop' parts of the 'append_args' and 'init' clauses. bool ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, OpenMPClauseKind Kind); + /// Parses 'fr(<foreign-runtime-id>)'. + ExprResult ParseOMPInteropFrSelector(); + + /// Parses 'attr(<string-literal>[, ...])', appending to \p Attrs. + bool ParseOMPInteropAttrSelector(SmallVectorImpl<Expr *> &Attrs); + /// Parses clause with an interop variable of kind \a Kind. /// /// \verbatim diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index aab5fb035b912..de844460c0a15 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3681,6 +3681,55 @@ bool Parser::ParseOpenMPIndirectClause( return false; } +ExprResult Parser::ParseOMPInteropFrSelector() { + ConsumeToken(); // 'fr' + BalancedDelimiterTracker FT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (FT.expectAndConsume(diag::err_expected_lparen_after, "fr")) { + SkipUntil( + {tok::comma, tok::r_brace, tok::r_paren, tok::annot_pragma_openmp_end}, + StopBeforeMatch); + return ExprError(); + } + SourceLocation Loc = Tok.getLocation(); + ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr); + ExprResult Arg = ParseRHSOfBinaryExpression(LHS, prec::Conditional); + Arg = Actions.ActOnFinishFullExpr(Arg.get(), Loc, /*DiscardedValue=*/false); + FT.consumeClose(); + return Arg; +} + +bool Parser::ParseOMPInteropAttrSelector(SmallVectorImpl<Expr *> &Attrs) { + ConsumeToken(); // 'attr' + BalancedDelimiterTracker AT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (AT.expectAndConsume(diag::err_expected_lparen_after, "attr")) { + SkipUntil( + {tok::comma, tok::r_brace, tok::r_paren, tok::annot_pragma_openmp_end}, + StopBeforeMatch); + return true; + } + bool HasError = false; + while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::r_brace) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + if (Tok.is(tok::string_literal)) { + ExprResult S = ParseStringLiteralExpression(); + if (S.isUsable()) + Attrs.push_back(S.get()); + else + HasError = true; + } else { + HasError = true; + Diag(Tok, diag::err_expected) << tok::string_literal; + ConsumeToken(); + } + if (Tok.is(tok::comma)) + ConsumeToken(); + } + AT.consumeClose(); + return HasError; +} + bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, OpenMPClauseKind Kind) { const Token &Tok = getCurToken(); @@ -3725,10 +3774,12 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::annot_pragma_openmp_end)) { - if (Tok.is(tok::identifier) && - Tok.getIdentifierInfo()->isStr("fr")) { + if (!Tok.is(tok::identifier)) { + HasError = true; + Diag(Tok, diag::err_omp_expected_interop_type); + ConsumeToken(); + } else if (Tok.getIdentifierInfo()->isStr("fr")) { if (SeenFr) { - // Diagnose and skip the duplicate fr(...) entirely Diag(Tok, diag::err_omp_interop_multiple_fr); HasError = true; ConsumeToken(); // 'fr' @@ -3738,56 +3789,14 @@ bool Parser::ParseOMPInteropInfo(OMPInteropInfo &InteropInfo, continue; } SeenFr = true; - ConsumeToken(); // 'fr' - BalancedDelimiterTracker FT(*this, tok::l_paren, - tok::annot_pragma_openmp_end); - if (FT.expectAndConsume(diag::err_expected_lparen_after, "fr")) { - HasError = true; - SkipUntil({tok::comma, tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end}, - StopBeforeMatch); - continue; - } - SourceLocation Loc = Tok.getLocation(); - ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr); - ExprResult Arg = - ParseRHSOfBinaryExpression(LHS, prec::Conditional); - Arg = Actions.ActOnFinishFullExpr(Arg.get(), Loc, false); - if (Arg.isUsable()) - FrExpr = Arg.get(); + ExprResult Fr = ParseOMPInteropFrSelector(); + if (Fr.isUsable()) + FrExpr = Fr.get(); else HasError = true; - FT.consumeClose(); - } else if (Tok.is(tok::identifier) && - Tok.getIdentifierInfo()->isStr("attr")) { - ConsumeToken(); // 'attr' - BalancedDelimiterTracker AT(*this, tok::l_paren, - tok::annot_pragma_openmp_end); - if (AT.expectAndConsume(diag::err_expected_lparen_after, - "attr")) { + } else if (Tok.getIdentifierInfo()->isStr("attr")) { + if (ParseOMPInteropAttrSelector(AttrExprs)) HasError = true; - SkipUntil({tok::comma, tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end}, - StopBeforeMatch); - continue; - } - while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::r_brace) && - Tok.isNot(tok::annot_pragma_openmp_end)) { - if (Tok.is(tok::string_literal)) { - ExprResult StrRes = ParseStringLiteralExpression(); - if (!StrRes.isUsable()) - HasError = true; - else - AttrExprs.push_back(StrRes.get()); - } else { - HasError = true; - Diag(Tok, diag::err_expected) << tok::string_literal; - ConsumeToken(); - } - if (Tok.is(tok::comma)) - ConsumeToken(); - } - AT.consumeClose(); } else { HasError = true; Diag(Tok, diag::err_omp_expected_interop_type); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
