Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -1389,6 +1389,14 @@ } }) +DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, { + for (auto &&Data : D->reductions()) { + TRY_TO(TraverseType(Data.ReductionType)); + TRY_TO(TraverseStmt(Data.Combiner)); + TRY_TO(TraverseStmt(Data.Initializer)); + } +}) + // 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 @@ -154,7 +154,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 @@ -284,7 +287,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,7 +15,8 @@ #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/Type.h" #include "llvm/ADT/ArrayRef.h" namespace clang { @@ -85,6 +86,98 @@ 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 NamedDecl, public DeclContext { +public: + /// \brief A record with details of a single 'declare reduction' construct. + struct ReductionData { + ReductionData(QualType ReductionType, SourceRange TyRange, Expr *Combiner, + Expr *Initializer) + : ReductionType(ReductionType), TyRange(TyRange), Combiner(Combiner), + Initializer(Initializer) {} + ReductionData() : Combiner(nullptr), Initializer(nullptr) {} + QualType ReductionType; + SourceRange TyRange; + Expr *Combiner; + Expr *Initializer; + }; + +private: + friend class ASTDeclReader; + /// \brief Number of reductions defined within current user-defined reduction + /// construct (depends on number of types). + unsigned NumReductions; + + virtual void anchor(); + + OMPDeclareReductionDecl(Kind DK, DeclContext *DC, SourceLocation L, + DeclarationName Name) + : NamedDecl(DK, DC, L, Name), DeclContext(DK), NumReductions(0) { + setModulePrivate(); + } + + /// \brief Get offset to the reduction data storage. + static intptr_t getReductionDataOffset(); + + /// \brief Get reduction data for the current declare reduction construct. + ArrayRef getReductionData() const { + return ArrayRef( + reinterpret_cast( + reinterpret_cast(this) + getReductionDataOffset()), + NumReductions); + } + + /// \brief Get reduction data for the current declare reduction construct. + llvm::MutableArrayRef getReductionData() { + return llvm::MutableArrayRef( + reinterpret_cast(reinterpret_cast(this) + + getReductionDataOffset()), + NumReductions); + } + +public: + /// \brief Create declare reduction node with \p N internal reductions. + static OMPDeclareReductionDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName Name, + unsigned N); + /// \brief Create deserialized declare reduction node with \p N internal + /// reductions. + static OMPDeclareReductionDecl *CreateDeserialized(ASTContext &C, unsigned ID, + unsigned N); + + /// \brief Add new reduction construct for type \p ReductionTypes with + /// combiners and initializers \p CombinersInitializers. + void addReductions(ArrayRef> ReductionTypes, + ArrayRef> CombinersInitializers); + + typedef llvm::MutableArrayRef reductions_list; + typedef ArrayRef reductions_const_list; + + unsigned reductions_size() const { return NumReductions; } + bool reductions_empty() const { return NumReductions == 0; } + reductions_list reductions() { return getReductionData(); } + reductions_const_list reductions() const { return getReductionData(); } + + 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, { + for (auto &&Data : D->reductions()) { + TRY_TO(TraverseType(Data.ReductionType)); + TRY_TO(TraverseStmt(Data.Combiner)); + TRY_TO(TraverseStmt(Data.Initializer)); + } +}) + // 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 @@ -71,6 +71,7 @@ def ObjCImplementation : DDecl; def ObjCProperty : DDecl; def ObjCCompatibleAlias : DDecl; + def OMPDeclareReduction : DDecl, DeclContext; def LinkageSpec : Decl, DeclContext; def ObjCPropertyImpl : Decl; def FileScopeAsm : Decl; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -989,6 +989,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 @@ -7643,6 +7643,14 @@ "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_qualified_type : Error<"a type name cannot be qualified with 'const', 'volatile' or 'restrict'">; +def err_omp_reduction_function_type : Error<"a type name cannot be a function type">; +def err_omp_reduction_reference_type : Error<"a type name cannot be a reference type">; +def err_omp_reduction_array_type : Error<"a type name cannot be an array type">; +def err_omp_reduction_redeclared : Error<"previous declaration with type %0 is found">; +def err_omp_wrong_var_for_combiner : Error<"only 'omp_in' or 'omp_out' variables are allowed in combiner expression">; +def err_omp_wrong_var_for_initializer : Error<"only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression">; +def err_omp_declare_reduction_redefinition : Error<"redefinition of user-defined reduction for type %0">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { Index: include/clang/Basic/OpenMPKinds.def =================================================================== --- include/clang/Basic/OpenMPKinds.def +++ include/clang/Basic/OpenMPKinds.def @@ -99,6 +99,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 @@ -2416,7 +2416,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/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; @@ -2631,6 +2632,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 }; @@ -7708,6 +7711,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( + SourceRange TyRange, QualType ReductionType, + ArrayRef> RegisteredReductionTypes); + ExprResult PerformOpenMPImplicitIntegerConversion(SourceLocation OpLoc, Expr *Op); /// \brief Called on start of new data sharing attribute block. @@ -7741,6 +7750,29 @@ OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl( SourceLocation Loc, ArrayRef VarList); + /// \brief Called on start of '#pragma omp declare reduction'. + DeclGroupPtrTy ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, SourceLocation Loc, DeclarationName Name, + unsigned NumReductionTypes, AccessSpecifier AS); + /// \brief Initialize next declare reduction construct combiner. + void InitOpenMPDeclareReductionCombiner(Scope *S, SourceLocation Loc, + DeclGroupPtrTy DeclReduction, + QualType ReductionType); + /// \brief Finish current declare reduction construct combiner. + bool FinishOpenMPDeclareReductionCombiner(Expr *Combiner); + /// \brief Initialize next declare reduction construct initializer. + void InitOpenMPDeclareReductionInitializer(Scope *S, SourceLocation Loc, + DeclGroupPtrTy DeclReduction, + QualType ReductionType); + /// \brief Finish current declare reduction construct combiner. + bool FinishOpenMPDeclareReductionInitializer(Expr *Initializer); + /// \brief Called on bad-formed '#pragma omp declare reduction'. + void ActOnOpenMPDeclareReductionDirectiveError(DeclGroupPtrTy DeclReduction); + /// \brief Called on well-formed '#pragma omp declare reduction'. + void ActOnOpenMPDeclareReductionDirectiveEnd( + Scope *S, DeclContext *DC, DeclGroupPtrTy DeclReduction, + ArrayRef> ReductionTypes, + ArrayRef> CombinersInitializers); /// \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 @@ -1108,6 +1108,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 @@ -8317,7 +8317,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 @@ -598,6 +598,9 @@ case TemplateTemplateParm: return IDNS_Ordinary | IDNS_Tag | IDNS_Type; + case OMPDeclareReduction: + return IDNS_OMPReduction; + // Never have names. case Friend: case FriendTemplate: @@ -924,6 +927,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,53 @@ std::copy(VL.begin(), VL.end(), Vars); } +//===----------------------------------------------------------------------===// +// OMPDeclareReductionDecl Implementation. +//===----------------------------------------------------------------------===// + +void OMPDeclareReductionDecl::anchor() {} + +intptr_t OMPDeclareReductionDecl::getReductionDataOffset() { + return llvm::RoundUpToAlignment(sizeof(OMPDeclareReductionDecl), + llvm::alignOf()); +} + +OMPDeclareReductionDecl *OMPDeclareReductionDecl::Create(ASTContext &C, + DeclContext *DC, + SourceLocation L, + DeclarationName Name, + unsigned N) { + auto Size = getReductionDataOffset() + N * sizeof(ReductionData); + + OMPDeclareReductionDecl *D = + new (C, DC, Size - sizeof(OMPDeclareReductionDecl)) + OMPDeclareReductionDecl(OMPDeclareReduction, DC, L, Name); + D->NumReductions = N; + return D; +} + +OMPDeclareReductionDecl * +OMPDeclareReductionDecl::CreateDeserialized(ASTContext &C, unsigned ID, + unsigned N) { + auto Size = getReductionDataOffset() + N * sizeof(ReductionData); + + OMPDeclareReductionDecl *D = + new (C, ID, Size - sizeof(OMPDeclareReductionDecl)) + OMPDeclareReductionDecl(OMPDeclareReduction, /*DC=*/nullptr, + SourceLocation(), DeclarationName()); + D->NumReductions = N; + return D; +} + +void OMPDeclareReductionDecl::addReductions( + ArrayRef> ReductionTypes, + ArrayRef> CombinersInitializers) { + assert(ReductionTypes.size() == CombinersInitializers.size()); + ReductionData *Data = reinterpret_cast( + reinterpret_cast(this) + getReductionDataOffset()); + for (unsigned i =0, e = ReductionTypes.size(); i < e; ++i) + Data[i] = ReductionData(ReductionTypes[i].first, ReductionTypes[i].second, + CombinersInitializers[i].first, + CombinersInitializers[i].second); +} + 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,23 @@ } } +void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + if (!D->isInvalidDecl() && !D->reductions_empty()) { + for (auto &&Data : D->reductions()) { + Out << "#pragma omp declare reduction ("; + D->printName(Out); + Out << " : "; + Data.ReductionType.print(Out, Policy); + Out << " : "; + Data.Combiner->printPretty(Out, 0, Policy, 0); + Out << ")"; + if (Data.Initializer) { + Out << " initializer("; + Data.Initializer->printPretty(Out, 0, Policy, 0); + Out << ")"; + } + Out << "\n"; + } + } +} + 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 @@ -350,6 +350,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); @@ -1802,3 +1806,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 @@ -3387,6 +3387,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 @@ -3603,6 +3603,11 @@ continue; } + if (Tok.is(tok::annot_pragma_openmp)) { + 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 @@ -2969,7 +2969,7 @@ } if (Tok.is(tok::annot_pragma_openmp)) { - ParseOpenMPDeclarativeDirective(); + ParseOpenMPDeclarativeDirective(CurAS); continue; } Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -32,6 +32,8 @@ const OpenMPDirectiveKind F[][3] = { {OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/, OMPD_cancellation_point}, + {OMPD_unknown /*declare*/, OMPD_unknown /*reduction*/, + OMPD_declare_reduction}, {OMPD_for, OMPD_simd, OMPD_for_simd}, {OMPD_parallel, OMPD_for, OMPD_parallel_for}, {OMPD_parallel_for, OMPD_simd, OMPD_parallel_for_simd}, @@ -43,25 +45,28 @@ : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); 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); auto SDKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); - if (!Tok.isAnnotation() && DKind == OMPD_unknown) { + if (!Tok.isAnnotation() && SDKind == OMPD_unknown) TokenMatched = - (i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point"); - } else { + ((i == 0) && + !P.getPreprocessor().getSpelling(Tok).compare("point")) || + ((i == 1) && + !P.getPreprocessor().getSpelling(Tok).compare("reduction")); + else TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown; - } if (TokenMatched) { P.ConsumeToken(); DKind = F[i][2]; @@ -71,12 +76,260 @@ return DKind; } +/// \brief Parse 'omp declare reduction' construct. +/// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' '(' ':' +/// {',' } ':' ')' ['initializer' '(' +/// ('omp_priv' '=' |) ')'] +/// annot_pragma_openmp_end +/// +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) { + SourceLocation Loc = Tok.getLocation(); + bool IsCorrect = true; + // 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; + switch (Tok.getKind()) { + case tok::plus: // '+' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("+")); + ConsumeAnyToken(); + break; + case tok::minus: // '-' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("-")); + ConsumeAnyToken(); + break; + case tok::star: // '*' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("*")); + ConsumeAnyToken(); + break; + case tok::amp: // '&' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("&")); + ConsumeAnyToken(); + break; + case tok::pipe: // '|' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("|")); + ConsumeAnyToken(); + break; + case tok::caret: // '^' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("^")); + ConsumeAnyToken(); + break; + case tok::ampamp: // '&&' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("&&")); + ConsumeAnyToken(); + break; + case tok::pipepipe: // '||' + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + &Actions.Context.Idents.get("||")); + ConsumeAnyToken(); + break; + case tok::identifier: // identifier + Name = Actions.getASTContext().DeclarationNames.getIdentifier( + Tok.getIdentifierInfo()); + ConsumeAnyToken(); + break; + default: + IsCorrect = false; + Diag(Tok.getLocation(), diag::err_omp_expected_reduction_identifier); + SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + break; + } + 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 (!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, ReductionType, + ReductionTypes)) + ReductionTypes.push_back(std::make_pair(ReductionType, Range)); + 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(), Loc, Name, + ReductionTypes.size(), AS); + + // Parse expression and then parse initializer if any for each + // correct type. + unsigned i = 0, e = ReductionTypes.size(); + for (auto &&Data : ReductionTypes) { + TentativeParsingAction TPA(*this); + ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope); + Actions.InitOpenMPDeclareReductionCombiner(getCurScope(), Loc, DRD, + Data.first); + // Parse expression. + ExprResult CombinerResult = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + Actions.FinishOpenMPDeclareReductionCombiner(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); + Actions.InitOpenMPDeclareReductionInitializer(getCurScope(), Loc, DRD, + Data.first); + // Parse expression. + InitializerResult = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + IsCorrect = !Actions.FinishOpenMPDeclareReductionInitializer( + InitializerResult.get()) && + IsCorrect; + 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(); + } + if (IsCorrect) + Actions.ActOnOpenMPDeclareReductionDirectiveEnd( + getCurScope(), Actions.getCurLexicalContext(), DRD, ReductionTypes, + CombinersInitializers); + else + Actions.ActOnOpenMPDeclareReductionDirectiveError(DRD); + return 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' '(' ':' +/// {',' } ':' ')' ['initializer' '(' +/// ('omp_priv' '=' |) ')'] +/// 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); @@ -100,6 +353,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; @@ -141,6 +409,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' [ '(' ')' ] | @@ -186,6 +460,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 @@ -653,7 +653,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/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 @@ -1827,6 +1831,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 @@ -1335,6 +1335,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"); @@ -2125,6 +2126,7 @@ Res = ActOnOpenMPCancelDirective(StartLoc, EndLoc, CancelRegion); break; case OMPD_threadprivate: + case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -6565,3 +6567,261 @@ DepLoc, ColonLoc, Vars); } +bool Sema::isOpenMPDeclareReductionTypeAllowed( + SourceRange TyRange, 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(TyRange.getBegin(), diag::err_omp_reduction_qualified_type) << TyRange; + return false; + } + + if (ReductionType->isFunctionType() || + ReductionType->isFunctionNoProtoType() || + ReductionType->isFunctionProtoType() || + ReductionType->isFunctionPointerType() || + ReductionType->isMemberFunctionPointerType()) { + Diag(TyRange.getBegin(), diag::err_omp_reduction_function_type) << TyRange; + return false; + } + if (ReductionType->isReferenceType()) { + Diag(TyRange.getBegin(), diag::err_omp_reduction_reference_type) << TyRange; + return false; + } + if (ReductionType->isArrayType()) { + Diag(TyRange.getBegin(), diag::err_omp_reduction_array_type) << TyRange; + return false; + } + + bool IsValid = true; + for (auto &&Data : RegisteredReductionTypes) { + if (Context.hasSameType(ReductionType, Data.first)) { + Diag(TyRange.getBegin(), diag::err_omp_reduction_redeclared) + << ReductionType << TyRange; + Diag(Data.second.getBegin(), diag::note_previous_declaration) + << Data.second; + IsValid = false; + break; + } + } + return IsValid; +} + +Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, SourceLocation Loc, DeclarationName Name, + unsigned NumReductionTypes, AccessSpecifier AS) { + auto *DRD = OMPDeclareReductionDecl::Create(Context, DC, Loc, Name, + NumReductionTypes); + DC->addDecl(DRD); + DRD->setAccess(AS); + Decl *Decls[] = {DRD}; + return DeclGroupPtrTy::make( + DeclGroupRef::Create(Context, Decls, /*NumDecls=*/1)); +} + +void Sema::InitOpenMPDeclareReductionCombiner(Scope *S, SourceLocation Loc, + DeclGroupPtrTy DeclReduction, + QualType ReductionType) { + assert(DeclReduction && DeclReduction.get().isSingleDecl()); + auto *DRD = + cast(DeclReduction.get().getSingleDecl()); + + // Enter new function scope. + PushFunctionScope(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + // Create 'T omp_in;' implicit param. + auto *OmpInParm = ImplicitParamDecl::Create( + Context, DRD, Loc, &Context.Idents.get("omp_in"), ReductionType); + // Create 'T &omp_out;' implicit param. + auto *OmpOutParm = ImplicitParamDecl::Create( + Context, DRD, Loc, &Context.Idents.get("omp_out"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpInParm, S); + PushOnScopeChains(OmpOutParm, S); + } else { + DRD->addDecl(OmpInParm); + DRD->addDecl(OmpOutParm); + } +} + +namespace { +class DeclareReductionCombinerInitializerChecker + : public ConstStmtVisitor { + Sema &SemaRef; + bool Combiner; + +public: + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (auto VD = dyn_cast(E->getDecl())) { + if (!VD->hasLocalStorage()) { + SemaRef.Diag(E->getLocStart(), + Combiner ? diag::err_omp_wrong_var_for_combiner + : diag::err_omp_wrong_var_for_initializer) + << E->getSourceRange(); + SemaRef.Diag(VD->getLocation(), diag::note_defined_here) + << VD << VD->getSourceRange(); + return true; + } + } + return false; + } + bool VisitStmt(const Stmt *S) { + for (auto Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } + explicit DeclareReductionCombinerInitializerChecker(Sema &SemaRef, + bool Combiner) + : SemaRef(SemaRef), Combiner(Combiner) {} +}; +} // namespace + +bool Sema::FinishOpenMPDeclareReductionCombiner(Expr *Combiner) { + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // Only the variables omp_in and omp_out are allowed in the combiner. + DeclareReductionCombinerInitializerChecker CombinerChecker(*this, + /*Combiner=*/true); + return !Combiner || CombinerChecker.Visit(Combiner); +} + +void Sema::InitOpenMPDeclareReductionInitializer(Scope *S, SourceLocation Loc, + DeclGroupPtrTy DeclReduction, + QualType ReductionType) { + assert(DeclReduction && DeclReduction.get().isSingleDecl()); + auto *DRD = + cast(DeclReduction.get().getSingleDecl()); + + // Enter new function scope. + PushFunctionScope(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + // Create 'T omp_orig;' implicit param. + auto *OmpOrigParm = ImplicitParamDecl::Create( + Context, DRD, Loc, &Context.Idents.get("omp_orig"), ReductionType); + // Create 'T &omp_priv;' implicit param. + auto *OmpPrivParm = ImplicitParamDecl::Create( + Context, DRD, Loc, &Context.Idents.get("omp_priv"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpPrivParm, S); + PushOnScopeChains(OmpOrigParm, S); + } else { + DRD->addDecl(OmpPrivParm); + DRD->addDecl(OmpOrigParm); + } +} + +bool Sema::FinishOpenMPDeclareReductionInitializer(Expr *Initializer) { + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // Only the variables omp_priv and omp_orig are allowed in the + // initializer-clause. + DeclareReductionCombinerInitializerChecker InitializerChecker( + *this, /*Combiner=*/false); + return !Initializer || InitializerChecker.Visit(Initializer); +} + +void Sema::ActOnOpenMPDeclareReductionDirectiveError( + DeclGroupPtrTy DeclReduction) { + assert(DeclReduction && DeclReduction.get().isSingleDecl()); + DeclReduction.get().getSingleDecl()->setInvalidDecl(); +} + +void Sema::ActOnOpenMPDeclareReductionDirectiveEnd( + Scope *S, DeclContext *DC, DeclGroupPtrTy DeclReduction, + ArrayRef> ReductionTypes, + ArrayRef> CombinersInitializers) { + assert(DeclReduction && DeclReduction.get().isSingleDecl()); + assert(!ReductionTypes.empty()); + assert(ReductionTypes.size() == CombinersInitializers.size()); + auto *DRD = + cast(DeclReduction.get().getSingleDecl()); + DRD->addReductions(ReductionTypes, CombinersInitializers); + + // [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 (size_t i = 0, e = ReductionTypes.size() - 1; i < e; ++i) { + for (size_t i1 = i + 1, e1 = e + 1; i1 < e1; ++i1) { + if (Context.typesAreCompatible(ReductionTypes[i].first, + ReductionTypes[i1].first, + /*CompareUnqualified=*/true)) { + Diag(ReductionTypes[i1].second.getBegin(), + diag::err_omp_declare_reduction_redefinition) + << ReductionTypes[i1].first << ReductionTypes[i1].second; + Diag(ReductionTypes[i].second.getBegin(), + diag::note_previous_definition) + << ReductionTypes[i].second; + DRD->setInvalidDecl(); + } + } + } + if (!S) + S = getScopeForContext(DC); + if (S) { + LookupResult Lookup(*this, DRD->getDeclName(), DRD->getLocation(), + LookupOMPReductionName); + Lookup.suppressDiagnostics(); + LookupName(Lookup, S); + FilterLookupForScope(Lookup, DC, S, /*ConsiderLinkage=*/false, + /*AllowInlineNamespace=*/false); + auto Filter = Lookup.makeFilter(); + while (Filter.hasNext()) { + auto *PrevDecl = cast(Filter.next()); + if (!PrevDecl->isInvalidDecl() && PrevDecl != DRD) + for (auto &&PrevRed : PrevDecl->reductions()) { + for (auto &&Red : ReductionTypes) { + if (Context.typesAreCompatible(Red.first, PrevRed.ReductionType, + /*CompareUnqualified=*/true)) { + Diag(Red.second.getBegin(), + diag::err_omp_declare_reduction_redefinition) + << Red.first << Red.second; + Diag(PrevRed.TyRange.getBegin(), diag::note_previous_definition) + << PrevRed.TyRange; + DRD->setInvalidDecl(); + } + } + } + if (PrevDecl->isInvalidDecl()) + Filter.erase(); + } + Filter.done(); + if (!DRD->isInvalidDecl()) + PushOnScopeChains(DRD, S, /*AddToContext=*/false); + } +} + Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2452,6 +2452,84 @@ return TD; } +Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl( + OMPDeclareReductionDecl *D) { + SmallVector, 8> ReductionTypes; + SmallVector, 8> CombinersInitializers; + ReductionTypes.reserve(D->reductions_size()); + CombinersInitializers.reserve(D->reductions_size()); + + // Create instantiated copy. + auto DRD = SemaRef.ActOnOpenMPDeclareReductionDirectiveStart( + /*S=*/nullptr, Owner, D->getLocStart(), D->getDeclName(), + D->reductions_size(), D->getAccess()); + auto *NewDRD = cast(DRD.get().getSingleDecl()); + if (isDeclWithinFunction(NewDRD)) + SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewDRD); + bool IsCorrect = true; + for (auto &&Data : D->reductions()) { + // Instantiate type and check if it is allowed. + QualType SubstReductionType = + SemaRef.SubstType(Data.ReductionType, TemplateArgs, + Data.TyRange.getBegin(), DeclarationName()); + if (SubstReductionType.isNull() || + !SemaRef.isOpenMPDeclareReductionTypeAllowed( + Data.TyRange, SubstReductionType, ReductionTypes)) { + IsCorrect = false; + continue; + } + Expr *SubstCombiner = nullptr; + Expr *SubstInitializer = nullptr; + // Combiners instantiation sequence. + if (Data.Combiner) { + SemaRef.InitOpenMPDeclareReductionCombiner( + /*S=*/nullptr, D->getLocation(), DRD, SubstReductionType); + for (auto *Local : D->noload_decls()) { + auto Lookup = + NewDRD->noload_lookup(cast(Local)->getDeclName()); + if (!Lookup.empty()) { + assert(Lookup.size() == 1); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(Local, + Lookup.front()); + } + } + SubstCombiner = SemaRef.SubstExpr(Data.Combiner, TemplateArgs).get(); + SemaRef.FinishOpenMPDeclareReductionCombiner(SubstCombiner); + } + // Initializers instantiation sequence. + if (Data.Initializer) { + SemaRef.InitOpenMPDeclareReductionInitializer( + /*S=*/nullptr, D->getLocation(), DRD, SubstReductionType); + for (auto *Local : D->noload_decls()) { + auto Lookup = + NewDRD->noload_lookup(cast(Local)->getDeclName()); + if (!Lookup.empty()) { + assert(Lookup.size() == 1); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(Local, + Lookup.front()); + } + } + SubstInitializer = + SemaRef.SubstExpr(Data.Initializer, TemplateArgs).get(); + SemaRef.FinishOpenMPDeclareReductionInitializer(SubstInitializer); + } + if (!SubstCombiner || (Data.Initializer && !SubstInitializer)) { + IsCorrect = false; + continue; + } + ReductionTypes.push_back(std::make_pair(SubstReductionType, Data.TyRange)); + CombinersInitializers.push_back( + std::make_pair(SubstCombiner, SubstInitializer)); + } + if (IsCorrect) + SemaRef.ActOnOpenMPDeclareReductionDirectiveEnd( + /*S=*/nullptr, Owner, DRD, ReductionTypes, CombinersInitializers); + else + SemaRef.ActOnOpenMPDeclareReductionDirectiveError(DRD); + + return DRD.get().getSingleDecl(); +} + 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 @@ -373,6 +373,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 @@ -2364,6 +2365,24 @@ D->setVars(Vars); } +void ASTDeclReader::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + VisitDecl(D); + D->setDeclName(Reader.ReadDeclarationName(F, Record, Idx)); + unsigned NumReductions = D->reductions_size(); + SmallVector, 16> ReductionTypes; + SmallVector, 16> CombinersInitializers; + ReductionTypes.reserve(NumReductions); + CombinersInitializers.reserve(NumReductions); + for (unsigned i = 0; i != NumReductions; ++i) { + ReductionTypes.push_back( + std::make_pair(Reader.readType(F, Record, Idx), + Reader.ReadSourceRange(F, Record, Idx))); + CombinersInitializers.push_back( + std::make_pair(Reader.ReadExpr(F), Reader.ReadExpr(F))); + } + D->addReductions(ReductionTypes, CombinersInitializers); +} + //===----------------------------------------------------------------------===// // Attribute Reading //===----------------------------------------------------------------------===// @@ -2413,7 +2432,8 @@ isa(D) || isa(D) || isa(D) || - isa(D)) + isa(D) || + isa(D)) return true; if (VarDecl *Var = dyn_cast(D)) return Var->isFileVarDecl() && @@ -3300,6 +3320,9 @@ case DECL_OMP_THREADPRIVATE: D = OMPThreadPrivateDecl::CreateDeserialized(Context, ID, Record[Idx++]); break; + case DECL_OMP_DECLARE_REDUCTION: + D = OMPDeclareReductionDecl::CreateDeserialized(Context, ID, Record[Idx++]); + 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,19 @@ Code = serialization::DECL_OMP_THREADPRIVATE; } +void ASTDeclWriter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + Record.push_back(D->reductions_size()); + VisitDecl(D); + Writer.AddDeclarationName(D->getDeclName(), Record); + for (auto &&Data : D->reductions()) { + Writer.AddTypeRef(Data.ReductionType, Record); + Writer.AddSourceRange(Data.TyRange, Record); + Writer.AddStmt(Data.Combiner); + Writer.AddStmt(Data.Initializer); + } + 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,38 @@ +// 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)) + 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,44 @@ +// 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)) + +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) + { + } +} + +#endif Index: test/OpenMP/declare_reduction_messages.c =================================================================== --- test/OpenMP/declare_reduction_messages.c +++ test/OpenMP/declare_reduction_messages.c @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 6 {{'temp' defined 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 '||'}} +#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 {{a type name cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{a type name 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-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun : long(*)(void) : omp_out += omp_in) // expected-error {{a type name cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{a type name 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(fun : 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'}} + } + return arg; +} Index: test/OpenMP/declare_reduction_messages.cpp =================================================================== --- test/OpenMP/declare_reduction_messages.cpp +++ test/OpenMP/declare_reduction_messages.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 8 {{'temp' defined 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 '||'}} +#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 {{a type name cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{a type name 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 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} expected-error {{expected 'initializer'}} +#pragma omp declare reduction(fun : long & : omp_out += omp_in) // expected-error {{a type name cannot be a reference type}} +#pragma omp declare reduction(fun : long(*)(void) : omp_out += omp_in) // expected-error {{a type name cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{a type name 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 {{a type name cannot be a reference type}} expected-error 2 {{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 {{a type name 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 {{a type name 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(fun : long : omp_out += omp_in) // expected-note {{previous definition is here}} +#pragma omp declare reduction(fun : 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(fun7 : 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(fun8 : 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_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'}} + } + return arg; +} + +int main() { + Class1 c1; // expected-note {{in instantiation of template class 'Class1' requested here}} + 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); +} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -5099,6 +5099,7 @@ case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: case Decl::ObjCTypeParam: return C;