Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -1389,6 +1389,13 @@ } }) +DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, { + TRY_TO(TraverseDeclaratorHelper(D)); + if (auto *Initializer = D->getInitializer()) + TRY_TO(TraverseStmt(Initializer)); + return true; +}) + // A helper method for TemplateDecl's children. template bool RecursiveASTVisitor::TraverseTemplateParameterListHelper( Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -161,7 +161,10 @@ /// This declaration is a function-local extern declaration of a /// variable or function. This may also be IDNS_Ordinary if it /// has been declared outside any function. - IDNS_LocalExtern = 0x0800 + IDNS_LocalExtern = 0x0800, + + /// This declaration is an OpenMP user defined reduction construction. + IDNS_OMPReduction = 0x1000 }; /// ObjCDeclQualifier - 'Qualifiers' written next to the return and @@ -251,7 +254,7 @@ SourceLocation Loc; /// DeclKind - This indicates which class this is. - unsigned DeclKind : 8; + unsigned DeclKind : 7; /// InvalidDecl - This indicates a semantic error occurred. unsigned InvalidDecl : 1; @@ -291,7 +294,7 @@ unsigned Hidden : 1; /// IdentifierNamespace - This specifies what IDNS_* namespace this lives in. - unsigned IdentifierNamespace : 12; + unsigned IdentifierNamespace : 13; /// \brief If 0, we have not computed the linkage of this declaration. /// Otherwise, it is the linkage + 1. Index: include/clang/AST/DeclOpenMP.h =================================================================== --- include/clang/AST/DeclOpenMP.h +++ include/clang/AST/DeclOpenMP.h @@ -15,11 +15,12 @@ #ifndef LLVM_CLANG_AST_DECLOPENMP_H #define LLVM_CLANG_AST_DECLOPENMP_H -#include "clang/AST/DeclBase.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "llvm/ADT/ArrayRef.h" namespace clang { -class Expr; /// \brief This represents '#pragma omp threadprivate ...' directive. /// For example, in the following, both 'a' and 'A::b' are threadprivate: @@ -85,6 +86,82 @@ static bool classofKind(Kind K) { return K == OMPThreadPrivate; } }; +/// \brief This represents '#pragma omp declare reduction ...' directive. +/// For example, in the following, declared reduction 'foo' for types 'int' and +/// 'float': +/// +/// \code +/// #pragma omp declare reduction (foo : int,float : omp_out += omp_in) +/// initializer (omp_priv = 0) +/// \endcode +/// +/// Here 'omp_out += omp_in' is a combiner and 'omp_priv = 0' is an initializer. +class OMPDeclareReductionDecl : public DeclaratorDecl, public DeclContext { +private: + friend class ASTDeclReader; + /// \brief Combiner for declare reduction construct. + Stmt *Combiner; + /// \brief Initializer for declare reduction construct. + Stmt *Initializer; + /// \brief Reference to the previous declare reduction construct in the same + /// scope with the same name. Required for proper templates instantiation if + /// the declare reduction construct is declared inside compound statement. + Decl *NextDeclInScope; + + virtual void anchor(); + + OMPDeclareReductionDecl(Kind DK, DeclContext *DC, SourceLocation L, + DeclarationName Name, QualType T) + : DeclaratorDecl(DK, DC, L, Name, T, nullptr, SourceLocation()), + DeclContext(DK), Combiner(nullptr), Initializer(nullptr), + NextDeclInScope(nullptr) {} + +public: + /// \brief Create declare reduction node. + /// \param PrevDeclInScope Previous declare reduction construct in the same + /// scope with the same but with different type. + static OMPDeclareReductionDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName Name, + QualType T); + static OMPDeclareReductionDecl *CreateDeserialized(ASTContext &C, + unsigned ID); + + /// \brief Get combiner expression of the declare reduction construct. + Expr *getCombiner() { return cast(Combiner); } + Expr *getCombiner() const { return cast(Combiner); } + /// \brief Set combiner expression for the declare reduction construct. + void setCombiner(Expr *E) { Combiner = E; } + + /// \brief Get initializer expression (if specified) of the declare reduction + /// construct. + Expr *getInitializer() { return cast_or_null(Initializer); } + Expr *getInitializer() const { return cast_or_null(Initializer); } + /// \brief Set initializer expression for the declare reduction construct. + void setInitializer(Expr *E) { Initializer = E; } + + /// \brief Get reference to previous declare reduction construct in the same + /// scope with the same name. + OMPDeclareReductionDecl *getNextDeclInScope() { + return cast_or_null(NextDeclInScope); + } + OMPDeclareReductionDecl *getNextDeclInScope() const { + return cast_or_null(NextDeclInScope); + } + /// \brief Set reference to previous declare reduction construct in the same + /// scope with the same name. + void setNextDeclInScope(OMPDeclareReductionDecl *D) { NextDeclInScope = D; } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == OMPDeclareReduction; } + static DeclContext *castToDeclContext(const OMPDeclareReductionDecl *D) { + return static_cast(const_cast(D)); + } + static OMPDeclareReductionDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast( + const_cast(DC)); + } +}; + } // end namespace clang #endif Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1463,6 +1463,14 @@ } }) +DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, { + TRY_TO(TraverseDeclaratorHelper(D)); + TRY_TO(TraverseStmt(D->getCombiner())); + if (auto *Initializer = D->getInitializer()) + TRY_TO(TraverseStmt(Initializer)); + return true; +}) + // A helper method for TemplateDecl's children. template bool RecursiveASTVisitor::TraverseTemplateParameterListHelper( Index: include/clang/Basic/DeclNodes.td =================================================================== --- include/clang/Basic/DeclNodes.td +++ include/clang/Basic/DeclNodes.td @@ -52,6 +52,7 @@ def ImplicitParam : DDecl; def ParmVar : DDecl; def NonTypeTemplateParm : DDecl; + def OMPDeclareReduction : DDecl, DeclContext; def Template : DDecl; def RedeclarableTemplate : DDecl; def FunctionTemplate : DDecl; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -993,6 +993,8 @@ "'#pragma omp %0' cannot be an immediate substatement">; def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; +def err_omp_expected_reduction_identifier : Error< + "expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'">; // Pragma loop support. def err_pragma_loop_missing_argument : Error< Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7667,6 +7667,10 @@ "parent region for 'omp %select{cancellation point/cancel}0' construct cannot be nowait">; def err_omp_parent_cancel_region_ordered : Error< "parent region for 'omp %select{cancellation point/cancel}0' construct cannot be ordered">; +def err_omp_reduction_wrong_type : Error<"reduction type cannot be %select{qualified with 'const', 'volatile' or 'restrict'|a function|a reference|an array}0 type">; +def err_omp_reduction_redeclared : Error<"previous declaration with type %0 is found">; +def err_omp_wrong_var_in_declare_reduction : Error<"only %select{'omp_priv' or 'omp_orig'|'omp_in' or 'omp_out'}0 variables are allowed in %select{initializer|combiner}0 expression">; +def err_omp_declare_reduction_redefinition : Error<"redefinition of user-defined reduction for type %0">; def err_omp_wrong_linear_modifier : Error< "expected %select{'val' modifier|one of 'ref', val' or 'uval' modifiers}0">; def err_omp_wrong_linear_modifier_non_reference : Error< Index: include/clang/Basic/OpenMPKinds.def =================================================================== --- include/clang/Basic/OpenMPKinds.def +++ include/clang/Basic/OpenMPKinds.def @@ -106,6 +106,7 @@ OPENMP_DIRECTIVE_EXT(parallel_sections, "parallel sections") OPENMP_DIRECTIVE_EXT(for_simd, "for simd") OPENMP_DIRECTIVE_EXT(cancellation_point, "cancellation point") +OPENMP_DIRECTIVE_EXT(declare_reduction, "declare reduction") // OpenMP clauses. OPENMP_CLAUSE(if, OMPIfClause) Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2424,7 +2424,9 @@ //===--------------------------------------------------------------------===// // OpenMP: Directives and clauses. /// \brief Parses declarative OpenMP directives. - DeclGroupPtrTy ParseOpenMPDeclarativeDirective(); + DeclGroupPtrTy ParseOpenMPDeclarativeDirective(AccessSpecifier AS); + /// \brief Parse 'omp declare reduction' construct. + DeclGroupPtrTy ParseOpenMPDeclareReductionDirective(AccessSpecifier AS); /// \brief Parses simple list of variables. /// /// \param Kind Kind of the directive. Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -104,6 +104,9 @@ /// \brief Whether a statement was dropped because it was invalid. bool HasDroppedStmt; + /// \brief True if current scope is for OpenMP declare reduction combiner. + bool HasOMPDeclareReductionCombiner; + /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). @@ -330,6 +333,10 @@ HasDroppedStmt = true; } + void setHasOMPDeclareReductionCombiner() { + HasOMPDeclareReductionCombiner = true; + } + void setHasCXXTry(SourceLocation TryLoc) { setHasBranchProtectedScope(); FirstCXXTryLoc = TryLoc; @@ -352,6 +359,7 @@ HasBranchIntoScope(false), HasIndirectGoto(false), HasDroppedStmt(false), + HasOMPDeclareReductionCombiner(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -144,6 +144,7 @@ class ObjCPropertyDecl; class ObjCProtocolDecl; class OMPThreadPrivateDecl; + class OMPDeclareReductionDecl; class OMPClause; class OverloadCandidateSet; class OverloadExpr; @@ -2627,6 +2628,8 @@ LookupObjCProtocolName, /// Look up implicit 'self' parameter of an objective-c method. LookupObjCImplicitSelfParam, + /// \brief Look up the name of an OpenMP user-defined reduction operation. + LookupOMPReductionName, /// \brief Look up any declaration with any name. LookupAnyName }; @@ -7703,6 +7706,12 @@ /// is performed. bool isOpenMPPrivateVar(VarDecl *VD, unsigned Level); + /// \brief Check if the specified type is allowed to be used in 'omp declare + /// reduction' construct. + bool isOpenMPDeclareReductionTypeAllowed( + SourceLocation TyLoc, QualType ReductionType, + ArrayRef> RegisteredReductionTypes); + ExprResult PerformOpenMPImplicitIntegerConversion(SourceLocation OpLoc, Expr *Op); /// \brief Called on start of new data sharing attribute block. @@ -7736,6 +7745,23 @@ OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl( SourceLocation Loc, ArrayRef VarList); + /// \brief Called on start of '#pragma omp declare reduction'. + DeclGroupPtrTy ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, DeclarationName Name, + ArrayRef> ReductionTypes, + AccessSpecifier AS, Decl *NextDeclInScope = nullptr); + /// \brief Initialize declare reduction construct initializer. + void ActOnOpenMPDeclareReductionCombinerStart(Scope *S, Decl *D); + /// \brief Finish current declare reduction construct initializer. + void ActOnOpenMPDeclareReductionCombinerEnd(Decl *D, Expr *Combiner); + /// \brief Initialize declare reduction construct initializer. + void ActOnOpenMPDeclareReductionInitializerStart(Scope *S, Decl *D); + /// \brief Finish current declare reduction construct initializer. + void ActOnOpenMPDeclareReductionInitializerEnd(Decl *D, Expr *Initializer); + /// \brief Called on well-formed '#pragma omp declare reduction'. + DeclGroupPtrTy + ActOnOpenMPDeclareReductionDirectiveEnd(Scope *S, + DeclGroupPtrTy DeclReductions); /// \brief Initialization of captured region for OpenMP region. void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1105,6 +1105,8 @@ DECL_EMPTY, /// \brief An ObjCTypeParamDecl record. DECL_OBJC_TYPE_PARAM, + /// \brief An OMPDeclareReductionDecl record. + DECL_OMP_DECLARE_REDUCTION, }; /// \brief Record codes for each kind of statement or expression. Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8318,7 +8318,7 @@ // We never need to emit an uninstantiated function template. if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) return false; - } else if (isa(D)) + } else if (isa(D) || isa(D)) return true; else return false; Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -18,6 +18,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -1459,6 +1460,10 @@ if (OldK == NewK) return true; + // Declare reduction are always replaceable. + if (OMPDeclareReductionDecl::classofKind(NewK)) + return false; + // A compatibility alias for a class can be replaced by an interface. if (ObjCCompatibleAliasDecl::classofKind(OldK) && ObjCInterfaceDecl::classofKind(NewK)) Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -613,6 +613,9 @@ case TemplateTemplateParm: return IDNS_Ordinary | IDNS_Tag | IDNS_Type; + case OMPDeclareReduction: + return IDNS_OMPReduction; + // Never have names. case Friend: case FriendTemplate: @@ -939,6 +942,7 @@ case Decl::LinkageSpec: case Decl::Block: case Decl::Captured: + case Decl::OMPDeclareReduction: // There is only one DeclContext for these entities. return this; Index: lib/AST/DeclOpenMP.cpp =================================================================== --- lib/AST/DeclOpenMP.cpp +++ lib/AST/DeclOpenMP.cpp @@ -52,3 +52,27 @@ std::copy(VL.begin(), VL.end(), Vars); } +//===----------------------------------------------------------------------===// +// OMPDeclareReductionDecl Implementation. +//===----------------------------------------------------------------------===// + +void OMPDeclareReductionDecl::anchor() {} + +OMPDeclareReductionDecl *OMPDeclareReductionDecl::Create(ASTContext &C, + DeclContext *DC, + SourceLocation L, + DeclarationName Name, + QualType T) { + OMPDeclareReductionDecl *D = + new (C, DC) OMPDeclareReductionDecl(OMPDeclareReduction, DC, L, Name, T); + return D; +} + +OMPDeclareReductionDecl * +OMPDeclareReductionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + OMPDeclareReductionDecl *D = new (C, ID) + OMPDeclareReductionDecl(OMPDeclareReduction, /*DC=*/nullptr, + SourceLocation(), DeclarationName(), QualType()); + return D; +} + Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -92,6 +92,7 @@ void VisitUsingDecl(UsingDecl *D); void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); void PrintTemplateParameters(const TemplateParameterList *Params, const TemplateArgumentList *Args = nullptr); @@ -304,7 +305,7 @@ // FIXME: Need to be able to tell the DeclPrinter when const char *Terminator = nullptr; - if (isa(*D)) + if (isa(*D) || isa(*D)) Terminator = nullptr; else if (isa(*D) && cast(*D)->isThisDeclarationADefinition()) @@ -1320,3 +1321,20 @@ } } +void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + if (!D->isInvalidDecl()) { + Out << "#pragma omp declare reduction ("; + D->printName(Out); + Out << " : "; + D->getType().print(Out, Policy); + Out << " : "; + D->getCombiner()->printPretty(Out, 0, Policy, 0); + Out << ")"; + if (auto *Init = D->getInitializer()) { + Out << " initializer("; + Init->printPretty(Out, 0, Policy, 0); + Out << ")"; + } + } +} + Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -68,6 +69,8 @@ const DeclContext *DC = D->getDeclContext(); if (const CapturedDecl *CD = dyn_cast(DC)) return getEffectiveDeclContext(CD); + if (auto *DR = dyn_cast(DC)) + return getEffectiveDeclContext(DR); if (const auto *VD = dyn_cast(D)) if (VD->isExternC()) Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -19,6 +19,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -59,6 +60,8 @@ const DeclContext *DC = D->getDeclContext(); if (const CapturedDecl *CD = dyn_cast(DC)) return getEffectiveDeclContext(CD); + if (auto *DR = dyn_cast(DC)) + return getEffectiveDeclContext(DR); return DC; } Index: lib/Basic/OpenMPKinds.cpp =================================================================== --- lib/Basic/OpenMPKinds.cpp +++ lib/Basic/OpenMPKinds.cpp @@ -375,6 +375,7 @@ case OMPD_cancellation_point: case OMPD_cancel: case OMPD_ordered: + case OMPD_declare_reduction: break; } return false; Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -20,6 +20,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -113,6 +114,9 @@ return EmitVarDecl(VD); } + case Decl::OMPDeclareReduction: + return CGM.EmitOMPDeclareReduction(cast(&D)); + case Decl::Typedef: // typedef int X; case Decl::TypeAlias: { // using X = int; [C++0x] const TypedefNameDecl &TD = cast(D); @@ -1803,3 +1807,9 @@ if (D.hasAttr()) EmitVarAnnotations(&D, DeclPtr); } + +void CodeGenModule::EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D) { + llvm_unreachable("Codegen for 'omp declare reduction' is not supported yet."); +} + + Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -20,6 +20,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" #include "clang/Basic/ABI.h" @@ -1126,6 +1127,10 @@ /// \param D Threadprivate declaration. void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); + /// \brief Emit a code for declare reduction construct. + /// + void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D); + /// Returns whether the given record is blacklisted from control flow /// integrity checks. bool IsCFIBlacklistedRecord(const CXXRecordDecl *RD); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -3385,6 +3385,10 @@ break; } + case Decl::OMPDeclareReduction: + EmitOMPDeclareReduction(cast(D)); + break; + default: // Make sure we handled everything we should, every other kind is a // non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -3616,13 +3616,11 @@ } if (Tok.is(tok::annot_pragma_openmp)) { - // Result can be ignored, because it must be always empty. - auto Res = ParseOpenMPDeclarativeDirective(); - assert(!Res); - // Silence possible warnings. - (void)Res; + // There may be declared reduction operator inside structure/union. + (void)ParseOpenMPDeclarativeDirective(AS_public); continue; } + if (!Tok.is(tok::at)) { auto CFieldCallback = [&](ParsingFieldDeclarator &FD) { // Install the declarator into the current TagDecl. Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2896,7 +2896,7 @@ } if (Tok.is(tok::annot_pragma_openmp)) - return ParseOpenMPDeclarativeDirective(); + return ParseOpenMPDeclarativeDirective(AS); // Parse all the comma separated declarators. return ParseCXXClassMemberDeclaration(AS, AccessAttrs.getList()); Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -33,6 +33,8 @@ const OpenMPDirectiveKind F[][3] = { {OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/, OMPD_cancellation_point}, + {OMPD_unknown /*declare*/, OMPD_unknown /*reduction*/, + OMPD_declare_reduction}, {OMPD_target, OMPD_unknown /*data*/, OMPD_target_data}, {OMPD_for, OMPD_simd, OMPD_for_simd}, {OMPD_parallel, OMPD_for, OMPD_parallel_for}, @@ -46,13 +48,14 @@ bool TokenMatched = false; for (unsigned i = 0; i < llvm::array_lengthof(F); ++i) { - if (!Tok.isAnnotation() && DKind == OMPD_unknown) { + if (!Tok.isAnnotation() && DKind == OMPD_unknown) TokenMatched = - (i == 0) && - !P.getPreprocessor().getSpelling(Tok).compare("cancellation"); - } else { + ((i == 0) && + !P.getPreprocessor().getSpelling(Tok).compare("cancellation")) || + ((i == 1) && + !P.getPreprocessor().getSpelling(Tok).compare("declare")); + else TokenMatched = DKind == F[i][0] && DKind != OMPD_unknown; - } if (TokenMatched) { Tok = P.getPreprocessor().LookAhead(0); @@ -61,15 +64,15 @@ TokenIsAnnotation ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); - - if (!TokenIsAnnotation && SDKind == OMPD_unknown) { + if (!Tok.isAnnotation() && SDKind == OMPD_unknown) TokenMatched = ((i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point")) || - ((i == 1) && !P.getPreprocessor().getSpelling(Tok).compare("data")); - } else { + ((i == 1) && + !P.getPreprocessor().getSpelling(Tok).compare("reduction")) || + ((i == 2) && !P.getPreprocessor().getSpelling(Tok).compare("data")); + else TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown; - } if (TokenMatched) { P.ConsumeToken(); @@ -80,12 +83,261 @@ return DKind; } +static DeclarationName parseOpenMPReductionId(Parser &P) { + DeclarationName Name; + const Token &Tok = P.getCurToken(); + Sema &Actions = P.getActions(); + switch (Tok.getKind()) { + case tok::plus: // '+' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("+")); + P.ConsumeToken(); + break; + case tok::minus: // '-' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("-")); + P.ConsumeToken(); + break; + case tok::star: // '*' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("*")); + P.ConsumeToken(); + break; + case tok::amp: // '&' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("&")); + P.ConsumeToken(); + break; + case tok::pipe: // '|' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("|")); + P.ConsumeToken(); + break; + case tok::caret: // '^' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("^")); + P.ConsumeToken(); + break; + case tok::ampamp: // '&&' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("&&")); + P.ConsumeToken(); + break; + case tok::pipepipe: // '||' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("||")); + P.ConsumeToken(); + break; + case tok::identifier: // identifier + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + Tok.getIdentifierInfo()); + P.ConsumeToken(); + break; + default: + P.Diag(Tok.getLocation(), diag::err_omp_expected_reduction_identifier); + P.SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, + Parser::StopBeforeMatch); + break; + } + return Name; +} + +/// \brief Parse 'omp declare reduction' construct. +/// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' +/// '(' ':' {',' } ':' ')' +/// ['initializer' '(' ('omp_priv' '=' )| ')'] +/// annot_pragma_openmp_end +/// is either a base language identifier or one of the following +/// operators: '+', '-', '*', '&', '|', '^', '&&' and '||'. +/// +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) { + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPDirectiveName(OMPD_declare_reduction))) { + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + return DeclGroupPtrTy(); + } + + DeclarationName Name = parseOpenMPReductionId(*this); + if (Name.isEmpty() && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + bool IsCorrect = true; + + // Consume ':'. + if (Tok.is(tok::colon)) { + ConsumeAnyToken(); + } else { + Diag(Tok.getLocation(), diag::err_expected) << "':'"; + IsCorrect = false; + } + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + if (Tok.is(tok::colon) || Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected_type); + IsCorrect = false; + } + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + SmallVector, 8> ReductionTypes; + SmallVector, 8> CombinersInitializers; + bool IsCommaFound = false; + bool FunctionsCorrect = true; + // Parse list of types until ':' token. + while (Tok.isNot(tok::colon) && Tok.isNot(tok::annot_pragma_openmp_end)) { + ColonProtectionRAIIObject ColonRAII(*this); + IsCommaFound = false; + SourceRange Range; + TypeResult TR = ParseTypeName(&Range, Declarator::PrototypeContext, AS); + if (TR.isUsable()) { + QualType ReductionType = Sema::GetTypeFromParser(TR.get()); + if (!ReductionType.isNull() && + Actions.isOpenMPDeclareReductionTypeAllowed( + Range.getBegin(), ReductionType, ReductionTypes)) + ReductionTypes.push_back( + std::make_pair(ReductionType, Range.getBegin())); + else + FunctionsCorrect = false; + } else { + SkipUntil(tok::comma, tok::colon, tok::annot_pragma_openmp_end, + StopBeforeMatch); + FunctionsCorrect = false; + } + + // Consume ','. + if (Tok.is(tok::comma)) { + ConsumeAnyToken(); + IsCommaFound = true; + } else if (Tok.isNot(tok::colon) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected) << "','"; + IsCorrect = false; + } + } + + if (IsCommaFound) { + Diag(Tok.getLocation(), diag::err_expected_type); + IsCorrect = false; + if (Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + } + + if (ReductionTypes.empty()) { + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + return DeclGroupPtrTy(); + } + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + // Consume ':'. + if (Tok.is(tok::colon)) + ConsumeAnyToken(); + else { + Diag(Tok.getLocation(), diag::err_expected) << "':'"; + IsCorrect = false; + } + + if (Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected_expression); + return DeclGroupPtrTy(); + } + + DeclGroupPtrTy DRD = Actions.ActOnOpenMPDeclareReductionDirectiveStart( + getCurScope(), Actions.getCurLexicalContext(), Name, ReductionTypes, AS); + + // Parse expression and then parse initializer if any for each + // correct type. + unsigned i = 0, e = ReductionTypes.size(); + for (auto *D : DRD.get()) { + TentativeParsingAction TPA(*this); + ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope | + Scope::OpenMPDirectiveScope); + // Parse expression. + Actions.ActOnOpenMPDeclareReductionCombinerStart(getCurScope(), D); + ExprResult CombinerResult = + Actions.ActOnFinishFullExpr(ParseAssignmentExpression().get(), + D->getLocation(), /*DiscardedValue=*/true); + Actions.ActOnOpenMPDeclareReductionCombinerEnd(D, CombinerResult.get()); + + if (CombinerResult.isInvalid() && Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + TPA.Commit(); + IsCorrect = false; + break; + } + IsCorrect = !T.consumeClose() && IsCorrect && !CombinerResult.isInvalid(); + ExprResult InitializerResult; + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + // Parse expression. + if (Tok.isAnyIdentifier() && + Tok.getIdentifierInfo()->isStr("initializer")) { + ConsumeToken(); + } else { + Diag(Tok.getLocation(), diag::err_expected) << "'initializer'"; + TPA.Commit(); + IsCorrect = false; + break; + } + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + IsCorrect = + !T.expectAndConsume(diag::err_expected_lparen_after, "initializer") && + IsCorrect; + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope | + Scope::OpenMPDirectiveScope); + // Parse expression. + Actions.ActOnOpenMPDeclareReductionInitializerStart(getCurScope(), D); + InitializerResult = Actions.ActOnFinishFullExpr( + ParseAssignmentExpression().get(), D->getLocation(), + /*DiscardedValue=*/true); + Actions.ActOnOpenMPDeclareReductionInitializerEnd( + D, InitializerResult.get()); + if (InitializerResult.isInvalid() && Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + TPA.Commit(); + IsCorrect = false; + break; + } + IsCorrect = + !T.consumeClose() && IsCorrect && !InitializerResult.isInvalid(); + } + } + + CombinersInitializers.push_back( + std::make_pair(CombinerResult.get(), InitializerResult.get())); + ++i; + // Revert parsing if not the last type, otherwise accept it, we're done with + // parsing. + if (i != e) + TPA.Revert(); + else + TPA.Commit(); + } + return Actions.ActOnOpenMPDeclareReductionDirectiveEnd(getCurScope(), DRD); +} + /// \brief Parsing of declarative OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list +/// annot_pragma_openmp_end +/// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' [...] +/// annot_pragma_openmp_end /// -Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclarativeDirective(AccessSpecifier AS) { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -109,6 +361,21 @@ return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); } break; + case OMPD_declare_reduction: + ConsumeToken(); + if (auto Res = ParseOpenMPDeclareReductionDirective(AS)) { + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_reduction); + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + } + // Skip the last annot_pragma_openmp_end. + ConsumeToken(); + return Res; + } + break; case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -151,6 +418,12 @@ /// annot_pragma_openmp 'threadprivate' simple-variable-list /// annot_pragma_openmp_end /// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' '(' ':' +/// {',' } ':' ')' ['initializer' '(' +/// ('omp_priv' '=' |) ')'] +/// annot_pragma_openmp_end +/// /// executable-directive: /// annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' | /// 'section' | 'single' | 'master' | 'critical' [ '(' ')' ] | @@ -196,6 +469,20 @@ } SkipUntil(tok::annot_pragma_openmp_end); break; + case OMPD_declare_reduction: + ConsumeToken(); + if (auto Res = ParseOpenMPDeclareReductionDirective(/*AS=*/AS_none)) { + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_reduction); + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + } + Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); + } + SkipUntil(tok::annot_pragma_openmp_end); + break; case OMPD_flush: if (PP.LookAhead(0).is(tok::l_paren)) { FlushHasClause = true; Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -657,7 +657,7 @@ HandlePragmaOpenCLExtension(); return DeclGroupPtrTy(); case tok::annot_pragma_openmp: - return ParseOpenMPDeclarativeDirective(); + return ParseOpenMPDeclarativeDirective(/*AS=*/AS_none); case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); return DeclGroupPtrTy(); Index: lib/Sema/ScopeInfo.cpp =================================================================== --- lib/Sema/ScopeInfo.cpp +++ lib/Sema/ScopeInfo.cpp @@ -28,6 +28,7 @@ HasBranchIntoScope = false; HasIndirectGoto = false; HasDroppedStmt = false; + HasOMPDeclareReductionCombiner = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5516,7 +5516,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { const DeclContext *DC = VD->getDeclContext()->getRedeclContext(); - if (DC->isFunctionOrMethod()) + if (DC->isFunctionOrMethod() || isa(DC)) return VD->hasExternalStorage(); if (DC->isFileContext()) return true; @@ -5527,7 +5527,8 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) { const DeclContext *DC = FD->getDeclContext()->getRedeclContext(); - if (DC->isFileContext() || DC->isFunctionOrMethod()) + if (DC->isFileContext() || DC->isFunctionOrMethod() || + isa(DC)) return true; if (DC->isRecord()) return false; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -367,6 +367,19 @@ DeduceReturnType(FD, Loc)) return true; } + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // Only the variables omp_in and omp_out are allowed in the combiner. + // Only the variables omp_priv and omp_orig are allowed in the + // initializer-clause. + auto *DRD = dyn_cast(CurContext); + if (LangOpts.OpenMP && DRD && !CurContext->containsDecl(D) && + isa(D)) { + Diag(Loc, diag::err_omp_wrong_var_in_declare_reduction) + << getCurFunction()->HasOMPDeclareReductionCombiner; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return true; + } DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, ObjCPropertyAccess); @@ -2860,6 +2873,9 @@ case Decl::ObjCIvar: llvm_unreachable("forming non-member reference to ivar?"); + case Decl::OMPDeclareReduction: + llvm_unreachable("forming a reference to OpenMP declare reduction"); + // Enum constants are always r-values and never references. // Unresolved using declarations are dependent. case Decl::EnumConstant: Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -279,6 +279,10 @@ IDNS = Decl::IDNS_ObjCProtocol; break; + case Sema::LookupOMPReductionName: + IDNS = Decl::IDNS_OMPReduction; + break; + case Sema::LookupAnyName: IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Using | Decl::IDNS_Namespace | Decl::IDNS_ObjCProtocol @@ -1870,6 +1874,7 @@ case LookupNamespaceName: case LookupObjCProtocolName: case LookupLabel: + case LookupOMPReductionName: // These lookups will never find a member in a C++ class (or base class). return false; Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -1345,6 +1345,7 @@ case OMPD_cancellation_point: case OMPD_cancel: case OMPD_flush: + case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -2144,6 +2145,7 @@ EndLoc); break; case OMPD_threadprivate: + case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -6886,6 +6888,218 @@ DepLoc, ColonLoc, Vars); } +bool Sema::isOpenMPDeclareReductionTypeAllowed( + SourceLocation TyLoc, QualType ReductionType, + ArrayRef> RegisteredReductionTypes) { + assert(!ReductionType.isNull()); + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions, C\C++ + // A type name in a declare reduction directive cannot be a function type, an + // array type, a reference type, or a type qualified with const, volatile or + // restrict. + if (ReductionType.hasQualifiers()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 0; + return false; + } + + if (ReductionType->isFunctionType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 1; + return false; + } + if (ReductionType->isReferenceType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 2; + return false; + } + if (ReductionType->isArrayType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 3; + return false; + } + + bool IsValid = true; + for (auto &&Data : RegisteredReductionTypes) { + if (Context.hasSameType(ReductionType, Data.first)) { + Diag(TyLoc, diag::err_omp_reduction_redeclared) << ReductionType; + Diag(Data.second, diag::note_previous_declaration) << Data.second; + IsValid = false; + break; + } + } + return IsValid; +} + +Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, DeclarationName Name, + ArrayRef> ReductionTypes, + AccessSpecifier AS, Decl *NextDeclInScope) { + SmallVector Decls; + Decls.reserve(ReductionTypes.size()); + + LookupResult Lookup(*this, Name, SourceLocation(), LookupOMPReductionName); + Lookup.suppressDiagnostics(); + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // A reduction-identifier may not be re-declared in the current scope for the + // same type or for a type that is compatible according to the base language + // rules. + if (S) { + LookupName(Lookup, S); + FilterLookupForScope(Lookup, DC, S, /*ConsiderLinkage=*/false, + /*AllowInlineNamespace=*/false); + } else + while (NextDeclInScope) { + auto *NextDRD = cast(NextDeclInScope); + Lookup.addDecl(NextDRD); + NextDeclInScope = NextDRD->getNextDeclInScope(); + } + OMPDeclareReductionDecl *PrevDeclInScope = nullptr; + for (auto &TyData : ReductionTypes) { + auto *DRD = OMPDeclareReductionDecl::Create(Context, DC, TyData.second, + Name, TyData.first); + DC->addDecl(DRD); + DRD->setAccess(AS); + Decls.push_back(DRD); + auto Filter = Lookup.makeFilter(); + while (Filter.hasNext()) { + auto *PrevDecl = cast(Filter.next()); + if (!PrevDeclInScope && !PrevDecl->getNextDeclInScope()) + PrevDeclInScope = PrevDecl; + if (PrevDecl->isInvalidDecl()) { + Filter.erase(); + continue; + } + if (Context.typesAreCompatible(TyData.first, PrevDecl->getType(), + /*CompareUnqualified=*/true)) { + Diag(S ? TyData.second : PrevDecl->getLocation(), + diag::err_omp_declare_reduction_redefinition) + << (S ? TyData.first : PrevDecl->getType()); + Diag(S ? PrevDecl->getLocation() : TyData.second, + diag::note_previous_definition); + DRD->setInvalidDecl(); + } + } + Filter.done(); + if (PrevDeclInScope) + PrevDeclInScope->setNextDeclInScope(DRD); + PrevDeclInScope = DRD; + } + + return DeclGroupPtrTy::make( + DeclGroupRef::Create(Context, Decls.begin(), Decls.size())); +} + +void Sema::ActOnOpenMPDeclareReductionCombinerStart(Scope *S, Decl *D) { + auto *DRD = cast(D); + + // Enter new function scope. + PushFunctionScope(); + getCurFunction()->setHasBranchProtectedScope(); + getCurFunction()->setHasOMPDeclareReductionCombiner(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + QualType ReductionType = DRD->getType(); + // Create 'T omp_in;' implicit param. + auto *OmpInParm = + ImplicitParamDecl::Create(Context, DRD, D->getLocation(), + &Context.Idents.get("omp_in"), ReductionType); + // Create 'T &omp_out;' implicit param. + auto *OmpOutParm = ImplicitParamDecl::Create( + Context, DRD, D->getLocation(), &Context.Idents.get("omp_out"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpInParm, S); + PushOnScopeChains(OmpOutParm, S); + } else { + DRD->addDecl(OmpInParm); + DRD->addDecl(OmpOutParm); + } +} + +void Sema::ActOnOpenMPDeclareReductionCombinerEnd(Decl *D, Expr *Combiner) { + auto *DRD = cast(D); + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + if (Combiner) + DRD->setCombiner(Combiner); + else + DRD->setInvalidDecl(); +} + +void Sema::ActOnOpenMPDeclareReductionInitializerStart(Scope *S, Decl *D) { + auto *DRD = cast(D); + + // Enter new function scope. + PushFunctionScope(); + getCurFunction()->setHasBranchProtectedScope(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + QualType ReductionType = DRD->getType(); + // Create 'T omp_orig;' implicit param. + auto *OmpOrigParm = + ImplicitParamDecl::Create(Context, DRD, D->getLocation(), + &Context.Idents.get("omp_orig"), ReductionType); + // Create 'T &omp_priv;' implicit param. + auto *OmpPrivParm = ImplicitParamDecl::Create( + Context, DRD, D->getLocation(), &Context.Idents.get("omp_priv"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpPrivParm, S); + PushOnScopeChains(OmpOrigParm, S); + } else { + DRD->addDecl(OmpPrivParm); + DRD->addDecl(OmpOrigParm); + } +} + +void Sema::ActOnOpenMPDeclareReductionInitializerEnd(Decl *D, + Expr *Initializer) { + auto *DRD = cast(D); + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + if (Initializer) + DRD->setInitializer(Initializer); + else + DRD->setInvalidDecl(); + + if (Initializer) + DRD->setInitializer(Initializer); + else + DRD->setInvalidDecl(); +} + +Sema::DeclGroupPtrTy +Sema::ActOnOpenMPDeclareReductionDirectiveEnd(Scope *S, + DeclGroupPtrTy DeclReductions) { + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // A reduction-identifier may not be re-declared in the current scope for the + // same type or for a type that is compatible according to the base language + // rules. + for (auto *D : DeclReductions.get()) { + auto *DRD = cast(D); + if (S) + PushOnScopeChains(DRD, S, /*AddToContext=*/false); + } + return DeclReductions; +} + OMPClause *Sema::ActOnOpenMPDeviceClause(Expr *Device, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc) { Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -2784,6 +2784,11 @@ if (isa(D)) return nullptr; + // Local declare reduction constructs referenced prior to definition may + // require instantiation. + if (isa(D)) + return nullptr; + // If we didn't find the decl, then we either have a sema bug, or we have a // forward reference to a label declaration. Return null to indicate that // we have an uninstantiated label. Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2453,6 +2453,70 @@ return TD; } +Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl( + OMPDeclareReductionDecl *D) { + if (auto PrevInst = SemaRef.CurrentInstantiationScope->findInstantiationOf(D)) + return PrevInst->get(); + // Instantiate type and check if it is allowed. + QualType SubstReductionType = SemaRef.SubstType( + D->getType(), TemplateArgs, D->getLocation(), DeclarationName()); + bool IsCorrect = !SubstReductionType.isNull() && + SemaRef.isOpenMPDeclareReductionTypeAllowed( + D->getLocation(), SubstReductionType, llvm::None); + // Create instantiated copy. + std::pair ReductionTypes[] = { + std::make_pair(SubstReductionType, D->getLocation())}; + Decl *NextDeclInScope = D->getNextDeclInScope(); + if (NextDeclInScope) { + while (NextDeclInScope && NextDeclInScope->isInvalidDecl()) + NextDeclInScope = + cast(NextDeclInScope)->getNextDeclInScope(); + if (NextDeclInScope) + NextDeclInScope = SemaRef.SubstDecl(NextDeclInScope, Owner, TemplateArgs); + } + auto DRD = SemaRef.ActOnOpenMPDeclareReductionDirectiveStart( + /*S=*/nullptr, Owner, D->getDeclName(), ReductionTypes, D->getAccess(), + NextDeclInScope); + auto *NewDRD = cast(DRD.get().getSingleDecl()); + if (isDeclWithinFunction(NewDRD)) + SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewDRD); + Expr *SubstCombiner = nullptr; + Expr *SubstInitializer = nullptr; + // Combiners instantiation sequence. + if (D->getCombiner()) { + SemaRef.ActOnOpenMPDeclareReductionCombinerStart( + /*S=*/nullptr, NewDRD); + for (auto *Local : D->decls()) { + auto Lookup = NewDRD->lookup(cast(Local)->getDeclName()); + if (!Lookup.empty()) { + assert(Lookup.size() == 1); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(Local, + Lookup.front()); + } + } + SubstCombiner = SemaRef.SubstExpr(D->getCombiner(), TemplateArgs).get(); + SemaRef.ActOnOpenMPDeclareReductionCombinerEnd(NewDRD, SubstCombiner); + // Initializers instantiation sequence. + if (D->getInitializer()) { + SemaRef.ActOnOpenMPDeclareReductionInitializerStart( + /*S=*/nullptr, NewDRD); + SubstInitializer = + SemaRef.SubstExpr(D->getInitializer(), TemplateArgs).get(); + IsCorrect = IsCorrect && SubstCombiner && + (!D->getInitializer() || SubstInitializer); + SemaRef.ActOnOpenMPDeclareReductionInitializerEnd(NewDRD, + SubstInitializer); + } + } else + IsCorrect = false; + + if (!IsCorrect) + NewDRD->setInvalidDecl(); + (void)SemaRef.ActOnOpenMPDeclareReductionDirectiveEnd(/*S=*/nullptr, DRD); + + return NewDRD; +} + Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) { return VisitFunctionDecl(D, nullptr); } Index: lib/Serialization/ASTCommon.cpp =================================================================== --- lib/Serialization/ASTCommon.cpp +++ lib/Serialization/ASTCommon.cpp @@ -215,6 +215,7 @@ case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: return false; // These indirectly derive from Redeclarable but are not actually Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -381,6 +381,7 @@ void VisitObjCPropertyDecl(ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); /// We've merged the definition \p MergedDef into the existing definition /// \p Def. Ensure that \p Def is made visible whenever \p MergedDef is made @@ -2371,6 +2372,14 @@ D->setVars(Vars); } +void ASTDeclReader::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + VisitDeclaratorDecl(D); + D->setLocation(Reader.ReadSourceLocation(F, Record, Idx)); + D->setCombiner(Reader.ReadExpr(F)); + D->setInitializer(Reader.ReadExpr(F)); + D->setNextDeclInScope(ReadDeclAs(Record, Idx)); +} + //===----------------------------------------------------------------------===// // Attribute Reading //===----------------------------------------------------------------------===// @@ -2420,7 +2429,8 @@ isa(D) || isa(D) || isa(D) || - isa(D)) + isa(D) || + isa(D)) return true; if (VarDecl *Var = dyn_cast(D)) return Var->isFileVarDecl() && @@ -3314,6 +3324,9 @@ case DECL_OMP_THREADPRIVATE: D = OMPThreadPrivateDecl::CreateDeserialized(Context, ID, Record[Idx++]); break; + case DECL_OMP_DECLARE_REDUCTION: + D = OMPDeclareReductionDecl::CreateDeserialized(Context, ID); + break; case DECL_EMPTY: D = EmptyDecl::CreateDeserialized(Context, ID); break; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -131,6 +131,7 @@ void VisitObjCPropertyDecl(ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); /// Add an Objective-C type parameter list to the given record. void AddObjCTypeParamList(ObjCTypeParamList *typeParams) { @@ -1544,6 +1545,15 @@ Code = serialization::DECL_OMP_THREADPRIVATE; } +void ASTDeclWriter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + VisitDeclaratorDecl(D); + Writer.AddSourceLocation(D->getLocStart(), Record); + Writer.AddStmt(D->getCombiner()); + Writer.AddStmt(D->getInitializer()); + Writer.AddDeclRef(D->getNextDeclInScope(), Record); + Code = serialization::DECL_OMP_DECLARE_REDUCTION; +} + //===----------------------------------------------------------------------===// // ASTWriter Implementation //===----------------------------------------------------------------------===// Index: test/OpenMP/declare_reduction_ast_print.c =================================================================== --- test/OpenMP/declare_reduction_ast_print.c +++ test/OpenMP/declare_reduction_ast_print.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) +// CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) + +#pragma omp declare reduction(fun : float : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) +// CHECK: #pragma omp declare reduction (fun : float : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + +// CHECK: struct SSS { +struct SSS { + int field; +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) + // CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) + // CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) +}; +// CHECK: }; + +void init(struct SSS *priv, struct SSS orig); + +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) +// CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + +// CHECK: int main() { +int main() { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + } + return 0; +} +// CHECK: } + +#endif Index: test/OpenMP/declare_reduction_ast_print.cpp =================================================================== --- test/OpenMP/declare_reduction_ast_print.cpp +++ test/OpenMP/declare_reduction_ast_print.cpp @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) +// CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) + +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + +template +class SSS { +public: +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + // CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) +}; + +SSS d; + +void init(SSS &lhs, SSS rhs); + +#pragma omp declare reduction(fun : SSS < int > : omp_out = omp_in) initializer(init(omp_priv, omp_orig)) +// CHECK: #pragma omp declare reduction (fun : SSS : omp_out = omp_in) initializer(init(omp_priv, omp_orig)) + +// CHECK: template int foo(int a) { +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: { +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: } +// CHECK: return a; +// CHECK: } + +// CHECK: template T foo(T a) { +// CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: { +// CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: } +// CHECK: return a; +// CHECK: } +template +T foo(T a) { +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + { +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + } + return a; +} + +int main() { + int i = 0; + SSS sss; + // TODO: Add support for scoped reduction identifiers + // #pragma omp parallel reduction(SSS::fun : i) + // TODO-CHECK: #pragma omp parallel reduction(SSS::fun: i) + { + i += 1; + } + // #pragma omp parallel reduction(::fun:sss) + // TODO-CHECK: #pragma omp parallel reduction(::fun: sss) + { + } + return foo(15); +} + +#endif Index: test/OpenMP/declare_reduction_messages.c =================================================================== --- test/OpenMP/declare_reduction_messages.c +++ test/OpenMP/declare_reduction_messages.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 6 {{'temp' declared here}} + +#pragma omp declare reduction // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction { // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction( // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(# // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(/ // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(+ // expected-error {{expected ':'}} +#pragma omp declare reduction(for // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(if: // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} expected-error {{expected a type}} +#pragma omp declare reduction(oper: // expected-error {{expected a type}} +#pragma omp declare reduction(oper; // expected-error {{expected ':'}} expected-error {{expected a type}} +#pragma omp declare reduction(fun : int // expected-error {{expected ':'}} expected-error {{expected expression}} +#pragma omp declare reduction(+ : const int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(* : int; // expected-error {{expected ','}} expected-error {{expected a type}} +#pragma omp declare reduction(& : double char: // expected-error {{cannot combine with previous 'double' declaration specifier}} expected-error {{expected expression}} +#pragma omp declare reduction(^ : double, char, : // expected-error {{expected a type}} expected-error {{expected expression}} +#pragma omp declare reduction(&& : int, S: // expected-error {{unknown type name 'S'}} expected-error {{expected expression}} +#pragma omp declare reduction(|| : int, double : temp += omp_in) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(| : char, float : omp_out += temp) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long : omp_out += omp_in) { // expected-error {{expected 'initializer'}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun : unsigned : omp_out += temp)) // expected-error {{expected 'initializer'}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long(void) : omp_out += omp_in) // expected-error {{reduction type cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{reduction type cannot be an array type}} +#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{previous declaration with type 'long' is found}} expected-note {{previous declaration is here}} + +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) +#pragma omp declare reduction(fun1 : long : omp_out += omp_in) initializer // expected-error {{expected '(' after 'initializer'}} +#pragma omp declare reduction(fun2 : long : omp_out += omp_in) initializer { // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun3 : long : omp_out += omp_in) initializer[ // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun4 : long : omp_out += omp_in) initializer() // expected-error {{expected expression}} +#pragma omp declare reduction(fun5 : long : omp_out += omp_in) initializer(temp) // expected-error {{only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression}} +#pragma omp declare reduction(fun6 : long : omp_out += omp_in) initializer(omp_orig // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun7 : long : omp_out += omp_in) initializer(omp_priv 12) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv = 23) // expected-note {{previous definition is here}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv = 23)) // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{redefinition of user-defined reduction for type 'long'}} +#pragma omp declare reduction(fun9 : long : omp_out += omp_in) initializer(omp_priv = ) // expected-error {{expected expression}} + +int fun(int arg) { +#pragma omp declare reduction(red : int : omp_out++) + { +#pragma omp declare reduction(red : int : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : int : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + { +#pragma omp declare reduction(red : int : omp_out++) + } + } + return arg; +} Index: test/OpenMP/declare_reduction_messages.cpp =================================================================== --- test/OpenMP/declare_reduction_messages.cpp +++ test/OpenMP/declare_reduction_messages.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 7 {{'temp' declared here}} + +#pragma omp declare reduction // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction { // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction( // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(# // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(/ // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(+ // expected-error {{expected ':'}} +#pragma omp declare reduction(operator // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} +#pragma omp declare reduction(operator: // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}} expected-error {{expected a type}} +#pragma omp declare reduction(oper: // expected-error {{expected a type}} +#pragma omp declare reduction(oper; // expected-error {{expected ':'}} expected-error {{expected a type}} +#pragma omp declare reduction(fun : int // expected-error {{expected ':'}} expected-error {{expected expression}} +#pragma omp declare reduction(+ : const int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(* : int; // expected-error {{expected ','}} expected-error {{expected a type}} +#pragma omp declare reduction(& : double char: // expected-error {{cannot combine with previous 'double' declaration specifier}} expected-error {{expected expression}} +#pragma omp declare reduction(^ : double, char, : // expected-error {{expected a type}} expected-error {{expected expression}} +#pragma omp declare reduction(&& : int, S: // expected-error {{unknown type name 'S'}} expected-error {{expected expression}} +#pragma omp declare reduction(|| : int, double : temp += omp_in) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(| : char, float : omp_out += ::temp) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long : omp_out += omp_in) { // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{expected 'initializer'}} +#pragma omp declare reduction(fun : unsigned : omp_out += ::temp)) // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{expected 'initializer'}} expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long & : omp_out += omp_in) // expected-error {{reduction type cannot be a reference type}} +#pragma omp declare reduction(fun : long(void) : omp_out += omp_in) // expected-error {{reduction type cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{reduction type cannot be an array type}} +#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{previous declaration with type 'long' is found}} expected-note {{previous declaration is here}} + +template +class Class1 { +#pragma omp declare reduction(fun : T : temp) // expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun1 : T : omp_out++) // expected-note {{previous definition is here}} expected-error {{reduction type cannot be a reference type}} +#pragma omp declare reduction(fun1 : T : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'T'}} +#pragma omp declare reduction(fun2 : T, T : omp_out++) // expected-error {{reduction type cannot be a reference type}} expected-error {{previous declaration with type 'T' is found}} expected-note {{previous declaration is here}} +}; + +Class1 e; // expected-note {{in instantiation of template class 'Class1' requested here}} + +template +class Class2 { +#pragma omp declare reduction(fun : T : omp_out += omp_in) +}; + +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) // expected-note {{previous definition is here}} +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'long'}} +#pragma omp declare reduction(fun1 : long : omp_out += omp_in) initializer // expected-error {{expected '(' after 'initializer'}} +#pragma omp declare reduction(fun2 : long : omp_out += omp_in) initializer { // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun3 : long : omp_out += omp_in) initializer[ // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun4 : long : omp_out += omp_in) initializer() // expected-error {{expected expression}} +#pragma omp declare reduction(fun5 : long : omp_out += omp_in) initializer(temp) // expected-error {{only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression}} +#pragma omp declare reduction(fun6 : long : omp_out += omp_in) initializer(omp_orig // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun7 : long : omp_out += omp_in) initializer(omp_priv Class1 < int > ()) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun77 : long : omp_out += omp_in) initializer(omp_priv Class2 < int > ()) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv 23) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun88 : long : omp_out += omp_in) initializer(omp_priv 23)) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun9 : long : omp_out += omp_priv) initializer(omp_in = 23) // expected-error {{use of undeclared identifier 'omp_priv'; did you mean 'omp_in'?}} expected-note {{'omp_in' is an implicit parameter}} +#pragma omp declare reduction(fun10 : long : omp_out += omp_in) initializer(omp_priv = 23) + +template +T fun(T arg) { +#pragma omp declare reduction(red : T : omp_out++) + { +#pragma omp declare reduction(red : T : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : T : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'T'}} +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = 23) + } + return arg; +} + +template +T foo(T arg) { + { +#pragma omp declare reduction(red : T : omp_out++) +#pragma omp declare reduction(red1 : T : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red1 : int : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + } + { +#pragma omp declare reduction(red1 : int : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : T : omp_out++) +#pragma omp declare reduction(red1 : T : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + } + return arg; +} +#pragma omp declare reduction(foo : int : ({int a = omp_in; a = a * 2; omp_out += a; })) +int main() { + Class1 c1; + int i; + // TODO: Add support for scoped reduction identifiers + // #pragma omp parallel reduction (::fun : c1) + { + } + // #pragma omp parallel reduction (::Class1::fun : c1) + { + } + // #pragma omp parallel reduction (::Class2::fun : i) + { + } + return fun(15) + foo(15); // expected-note {{in instantiation of function template specialization 'foo' requested here}} +} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -5072,6 +5072,7 @@ case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: case Decl::ObjCTypeParam: return C;