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

Code before:
        if ((dsa & dataSharingAttributeFlags).any()) {
          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):
This doesn't work when the DSA is specified by a clause in a nested construct. Example:
```
!$omp parallel default(none)
!$omp task shared(a)
a = 1
!$omp end task
!$omp end parallel
```

At least gfortran and ifx support this.

You will probably need an additional variable to keep track of the default DSA and check if the symbol in the current scope being processed (`scope`) has a predetermined/explicit flag set.

Code after:
        dsa = {Symbol::Flag::OmpFirstPrivate};
        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;
}