Repository: llvm/llvm-project
File: flang/lib/Semantics/resolve-directives.cpp (C++)

Code before:
          makeSymbol(dsa);
        }
        // Fix host association of explicit symbols, as they can be created
        // before implicit ones in enclosing scope.
        if (prevDeclSymbol && prevDeclSymbol != lastDeclSymbol &&
            lastDeclSymbol->test(Symbol::Flag::OmpExplicit)) {
          const auto *hostAssoc{lastDeclSymbol->detailsIf<HostAssocDetails>()};
          if (hostAssoc && hostAssoc->symbol() != *prevDeclSymbol) {
            lastDeclSymbol->set_details(HostAssocDetails{*prevDeclSymbol});
          }
        }
      }
      prevDSA = dsa;
      PRINT_IMPLICIT_RULE("0) already has DSA");
      continue;
    }

    // NOTE Because of how lowering uses OmpImplicit flag, we can only set it
    //      for symbols with private DSA.
    //      Also, as the default clause is handled separately in lowering,
    //      don't mark its symbols with OmpImplicit either.
    //      Ideally, lowering should be changed and all implicit symbols
    //      should be marked with OmpImplicit.

    if (dirContext.defaultDSA == Symbol::Flag::OmpNone) {
      if (!symbol->GetUltimate().test(Symbol::Flag::OmpThreadprivate) &&
          !symbol->test(Symbol::Flag::OmpPrivate)) {
        if (symbol->GetUltimate().test(Symbol::Flag::CrayPointee)) {
          std::string crayPtrName{
              semantics::GetCrayPointer(*symbol).name().ToString()};
          if (!IsObjectWithDSA(*currScope().FindSymbol(crayPtrName))) {
            context_.Say(name.source,
                "The DEFAULT(NONE) clause requires that the Cray Pointer '%s' must be listed in a data-sharing attribute clause"_err_en_US,
                crayPtrName);
          }
        } else {
          context_.Say(name.source,
              "The DEFAULT(NONE) clause requires that '%s' must be listed in a data-sharing attribute clause"_err_en_US,
              symbol->name());
        }
      }
    }

    if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
        dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
        dirContext.defaultDSA == Symbol::Flag::OmpShared) {
      // 1) default
      // Allowed only with parallel, teams and task generating constructs.
      if (!parallelDir && !taskGenDir && !teamsDir) {
        return;
      }

Review comment (suggestion):
Threadprivate symbols are already handled in `OmpAttributeVisitor::Post(const parser::Name &name)`, line 3115.

Code after:
        makeSymbol(dsa)->set(Symbol::Flag::OmpImplicit);
        PRINT_IMPLICIT_RULE("7) taskgen: firstprivate");
      }
    }
    prevDSA = dsa;
  }
}

static bool IsOpenMPPointer(const Symbol &symbol) {
  if (IsPointer(symbol) || IsBuiltinCPtr(symbol))
    return true;
  return false;
}

static bool IsOpenMPAggregate(const Symbol &symbol) {
  if (IsAllocatable(symbol) || IsOpenMPPointer(symbol))
    return false;

  const auto *type{symbol.GetType()};
  // OpenMP categorizes Fortran characters as aggregates.
  if (type->category() == Fortran::semantics::DeclTypeSpec::Category::Character)
    return true;

  if (const auto *det{symbol.GetUltimate()
              .detailsIf<Fortran::semantics::ObjectEntityDetails>()})
    if (det->IsArray())
      return true;

  if (type->AsDerived())
    return true;

  if (IsDeferredShape(symbol) || IsAssumedRank(symbol) ||
      IsAssumedShape(symbol))
    return true;
  return false;
}

static bool IsOpenMPScalar(const Symbol &symbol) {
  if (IsOpenMPAggregate(symbol) || IsOpenMPPointer(symbol) ||
      IsAllocatable(symbol))
    return false;
  const auto *type{symbol.GetType()};
  if ((!symbol.GetShape() || symbol.GetShape()->empty()) &&
      (type->category() ==
              Fortran::semantics::DeclTypeSpec::Category::Numeric ||
          type->category() ==
              Fortran::semantics::DeclTypeSpec::Category::Logical))
    return true;
  return false;
}