https://github.com/ykhatav updated https://github.com/llvm/llvm-project/pull/225081
>From e17db0202d9d0ad4602ff9ab39a7094b1751e949 Mon Sep 17 00:00:00 2001 From: "Khatavkar, Yashasvi" <[email protected]> Date: Mon, 21 Sep 2026 06:17:20 -0700 Subject: [PATCH 1/2] Add support for adjust_args extensions in OMP6.0 to take positional arguments --- clang/include/clang-c/Index.h | 14 +- clang/include/clang/AST/ComputeDependence.h | 4 + clang/include/clang/AST/ExprOpenMP.h | 147 +++++++++++ clang/include/clang/AST/OpenMPClause.h | 23 ++ clang/include/clang/AST/RecursiveASTVisitor.h | 2 + clang/include/clang/AST/TextNodeDumper.h | 1 + .../clang/Basic/DiagnosticParseKinds.td | 4 + .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Basic/StmtNodes.td | 2 + clang/include/clang/Parse/Parser.h | 11 + clang/include/clang/Sema/SemaOpenMP.h | 13 + .../include/clang/Serialization/ASTBitCodes.h | 2 + clang/lib/AST/AttrImpl.cpp | 19 +- clang/lib/AST/ComputeDependence.cpp | 20 ++ clang/lib/AST/Expr.cpp | 2 + clang/lib/AST/ExprClassification.cpp | 2 + clang/lib/AST/ExprConstant.cpp | 2 + clang/lib/AST/ItaniumMangle.cpp | 2 + clang/lib/AST/OpenMPClause.cpp | 88 +++++++ clang/lib/AST/StmtPrinter.cpp | 20 ++ clang/lib/AST/StmtProfile.cpp | 13 + clang/lib/AST/TextNodeDumper.cpp | 6 + clang/lib/Parse/ParseOpenMP.cpp | 114 ++++++++- clang/lib/Sema/SemaExceptionSpec.cpp | 2 + clang/lib/Sema/SemaOpenMP.cpp | 190 +++++++++++++-- clang/lib/Sema/TreeTransform.h | 64 +++++ clang/lib/Serialization/ASTReaderStmt.cpp | 23 ++ clang/lib/Serialization/ASTWriterStmt.cpp | 17 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 + ...riant_adjust_args_positional_ast_print.cpp | 175 ++++++++++++++ ...ariant_adjust_args_positional_messages.cpp | 228 ++++++++++++++++++ ...ariant_adjust_args_positional_template.cpp | 62 +++++ .../declare_variant_clauses_messages.cpp | 16 +- clang/tools/libclang/CIndex.cpp | 4 + clang/tools/libclang/CXCursor.cpp | 8 + 35 files changed, 1270 insertions(+), 35 deletions(-) create mode 100644 clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp create mode 100644 clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp create mode 100644 clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 1c8d097f1beab..1ab3635f2ea3a 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -1691,7 +1691,19 @@ enum CXCursorKind { */ CXCursor_PackIndexingExpr = 156, - CXCursor_LastExpr = CXCursor_PackIndexingExpr, + /** + * OpenMP 6.0 [5.2.1, Parameter List Items] + * The 'omp_num_args' identifier with an optional logical offset. + */ + CXCursor_OMPNumArgsExpr = 157, + + /** + * OpenMP 6.0 [5.2.1, Parameter List Items] + * A parameter range 'lb:ub', either bound of which may be omitted. + */ + CXCursor_OMPArgumentRangeExpr = 158, + + CXCursor_LastExpr = CXCursor_OMPArgumentRangeExpr, /* Statements */ CXCursor_FirstStmt = 200, diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 6430081a40350..4a36588d399e1 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -100,6 +100,8 @@ class AtomicExpr; class ArraySectionExpr; class OMPArrayShapingExpr; class OMPIteratorExpr; +class OMPNumArgsExpr; +class OMPArgumentRangeExpr; class ObjCArrayLiteral; class ObjCDictionaryLiteral; class ObjCBoxedExpr; @@ -200,6 +202,8 @@ ExprDependence computeDependence(AtomicExpr *E); ExprDependence computeDependence(ArraySectionExpr *E); ExprDependence computeDependence(OMPArrayShapingExpr *E); ExprDependence computeDependence(OMPIteratorExpr *E); +ExprDependence computeDependence(OMPNumArgsExpr *E); +ExprDependence computeDependence(OMPArgumentRangeExpr *E); ExprDependence computeDependence(ObjCArrayLiteral *E); ExprDependence computeDependence(ObjCDictionaryLiteral *E); diff --git a/clang/include/clang/AST/ExprOpenMP.h b/clang/include/clang/AST/ExprOpenMP.h index 4d3c5f54ad7dd..4c0bd0cf6974c 100644 --- a/clang/include/clang/AST/ExprOpenMP.h +++ b/clang/include/clang/AST/ExprOpenMP.h @@ -296,6 +296,153 @@ class OMPIteratorExpr final } }; +/// OpenMP 6.0 [5.2.1, Parameter List Items] +/// Represents the 'omp_num_args' identifier used as a bound of a parameter +/// range, together with an optional logical offset: +/// \code +/// omp_num_args [ ('+' | '-') logical_offset ] +/// \endcode +/// 'omp_num_args' is not a declaration and is never looked up; it stands for +/// the number of arguments at a call site, which by OpenMP 6.0 [20.1] is the +/// number of parameters of the associated declaration plus any variadic +/// arguments actually passed. For a non-variadic callee that is a compile-time +/// constant, so only a variadic callee makes the value call-site dependent. +/// The node is therefore left unresolved in the AST; whoever consumes the +/// 'adjust_args' parameter list substitutes the value. +class OMPNumArgsExpr final : public Expr { + friend class ASTStmtReader; + friend class ASTStmtWriter; + + /// The logical offset, or null if none was written. + Stmt *Offset = nullptr; + /// Location of the 'omp_num_args' identifier. + SourceLocation NumArgsLoc; + /// Location of the '+' or '-'; invalid if there is no offset. + SourceLocation OpLoc; + /// True if the offset was written with '-'. + bool IsSubtraction = false; + +public: + OMPNumArgsExpr(QualType Type, SourceLocation NumArgsLoc, SourceLocation OpLoc, + bool IsSubtraction, Expr *Offset) + : Expr(OMPNumArgsExprClass, Type, VK_PRValue, OK_Ordinary), + Offset(Offset), NumArgsLoc(NumArgsLoc), OpLoc(OpLoc), + IsSubtraction(IsSubtraction) { + setDependence(computeDependence(this)); + } + + /// Create an empty 'omp_num_args' expression. + explicit OMPNumArgsExpr(EmptyShell Shell) + : Expr(OMPNumArgsExprClass, Shell) {} + + /// Gets the logical offset, or null if none was written. + Expr *getOffset() { return cast_or_null<Expr>(Offset); } + const Expr *getOffset() const { return cast_or_null<Expr>(Offset); } + void setOffset(Expr *E) { Offset = E; } + + /// True if the offset was written with '-' rather than '+'. + bool isSubtraction() const { return IsSubtraction; } + void setIsSubtraction(bool IS) { IsSubtraction = IS; } + + SourceLocation getNumArgsLoc() const { return NumArgsLoc; } + void setNumArgsLoc(SourceLocation L) { NumArgsLoc = L; } + + SourceLocation getOperatorLoc() const { return OpLoc; } + void setOperatorLoc(SourceLocation L) { OpLoc = L; } + + SourceLocation getBeginLoc() const LLVM_READONLY { return NumArgsLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { + return Offset ? Offset->getEndLoc() : NumArgsLoc; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPNumArgsExprClass; + } + + // Iterators + child_range children() { return child_range(&Offset, &Offset + 1); } + const_child_range children() const { + return const_child_range(&Offset, &Offset + 1); + } +}; + +/// OpenMP 6.0 [5.2.1, Parameter List Items] +/// Represents a parameter range, one list item that stands for every parameter +/// position from a lower to an upper bound: +/// \code +/// [ lb ] ':' [ ub ] +/// \endcode +/// Either bound may be omitted: an omitted \c lb defaults to 1 and an omitted +/// \c ub defaults to 'omp_num_args'. A bound may be an 'omp_num_args' +/// expression, which is why this node's children are general expressions rather +/// than integer literals. +/// +/// The type is 'void': a range is never a value, it only ever appears as an +/// item of an 'adjust_args' parameter list in \c OMPDeclareVariantAttr. +class OMPArgumentRangeExpr final : public Expr { + friend class ASTStmtReader; + friend class ASTStmtWriter; + + enum { LOWER_BOUND, UPPER_BOUND, NUM_SUBEXPRS }; + + /// The two bounds; either may be null when the bound was omitted. + Stmt *SubExprs[NUM_SUBEXPRS] = {nullptr, nullptr}; + /// Location of the ':' separating the bounds. + SourceLocation ColonLoc; + +public: + OMPArgumentRangeExpr(QualType Type, Expr *LowerBound, SourceLocation ColonLoc, + Expr *UpperBound) + : Expr(OMPArgumentRangeExprClass, Type, VK_PRValue, OK_Ordinary), + ColonLoc(ColonLoc) { + SubExprs[LOWER_BOUND] = LowerBound; + SubExprs[UPPER_BOUND] = UpperBound; + setDependence(computeDependence(this)); + } + + /// Create an empty parameter range expression. + explicit OMPArgumentRangeExpr(EmptyShell Shell) + : Expr(OMPArgumentRangeExprClass, Shell) {} + + /// Gets the lower bound, or null if it was omitted (meaning 1). + Expr *getLowerBound() { return cast_or_null<Expr>(SubExprs[LOWER_BOUND]); } + const Expr *getLowerBound() const { + return cast_or_null<Expr>(SubExprs[LOWER_BOUND]); + } + void setLowerBound(Expr *E) { SubExprs[LOWER_BOUND] = E; } + + /// Gets the upper bound, or null if it was omitted (meaning 'omp_num_args'). + Expr *getUpperBound() { return cast_or_null<Expr>(SubExprs[UPPER_BOUND]); } + const Expr *getUpperBound() const { + return cast_or_null<Expr>(SubExprs[UPPER_BOUND]); + } + void setUpperBound(Expr *E) { SubExprs[UPPER_BOUND] = E; } + + SourceLocation getColonLoc() const { return ColonLoc; } + void setColonLoc(SourceLocation L) { ColonLoc = L; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return SubExprs[LOWER_BOUND] ? SubExprs[LOWER_BOUND]->getBeginLoc() + : ColonLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return SubExprs[UPPER_BOUND] ? SubExprs[UPPER_BOUND]->getEndLoc() + : ColonLoc; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPArgumentRangeExprClass; + } + + // Iterators + child_range children() { + return child_range(&SubExprs[LOWER_BOUND], &SubExprs[NUM_SUBEXPRS]); + } + const_child_range children() const { + return const_child_range(&SubExprs[LOWER_BOUND], &SubExprs[NUM_SUBEXPRS]); + } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 9e9295e1a0c54..645f0fe8f9281 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -10566,6 +10566,29 @@ class OMPXBareClause : public OMPNoChildClause<llvm::omp::OMPC_ompx_bare> { OMPXBareClause() = default; }; +/// Resolve one 'adjust_args' parameter-list item to the 1-based argument +/// positions it identifies (OpenMP 6.0 [5.2.1] p162). +/// +/// \param Item A named item (\c DeclRefExpr to a \c ParmVarDecl of \p FD), +/// a positional item (a constant integer expression), or an +/// \c OMPArgumentRangeExpr. Callers pass IgnoreParenImpCasts(). +/// \param FD The base function the OMPDeclareVariantAttr is attached to. +/// \param NumArgs The value of 'omp_num_args' at the point of resolution: +/// \c max(FD->getNumParams(), Call->getNumArgs()) at a call +/// site, or \c FD->getNumParams() with no call site available +/// (OpenMP 6.0 [20.1] p534). +/// \param Positions Resolved positions are appended here, ascending. Positions +/// outside [1, NumArgs] are silently dropped +/// (OpenMP 6.0 [9.6.2] p332 L1-2). +/// \returns false if \p Item is not a resolvable item shape, or if a bound is +/// dependent or not a constant expression. +/// +/// Emits no diagnostics: it lives in the AST library so it can later be +/// shared with CodeGen. Sema diagnoses separately, before calling this. +bool resolveOMPAdjustArgsItem(const Expr *Item, const FunctionDecl *FD, + unsigned NumArgs, const ASTContext &Ctx, + SmallVectorImpl<unsigned> &Positions); + } // namespace clang #endif // LLVM_CLANG_AST_OPENMPCLAUSE_H diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 1f7c8d762e1b5..5a66c147ac8ca 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2954,6 +2954,8 @@ DEF_TRAVERSE_STMT(MatrixSubscriptExpr, {}) DEF_TRAVERSE_STMT(ArraySectionExpr, {}) DEF_TRAVERSE_STMT(OMPArrayShapingExpr, {}) DEF_TRAVERSE_STMT(OMPIteratorExpr, {}) +DEF_TRAVERSE_STMT(OMPNumArgsExpr, {}) +DEF_TRAVERSE_STMT(OMPArgumentRangeExpr, {}) DEF_TRAVERSE_STMT(BlockExpr, { TRY_TO(TraverseDecl(S->getBlockDecl())); diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 1cdd8c37c7fc6..f2fd730d34c5d 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -328,6 +328,7 @@ class TextNodeDumper void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node); void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node); void VisitOMPIteratorExpr(const OMPIteratorExpr *Node); + void VisitOMPNumArgsExpr(const OMPNumArgsExpr *Node); void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node); void VisitRequiresExpr(const RequiresExpr *Node); diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 6a48d74079f4e..5c826aba1c200 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1623,6 +1623,10 @@ def err_omp_unknown_adjust_args_op : Error< "incorrect 'adjust_args' type, expected 'need_device_ptr'%select{|, " "'need_device_addr',}0 or 'nothing'">; +def err_omp_num_args_invalid_form + : Error<"'omp_num_args' %select{is only allowed as a bound of a parameter " + "range|may only be followed by '+' or '-' and a constant logical " + "offset}0">; def err_omp_declare_variant_wrong_clause : Error< "expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause " "on 'omp declare variant' directive">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3a910c9c3f2b9..090acdfa4cac8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12815,6 +12815,9 @@ def err_omp_instantiation_not_supported : Error<"instantiation of '%0' not supported yet">; def err_omp_adjust_arg_multiple_clauses : Error< "'adjust_arg' argument %0 used in multiple clauses">; +def err_omp_adjust_args_invalid_item : Error< + "expected a parameter name, a parameter position, or a parameter range in " + "'adjust_args' clause">; def err_omp_clause_requires_dispatch_construct : Error< "'%0' clause requires 'dispatch' context selector">; def err_omp_append_args_with_varargs : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 84804d6705d2b..e9f64565677e9 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -85,6 +85,8 @@ def MatrixSingleSubscriptExpr : StmtNode<Expr>; def MatrixSubscriptExpr : StmtNode<Expr>; def ArraySectionExpr : StmtNode<Expr>; def OMPIteratorExpr : StmtNode<Expr>; +def OMPNumArgsExpr : StmtNode<Expr>; +def OMPArgumentRangeExpr : StmtNode<Expr>; def CallExpr : StmtNode<Expr>; def MemberExpr : StmtNode<Expr>; def CastExpr : StmtNode<Expr, 1>; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 163aa483a84e3..832835b27edc5 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -7053,6 +7053,17 @@ class Parser : public CodeCompletionHandler { bool ParseOpenMPReservedLocator(OpenMPClauseKind Kind, SemaOpenMP::OpenMPVarListDataTy &Data, const LangOptions &LangOpts); + + /// Parses one bound of an OpenMP 6.0 'adjust_args' parameter range, which may + /// be 'omp_num_args' with an optional logical offset, or the whole + /// parameter-list item when no range colon follows it. + ExprResult ParseOpenMPAdjustArgsBound(); + + /// Parses an OpenMP 6.0 'adjust_args' parameter list, whose items may be + /// parameter names, positions, or ranges with optional bounds. + /// Returns true on error. + bool ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars); + /// Parses clauses with list. bool ParseOpenMPVarList(OpenMPDirectiveKind DKind, OpenMPClauseKind Kind, SmallVectorImpl<Expr *> &Vars, diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 361473140e236..1e1870f469084 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -1491,6 +1491,19 @@ class SemaOpenMP : public SemaBase { SourceLocation LLoc, SourceLocation RLoc, ArrayRef<OMPIteratorData> Data); + /// Called on a well-formed 'omp_num_args' expression appearing as a bound of + /// an 'adjust_args' parameter range. \a Offset is null if no logical offset + /// was written, in which case \a OpLoc is invalid. + ExprResult ActOnOMPNumArgsExpr(SourceLocation NumArgsLoc, + SourceLocation OpLoc, bool IsSubtraction, + Expr *Offset); + + /// Called on a well-formed 'adjust_args' parameter range 'lb:ub'. Either + /// bound may be null, meaning 1 for \a LowerBound and 'omp_num_args' for + /// \a UpperBound. + ExprResult ActOnOMPArgumentRangeExpr(Expr *LowerBound, SourceLocation ColonLoc, + Expr *UpperBound); + ExprResult ActOnOpenMPDimsModifier(OpenMPClauseKind Kind, int Modifier, Expr *ModifierExpr, SourceLocation ModifierLoc, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 6a52a9e4fa780..678b17b63a9ff 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -2057,6 +2057,8 @@ enum StmtCode { EXPR_ARRAY_SECTION, EXPR_OMP_ARRAY_SHAPING, EXPR_OMP_ITERATOR, + EXPR_OMP_NUM_ARGS, + EXPR_OMP_ARGUMENT_RANGE, // ARC EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index 7272ad0de9a2c..c56fd28445171 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprOpenMP.h" #include "clang/AST/Type.h" #include <optional> #include <type_traits> @@ -209,12 +210,20 @@ void OMPDeclareVariantAttr::printPrettyPragma( OS << " match(" << traitInfos << ")"; auto PrintExprs = [&OS, &Policy](Expr **Begin, Expr **End) { - for (Expr **I = Begin; I != End; ++I) { - assert(*I && "Expected non-null Stmt"); - if (I != Begin) - OS << ","; - (*I)->printPretty(OS, nullptr, Policy); + if (Begin != End) { + if (const auto *Range = dyn_cast<OMPArgumentRangeExpr>(*Begin); + Range && !Range->getLowerBound()) + // A first item with an omitted lower bound starts with ':', which + // would form a '::' token together with the adjust-op separator. + OS << " "; } + llvm::interleave( + Begin, End, + [&](Expr *E) { + assert(E && "Expected non-null Stmt"); + E->printPretty(OS, nullptr, Policy); + }, + [&OS] { OS << ","; }); }; if (adjustArgsNothing_size()) { OS << " adjust_args(nothing:"; diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 0fc9da18b9394..4f33ed8beca4e 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -500,6 +500,26 @@ ExprDependence clang::computeDependence(OMPIteratorExpr *E) { return D; } +ExprDependence clang::computeDependence(OMPNumArgsExpr *E) { + // The type is always 'int', so the expression is never type-dependent; only + // the logical offset can make it value- or instantiation-dependent. + if (Expr *Offset = E->getOffset()) + return Offset->getDependence() & ~ExprDependence::Type; + return ExprDependence::None; +} + +ExprDependence clang::computeDependence(OMPArgumentRangeExpr *E) { + // The type is always 'void', so the expression is never type-dependent. + // Either bound may be omitted, meaning 1 for the lower bound and + // 'omp_num_args' for the upper bound. + auto D = ExprDependence::None; + if (Expr *LB = E->getLowerBound()) + D |= LB->getDependence(); + if (Expr *UB = E->getUpperBound()) + D |= UB->getDependence(); + return D & ~ExprDependence::Type; +} + /// Compute the type-, value-, and instantiation-dependence of a /// declaration reference /// based on the declaration being referenced. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e501527ed9b04..90622bde37bed 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3831,6 +3831,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ArraySectionExprClass: case OMPArrayShapingExprClass: case OMPIteratorExprClass: + case OMPNumArgsExprClass: + case OMPArgumentRangeExprClass: case MemberExprClass: case ConditionalOperatorClass: case BinaryConditionalOperatorClass: diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index eebae17d7b948..6f3cd087b2660 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -220,6 +220,8 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::RequiresExprClass: case Expr::CXXReflectExprClass: case Expr::CXXExpansionSelectExprClass: + case Expr::OMPNumArgsExprClass: + case Expr::OMPArgumentRangeExprClass: return Cl::CL_PRValue; case Expr::EmbedExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d55749100658f..034142aac4c70 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -22398,6 +22398,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: + case Expr::OMPNumArgsExprClass: + case Expr::OMPArgumentRangeExprClass: case Expr::CompoundAssignOperatorClass: case Expr::CompoundLiteralExprClass: case Expr::ExtVectorElementExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 7fb162a68fe88..64d81b17ed278 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5013,6 +5013,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: + case Expr::OMPNumArgsExprClass: + case Expr::OMPArgumentRangeExprClass: case Expr::CXXInheritedCtorInitExprClass: case Expr::CXXParenListInitExprClass: case Expr::CXXExpansionSelectExprClass: diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index cac701994def8..7e471d25c76b4 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -3269,3 +3269,91 @@ bool TargetOMPContext::matchesISATrait(StringRef RawString) const { DiagUnknownTrait(RawString); return false; } + +/// Evaluate one bound of an 'adjust_args' parameter range: either an +/// 'omp_num_args [+- logical_offset]' expression, or a plain constant integer +/// expression. Returns false if the bound is dependent or not constant. +static bool evalOMPAdjustArgsBound(const Expr *Bound, unsigned NumArgs, + const ASTContext &Ctx, int64_t &Result) { + Bound = Bound->IgnoreParenImpCasts(); + if (const auto *NumArgsExpr = dyn_cast<OMPNumArgsExpr>(Bound)) { + int64_t Offset = 0; + if (const Expr *OffsetExpr = NumArgsExpr->getOffset()) { + if (OffsetExpr->isValueDependent()) + return false; + std::optional<llvm::APSInt> Val = + OffsetExpr->getIntegerConstantExpr(Ctx); + if (!Val) + return false; + Offset = Val->getExtValue(); + } + Result = static_cast<int64_t>(NumArgs) + + (NumArgsExpr->isSubtraction() ? -Offset : Offset); + return true; + } + if (Bound->isValueDependent()) + return false; + std::optional<llvm::APSInt> Val = Bound->getIntegerConstantExpr(Ctx); + if (!Val) + return false; + Result = Val->getExtValue(); + return true; +} + +bool clang::resolveOMPAdjustArgsItem(const Expr *Item, const FunctionDecl *FD, + unsigned NumArgs, const ASTContext &Ctx, + SmallVectorImpl<unsigned> &Positions) { + auto AppendIfInRange = [&](int64_t Pos) { + if (Pos >= 1 && Pos <= static_cast<int64_t>(NumArgs)) + Positions.push_back(static_cast<unsigned>(Pos)); + }; + + // A named parameter list item. + if (const auto *DRE = dyn_cast<DeclRefExpr>(Item)) { + const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()); + if (!PVD) + return false; + unsigned Index = PVD->getFunctionScopeIndex(); + if (FD->getNumParams() > Index && + FD->getParamDecl(Index)->getCanonicalDecl() == + PVD->getCanonicalDecl()) + AppendIfInRange(static_cast<int64_t>(Index) + 1); + return true; + } + + // A parameter range 'lb:ub'. An omitted lb defaults to 1, an omitted ub to + // 'NumArgs' (OpenMP 6.0 [5.2.1] p163). + if (const auto *Range = dyn_cast<OMPArgumentRangeExpr>(Item)) { + int64_t Lower = 1; + if (const Expr *LB = Range->getLowerBound()) { + if (!evalOMPAdjustArgsBound(LB, NumArgs, Ctx, Lower)) + return false; + } + int64_t Upper = NumArgs; + if (const Expr *UB = Range->getUpperBound()) { + if (!evalOMPAdjustArgsBound(UB, NumArgs, Ctx, Upper)) + return false; + } + // Clamp before looping, not just inside it: an out-of-range literal bound + // (e.g. 'omp_num_args-1:9223372036854775807') must not turn this into an + // unbounded loop. + Lower = std::max<int64_t>(Lower, 1); + Upper = std::min<int64_t>(Upper, NumArgs); + for (int64_t Pos = Lower; Pos <= Upper; ++Pos) + AppendIfInRange(Pos); + return true; + } + + // The position of a parameter, given as a constant integer expression. + if (Item->getType()->isIntegerType()) { + if (Item->isValueDependent()) + return false; + std::optional<llvm::APSInt> Val = Item->getIntegerConstantExpr(Ctx); + if (!Val) + return false; + AppendIfInRange(Val->getExtValue()); + return true; + } + + return false; +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index e957b03b7e898..3349f2533c851 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1814,6 +1814,26 @@ void StmtPrinter::VisitOMPIteratorExpr(OMPIteratorExpr *Node) { OS << ")"; } +void StmtPrinter::VisitOMPNumArgsExpr(OMPNumArgsExpr *Node) { + OS << "omp_num_args"; + if (Expr *Offset = Node->getOffset()) { + // No surrounding spaces, so that the sign of the logical offset cannot be + // misread as part of a neighbouring parameter range colon. + OS << (Node->isSubtraction() ? "-" : "+"); + PrintExpr(Offset); + } +} + +void StmtPrinter::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *Node) { + // An omitted bound is simply not printed: `lb:`, `:ub` and `:` are all + // spellings the parser accepts. + if (Expr *LowerBound = Node->getLowerBound()) + PrintExpr(LowerBound); + OS << ":"; + if (Expr *UpperBound = Node->getUpperBound()) + PrintExpr(UpperBound); +} + void StmtPrinter::PrintCallArgs(CallExpr *Call) { for (unsigned i = 0, e = Call->getNumArgs(); i != e; ++i) { if (isa<CXXDefaultArgExpr>(Call->getArg(i))) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 08b42c1477664..c06a1e07def8b 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1594,6 +1594,19 @@ void StmtProfiler::VisitOMPIteratorExpr(const OMPIteratorExpr *S) { VisitDecl(S->getIteratorDecl(I)); } +void StmtProfiler::VisitOMPNumArgsExpr(const OMPNumArgsExpr *S) { + VisitExpr(S); + // The sign is not recoverable from the children: 'omp_num_args+1' and + // 'omp_num_args-1' have the same offset expression. + ID.AddBoolean(S->isSubtraction()); +} + +void StmtProfiler::VisitOMPArgumentRangeExpr(const OMPArgumentRangeExpr *S) { + // Both bounds are children, and an omitted bound is a null child, so 'lb:' + // and ':ub' profile differently. + VisitExpr(S); +} + void StmtProfiler::VisitCallExpr(const CallExpr *S) { VisitExpr(S); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index f58cc4f5761b7..8afeb97f01042 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2051,6 +2051,12 @@ void TextNodeDumper::VisitOMPIteratorExpr(const OMPIteratorExpr *Node) { } } +void TextNodeDumper::VisitOMPNumArgsExpr(const OMPNumArgsExpr *Node) { + // The offset itself is dumped as a child; only its sign needs printing. + if (Node->getOffset()) + OS << " '" << (Node->isSubtraction() ? '-' : '+') << "'"; +} + void TextNodeDumper::VisitConceptSpecializationExpr( const ConceptSpecializationExpr *Node) { OS << " "; diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 30b6c64e69f4c..170b3955439d4 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -4805,6 +4805,99 @@ bool Parser::ParseOpenMPReservedLocator(OpenMPClauseKind Kind, return false; } +ExprResult Parser::ParseOpenMPAdjustArgsBound() { + // 'omp_num_args' is recognised by spelling: OpenMP 6.0 [5.2.1] gives it no + // declaration, so it is never looked up. + if (Tok.isNot(tok::identifier) || + !Tok.getIdentifierInfo()->isStr("omp_num_args")) + return ParseAssignmentExpression(); + + SourceLocation NumArgsLoc = ConsumeToken(); + SourceLocation OpLoc; + bool IsSubtraction = false; + ExprResult Offset; + if (Tok.isOneOf(tok::plus, tok::minus)) { + IsSubtraction = Tok.is(tok::minus); + OpLoc = ConsumeToken(); + // OpenMP 6.0 [5.2.1]: the logical offset is a constant expression. + Offset = ParseConstantExpression(); + if (Offset.isInvalid()) + return ExprError(); + } + // 'omp_num_args' is a whole bound, not an operand of a larger expression, so + // nothing else may follow it. + if (!Tok.isOneOf(tok::colon, tok::comma, tok::r_paren, + tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::err_omp_num_args_invalid_form) << 1; + return ExprError(); + } + return Actions.OpenMP().ActOnOMPNumArgsExpr(NumArgsLoc, OpLoc, IsSubtraction, + Offset.get()); +} + +bool Parser::ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars) { + bool IsError = false; + while (true) { + // An omitted lower bound stands for 1, and is written as a leading ':'. + ExprResult LowerBound; + if (Tok.isNot(tok::colon)) { + LowerBound = ParseOpenMPAdjustArgsBound(); + if (!LowerBound.isUsable()) { + IsError = true; + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + } + + if (Tok.is(tok::colon)) { + SourceLocation ColonLoc = ConsumeToken(); + // An omitted upper bound stands for 'omp_num_args'. + ExprResult UpperBound; + if (!Tok.isOneOf(tok::comma, tok::r_paren, + tok::annot_pragma_openmp_end)) { + UpperBound = ParseOpenMPAdjustArgsBound(); + if (!UpperBound.isUsable()) { + IsError = true; + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + } + Vars.push_back(Actions.OpenMP() + .ActOnOMPArgumentRangeExpr(LowerBound.get(), ColonLoc, + UpperBound.get()) + .get()); + } else if (LowerBound.isUsable()) { + // Without a colon the item is a parameter name or a position, and is + // pushed unchanged so that pre-6.0 lists keep their exact AST shape. + if (isa<OMPNumArgsExpr>(LowerBound.get())) { + Diag(LowerBound.get()->getBeginLoc(), + diag::err_omp_num_args_invalid_form) + << 0; + IsError = true; + } else { + Vars.push_back(LowerBound.get()); + } + } + + // Separator handling mirrors the shared var-list loop, so that a malformed + // item such as '1:2:3' diagnoses instead of silently mis-parsing. + if (Tok.isNot(tok::comma)) { + if (Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::err_omp_expected_punc) + << getOpenMPClauseName(OMPC_adjust_args) << /*IsDirective=*/0; + IsError = true; + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + if (Tok.isNot(tok::comma)) + break; + } + ConsumeToken(); + } + return IsError; +} + /// Parse step size expression. Returns true if parsing is successfull, /// otherwise returns false. static bool parseStepSize(Parser &P, SemaOpenMP::OpenMPVarListDataTy &Data, @@ -4927,6 +5020,11 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, bool HasIterator = false; bool InvalidIterator = false; bool NeedRParenForLinear = false; + // Set when the OpenMP 6.0 'adjust_args' parameter list has already been + // parsed by ParseOpenMPAdjustArgsList, so the shared list loop must be + // skipped. + bool ParsedAdjustArgsList = false; + bool InvalidAdjustArgsList = false; BalancedDelimiterTracker LinearT(*this, tok::l_paren, tok::annot_pragma_openmp_end); // Handle reduction-identifier for reduction clause. @@ -5311,6 +5409,14 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, } ExpectAndConsume(tok::colon, diag::warn_pragma_expected_colon, "adjust-op"); + // OpenMP 6.0 [5.2.1, Parameter List Items] adds positions and parameter + // ranges, which the shared list loop below cannot express because a range + // colon terminates it. + if (getLangOpts().OpenMP >= 60 && + Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown) { + ParsedAdjustArgsList = true; + InvalidAdjustArgsList = ParseOpenMPAdjustArgsList(Vars); + } } } else if (Kind == OMPC_use_device_ptr) { // Handle optional fallback modifier for use_device_ptr clause. @@ -5413,8 +5519,9 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, (Kind == OMPC_adjust_args && Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown); const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned); - while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) && - Tok.isNot(tok::annot_pragma_openmp_end))) { + while (!ParsedAdjustArgsList && + (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) && + Tok.isNot(tok::annot_pragma_openmp_end)))) { ParseScope OMPListScope(this, Scope::OpenMPDirectiveScope); ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail); if (!ParseOpenMPReservedLocator(Kind, Data, getLangOpts())) { @@ -5534,7 +5641,8 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, return (Kind != OMPC_depend && Kind != OMPC_doacross && Kind != OMPC_map && Vars.empty()) || (MustHaveTail && !Data.DepModOrTailExpr && StepFound) || - InvalidReductionId || IsInvalidMapperModifier || InvalidIterator; + InvalidReductionId || IsInvalidMapperModifier || InvalidIterator || + InvalidAdjustArgsList; } OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind, diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 8f08e17276ef3..fb7b1d109cf5c 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1323,6 +1323,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: + case Expr::OMPNumArgsExprClass: + case Expr::OMPArgumentRangeExprClass: case Expr::BinaryOperatorClass: case Expr::DependentCoawaitExprClass: case Expr::CompoundAssignOperatorClass: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index b39dd853ab378..f32e37d85c11b 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -7900,6 +7900,67 @@ static bool checkPreferTypeArgs(SemaOpenMP &S, const OMPInteropInfo &Info) { return true; } +enum class OMPAdjustArgsVal { Known, Dependent, Invalid }; + +/// Check one 'adjust_args' integer expression for the constant property plus +/// either the positive or the non-negative property (OpenMP 6.0 [5.2.1] p162 +/// L32-33, p163 L1). +static OMPAdjustArgsVal checkOMPAdjustArgsValue(SemaOpenMP &S, Expr *E, + bool StrictlyPositive, + llvm::APSInt &Result) { + if (E->isValueDependent() || E->isTypeDependent() || + E->isInstantiationDependent()) + return OMPAdjustArgsVal::Dependent; // re-checked on instantiation + if (S.SemaRef.VerifyIntegerConstantExpression(E, &Result).isInvalid()) + return OMPAdjustArgsVal::Invalid; // already diagnosed + if (StrictlyPositive ? !Result.isStrictlyPositive() : Result.isNegative()) { + S.Diag(E->getExprLoc(), diag::err_omp_negative_expression_in_clause) + << getOpenMPClauseNameForDiag(OMPC_adjust_args) + << (StrictlyPositive ? 1 : 0) << E->getSourceRange(); + return OMPAdjustArgsVal::Invalid; + } + return OMPAdjustArgsVal::Known; +} + +/// Check both bounds of a parameter range 'lb:ub' (OpenMP 6.0 [5.2.1] +/// p162-163). A plain bound has the positive property, checked below via +/// checkOMPAdjustArgsValue. An 'omp_num_args' bound's logical_offset has the +/// non-negative property instead; its constant property was already checked +/// when it was parsed (ActOnOMPNumArgsExpr), so only the sign is checked here +/// via a direct read of the already-verified value. +/// +/// Deliberately does not diagnose 'lb > ub': an empty range specifies no +/// parameters, which OpenMP 6.0 [5.2.1] p163's "as if specified individually" +/// makes harmless, and the spec places no restriction on it. +static bool checkOMPAdjustArgsRange(SemaOpenMP &S, + OMPArgumentRangeExpr *Range) { + for (Expr *Bound : {Range->getLowerBound(), Range->getUpperBound()}) { + if (!Bound) + continue; + if (auto *NumArgsExpr = + dyn_cast<OMPNumArgsExpr>(Bound->IgnoreParenImpCasts())) { + Expr *Offset = NumArgsExpr->getOffset(); + if (!Offset || Offset->isValueDependent()) + continue; + std::optional<llvm::APSInt> Val = + Offset->getIntegerConstantExpr(S.getASTContext()); + if (Val && Val->isNegative()) { + S.Diag(Offset->getExprLoc(), + diag::err_omp_negative_expression_in_clause) + << getOpenMPClauseNameForDiag(OMPC_adjust_args) << 0 + << Offset->getSourceRange(); + return false; + } + continue; + } + llvm::APSInt Result; + if (checkOMPAdjustArgsValue(S, Bound, /*StrictlyPositive=*/true, Result) == + OMPAdjustArgsVal::Invalid) + return false; + } + return true; +} + void SemaOpenMP::ActOnOpenMPDeclareVariantDirective( FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI, ArrayRef<Expr *> AdjustArgsNothing, @@ -7934,20 +7995,26 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective( } } - // OpenMP 5.1 [2.3.5, declare variant directive, Restrictions] - // Each argument can only appear in a single adjust_args clause for each - // declare variant directive. - llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars; + // OpenMP 6.0 [5.2.1] Restrictions (p162): unless otherwise specified, any + // given parameter list item can only be specified once across all clauses + // of the same type in a given directive. The subject is the *item*, not the + // parameter it identifies, so a range never participates in the check below + // and a name does not collide with a position that happens to resolve to + // it. In 5.1 this restriction lived in the declare variant directive's own + // restrictions and was worded per-argument rather than per-item. + llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars; // named items + llvm::SmallSet<uint64_t, 4> AdjustPositions; // literal positional items for (Expr *E : AllAdjustArgs) { - E = E->IgnoreParenImpCasts(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + Expr *Item = E->IgnoreParenImpCasts(); + + // A named parameter list item — unchanged from OpenMP 5.1. + if (const auto *DRE = dyn_cast<DeclRefExpr>(Item)) { if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) { const VarDecl *CanonPVD = PVD->getCanonicalDecl(); if (FD->getNumParams() > PVD->getFunctionScopeIndex() && FD->getParamDecl(PVD->getFunctionScopeIndex()) ->getCanonicalDecl() == CanonPVD) { - // It's a parameter of the function, check duplicates. if (!AdjustVars.insert(CanonPVD).second) { Diag(DRE->getLocation(), diag::err_omp_adjust_arg_multiple_clauses) << PVD; @@ -7957,23 +8024,76 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective( } } } - // Anything that is not a function parameter is an error. - Diag(E->getExprLoc(), diag::err_omp_param_or_this_in_clause) << FD << 0; + + if (getLangOpts().OpenMP < 60) { + // Anything that is not a function parameter is an error before 6.0. + Diag(Item->getExprLoc(), diag::err_omp_param_or_this_in_clause) + << FD << 0; + return; + } + + // OpenMP 6.0 [5.2.1] p162 L32-33: a parameter range 'lb:ub'. A range is + // exempt from the duplicate restriction above — it is one item + // identifying one or more parameters — so nothing is recorded for dedup. + if (auto *Range = dyn_cast<OMPArgumentRangeExpr>(Item)) { + if (!checkOMPAdjustArgsRange(*this, Range)) + return; + continue; + } + + // A bare 'omp_num_args' is rejected by the parser (ParseOpenMPAdjustArgs- + // List), so it can only ever reach here as a range bound, already handled + // above. + assert(!isa<OMPNumArgsExpr>(Item) && "bare omp_num_args reached Sema"); + + // OpenMP 6.0 [5.2.1] p162 L30-31: the position of a parameter, given as a + // positive constant integer expression. A dependent item is skipped here + // and rechecked when the template is instantiated. + if (Item->getType()->isIntegerType()) { + llvm::APSInt Pos; + switch (checkOMPAdjustArgsValue(*this, Item, /*StrictlyPositive=*/true, + Pos)) { + case OMPAdjustArgsVal::Invalid: + return; + case OMPAdjustArgsVal::Dependent: + continue; + case OMPAdjustArgsVal::Known: + if (!AdjustPositions.insert(Pos.getZExtValue()).second) { + Diag(Item->getExprLoc(), diag::err_omp_adjust_arg_multiple_clauses) + << static_cast<unsigned>(Pos.getZExtValue()); + return; + } + continue; + } + } + + // Not a name, a range, or a position: none of the three forms OpenMP 6.0 + // [5.2.1] p162 L28-33 allows. + Diag(Item->getExprLoc(), diag::err_omp_adjust_args_invalid_item); return; } - // OpenMP 6.0 [9.6.2 (page 332, line 31-33, adjust_args clause, Restrictions] - // If the `need_device_addr` adjust-op modifier is present, each list item - // that appears in the clause must refer to an argument in the declaration of - // the function variant that has a reference type + // OpenMP 6.0 [9.6.2] p332 L31-33: if the need_device_addr adjust-op modifier + // is present, each list item that appears in the clause must refer to an + // argument in the declaration of the function variant that has a reference + // type. Unlike the need_device_ptr restriction, this one is not scoped to + // named items ("that refers to a specific named argument" is absent here), + // so positions and ranges are checked too, via the shared item-to-positions + // resolver. if (getLangOpts().OpenMP >= 60) { for (Expr *E : AdjustArgsNeedDeviceAddr) { - E = E->IgnoreParenImpCasts(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { - if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { - if (!VD->getType()->isReferenceType()) - Diag(E->getExprLoc(), - diag::err_omp_non_by_ref_need_device_addr_modifier_argument); + SmallVector<unsigned, 8> Positions; + // With no call site in hand, 'omp_num_args' is the declared parameter + // count (OpenMP 6.0 [20.1] p534). Positions past it denote variadic + // actuals, which have no declared parameter to check against, and are + // dropped by the resolver (OpenMP 6.0 [9.6.2] p332 L1-2). + resolveOMPAdjustArgsItem(E->IgnoreParenImpCasts(), FD, FD->getNumParams(), + getASTContext(), Positions); + for (unsigned Pos : Positions) { + if (!FD->getParamDecl(Pos - 1)->getType()->isReferenceType()) { + Diag(E->getExprLoc(), + diag::err_omp_non_by_ref_need_device_addr_modifier_argument); + break; // One diagnostic per written item, not per swept position. } } } @@ -26968,6 +27088,38 @@ ExprResult SemaOpenMP::ActOnOMPIteratorExpr(Scope *S, LLoc, RLoc, ID, Helpers); } +ExprResult SemaOpenMP::ActOnOMPNumArgsExpr(SourceLocation NumArgsLoc, + SourceLocation OpLoc, + bool IsSubtraction, Expr *Offset) { + ASTContext &Context = getASTContext(); + // OpenMP 6.0 [5.2.1, Parameter List Items] + // The logical offset is a constant integer expression. Leave a dependent + // offset alone; it is rechecked when the template is instantiated. + if (Offset && !Offset->isValueDependent()) { + ExprResult Res = + SemaRef.VerifyIntegerConstantExpression(Offset, /*Result=*/nullptr); + if (Res.isInvalid()) + return ExprError(); + Offset = Res.get(); + } + // The value stands for a number of arguments, so the expression is an int + // even though it cannot be evaluated until the parameter list is consumed. + return new (Context) OMPNumArgsExpr(Context.IntTy, NumArgsLoc, OpLoc, + IsSubtraction, Offset); +} + +ExprResult SemaOpenMP::ActOnOMPArgumentRangeExpr(Expr *LowerBound, + SourceLocation ColonLoc, + Expr *UpperBound) { + ASTContext &Context = getASTContext(); + // Whether the bounds denote parameters that actually exist depends on the + // function the enclosing 'adjust_args' clause is attached to, so it is + // checked in ActOnOpenMPDeclareVariantDirective rather than here. A range is + // never a value, hence 'void'. + return new (Context) + OMPArgumentRangeExpr(Context.VoidTy, LowerBound, ColonLoc, UpperBound); +} + /// Check if \p AssumptionStr is a known assumption and warn if not. static void checkOMPAssumeAttr(Sema &S, SourceLocation Loc, StringRef AssumptionStr) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4799f72dd6177..28d83cce1f78a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2947,6 +2947,28 @@ class TreeTransform { /*Scope=*/nullptr, IteratorKwLoc, LLoc, RLoc, Data); } + /// Build a new 'omp_num_args' expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildOMPNumArgsExpr(SourceLocation NumArgsLoc, + SourceLocation OpLoc, bool IsSubtraction, + Expr *Offset) { + return getSema().OpenMP().ActOnOMPNumArgsExpr(NumArgsLoc, OpLoc, + IsSubtraction, Offset); + } + + /// Build a new parameter range expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildOMPArgumentRangeExpr(Expr *LowerBound, + SourceLocation ColonLoc, + Expr *UpperBound) { + return getSema().OpenMP().ActOnOMPArgumentRangeExpr(LowerBound, ColonLoc, + UpperBound); + } + /// Build a new call expression. /// /// By default, performs semantic analysis to build the new expression. @@ -14026,6 +14048,48 @@ TreeTransform<Derived>::TransformOMPIteratorExpr(OMPIteratorExpr *E) { return Res; } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformOMPNumArgsExpr(OMPNumArgsExpr *E) { + ExprResult Offset; + if (E->getOffset()) { + Offset = getDerived().TransformExpr(E->getOffset()); + if (Offset.isInvalid()) + return ExprError(); + } + + if (!getDerived().AlwaysRebuild() && Offset.get() == E->getOffset()) + return E; + + return getDerived().RebuildOMPNumArgsExpr( + E->getNumArgsLoc(), E->getOperatorLoc(), E->isSubtraction(), Offset.get()); +} + +template <typename Derived> +ExprResult +TreeTransform<Derived>::TransformOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) { + ExprResult LowerBound; + if (E->getLowerBound()) { + LowerBound = getDerived().TransformExpr(E->getLowerBound()); + if (LowerBound.isInvalid()) + return ExprError(); + } + + ExprResult UpperBound; + if (E->getUpperBound()) { + UpperBound = getDerived().TransformExpr(E->getUpperBound()); + if (UpperBound.isInvalid()) + return ExprError(); + } + + if (!getDerived().AlwaysRebuild() && + LowerBound.get() == E->getLowerBound() && + UpperBound.get() == E->getUpperBound()) + return E; + + return getDerived().RebuildOMPArgumentRangeExpr( + LowerBound.get(), E->getColonLoc(), UpperBound.get()); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformCallExpr(CallExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 92c555dc427b3..dace5720f038d 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1063,6 +1063,21 @@ void ASTStmtReader::VisitOMPIteratorExpr(OMPIteratorExpr *E) { } } +void ASTStmtReader::VisitOMPNumArgsExpr(OMPNumArgsExpr *E) { + VisitExpr(E); + E->setOffset(Record.readSubExpr()); + E->setNumArgsLoc(readSourceLocation()); + E->setOperatorLoc(readSourceLocation()); + E->setIsSubtraction(Record.readInt() != 0); +} + +void ASTStmtReader::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) { + VisitExpr(E); + E->setLowerBound(Record.readSubExpr()); + E->setUpperBound(Record.readSubExpr()); + E->setColonLoc(readSourceLocation()); +} + void ASTStmtReader::VisitCallExpr(CallExpr *E) { VisitExpr(E); @@ -3395,6 +3410,14 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { Record[ASTStmtReader::NumExprFields]); break; + case EXPR_OMP_NUM_ARGS: + S = new (Context) OMPNumArgsExpr(Empty); + break; + + case EXPR_OMP_ARGUMENT_RANGE: + S = new (Context) OMPArgumentRangeExpr(Empty); + break; + case EXPR_CALL: { auto NumArgs = Record[ASTStmtReader::NumExprFields]; BitsUnpacker CallExprBits(Record[ASTStmtReader::NumExprFields + 1]); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 782fecdbd0c80..c3f60b736cdbd 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -998,6 +998,23 @@ void ASTStmtWriter::VisitOMPIteratorExpr(OMPIteratorExpr *E) { Code = serialization::EXPR_OMP_ITERATOR; } +void ASTStmtWriter::VisitOMPNumArgsExpr(OMPNumArgsExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getOffset()); + Record.AddSourceLocation(E->getNumArgsLoc()); + Record.AddSourceLocation(E->getOperatorLoc()); + Record.push_back(E->isSubtraction() ? 1 : 0); + Code = serialization::EXPR_OMP_NUM_ARGS; +} + +void ASTStmtWriter::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getLowerBound()); + Record.AddStmt(E->getUpperBound()); + Record.AddSourceLocation(E->getColonLoc()); + Code = serialization::EXPR_OMP_ARGUMENT_RANGE; +} + void ASTStmtWriter::VisitCallExpr(CallExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index e6349eb4eba2a..19c179f77402e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1939,6 +1939,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ArraySectionExprClass: case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: + case Stmt::OMPNumArgsExprClass: + case Stmt::OMPArgumentRangeExprClass: case Stmt::SYCLUniqueStableNameExprClass: case Stmt::OpenACCAsteriskSizeExprClass: case Stmt::TypeTraitExprClass: { diff --git a/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp b/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp new file mode 100644 index 0000000000000..7945b2dbd2679 --- /dev/null +++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp @@ -0,0 +1,175 @@ +// 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 | FileCheck %s --check-prefix=DUMP + +#ifndef HEADER +#define HEADER + +void v_pos(int *A, int *B, int *C, int *D); +void v_range(int *A, int *B, int *C, int *D); +void v_variadic(int *A, int *B, int *C, int *D, ...); +void v_offset(int *A, int *B, int *C, ...); +void v_lb(int *A, int *B); +void v_ub(int *A, int *B); +void v_both(int *A, int *B); +void v_named(int *A, int *B); +void v_mixed(int *A, int *B); +void v_cond(int *A, int *B, int *C); +void v_addr(int &A, int &B); + +// A list item may be a position: a constant integer expression. +// PRINT: #pragma omp declare variant(v_pos) match(construct={dispatch}) adjust_args(need_device_ptr:2,4) +// DUMP: FunctionDecl{{.*}}pos 'void (int *, int *, int *, int *)' +// DUMP: OMPDeclareVariantAttr +// DUMP: IntegerLiteral{{.*}}'int' 2 +// DUMP-NEXT: IntegerLiteral{{.*}}'int' 4 +#pragma omp declare variant(v_pos) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2, 4) +void pos(int *A, int *B, int *C, int *D); + +// A list item may be a parameter range 'lb:ub'. +// PRINT: #pragma omp declare variant(v_range) match(construct={dispatch}) adjust_args(need_device_ptr:1:3) +// DUMP: FunctionDecl{{.*}}range 'void (int *, int *, int *, int *)' +// DUMP: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: IntegerLiteral{{.*}}'int' 1 +// DUMP-NEXT: IntegerLiteral{{.*}}'int' 3 +#pragma omp declare variant(v_range) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:3) +void range(int *A, int *B, int *C, int *D); + +// 'omp_num_args' is spelled only as a range bound, optionally with a logical +// offset. The offset is a constant expression, hence the ConstantExpr wrapper. +// PRINT: #pragma omp declare variant(v_variadic) match(construct={dispatch}) adjust_args(need_device_ptr:1:3,5,omp_num_args-1:omp_num_args) +// DUMP: FunctionDecl{{.*}}variadic 'void (int *, int *, int *, int *, ...)' +// DUMP: IntegerLiteral{{.*}}'int' 5 +// DUMP-NEXT: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: OMPNumArgsExpr{{.*}}'int' '-' +// DUMP-NEXT: ConstantExpr{{.*}}'int' +// DUMP: IntegerLiteral{{.*}}'int' 1 +// DUMP-NEXT: OMPNumArgsExpr{{.*}}'int' +// DUMP-NEXT: <<<NULL>>> +#pragma omp declare variant(v_variadic) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:3, 5, omp_num_args-1:omp_num_args) +void variadic(int *A, int *B, int *C, int *D, ...); + +// Both signs of the logical offset are accepted. An item that resolves outside +// the parameter list is ignored rather than diagnosed, per OpenMP 6.0 [9.6.2]. +// PRINT: #pragma omp declare variant(v_offset) match(construct={dispatch}) adjust_args(nothing:omp_num_args-2:omp_num_args,omp_num_args+1:) +// DUMP: FunctionDecl{{.*}}offset 'void (int *, int *, int *, ...)' +// DUMP: OMPNumArgsExpr{{.*}}'int' '+' +#pragma omp declare variant(v_offset) match(construct={dispatch}) \ + adjust_args(nothing: omp_num_args-2:omp_num_args, omp_num_args+1:) +void offset(int *A, int *B, int *C, ...); + +// OpenMP 6.0 [5.2.1] p163: an omitted 'lb' stands for 1. As the first list item +// it must still be separated from the adjust-op colon, or the two colons would +// lex as a single '::' and the printed clause would not parse back. +// PRINT: #pragma omp declare variant(v_lb) match(construct={dispatch}) adjust_args(need_device_ptr: :2) +// DUMP: FunctionDecl{{.*}}lb 'void (int *, int *)' +// DUMP: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: <<<NULL>>> +// DUMP-NEXT: IntegerLiteral{{.*}}'int' 2 +#pragma omp declare variant(v_lb) match(construct={dispatch}) \ + adjust_args(need_device_ptr: :2) +void lb(int *A, int *B); + +// An omitted 'ub' stands for 'omp_num_args'. +// PRINT: #pragma omp declare variant(v_ub) match(construct={dispatch}) adjust_args(need_device_ptr:1:) +// DUMP: FunctionDecl{{.*}}ub 'void (int *, int *)' +// DUMP: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: IntegerLiteral{{.*}}'int' 1 +// DUMP-NEXT: <<<NULL>>> +#pragma omp declare variant(v_ub) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:) +void ub(int *A, int *B); + +// The two bounds are optional independently, so omitting both denotes +// 1:omp_num_args, that is every parameter. +// PRINT: #pragma omp declare variant(v_both) match(construct={dispatch}) adjust_args(need_device_ptr: :) +// DUMP: FunctionDecl{{.*}}both 'void (int *, int *)' +// DUMP: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: <<<NULL>>> +// DUMP-NEXT: <<<NULL>>> +#pragma omp declare variant(v_both) match(construct={dispatch}) \ + adjust_args(need_device_ptr: :) +void both(int *A, int *B); + +// The pre-6.0 named form keeps its exact spelling and AST shape. +// PRINT: #pragma omp declare variant(v_named) match(construct={dispatch}) adjust_args(need_device_ptr:A,B) +// DUMP: FunctionDecl{{.*}}named 'void (int *, int *)' +// DUMP: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'A' 'int *' +// DUMP-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'B' 'int *' +#pragma omp declare variant(v_named) match(construct={dispatch}) \ + adjust_args(need_device_ptr: A, B) +void named(int *A, int *B); + +// Named items, positions and ranges may be mixed in one clause. +// PRINT: #pragma omp declare variant(v_mixed) match(construct={dispatch}) adjust_args(need_device_ptr:A,2:2) +// DUMP: FunctionDecl{{.*}}mixed 'void (int *, int *)' +// DUMP: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'A' 'int *' +// DUMP-NEXT: OMPArgumentRangeExpr{{.*}}'void' +#pragma omp declare variant(v_mixed) match(construct={dispatch}) \ + adjust_args(need_device_ptr: A, 2:2) +void mixed(int *A, int *B); + +// A conditional operator keeps its own colon: the item is one position, not a +// range, so a bound is never split at a '?:' colon. +// PRINT: #pragma omp declare variant(v_cond) match(construct={dispatch}) adjust_args(need_device_ptr:1 ? 2 : 3) +// DUMP: FunctionDecl{{.*}}cond 'void (int *, int *, int *)' +// DUMP: ConditionalOperator{{.*}}'int' +#pragma omp declare variant(v_cond) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1 ? 2 : 3) +void cond(int *A, int *B, int *C); + +// A range does not name a parameter, so the 'need_device_addr' reference-type +// restriction of OpenMP 6.0 [9.6.2] does not apply to it. +// PRINT: #pragma omp declare variant(v_addr) match(construct={dispatch}) adjust_args(need_device_addr:1:2) +#pragma omp declare variant(v_addr) match(construct={dispatch}) \ + adjust_args(need_device_addr: 1:2) +void addr(int &A, int &B); + +// Dependent bounds are accepted in the template pattern and rechecked on +// instantiation. +template <int N> +void tmpl_v(int *A, int *B, int *C, ...); + +template <int N> +void tmpl(int *A, int *B, int *C, ...); + +// The instantiation is dumped before the pattern it came from, so its checks +// come first. Substituting N=2 makes the logical offset a constant expression. +// DUMP: FunctionDecl{{.*}}tmpl 'void (int *, int *, int *, ...)' explicit_instantiation_definition +// DUMP: OMPArgumentRangeExpr{{.*}}'void' +// DUMP-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'int' +// DUMP: BinaryOperator{{.*}}'int' '+' +// DUMP: OMPNumArgsExpr{{.*}}'int' '-' +// DUMP-NEXT: ConstantExpr{{.*}}'int' +// DUMP-NEXT: value: Int 2 +// +// In the pattern the offset stays dependent, so it is not wrapped. +// DUMP: OMPNumArgsExpr{{.*}}'int' '-' +// DUMP-NEXT: DeclRefExpr{{.*}}'int' NonTypeTemplateParm{{.*}}'N' 'int' +// +// PRINT: #pragma omp declare variant(tmpl_v<N>) match(construct={dispatch}) adjust_args(need_device_ptr:N:N + 1,omp_num_args-N:omp_num_args) +#pragma omp declare variant(tmpl_v<N>) match(construct={dispatch}) \ + adjust_args(need_device_ptr: N:N + 1, omp_num_args-N:omp_num_args) +template <int N> +void tmpl(int *A, int *B, int *C, ...) {} + +template void tmpl<2>(int *, int *, int *, ...); + +#endif // HEADER diff --git a/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp b/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp new file mode 100644 index 0000000000000..6b38d6a7f3a11 --- /dev/null +++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp @@ -0,0 +1,228 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -x c++ -std=c++14 -ferror-limit 100 -fsyntax-only -verify=expected %s + +// The same syntax must still be rejected before OpenMP 6.0, which pins that the +// new parsing is version-gated. +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -x c++ -std=c++14 -ferror-limit 100 -DOMP51 -fsyntax-only \ +// RUN: -verify=omp51 %s + +int G; // expected-note 2 {{declared here}} + +void v1(int *aaa, int *bbb, ...); + +#ifndef OMP51 + +// OpenMP 6.0 [5.2.1] p162: 'omp_num_args' may only be followed by a signed +// constant logical offset, so no other operator may continue the bound. +// expected-error@+2 {{'omp_num_args' may only be followed by '+' or '-' and a constant logical offset}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args/2) +void f1(int *aaa, int *bbb, ...); + +// expected-error@+2 {{'omp_num_args' may only be followed by '+' or '-' and a constant logical offset}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args*2) +void f2(int *aaa, int *bbb, ...); + +// 'omp_num_args' is recognised by spelling, and only where a bound starts. It +// has no declaration, so anywhere else it is just an unknown identifier. +// expected-error@+2 {{use of undeclared identifier 'omp_num_args'}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2*omp_num_args) +void f3(int *aaa, int *bbb, ...); + +// expected-error@+2 {{use of undeclared identifier 'omp_num_args'}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: (omp_num_args):2) +void f4(int *aaa, int *bbb, ...); + +// A sign must be followed by an actual offset expression. +// expected-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args +) +void f5(int *aaa, int *bbb, ...); + +// expected-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:omp_num_args-) +void f6(int *aaa, int *bbb, ...); + +// The logical offset must be a constant integer expression. +// expected-error@+3 {{expression is not an integral constant expression}} +// expected-note@+2 {{read of non-const variable 'G' is not allowed in a constant expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-G:omp_num_args) +void f7(int *aaa, int *bbb, ...); + +// expected-error@+2 {{integral constant expression must have integral or unscoped enumeration type, not 'double'}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-1.5:omp_num_args) +void f8(int *aaa, int *bbb, ...); + +// OpenMP 6.0 [5.2.1] p162 lists three forms of parameter list item, and a bare +// 'omp_num_args' is not one of them: it is legal only as a range bound. +// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a parameter range}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args) +void f9(int *aaa, int *bbb, ...); + +// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a parameter range}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-1) +void f10(int *aaa, int *bbb, ...); + +// The rejection is per item, not only for the first one. +// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a parameter range}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: aaa, omp_num_args) +void f11(int *aaa, int *bbb, ...); + +// A range has exactly one colon, so a third bound is a malformed item rather +// than a silently mis-parsed one. +// expected-error@+2 {{expected ',' or ')' in 'adjust_args' clause}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2:3) +void f12(int *aaa, int *bbb, ...); + +// A non-constant, non-parameter item is still not a position: it must be a +// constant integer expression (OpenMP 6.0 [5.2.1] p162 L30-31). +// expected-error@+3 {{expression is not an integral constant expression}} +// expected-note@+2 {{read of non-const variable 'G' is not allowed in a constant expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G) +void f13(int *aaa, int *bbb, ...); + +// The list must have at least one item. +// expected-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: ) +void f14(int *aaa, int *bbb, ...); + +// A trailing separator starts an item that is not there. +// expected-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2,) +void f15(int *aaa, int *bbb, ...); + +// A leading separator is not an omitted item either; only a colon may open one. +// expected-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: , 1) +void f16(int *aaa, int *bbb, ...); + +// The list loop must give up at the end of the directive rather than spin on a +// clause that is never closed. +// expected-error@+3 {{expected ')'}} +// expected-note@+2 {{to match this '('}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2 +void f17(int *aaa, int *bbb, ...); + +// A malformed item is skipped up to the separator, so it neither swallows nor +// duplicates a diagnostic for the item that follows it. +// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a parameter range}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args, 2) +void f18(int *aaa, int *bbb, ...); + +// OpenMP 6.0 [5.2.1] p162 L30-31: a position has the positive property. +// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly positive integer value}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 0) +void h1(int *aaa, int *bbb, ...); + +// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly positive integer value}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: -1) +void h2(int *aaa, int *bbb, ...); + +// A plain range bound has the positive property too. +// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly positive integer value}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 0:5) +void h3(int *aaa, int *bbb, ...); + +// The logical offset has the non-negative property, checked independently of +// its already-verified constant property (OpenMP 6.0 [5.2.1] p163 L1). +// expected-error@+2 {{argument to 'adjust_args' clause must be a non-negative integer value}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-(-1):omp_num_args) +void h4(int *aaa, int *bbb, ...); + +// The duplicate restriction (OpenMP 6.0 [5.2.1] p162) applies to positions, +// not only to names. +// expected-error@+2 {{'adjust_arg' argument 2 used in multiple clauses}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2, 2) +void h5(int *aaa, int *bbb, ...); + +// A name and a position that happen to resolve to the same parameter are two +// distinct items (OpenMP 6.0 [5.2.1] p162), so this is accepted, not a +// duplicate. +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: aaa, 1) +void h6(int *aaa, int *bbb, ...); + +// Not a name, a range, or a position: none of the three forms is satisfied. +// expected-error@+2 {{expected a parameter name, a parameter position, or a parameter range in 'adjust_args' clause}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1.5) +void h7(int *aaa, int *bbb, ...); + +// need_device_addr's reference-type restriction is not scoped to named items +// (OpenMP 6.0 [9.6.2] p332 L31-33), so a position is checked too. +// expected-error@+2 {{expected reference type argument on 'adjust_args' clause with 'need_device_addr' modifier}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_addr: 1) +void h8(int *aaa, int *bbb, ...); + +// A huge literal upper bound must not turn range resolution into an unbounded +// loop: out-of-range positions are dropped (OpenMP 6.0 [9.6.2] p332 L1-2), not +// enumerated one at a time up to the written value. +void v2(int &aaa, int &bbb, ...); +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_addr: 1:9223372036854775807) +void h9(int &aaa, int &bbb, ...); + +// need_device_addr's reference-type check via a range covers every position +// it sweeps, but reports only one diagnostic per written item, not one per +// position (OpenMP 6.0 [9.6.2] p332 L31-33). +// expected-error@+2 {{expected reference type argument on 'adjust_args' clause with 'need_device_addr' modifier}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_addr: 1:2) +void h10(int *aaa, int *bbb, ...); + +// 'lb > ub' specifies no parameters and is accepted silently: the spec places +// no restriction on it (OpenMP 6.0 [5.2.1] p163). +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 5:2) +void h11(int *aaa, int *bbb, ...); + +#else // OMP51 + +// Before 6.0 a range is not parsed at all: the list stops at the colon, so the +// clause is left unterminated and the leading bound is then rejected by Sema. +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2) // omp51-error {{expected ',' or ')' in 'adjust_args' clause}} omp51-error {{expected ')'}} omp51-note {{to match this '('}} omp51-error {{expected reference to one of the parameters of function 'g1'}} +void g1(int *aaa, int *bbb, ...); + +// A position is not a parameter name before 6.0. +// omp51-error@+2 {{expected reference to one of the parameters of function 'g2'}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2) +void g2(int *aaa, int *bbb, ...); + +// An omitted lower bound is not an item, so the list is simply empty. +// omp51-error@+2 {{expected expression}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: :) +void g3(int *aaa, int *bbb, ...); + +// 'omp_num_args' is not recognised at all, and is looked up as a name. +// omp51-error@+2 {{use of undeclared identifier 'omp_num_args'}} +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-1:omp_num_args) +void g4(int *aaa, int *bbb, ...); + +#endif // OMP51 diff --git a/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp b/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp new file mode 100644 index 0000000000000..a6c60221e57ef --- /dev/null +++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \ +// RUN: -x c++ -std=c++14 -fsyntax-only -verify %s + +// OpenMP 6.0 [5.2.1]: a dependent parameter-list item is accepted at the +// template definition and rechecked only once substitution makes it +// non-dependent (S6). SemaTemplateInstantiateDecl re-invokes +// ActOnOpenMPDeclareVariantDirective per specialization, so nothing at +// definition time is lost by deferring. + +template <int N> +void v_tmpl(int *aaa, int *bbb, ...); + +// No error here: 'N' is value-dependent at definition time. +#pragma omp declare variant(v_tmpl<N>) match(construct={dispatch}) \ + adjust_args(need_device_ptr: N) +template <int N> +void tmpl_pos(int *aaa, int *bbb, ...) {} + +// Clean once substituted: position 2 is 'bbb', a valid, positive position. +template void tmpl_pos<2>(int *, int *, ...); + +// The positive property (OpenMP 6.0 [5.2.1] p162 L30-31) is only checkable +// once 'N' is substituted. +// expected-error@15 {{argument to 'adjust_args' clause must be a strictly positive integer value}} +// expected-note@+1 {{in instantiation of function template specialization 'tmpl_pos<0>' requested here}} +template void tmpl_pos<0>(int *, int *, ...); + +template <int N> +void v_tmpl_range(int *aaa, int *bbb, int *ccc, ...); + +// A dependent range bound is accepted at definition time too. +#pragma omp declare variant(v_tmpl_range<N>) match(construct={dispatch}) \ + adjust_args(need_device_ptr: N:N + 1) +template <int N> +void tmpl_range(int *aaa, int *bbb, int *ccc, ...) {} + +// Clean once substituted: range 2:3 covers 'bbb' and 'ccc'. +template void tmpl_range<2>(int *, int *, int *, ...); + +// The positive property on a range bound is likewise only checkable once 'N' +// is substituted. +// expected-error@33 {{argument to 'adjust_args' clause must be a strictly positive integer value}} +// expected-note@+1 {{in instantiation of function template specialization 'tmpl_range<0>' requested here}} +template void tmpl_range<0>(int *, int *, int *, ...); + +template <int N> +void v_tmpl_offset(int *aaa, int *bbb, ...); + +// A dependent 'omp_num_args' logical offset is accepted at definition time. +#pragma omp declare variant(v_tmpl_offset<N>) match(construct={dispatch}) \ + adjust_args(need_device_ptr: omp_num_args-N:omp_num_args) +template <int N> +void tmpl_offset(int *aaa, int *bbb, ...) {} + +// Clean once substituted: offset 1 is non-negative. +template void tmpl_offset<1>(int *, int *, ...); + +// The non-negative property on the offset (OpenMP 6.0 [5.2.1] p163 L1) is +// likewise only checkable once 'N' is substituted. +// expected-error@51 {{argument to 'adjust_args' clause must be a non-negative integer value}} +// expected-note@+1 {{in instantiation of function template specialization 'tmpl_offset<-1>' requested here}} +template void tmpl_offset<-1>(int *, int *, ...); diff --git a/clang/test/OpenMP/declare_variant_clauses_messages.cpp b/clang/test/OpenMP/declare_variant_clauses_messages.cpp index 916d15fde9ff2..c09619f2b7fcc 100644 --- a/clang/test/OpenMP/declare_variant_clauses_messages.cpp +++ b/clang/test/OpenMP/declare_variant_clauses_messages.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 -o - %s -// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 \ +// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 -o - %s +// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 \ // RUN: -DNO_INTEROP_T_DEF -o - %s -// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 -o - %s -// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -Wno-strict-prototypes -DC -x c -o - %s -// RUN: %clang_cc1 -verify -triple x86_64-pc-windows-msvc -fms-compatibility \ +// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -std=c++11 -o - %s +// RUN: %clang_cc1 -verify=expected,c -triple x86_64-unknown-linux -fopenmp -fopenmp-version=60 -Wno-strict-prototypes -DC -x c -o - %s +// RUN: %clang_cc1 -verify=expected,c -triple x86_64-pc-windows-msvc -fms-compatibility \ // RUN: -fopenmp -fopenmp-version=60 -Wno-strict-prototypes -DC -DWIN -x c -o - %s #ifdef NO_INTEROP_T_DEF @@ -15,7 +15,7 @@ void foo_v1(float *); #else typedef void *omp_interop_t; -int Other; +int Other; // cxx-note {{declared here}} #if _OPENMP >= 202011 // At least OpenMP 5.1 #ifdef __cplusplus @@ -135,7 +135,9 @@ void vararg_bar2(const char *fmt) { return; } adjust_args(need_device_addr:AAA) \ match(construct={dispatch}, device={arch(x86,x86_64)}) -// expected-error@+2 {{expected reference to one of the parameters of function 'foo'}} +// cxx-error@+4 {{expression is not an integral constant expression}} +// cxx-note@+3 {{read of non-const variable 'Other' is not allowed in a constant expression}} +// c-error@+2 {{expression is not an integer constant expression}} #pragma omp declare variant(foo_v3) \ adjust_args(nothing:Other) \ match(construct={dispatch}, device={arch(x86,x86_64)}) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 2c5e799d12f18..271fc889fb6e2 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6043,6 +6043,10 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPArrayShapingExpr"); case CXCursor_OMPIteratorExpr: return cxstring::createRef("OMPIteratorExpr"); + case CXCursor_OMPNumArgsExpr: + return cxstring::createRef("OMPNumArgsExpr"); + case CXCursor_OMPArgumentRangeExpr: + return cxstring::createRef("OMPArgumentRangeExpr"); case CXCursor_BinaryOperator: return cxstring::createRef("BinaryOperator"); case CXCursor_CompoundAssignOperator: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 768f5465a166a..8ad3136bf1b95 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -458,6 +458,14 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_OMPIteratorExpr; break; + case Stmt::OMPNumArgsExprClass: + K = CXCursor_OMPNumArgsExpr; + break; + + case Stmt::OMPArgumentRangeExprClass: + K = CXCursor_OMPArgumentRangeExpr; + break; + case Stmt::BinaryOperatorClass: K = CXCursor_BinaryOperator; break; >From 173f4c62fb422b238bb962ee82cec3721888f8b7 Mon Sep 17 00:00:00 2001 From: "Khatavkar, Yashasvi" <[email protected]> Date: Mon, 21 Sep 2026 06:23:57 -0700 Subject: [PATCH 2/2] Apply clang-format --- clang/include/clang/Sema/SemaOpenMP.h | 3 ++- clang/lib/AST/OpenMPClause.cpp | 6 ++---- clang/lib/Parse/ParseOpenMP.cpp | 5 ++--- clang/lib/Sema/SemaOpenMP.cpp | 8 ++++---- clang/lib/Sema/TreeTransform.h | 8 ++++---- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 1e1870f469084..99199ac06c2a2 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -1501,7 +1501,8 @@ class SemaOpenMP : public SemaBase { /// Called on a well-formed 'adjust_args' parameter range 'lb:ub'. Either /// bound may be null, meaning 1 for \a LowerBound and 'omp_num_args' for /// \a UpperBound. - ExprResult ActOnOMPArgumentRangeExpr(Expr *LowerBound, SourceLocation ColonLoc, + ExprResult ActOnOMPArgumentRangeExpr(Expr *LowerBound, + SourceLocation ColonLoc, Expr *UpperBound); ExprResult ActOnOpenMPDimsModifier(OpenMPClauseKind Kind, int Modifier, diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 7e471d25c76b4..85db30fc3fbc6 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -3281,8 +3281,7 @@ static bool evalOMPAdjustArgsBound(const Expr *Bound, unsigned NumArgs, if (const Expr *OffsetExpr = NumArgsExpr->getOffset()) { if (OffsetExpr->isValueDependent()) return false; - std::optional<llvm::APSInt> Val = - OffsetExpr->getIntegerConstantExpr(Ctx); + std::optional<llvm::APSInt> Val = OffsetExpr->getIntegerConstantExpr(Ctx); if (!Val) return false; Offset = Val->getExtValue(); @@ -3315,8 +3314,7 @@ bool clang::resolveOMPAdjustArgsItem(const Expr *Item, const FunctionDecl *FD, return false; unsigned Index = PVD->getFunctionScopeIndex(); if (FD->getNumParams() > Index && - FD->getParamDecl(Index)->getCanonicalDecl() == - PVD->getCanonicalDecl()) + FD->getParamDecl(Index)->getCanonicalDecl() == PVD->getCanonicalDecl()) AppendIfInRange(static_cast<int64_t>(Index) + 1); return true; } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 170b3955439d4..876340cdd1019 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -4864,7 +4864,7 @@ bool Parser::ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars) { } Vars.push_back(Actions.OpenMP() .ActOnOMPArgumentRangeExpr(LowerBound.get(), ColonLoc, - UpperBound.get()) + UpperBound.get()) .get()); } else if (LowerBound.isUsable()) { // Without a colon the item is a parameter name or a position, and is @@ -4882,8 +4882,7 @@ bool Parser::ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars) { // Separator handling mirrors the shared var-list loop, so that a malformed // item such as '1:2:3' diagnoses instead of silently mis-parsing. if (Tok.isNot(tok::comma)) { - if (Tok.isNot(tok::r_paren) && - Tok.isNot(tok::annot_pragma_openmp_end)) { + if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::err_omp_expected_punc) << getOpenMPClauseName(OMPC_adjust_args) << /*IsDirective=*/0; IsError = true; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index f32e37d85c11b..edb29f5988f8a 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -8002,8 +8002,8 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective( // and a name does not collide with a position that happens to resolve to // it. In 5.1 this restriction lived in the declare variant directive's own // restrictions and was worded per-argument rather than per-item. - llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars; // named items - llvm::SmallSet<uint64_t, 4> AdjustPositions; // literal positional items + llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars; // named items + llvm::SmallSet<uint64_t, 4> AdjustPositions; // literal positional items for (Expr *E : AllAdjustArgs) { Expr *Item = E->IgnoreParenImpCasts(); @@ -27104,8 +27104,8 @@ ExprResult SemaOpenMP::ActOnOMPNumArgsExpr(SourceLocation NumArgsLoc, } // The value stands for a number of arguments, so the expression is an int // even though it cannot be evaluated until the parameter list is consumed. - return new (Context) OMPNumArgsExpr(Context.IntTy, NumArgsLoc, OpLoc, - IsSubtraction, Offset); + return new (Context) + OMPNumArgsExpr(Context.IntTy, NumArgsLoc, OpLoc, IsSubtraction, Offset); } ExprResult SemaOpenMP::ActOnOMPArgumentRangeExpr(Expr *LowerBound, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 28d83cce1f78a..ee157a5e5f9c3 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14060,8 +14060,9 @@ ExprResult TreeTransform<Derived>::TransformOMPNumArgsExpr(OMPNumArgsExpr *E) { if (!getDerived().AlwaysRebuild() && Offset.get() == E->getOffset()) return E; - return getDerived().RebuildOMPNumArgsExpr( - E->getNumArgsLoc(), E->getOperatorLoc(), E->isSubtraction(), Offset.get()); + return getDerived().RebuildOMPNumArgsExpr(E->getNumArgsLoc(), + E->getOperatorLoc(), + E->isSubtraction(), Offset.get()); } template <typename Derived> @@ -14081,8 +14082,7 @@ TreeTransform<Derived>::TransformOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) { return ExprError(); } - if (!getDerived().AlwaysRebuild() && - LowerBound.get() == E->getLowerBound() && + if (!getDerived().AlwaysRebuild() && LowerBound.get() == E->getLowerBound() && UpperBound.get() == E->getUpperBound()) return E; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
