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"
@@ -3520,6 +3521,17 @@ bool OmpStructureChecker::CheckReductionOperator(
35203521 break ;
35213522 }
35223523 }
3524+ // User-defined operators are OK if there has been a declared reduction
3525+ // for that. We mangle those names to store the user details.
3526+ if (const auto *definedOp{std::get_if<parser::DefinedOpName>(&dOpr.u )}) {
3527+ std::string mangled{MangleDefinedOperator (definedOp->v .symbol ->name ())};
3528+ const Scope &scope{definedOp->v .symbol ->owner ()};
3529+ if (const Symbol *symbol{scope.FindSymbol (mangled)}) {
3530+ if (symbol->detailsIf <UserReductionDetails>()) {
3531+ return true ;
3532+ }
3533+ }
3534+ }
35233535 context_.Say (source, " Invalid reduction operator in %s clause." _err_en_US,
35243536 parser::ToUpperCaseLetters (getClauseName (clauseId).str ()));
35253537 return false ;
@@ -3533,8 +3545,7 @@ bool OmpStructureChecker::CheckReductionOperator(
35333545 valid =
35343546 llvm::is_contained ({" max" , " min" , " iand" , " ior" , " ieor" }, realName);
35353547 if (!valid) {
3536- auto *misc{name->symbol ->detailsIf <MiscDetails>()};
3537- valid = misc && misc->kind () == MiscDetails::Kind::ConstructName;
3548+ valid = name->symbol ->detailsIf <UserReductionDetails>();
35383549 }
35393550 }
35403551 if (!valid) {
@@ -3614,8 +3625,20 @@ void OmpStructureChecker::CheckReductionObjects(
36143625 }
36153626}
36163627
3628+ static bool CheckSymbolSupportsType (const Scope &scope,
3629+ const parser::CharBlock &name, const DeclTypeSpec &type) {
3630+ if (const auto *symbol{scope.FindSymbol (name)}) {
3631+ if (const auto *reductionDetails{
3632+ symbol->detailsIf <UserReductionDetails>()}) {
3633+ return reductionDetails->SupportsType (type);
3634+ }
3635+ }
3636+ return false ;
3637+ }
3638+
36173639static bool IsReductionAllowedForType (
3618- const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type) {
3640+ const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type,
3641+ const Scope &scope, SemanticsContext &context) {
36193642 auto isLogical{[](const DeclTypeSpec &type) -> bool {
36203643 return type.category () == DeclTypeSpec::Logical;
36213644 }};
@@ -3635,27 +3658,40 @@ static bool IsReductionAllowedForType(
36353658 case parser::DefinedOperator::IntrinsicOperator::Multiply:
36363659 case parser::DefinedOperator::IntrinsicOperator::Add:
36373660 case parser::DefinedOperator::IntrinsicOperator::Subtract:
3638- return type.IsNumeric (TypeCategory::Integer) ||
3661+ if ( type.IsNumeric (TypeCategory::Integer) ||
36393662 type.IsNumeric (TypeCategory::Real) ||
3640- type.IsNumeric (TypeCategory::Complex);
3663+ type.IsNumeric (TypeCategory::Complex))
3664+ return true ;
3665+ break ;
36413666
36423667 case parser::DefinedOperator::IntrinsicOperator::AND:
36433668 case parser::DefinedOperator::IntrinsicOperator::OR:
36443669 case parser::DefinedOperator::IntrinsicOperator::EQV:
36453670 case parser::DefinedOperator::IntrinsicOperator::NEQV:
3646- return isLogical (type);
3671+ if (isLogical (type)) {
3672+ return true ;
3673+ }
3674+ break ;
36473675
36483676 // Reduction identifier is not in OMP5.2 Table 5.2
36493677 default :
36503678 DIE (" This should have been caught in CheckIntrinsicOperator" );
36513679 return false ;
36523680 }
3653- }
3654- return true ;
3681+ parser::CharBlock name{MakeNameFromOperator (*intrinsicOp, context)};
3682+ return CheckSymbolSupportsType (scope, name, type);
3683+ } else if (const auto *definedOp{
3684+ std::get_if<parser::DefinedOpName>(&dOpr.u )}) {
3685+ return CheckSymbolSupportsType (
3686+ scope, MangleDefinedOperator (definedOp->v .symbol ->name ()), type);
3687+ }
3688+ llvm_unreachable (
3689+ " A DefinedOperator is either a DefinedOpName or an IntrinsicOperator" );
36553690 }};
36563691
36573692 auto checkDesignator{[&](const parser::ProcedureDesignator &procD) {
36583693 const parser::Name *name{std::get_if<parser::Name>(&procD.u )};
3694+ CHECK (name && name->symbol );
36593695 if (name && name->symbol ) {
36603696 const SourceName &realName{name->symbol ->GetUltimate ().name ()};
36613697 // OMP5.2: The type [...] of a list item that appears in a
@@ -3664,18 +3700,35 @@ static bool IsReductionAllowedForType(
36643700 // IAND: arguments must be integers: F2023 16.9.100
36653701 // IEOR: arguments must be integers: F2023 16.9.106
36663702 // IOR: arguments must be integers: F2023 16.9.111
3667- return type.IsNumeric (TypeCategory::Integer);
3703+ if (type.IsNumeric (TypeCategory::Integer)) {
3704+ return true ;
3705+ }
36683706 } else if (realName == " max" || realName == " min" ) {
36693707 // MAX: arguments must be integer, real, or character:
36703708 // F2023 16.9.135
36713709 // MIN: arguments must be integer, real, or character:
36723710 // F2023 16.9.141
3673- return type.IsNumeric (TypeCategory::Integer) ||
3674- type.IsNumeric (TypeCategory::Real) || isCharacter (type);
3711+ if (type.IsNumeric (TypeCategory::Integer) ||
3712+ type.IsNumeric (TypeCategory::Real) || isCharacter (type)) {
3713+ return true ;
3714+ }
36753715 }
3716+
3717+ // If we get here, it may be a user declared reduction, so check
3718+ // if the symbol has UserReductionDetails, and if so, the type is
3719+ // supported.
3720+ if (const auto *reductionDetails{
3721+ name->symbol ->detailsIf <UserReductionDetails>()}) {
3722+ return reductionDetails->SupportsType (type);
3723+ }
3724+
3725+ // We also need to check for mangled names (max, min, iand, ieor and ior)
3726+ // and then check if the type is there.
3727+ parser::CharBlock mangledName{MangleSpecialFunctions (name->source )};
3728+ return CheckSymbolSupportsType (scope, mangledName, type);
36763729 }
3677- // TODO: user defined reduction operators. Just allow everything for now .
3678- return true ;
3730+ // Everything else is "not matching type" .
3731+ return false ;
36793732 }};
36803733
36813734 return common::visit (
@@ -3690,7 +3743,8 @@ void OmpStructureChecker::CheckReductionObjectTypes(
36903743
36913744 for (auto &[symbol, source] : symbols) {
36923745 if (auto *type{symbol->GetType ()}) {
3693- if (!IsReductionAllowedForType (ident, *type)) {
3746+ const auto &scope{context_.FindScope (symbol->name ())};
3747+ if (!IsReductionAllowedForType (ident, *type, scope, context_)) {
36943748 context_.Say (source,
36953749 " The type of '%s' is incompatible with the reduction operator." _err_en_US,
36963750 symbol->name ());
0 commit comments