88
99#include " check-omp-structure.h"
1010#include " definable.h"
11+ #include " resolve-names-utils.h"
1112#include " flang/Evaluate/check-expression.h"
1213#include " flang/Evaluate/expression.h"
1314#include " flang/Evaluate/type.h"
@@ -3507,6 +3508,17 @@ bool OmpStructureChecker::CheckReductionOperator(
35073508 break ;
35083509 }
35093510 }
3511+ // User-defined operators are OK if there has been a declared reduction
3512+ // for that. We mangle those names to store the user details.
3513+ if (const auto *definedOp{std::get_if<parser::DefinedOpName>(&dOpr.u )}) {
3514+ std::string mangled{MangleDefinedOperator (definedOp->v .symbol ->name ())};
3515+ const Scope &scope{definedOp->v .symbol ->owner ()};
3516+ if (const Symbol *symbol{scope.FindSymbol (mangled)}) {
3517+ if (symbol->detailsIf <UserReductionDetails>()) {
3518+ return true ;
3519+ }
3520+ }
3521+ }
35103522 context_.Say (source, " Invalid reduction operator in %s clause." _err_en_US,
35113523 parser::ToUpperCaseLetters (getClauseName (clauseId).str ()));
35123524 return false ;
@@ -3520,8 +3532,7 @@ bool OmpStructureChecker::CheckReductionOperator(
35203532 valid =
35213533 llvm::is_contained ({" max" , " min" , " iand" , " ior" , " ieor" }, realName);
35223534 if (!valid) {
3523- auto *misc{name->symbol ->detailsIf <MiscDetails>()};
3524- valid = misc && misc->kind () == MiscDetails::Kind::ConstructName;
3535+ valid = name->symbol ->detailsIf <UserReductionDetails>();
35253536 }
35263537 }
35273538 if (!valid) {
@@ -3601,8 +3612,20 @@ void OmpStructureChecker::CheckReductionObjects(
36013612 }
36023613}
36033614
3615+ static bool CheckSymbolSupportsType (const Scope &scope,
3616+ const parser::CharBlock &name, const DeclTypeSpec &type) {
3617+ if (const auto *symbol{scope.FindSymbol (name)}) {
3618+ if (const auto *reductionDetails{
3619+ symbol->detailsIf <UserReductionDetails>()}) {
3620+ return reductionDetails->SupportsType (type);
3621+ }
3622+ }
3623+ return false ;
3624+ }
3625+
36043626static bool IsReductionAllowedForType (
3605- const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type) {
3627+ const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type,
3628+ const Scope &scope, SemanticsContext &context) {
36063629 auto isLogical{[](const DeclTypeSpec &type) -> bool {
36073630 return type.category () == DeclTypeSpec::Logical;
36083631 }};
@@ -3622,27 +3645,40 @@ static bool IsReductionAllowedForType(
36223645 case parser::DefinedOperator::IntrinsicOperator::Multiply:
36233646 case parser::DefinedOperator::IntrinsicOperator::Add:
36243647 case parser::DefinedOperator::IntrinsicOperator::Subtract:
3625- return type.IsNumeric (TypeCategory::Integer) ||
3648+ if ( type.IsNumeric (TypeCategory::Integer) ||
36263649 type.IsNumeric (TypeCategory::Real) ||
3627- type.IsNumeric (TypeCategory::Complex);
3650+ type.IsNumeric (TypeCategory::Complex))
3651+ return true ;
3652+ break ;
36283653
36293654 case parser::DefinedOperator::IntrinsicOperator::AND:
36303655 case parser::DefinedOperator::IntrinsicOperator::OR:
36313656 case parser::DefinedOperator::IntrinsicOperator::EQV:
36323657 case parser::DefinedOperator::IntrinsicOperator::NEQV:
3633- return isLogical (type);
3658+ if (isLogical (type)) {
3659+ return true ;
3660+ }
3661+ break ;
36343662
36353663 // Reduction identifier is not in OMP5.2 Table 5.2
36363664 default :
36373665 DIE (" This should have been caught in CheckIntrinsicOperator" );
36383666 return false ;
36393667 }
3640- }
3641- return true ;
3668+ parser::CharBlock name{MakeNameFromOperator (*intrinsicOp, context)};
3669+ return CheckSymbolSupportsType (scope, name, type);
3670+ } else if (const auto *definedOp{
3671+ std::get_if<parser::DefinedOpName>(&dOpr.u )}) {
3672+ return CheckSymbolSupportsType (
3673+ scope, MangleDefinedOperator (definedOp->v .symbol ->name ()), type);
3674+ }
3675+ llvm_unreachable (
3676+ " A DefinedOperator is either a DefinedOpName or an IntrinsicOperator" );
36423677 }};
36433678
36443679 auto checkDesignator{[&](const parser::ProcedureDesignator &procD) {
36453680 const parser::Name *name{std::get_if<parser::Name>(&procD.u )};
3681+ CHECK (name && name->symbol );
36463682 if (name && name->symbol ) {
36473683 const SourceName &realName{name->symbol ->GetUltimate ().name ()};
36483684 // OMP5.2: The type [...] of a list item that appears in a
@@ -3651,18 +3687,35 @@ static bool IsReductionAllowedForType(
36513687 // IAND: arguments must be integers: F2023 16.9.100
36523688 // IEOR: arguments must be integers: F2023 16.9.106
36533689 // IOR: arguments must be integers: F2023 16.9.111
3654- return type.IsNumeric (TypeCategory::Integer);
3690+ if (type.IsNumeric (TypeCategory::Integer)) {
3691+ return true ;
3692+ }
36553693 } else if (realName == " max" || realName == " min" ) {
36563694 // MAX: arguments must be integer, real, or character:
36573695 // F2023 16.9.135
36583696 // MIN: arguments must be integer, real, or character:
36593697 // F2023 16.9.141
3660- return type.IsNumeric (TypeCategory::Integer) ||
3661- type.IsNumeric (TypeCategory::Real) || isCharacter (type);
3698+ if (type.IsNumeric (TypeCategory::Integer) ||
3699+ type.IsNumeric (TypeCategory::Real) || isCharacter (type)) {
3700+ return true ;
3701+ }
36623702 }
3703+
3704+ // If we get here, it may be a user declared reduction, so check
3705+ // if the symbol has UserReductionDetails, and if so, the type is
3706+ // supported.
3707+ if (const auto *reductionDetails{
3708+ name->symbol ->detailsIf <UserReductionDetails>()}) {
3709+ return reductionDetails->SupportsType (type);
3710+ }
3711+
3712+ // We also need to check for mangled names (max, min, iand, ieor and ior)
3713+ // and then check if the type is there.
3714+ parser::CharBlock mangledName{MangleSpecialFunctions (name->source )};
3715+ return CheckSymbolSupportsType (scope, mangledName, type);
36633716 }
3664- // TODO: user defined reduction operators. Just allow everything for now .
3665- return true ;
3717+ // Everything else is "not matching type" .
3718+ return false ;
36663719 }};
36673720
36683721 return common::visit (
@@ -3677,7 +3730,8 @@ void OmpStructureChecker::CheckReductionObjectTypes(
36773730
36783731 for (auto &[symbol, source] : symbols) {
36793732 if (auto *type{symbol->GetType ()}) {
3680- if (!IsReductionAllowedForType (ident, *type)) {
3733+ const auto &scope{context_.FindScope (symbol->name ())};
3734+ if (!IsReductionAllowedForType (ident, *type, scope, context_)) {
36813735 context_.Say (source,
36823736 " The type of '%s' is incompatible with the reduction operator." _err_en_US,
36833737 symbol->name ());
0 commit comments