diff options
Diffstat (limited to 'flang/lib/Semantics')
| -rw-r--r-- | flang/lib/Semantics/canonicalize-omp.cpp | 156 | ||||
| -rw-r--r-- | flang/lib/Semantics/check-omp-structure.cpp | 703 | ||||
| -rw-r--r-- | flang/lib/Semantics/check-omp-structure.h | 37 | ||||
| -rw-r--r-- | flang/lib/Semantics/openmp-utils.cpp | 17 | ||||
| -rw-r--r-- | flang/lib/Semantics/resolve-directives.cpp | 59 | ||||
| -rw-r--r-- | flang/lib/Semantics/resolve-names.cpp | 4 | 
6 files changed, 569 insertions, 407 deletions
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp index c884658..a11c525 100644 --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -51,8 +51,6 @@ public:      } // Block list    } -  void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); } -    // Pre-visit all constructs that have both a specification part and    // an execution part, and store the connection between the two.    bool Pre(parser::BlockConstruct &x) { @@ -88,6 +86,7 @@ public:    void Post(parser::SpecificationPart &spec) {      CanonicalizeUtilityConstructs(spec); +    CanonicalizeAllocateDirectives(spec);    }    void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); } @@ -239,33 +238,138 @@ private:      }    } -  void RewriteOmpAllocations(parser::ExecutionPart &body) { -    // Rewrite leading declarative allocations so they are nested -    // within their respective executable allocate directive -    // -    // Original: -    //   ExecutionPartConstruct -> OpenMPDeclarativeAllocate -    //   ExecutionPartConstruct -> OpenMPDeclarativeAllocate -    //   ExecutionPartConstruct -> OpenMPExecutableAllocate -    // -    // After rewriting: -    //   ExecutionPartConstruct -> OpenMPExecutableAllocate -    //     ExecutionPartConstruct -> OpenMPDeclarativeAllocate -    //     ExecutionPartConstruct -> OpenMPDeclarativeAllocate -    for (auto it = body.v.rbegin(); it != body.v.rend();) { -      if (auto *exec = GetOmpIf<parser::OpenMPExecutableAllocate>(*(it++))) { -        parser::OpenMPDeclarativeAllocate *decl; -        std::list<parser::OpenMPDeclarativeAllocate> subAllocates; -        while (it != body.v.rend() && -            (decl = GetOmpIf<parser::OpenMPDeclarativeAllocate>(*it))) { -          subAllocates.push_front(std::move(*decl)); -          it = decltype(it)(body.v.erase(std::next(it).base())); +  // Canonicalization of allocate directives +  // +  // In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative +  // one or an executable one. As usual in such cases, this poses a problem +  // when the directive appears at the boundary between the specification part +  // and the execution part. +  // The executable form can actually consist of several adjacent directives, +  // whereas the declarative form is always standalone. Additionally, the +  // executable form must be associated with an allocate statement. +  // +  // The parser tries to parse declarative statements first, so in the +  // following case, the two directives will be declarative, even though +  // they should be treated as a single executable form: +  //   integer, allocatable :: x, y   ! Specification +  //   !$omp allocate(x) +  //   !$omp allocate(y) +  //   allocate(x, y)                 ! Execution +  // +  void CanonicalizeAllocateDirectives(parser::SpecificationPart &spec) { +    auto found = blockForSpec_.find(&spec); +    if (found == blockForSpec_.end()) { +      // There is no corresponding execution part, so there is nothing to do. +      return; +    } +    parser::Block &block = *found->second; + +    auto isAllocateStmt = [](const parser::ExecutionPartConstruct &epc) { +      if (auto *ec = std::get_if<parser::ExecutableConstruct>(&epc.u)) { +        if (auto *as = +                std::get_if<parser::Statement<parser::ActionStmt>>(&ec->u)) { +          return std::holds_alternative< +              common::Indirection<parser::AllocateStmt>>(as->statement.u); +        } +      } +      return false; +    }; + +    if (!block.empty() && isAllocateStmt(block.front())) { +      // There are two places where an OpenMP declarative construct can +      // show up in the tuple in specification part: +      // (1) in std::list<OpenMPDeclarativeConstruct>, or +      // (2) in std::list<DeclarationConstruct>. +      // The case (1) is only possible if the list (2) is empty. + +      auto &omps = +          std::get<std::list<parser::OpenMPDeclarativeConstruct>>(spec.t); +      auto &decls = std::get<std::list<parser::DeclarationConstruct>>(spec.t); + +      if (!decls.empty()) { +        MakeExecutableAllocateFromDecls(decls, block); +      } else { +        MakeExecutableAllocateFromOmps(omps, block); +      } +    } +  } + +  parser::ExecutionPartConstruct EmbedInExec( +      parser::OmpAllocateDirective *alo, parser::ExecutionPartConstruct &&epc) { +    // Nest current epc inside the allocate directive. +    std::get<parser::Block>(alo->t).push_front(std::move(epc)); +    // Set the new epc to be the ExecutionPartConstruct made from +    // the allocate directive. +    parser::OpenMPConstruct opc(std::move(*alo)); +    common::Indirection<parser::OpenMPConstruct> ind(std::move(opc)); +    parser::ExecutableConstruct ec(std::move(ind)); +    return parser::ExecutionPartConstruct(std::move(ec)); +  } + +  void MakeExecutableAllocateFromDecls( +      std::list<parser::DeclarationConstruct> &decls, parser::Block &body) { +    using OpenMPDeclarativeConstruct = +        common::Indirection<parser::OpenMPDeclarativeConstruct>; + +    auto getAllocate = [](parser::DeclarationConstruct *dc) { +      if (auto *sc = std::get_if<parser::SpecificationConstruct>(&dc->u)) { +        if (auto *odc = std::get_if<OpenMPDeclarativeConstruct>(&sc->u)) { +          if (auto *alo = +                  std::get_if<parser::OmpAllocateDirective>(&odc->value().u)) { +            return alo; +          } +        } +      } +      return static_cast<parser::OmpAllocateDirective *>(nullptr); +    }; + +    std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() { +      for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) { +        if (getAllocate(&*rit) == nullptr) { +          return rit;          } -        if (!subAllocates.empty()) { -          std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>( -              exec->t) = {std::move(subAllocates)}; +      } +      return decls.rend(); +    }(); + +    if (rlast != decls.rbegin()) { +      // We have already checked that the first statement in body is +      // ALLOCATE. +      parser::ExecutionPartConstruct epc(std::move(body.front())); +      for (auto rit = decls.rbegin(); rit != rlast; ++rit) { +        epc = EmbedInExec(getAllocate(&*rit), std::move(epc)); +      } + +      body.pop_front(); +      body.push_front(std::move(epc)); +      decls.erase(rlast.base(), decls.end()); +    } +  } + +  void MakeExecutableAllocateFromOmps( +      std::list<parser::OpenMPDeclarativeConstruct> &omps, +      parser::Block &body) { +    using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct; + +    std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() { +      for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) { +        if (!std::holds_alternative<parser::OmpAllocateDirective>(rit->u)) { +          return rit;          }        } +      return omps.rend(); +    }(); + +    if (rlast != omps.rbegin()) { +      parser::ExecutionPartConstruct epc(std::move(body.front())); +      for (auto rit = omps.rbegin(); rit != rlast; ++rit) { +        epc = EmbedInExec( +            &std::get<parser::OmpAllocateDirective>(rit->u), std::move(epc)); +      } + +      body.pop_front(); +      body.push_front(std::move(epc)); +      omps.erase(rlast.base(), omps.end());      }    } diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index aaaf1ec..d7db15d 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -179,29 +179,21 @@ void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {    }  } -// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'. -#define CHECK_SIMPLE_CLAUSE(X, Y) \ -  void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \ -    CheckAllowedClause(llvm::omp::Clause::Y); \ -  } +void OmpStructureChecker::Enter(const parser::SpecificationPart &) { +  partStack_.push_back(PartKind::SpecificationPart); +} -#define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \ -  void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ -    CheckAllowedClause(llvm::omp::Clause::Y); \ -    RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \ -  } +void OmpStructureChecker::Leave(const parser::SpecificationPart &) { +  partStack_.pop_back(); +} -#define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \ -  void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ -    CheckAllowedClause(llvm::omp::Clause::Y); \ -    RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \ -  } +void OmpStructureChecker::Enter(const parser::ExecutionPart &) { +  partStack_.push_back(PartKind::ExecutionPart); +} -// Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'. -#define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \ -  void OmpStructureChecker::Enter(const parser::X &) { \ -    CheckAllowedClause(llvm::omp::Y); \ -  } +void OmpStructureChecker::Leave(const parser::ExecutionPart &) { +  partStack_.pop_back(); +}  // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment  // statements and the expressions enclosed in an OpenMP Workshare construct @@ -667,49 +659,6 @@ void OmpStructureChecker::HasInvalidTeamsNesting(    }  } -void OmpStructureChecker::CheckPredefinedAllocatorRestriction( -    const parser::CharBlock &source, const parser::Name &name) { -  if (const auto *symbol{name.symbol}) { -    const auto *commonBlock{FindCommonBlockContaining(*symbol)}; -    const auto &scope{context_.FindScope(symbol->name())}; -    const Scope &containingScope{GetProgramUnitContaining(scope)}; -    if (!isPredefinedAllocator && -        (IsSaved(*symbol) || commonBlock || -            containingScope.kind() == Scope::Kind::Module)) { -      context_.Say(source, -          "If list items within the %s directive have the " -          "SAVE attribute, are a common block name, or are " -          "declared in the scope of a module, then only " -          "predefined memory allocator parameters can be used " -          "in the allocator clause"_err_en_US, -          ContextDirectiveAsFortran()); -    } -  } -} - -void OmpStructureChecker::CheckPredefinedAllocatorRestriction( -    const parser::CharBlock &source, -    const parser::OmpObjectList &ompObjectList) { -  for (const auto &ompObject : ompObjectList.v) { -    common::visit( -        common::visitors{ -            [&](const parser::Designator &designator) { -              if (const auto *dataRef{ -                      std::get_if<parser::DataRef>(&designator.u)}) { -                if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) { -                  CheckPredefinedAllocatorRestriction(source, *name); -                } -              } -            }, -            [&](const parser::Name &name) { -              CheckPredefinedAllocatorRestriction(source, name); -            }, -            [&](const parser::OmpObject::Invalid &invalid) {}, -        }, -        ompObject.u); -  } -} -  void OmpStructureChecker::Enter(const parser::OmpClause::Hint &x) {    CheckAllowedClause(llvm::omp::Clause::OMPC_hint);    auto &dirCtx{GetContext()}; @@ -763,18 +712,10 @@ template <typename Checker> struct DirectiveSpellingVisitor {      return std::get<parser::OmpBeginDirective>(t).DirName();    } -  bool Pre(const parser::OpenMPDeclarativeAllocate &x) { -    checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate); -    return false; -  }    bool Pre(const parser::OpenMPDispatchConstruct &x) {      checker_(GetDirName(x.t).source, Directive::OMPD_dispatch);      return false;    } -  bool Pre(const parser::OpenMPExecutableAllocate &x) { -    checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate); -    return false; -  }    bool Pre(const parser::OpenMPAllocatorsConstruct &x) {      checker_(GetDirName(x.t).source, Directive::OMPD_allocators);      return false; @@ -1710,12 +1651,39 @@ void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {    dirContext_.pop_back();  } -void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source, -    const parser::OmpObjectList &objects, -    const parser::OmpClauseList &clauses) { -  const Scope &thisScope{context_.FindScope(source)}; -  SymbolSourceMap symbols; -  GetSymbolsInObjectList(objects, symbols); +static std::pair<const parser::AllocateStmt *, parser::CharBlock> +getAllocateStmtAndSource(const parser::ExecutionPartConstruct *epc) { +  if (SourcedActionStmt as{GetActionStmt(epc)}) { +    using IndirectionAllocateStmt = common::Indirection<parser::AllocateStmt>; +    if (auto *indirect{std::get_if<IndirectionAllocateStmt>(&as.stmt->u)}) { +      return {&indirect->value(), as.source}; +    } +  } +  return {nullptr, ""}; +} + +// Collect symbols that correspond to non-component objects on the +// ALLOCATE statement. +static UnorderedSymbolSet GetNonComponentSymbols( +    const parser::AllocateStmt &stmt) { +  UnorderedSymbolSet symbols; +  for (auto &alloc : std::get<std::list<parser::Allocation>>(stmt.t)) { +    auto &object{std::get<parser::AllocateObject>(alloc.t)}; +    if (auto *name{std::get_if<parser::Name>(&object.u)}) { +      if (name->symbol) { +        symbols.insert(name->symbol->GetUltimate()); +      } +    } +  } +  return symbols; +} + +void OmpStructureChecker::CheckIndividualAllocateDirective( +    const parser::OmpAllocateDirective &x, bool isExecutable) { +  const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()}; +  const parser::OmpDirectiveName &dirName{beginSpec.DirName()}; + +  const Scope &thisScope{context_.FindScope(dirName.source)};    auto maybeHasPredefinedAllocator{[&](const parser::OmpClause *calloc) {      // Return "true" if the ALLOCATOR clause was provided with an argument @@ -1741,73 +1709,200 @@ void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,      return true;    }}; -  const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)}; +  const auto *allocator{[&]() { +    // Can't use FindClause in Enter (because clauses haven't been visited +    // yet). +    for (const parser::OmpClause &c : beginSpec.Clauses().v) { +      if (c.Id() == llvm::omp::Clause::OMPC_allocator) { +        return &c; +      } +    } +    return static_cast<const parser::OmpClause *>(nullptr); +  }()}; +    if (InTargetRegion()) {      bool hasDynAllocators{          HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};      if (!allocator && !hasDynAllocators) { -      context_.Say(source, +      context_.Say(dirName.source,            "An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);      }    }    auto maybePredefined{maybeHasPredefinedAllocator(allocator)}; -  for (auto &[symbol, source] : symbols) { -    if (!inExecutableAllocate_) { -      if (symbol->owner() != thisScope) { +  unsigned version{context_.langOptions().OpenMPVersion}; +  std::string condStr{version == 50 +          ? "a named common block, has SAVE attribute or is declared in the " +            "scope of a module" +          : "a named common block or has SAVE attribute"}; + +  auto checkSymbol{[&](const Symbol &symbol, parser::CharBlock source) { +    if (!isExecutable) { +      // For structure members, the scope is the derived type, which is +      // never "this" scope. Ignore this check for members, they will be +      // flagged anyway. +      if (symbol.owner() != thisScope && !IsStructureComponent(symbol)) {          context_.Say(source,              "A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);        } -      if (IsPointer(*symbol) || IsAllocatable(*symbol)) { +      if (IsPointer(symbol) || IsAllocatable(symbol)) {          context_.Say(source,              "A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);        }      } -    if (symbol->GetUltimate().has<AssocEntityDetails>()) { +    if (symbol.GetUltimate().has<AssocEntityDetails>()) {        context_.Say(source,            "A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);      } -    if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) { +    bool inModule{ +        version == 50 && symbol.owner().kind() == Scope::Kind::Module}; +    if (symbol.attrs().test(Attr::SAVE) || IsCommonBlock(symbol) || inModule) {        if (!allocator) {          context_.Say(source, -            "If a list item is a named common block or has SAVE attribute, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US); +            "If a list item is %s, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US, +            condStr);        } else if (!maybePredefined) {          context_.Say(source, -            "If a list item is a named common block or has SAVE attribute, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US); +            "If a list item is %s, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US, +            condStr);        }      } -    if (FindCommonBlockContaining(*symbol)) { +    if (FindCommonBlockContaining(symbol)) {        context_.Say(source,            "A variable that is part of a common block may not be specified as a list item in an ALLOCATE directive, except implicitly via the named common block"_err_en_US);      } +  }}; + +  for (const parser::OmpArgument &arg : beginSpec.Arguments().v) { +    const parser::OmpObject *object{GetArgumentObject(arg)}; +    if (!object) { +      context_.Say(arg.source, +          "An argument to ALLOCATE directive must be a variable list item"_err_en_US); +      continue; +    } + +    if (const Symbol *symbol{GetObjectSymbol(*object)}) { +      if (!IsTypeParamInquiry(*symbol)) { +        checkSymbol(*symbol, arg.source); +      } +      CheckVarIsNotPartOfAnotherVar(dirName.source, *object); +    }    } -  CheckVarIsNotPartOfAnotherVar(source, objects);  } -void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) { -  const auto &dir{std::get<parser::Verbatim>(x.t)}; -  PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); +void OmpStructureChecker::CheckExecutableAllocateDirective( +    const parser::OmpAllocateDirective &x) { +  parser::omp::OmpAllocateInfo info{SplitOmpAllocate(x)}; + +  auto [allocStmt, allocSource]{getAllocateStmtAndSource(info.body)}; +  if (!allocStmt) { +    // This has been diagnosed already. +    return; +  } + +  UnorderedSymbolSet allocateSyms{GetNonComponentSymbols(*allocStmt)}; +  SymbolSourceMap directiveSyms; +  bool hasEmptyList{false}; + +  for (const parser::OmpAllocateDirective *ompAlloc : info.dirs) { +    const parser::OmpDirectiveSpecification &spec{DEREF(ompAlloc).BeginDir()}; +    if (spec.Arguments().v.empty()) { +      if (hasEmptyList && info.dirs.size() > 1) { +        context_.Say(spec.DirName().source, +            "If multiple directives are present in an executable ALLOCATE directive, at most one of them may specify no list items"_err_en_US); +      } +      hasEmptyList = true; +    } +    for (const parser::OmpArgument &arg : spec.Arguments().v) { +      if (auto *sym{GetArgumentSymbol(arg)}) { +        // Ignore these checks for structure members. They are not allowed +        // in the first place, so don't tell the users that they need to +        // be specified somewhere, +        if (IsStructureComponent(*sym)) { +          continue; +        } +        if (auto f{directiveSyms.find(sym)}; f != directiveSyms.end()) { +          parser::MessageFormattedText txt( +              "A list item on an executable ALLOCATE may only be specified once"_err_en_US); +          parser::Message message(arg.source, txt); +          message.Attach(f->second, "The list item was specified here"_en_US); +          context_.Say(std::move(message)); +        } else { +          directiveSyms.insert(std::make_pair(sym, arg.source)); +        } + +        if (auto f{allocateSyms.find(*sym)}; f == allocateSyms.end()) { +          context_ +              .Say(arg.source, +                  "A list item on an executable ALLOCATE must be specified on the associated ALLOCATE statement"_err_en_US) +              .Attach(allocSource, "The ALLOCATE statement"_en_US); +        } +      } +    } +  }  } -void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) { -  if (!inExecutableAllocate_) { -    const auto &dir{std::get<parser::Verbatim>(x.t)}; -    const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; -    const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; +void OmpStructureChecker::Enter(const parser::OmpAllocateDirective &x) { +  const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()}; +  const parser::OmpDirectiveName &dirName{beginSpec.DirName()}; +  PushContextAndClauseSets(dirName.source, dirName.v); +  ++allocateDirectiveLevel; -    isPredefinedAllocator = true; -    CheckAllocateDirective(dir.source, objectList, clauseList); +  bool isExecutable{partStack_.back() == PartKind::ExecutionPart}; + +  unsigned version{context_.langOptions().OpenMPVersion}; +  if (isExecutable && allocateDirectiveLevel == 1 && version >= 52) { +    context_.Warn(common::UsageWarning::OpenMPUsage, dirName.source, +        "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);    } + +  CheckIndividualAllocateDirective(x, isExecutable); + +  if (isExecutable) { +    auto isOmpAllocate{[](const parser::ExecutionPartConstruct &epc) { +      if (auto *omp{GetOmp(epc)}) { +        auto odn{GetOmpDirectiveName(*omp)}; +        return odn.v == llvm::omp::Directive::OMPD_allocate; +      } +      return false; +    }}; + +    auto &body{std::get<parser::Block>(x.t)}; +    // The parser should put at most one statement in the body. +    assert(body.size() <= 1 && "Multiple statements in allocate"); +    if (body.empty()) { +      context_.Say(dirName.source, +          "An executable ALLOCATE directive must be associated with an ALLOCATE statement"_err_en_US); +    } else { +      const parser::ExecutionPartConstruct &first{body.front()}; +      auto [allocStmt, _]{getAllocateStmtAndSource(&body.front())}; +      if (!isOmpAllocate(first) && !allocStmt) { +        parser::CharBlock source{[&]() { +          if (auto &&maybeSource{parser::GetSource(first)}) { +            return *maybeSource; +          } +          return dirName.source; +        }()}; +        context_.Say(source, +            "The statement associated with executable ALLOCATE directive must be an ALLOCATE statement"_err_en_US); +      } +    } +  } +} + +void OmpStructureChecker::Leave(const parser::OmpAllocateDirective &x) { +  bool isExecutable{partStack_.back() == PartKind::ExecutionPart}; +  if (isExecutable && allocateDirectiveLevel == 1) { +    CheckExecutableAllocateDirective(x); +  } + +  --allocateDirectiveLevel;    dirContext_.pop_back();  }  void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) {    CheckAllowedClause(llvm::omp::Clause::OMPC_allocator); -  // Note: Predefined allocators are stored in ScalarExpr as numbers -  //   whereas custom allocators are stored as strings, so if the ScalarExpr -  //   actually has an int value, then it must be a predefined allocator -  isPredefinedAllocator = GetIntValue(x.v).has_value();    RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v);  } @@ -1823,16 +1918,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) {              "The alignment value should be a constant positive integer"_err_en_US);        }      } -    // The simple and complex modifiers have the same structure. They only -    // differ in their syntax. -    if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorComplexModifier>( -            modifiers)}) { -      isPredefinedAllocator = GetIntValue(alloc->v).has_value(); -    } -    if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>( -            modifiers)}) { -      isPredefinedAllocator = GetIntValue(alloc->v).has_value(); -    }    }  } @@ -2115,168 +2200,88 @@ void OmpStructureChecker::Enter(const parser::OmpClause::At &x) {    }  } -// Goes through the names in an OmpObjectList and checks if each name appears -// in the given allocate statement -void OmpStructureChecker::CheckAllNamesInAllocateStmt( -    const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList, -    const parser::AllocateStmt &allocate) { -  for (const auto &obj : ompObjectList.v) { -    if (const auto *d{std::get_if<parser::Designator>(&obj.u)}) { -      if (const auto *ref{std::get_if<parser::DataRef>(&d->u)}) { -        if (const auto *n{std::get_if<parser::Name>(&ref->u)}) { -          CheckNameInAllocateStmt(source, *n, allocate); -        } -      } -    } -  } -} - -void OmpStructureChecker::CheckNameInAllocateStmt( -    const parser::CharBlock &source, const parser::Name &name, -    const parser::AllocateStmt &allocate) { -  for (const auto &allocation : -      std::get<std::list<parser::Allocation>>(allocate.t)) { -    const auto &allocObj = std::get<parser::AllocateObject>(allocation.t); -    if (const auto *n{std::get_if<parser::Name>(&allocObj.u)}) { -      if (n->source == name.source) { -        return; -      } -    } -  } -  unsigned version{context_.langOptions().OpenMPVersion}; -  context_.Say(source, -      "Object '%s' in %s directive not " -      "found in corresponding ALLOCATE statement"_err_en_US, -      name.ToString(), -      parser::ToUpperCaseLetters( -          llvm::omp::getOpenMPDirectiveName(GetContext().directive, version) -              .str())); -} - -void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) { -  inExecutableAllocate_ = true; -  const auto &dir{std::get<parser::Verbatim>(x.t)}; -  PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); - -  unsigned version{context_.langOptions().OpenMPVersion}; -  if (version >= 52) { -    context_.Warn(common::UsageWarning::OpenMPUsage, x.source, -        "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US); -  } - -  const auto &allocateStmt = -      std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement; -  if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) { -    CheckAllNamesInAllocateStmt( -        std::get<parser::Verbatim>(x.t).source, *list, allocateStmt); -  } -  if (const auto &subDirs{ -          std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>( -              x.t)}) { -    for (const auto &dalloc : *subDirs) { -      CheckAllNamesInAllocateStmt(std::get<parser::Verbatim>(dalloc.t).source, -          std::get<parser::OmpObjectList>(dalloc.t), allocateStmt); -    } -  } - -  isPredefinedAllocator = true; -} +void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { +  const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()}; +  const parser::OmpDirectiveName &dirName{beginSpec.DirName()}; +  PushContextAndClauseSets( +      dirName.source, llvm::omp::Directive::OMPD_allocators); -void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { -  parser::OmpObjectList empty{std::list<parser::OmpObject>{}}; -  auto &objects{[&]() -> const parser::OmpObjectList & { -    if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) { -      return *objects; -    } else { -      return empty; +  for (const auto &clause : beginSpec.Clauses().v) { +    auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)}; +    if (!alloc) { +      continue;      } -  }()}; -  auto &clauses{std::get<parser::OmpClauseList>(x.t)}; -  CheckAllocateDirective( -      std::get<parser::Verbatim>(x.t).source, objects, clauses); - -  if (const auto &subDirs{ -          std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>( -              x.t)}) { -    for (const auto &dalloc : *subDirs) { -      const auto &dir{std::get<parser::Verbatim>(x.t)}; -      const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)}; -      const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)}; -      CheckAllocateDirective(dir.source, objects, clauses); +    using OmpAllocatorSimpleModifier = parser::OmpAllocatorSimpleModifier; +    using OmpAllocatorComplexModifier = parser::OmpAllocatorComplexModifier; + +    if (InTargetRegion()) { +      auto &modifiers{OmpGetModifiers(alloc->v)}; +      bool hasAllocator{ +          OmpGetUniqueModifier<OmpAllocatorSimpleModifier>(modifiers) || +          OmpGetUniqueModifier<OmpAllocatorComplexModifier>(modifiers)}; +      bool hasDynAllocators{ +          HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)}; +      if (!hasAllocator && !hasDynAllocators) { +        context_.Say(clause.source, +            "An ALLOCATE clause in a TARGET region must specify an allocator or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US); +      }      }    } -  dirContext_.pop_back(); -  inExecutableAllocate_ = false; -} - -void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { -  isPredefinedAllocator = true; - -  const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; -  auto &block{std::get<parser::Block>(x.t)}; -  PushContextAndClauseSets( -      dirSpec.DirName().source, llvm::omp::Directive::OMPD_allocators); - -  if (block.empty()) { -    context_.Say(dirSpec.source, -        "The ALLOCATORS construct should contain a single ALLOCATE statement"_err_en_US); +  auto &body{std::get<parser::Block>(x.t)}; +  // The parser should put at most one statement in the body. +  assert(body.size() <= 1 && "Malformed body in allocators"); +  if (body.empty()) { +    context_.Say(dirName.source, +        "The body of an ALLOCATORS construct should be an ALLOCATE statement"_err_en_US);      return;    } -  omp::SourcedActionStmt action{omp::GetActionStmt(block)}; -  const auto *allocate{ -      action ? parser::Unwrap<parser::AllocateStmt>(action.stmt) : nullptr}; - -  if (allocate) { -    for (const auto &clause : dirSpec.Clauses().v) { -      if (auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)}) { -        CheckAllNamesInAllocateStmt( -            x.source, std::get<parser::OmpObjectList>(alloc->v.t), *allocate); - -        using OmpAllocatorSimpleModifier = parser::OmpAllocatorSimpleModifier; -        using OmpAllocatorComplexModifier = parser::OmpAllocatorComplexModifier; - -        auto &modifiers{OmpGetModifiers(alloc->v)}; -        bool hasAllocator{ -            OmpGetUniqueModifier<OmpAllocatorSimpleModifier>(modifiers) || -            OmpGetUniqueModifier<OmpAllocatorComplexModifier>(modifiers)}; - -        // TODO: As with allocate directive, exclude the case when a requires -        //       directive with the dynamic_allocators clause is present in -        //       the same compilation unit (OMP5.0 2.11.3). -        if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && -            !hasAllocator) { -          context_.Say(x.source, -              "ALLOCATORS directives that appear in a TARGET region must specify an allocator"_err_en_US); -        } +  auto [allocStmt, allocSource]{getAllocateStmtAndSource(&body.front())}; +  if (!allocStmt) { +    parser::CharBlock source{[&]() { +      if (auto &&maybeSource{parser::GetSource(body.front())}) { +        return *maybeSource;        } -    } -  } else { -    const parser::CharBlock &source = action ? action.source : x.source; +      return dirName.source; +    }()};      context_.Say(source, -        "The body of the ALLOCATORS construct should be an ALLOCATE statement"_err_en_US); +        "The body of an ALLOCATORS construct should be an ALLOCATE statement"_err_en_US); +    return;    } -  for (const auto &clause : dirSpec.Clauses().v) { -    if (const auto *allocClause{ -            parser::Unwrap<parser::OmpClause::Allocate>(clause)}) { -      CheckVarIsNotPartOfAnotherVar( -          dirSpec.source, std::get<parser::OmpObjectList>(allocClause->v.t)); +  UnorderedSymbolSet allocateSyms{GetNonComponentSymbols(*allocStmt)}; +  for (const auto &clause : beginSpec.Clauses().v) { +    auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)}; +    if (!alloc) { +      continue; +    } +    for (auto &object : DEREF(GetOmpObjectList(clause)).v) { +      CheckVarIsNotPartOfAnotherVar(dirName.source, object); +      if (auto *symbol{GetObjectSymbol(object)}) { +        if (IsStructureComponent(*symbol)) { +          continue; +        } +        parser::CharBlock source{[&]() { +          if (auto &&objectSource{GetObjectSource(object)}) { +            return *objectSource; +          } +          return clause.source; +        }()}; +        if (!IsTypeParamInquiry(*symbol)) { +          if (auto f{allocateSyms.find(*symbol)}; f == allocateSyms.end()) { +            context_ +                .Say(source, +                    "A list item in an ALLOCATORS construct must be specified on the associated ALLOCATE statement"_err_en_US) +                .Attach(allocSource, "The ALLOCATE statement"_en_US); +          } +        } +      }      }    }  }  void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) { -  const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; - -  for (const auto &clause : dirSpec.Clauses().v) { -    if (const auto *allocClause{ -            std::get_if<parser::OmpClause::Allocate>(&clause.u)}) { -      CheckPredefinedAllocatorRestriction( -          dirSpec.source, std::get<parser::OmpObjectList>(allocClause->v.t)); -    } -  }    dirContext_.pop_back();  } @@ -3362,88 +3367,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {          /*paramName=*/"parameter", /*allowZero=*/false);  } -// Following clauses do not have a separate node in parse-tree.h. -CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent) -CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) -CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) -CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains) -CHECK_SIMPLE_CLAUSE(Default, OMPC_default) -CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) -CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) -CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) -CHECK_SIMPLE_CLAUSE(DynGroupprivate, OMPC_dyn_groupprivate) -CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) -CHECK_SIMPLE_CLAUSE(Final, OMPC_final) -CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) -CHECK_SIMPLE_CLAUSE(Full, OMPC_full) -CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize) -CHECK_SIMPLE_CLAUSE(GraphId, OMPC_graph_id) -CHECK_SIMPLE_CLAUSE(GraphReset, OMPC_graph_reset) -CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds) -CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) -CHECK_SIMPLE_CLAUSE(Initializer, OMPC_initializer) -CHECK_SIMPLE_CLAUSE(Match, OMPC_match) -CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) -CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) -CHECK_SIMPLE_CLAUSE(Order, OMPC_order) -CHECK_SIMPLE_CLAUSE(Read, OMPC_read) -CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) -CHECK_SIMPLE_CLAUSE(Groupprivate, OMPC_groupprivate) -CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) -CHECK_SIMPLE_CLAUSE(Threadset, OMPC_threadset) -CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) -CHECK_SIMPLE_CLAUSE(Link, OMPC_link) -CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect) -CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) -CHECK_SIMPLE_CLAUSE(NoOpenmp, OMPC_no_openmp) -CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines, OMPC_no_openmp_routines) -CHECK_SIMPLE_CLAUSE(NoOpenmpConstructs, OMPC_no_openmp_constructs) -CHECK_SIMPLE_CLAUSE(NoParallelism, OMPC_no_parallelism) -CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) -CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) -CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial) -CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) -CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) -CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation) -CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) -CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) -CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) -CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) -CHECK_SIMPLE_CLAUSE(Write, OMPC_write) -CHECK_SIMPLE_CLAUSE(Init, OMPC_init) -CHECK_SIMPLE_CLAUSE(Use, OMPC_use) -CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) -CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) -CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity) -CHECK_SIMPLE_CLAUSE(Message, OMPC_message) -CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) -CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise) -CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) -CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args) -CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order) -CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind) -CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare) -CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute) -CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak) -CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) -CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) -CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) -CHECK_SIMPLE_CLAUSE(Release, OMPC_release) -CHECK_SIMPLE_CLAUSE(Replayable, OMPC_replayable) -CHECK_SIMPLE_CLAUSE(Transparent, OMPC_transparent) -CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) -CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail) - -CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) -CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) -CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem) -CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) -CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) - -CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) -CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) -CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) -  void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {    context_.Say(GetContext().clauseSource,        "LOOPRANGE clause is not implemented yet"_err_en_US, @@ -5516,4 +5439,104 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {    }  } +// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'. +#define CHECK_SIMPLE_CLAUSE(X, Y) \ +  void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \ +    CheckAllowedClause(llvm::omp::Clause::Y); \ +  } + +#define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \ +  void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ +    CheckAllowedClause(llvm::omp::Clause::Y); \ +    RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \ +  } + +#define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \ +  void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ +    CheckAllowedClause(llvm::omp::Clause::Y); \ +    RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \ +  } + +// Following clauses do not have a separate node in parse-tree.h. +CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent) +CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) +CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) +CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) +CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) +CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args) +CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind) +CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) +CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare) +CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains) +CHECK_SIMPLE_CLAUSE(Default, OMPC_default) +CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) +CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) +CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) +CHECK_SIMPLE_CLAUSE(DynGroupprivate, OMPC_dyn_groupprivate) +CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) +CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail) +CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) +CHECK_SIMPLE_CLAUSE(Final, OMPC_final) +CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) +CHECK_SIMPLE_CLAUSE(Full, OMPC_full) +CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize) +CHECK_SIMPLE_CLAUSE(GraphId, OMPC_graph_id) +CHECK_SIMPLE_CLAUSE(GraphReset, OMPC_graph_reset) +CHECK_SIMPLE_CLAUSE(Groupprivate, OMPC_groupprivate) +CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds) +CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) +CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) +CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect) +CHECK_SIMPLE_CLAUSE(Initializer, OMPC_initializer) +CHECK_SIMPLE_CLAUSE(Init, OMPC_init) +CHECK_SIMPLE_CLAUSE(Link, OMPC_link) +CHECK_SIMPLE_CLAUSE(Match, OMPC_match) +CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order) +CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) +CHECK_SIMPLE_CLAUSE(Message, OMPC_message) +CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) +CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) +CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) +CHECK_SIMPLE_CLAUSE(NoOpenmpConstructs, OMPC_no_openmp_constructs) +CHECK_SIMPLE_CLAUSE(NoOpenmp, OMPC_no_openmp) +CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines, OMPC_no_openmp_routines) +CHECK_SIMPLE_CLAUSE(NoParallelism, OMPC_no_parallelism) +CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) +CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) +CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) +CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute) +CHECK_SIMPLE_CLAUSE(Order, OMPC_order) +CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise) +CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial) +CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation) +CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) +CHECK_SIMPLE_CLAUSE(Read, OMPC_read) +CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) +CHECK_SIMPLE_CLAUSE(Release, OMPC_release) +CHECK_SIMPLE_CLAUSE(Replayable, OMPC_replayable) +CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) +CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity) +CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) +CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) +CHECK_SIMPLE_CLAUSE(Threadset, OMPC_threadset) +CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) +CHECK_SIMPLE_CLAUSE(Transparent, OMPC_transparent) +CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) +CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) +CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) +CHECK_SIMPLE_CLAUSE(Use, OMPC_use) +CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) +CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak) +CHECK_SIMPLE_CLAUSE(Write, OMPC_write) + +CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) +CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) +CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem) +CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) +CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) + +CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) +CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) +CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) +  } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index 7426559..1b84bc5 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -82,6 +82,11 @@ public:    bool Enter(const parser::BlockConstruct &);    void Leave(const parser::BlockConstruct &); +  void Enter(const parser::SpecificationPart &); +  void Leave(const parser::SpecificationPart &); +  void Enter(const parser::ExecutionPart &); +  void Leave(const parser::ExecutionPart &); +    void Enter(const parser::OpenMPConstruct &);    void Leave(const parser::OpenMPConstruct &);    void Enter(const parser::OpenMPInteropConstruct &); @@ -113,8 +118,8 @@ public:    void Leave(const parser::OmpDeclareVariantDirective &);    void Enter(const parser::OpenMPDeclareSimdConstruct &);    void Leave(const parser::OpenMPDeclareSimdConstruct &); -  void Enter(const parser::OpenMPDeclarativeAllocate &); -  void Leave(const parser::OpenMPDeclarativeAllocate &); +  void Enter(const parser::OmpAllocateDirective &); +  void Leave(const parser::OmpAllocateDirective &);    void Enter(const parser::OpenMPDeclareMapperConstruct &);    void Leave(const parser::OpenMPDeclareMapperConstruct &);    void Enter(const parser::OpenMPDeclareReductionConstruct &); @@ -129,8 +134,6 @@ public:    void Leave(const parser::OmpErrorDirective &);    void Enter(const parser::OmpNothingDirective &);    void Leave(const parser::OmpNothingDirective &); -  void Enter(const parser::OpenMPExecutableAllocate &); -  void Leave(const parser::OpenMPExecutableAllocate &);    void Enter(const parser::OpenMPAllocatorsConstruct &);    void Leave(const parser::OpenMPAllocatorsConstruct &);    void Enter(const parser::OpenMPRequiresConstruct &); @@ -263,9 +266,9 @@ private:    bool CheckTargetBlockOnlyTeams(const parser::Block &);    void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);    void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock); -  void CheckAllocateDirective(parser::CharBlock source, -      const parser::OmpObjectList &objects, -      const parser::OmpClauseList &clauses); +  void CheckIndividualAllocateDirective( +      const parser::OmpAllocateDirective &x, bool isExecutable); +  void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);    void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);    void CheckIteratorModifier(const parser::OmpIterator &x); @@ -325,11 +328,6 @@ private:        const std::optional<parser::OmpClauseList> &maybeClauses);    void CheckCancellationNest(        const parser::CharBlock &source, llvm::omp::Directive type); -  void CheckAllNamesInAllocateStmt(const parser::CharBlock &source, -      const parser::OmpObjectList &ompObjectList, -      const parser::AllocateStmt &allocate); -  void CheckNameInAllocateStmt(const parser::CharBlock &source, -      const parser::Name &ompObject, const parser::AllocateStmt &allocate);    std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);    void CheckReductionObjects(        const parser::OmpObjectList &objects, llvm::omp::Clause clauseId); @@ -353,11 +351,6 @@ private:        const parser::OmpObjectList &ompObjectList);    void CheckIfContiguous(const parser::OmpObject &object);    const parser::Name *GetObjectName(const parser::OmpObject &object); -  void CheckPredefinedAllocatorRestriction(const parser::CharBlock &source, -      const parser::OmpObjectList &ompObjectList); -  void CheckPredefinedAllocatorRestriction( -      const parser::CharBlock &source, const parser::Name &name); -  bool isPredefinedAllocator{false};    void CheckAllowedRequiresClause(llvmOmpClause clause);    bool deviceConstructFound_{false}; @@ -383,7 +376,7 @@ private:    };    int directiveNest_[LastType + 1] = {0}; -  bool inExecutableAllocate_{false}; +  int allocateDirectiveLevel{0};    parser::CharBlock visitedAtomicSource_;    SymbolSourceMap deferredNonVariables_; @@ -392,6 +385,14 @@ private:    std::vector<LoopConstruct> loopStack_;    // Scopes for scoping units.    std::vector<const Scope *> scopeStack_; + +  enum class PartKind : int { +    // There are also other "parts", such as internal-subprogram-part, etc, +    // but we're keeping track of these two for now. +    SpecificationPart, +    ExecutionPart, +  }; +  std::vector<PartKind> partStack_;  };  /// Find a duplicate entry in the range, and return an iterator to it. diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp index 6b304b6..4a40d6e 100644 --- a/flang/lib/Semantics/openmp-utils.cpp +++ b/flang/lib/Semantics/openmp-utils.cpp @@ -186,6 +186,23 @@ bool IsExtendedListItem(const Symbol &sym) {    return IsVariableListItem(sym) || sym.IsSubprogram();  } +bool IsTypeParamInquiry(const Symbol &sym) { +  return common::visit( // +      common::visitors{ +          [&](const MiscDetails &d) { +            return d.kind() == MiscDetails::Kind::KindParamInquiry || +                d.kind() == MiscDetails::Kind::LenParamInquiry; +          }, +          [&](const TypeParamDetails &s) { return true; }, +          [&](auto &&) { return false; }, +      }, +      sym.details()); +} + +bool IsStructureComponent(const Symbol &sym) { +  return sym.owner().kind() == Scope::Kind::DerivedType; +} +  bool IsVarOrFunctionRef(const MaybeExpr &expr) {    if (expr) {      return evaluate::UnwrapProcedureRef(*expr) != nullptr || diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 628068f..deb57e0 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -415,6 +415,18 @@ public:      return true;    } +  bool Pre(const parser::SpecificationPart &) { +    partStack_.push_back(PartKind::SpecificationPart); +    return true; +  } +  void Post(const parser::SpecificationPart &) { partStack_.pop_back(); } + +  bool Pre(const parser::ExecutionPart &) { +    partStack_.push_back(PartKind::ExecutionPart); +    return true; +  } +  void Post(const parser::ExecutionPart &) { partStack_.pop_back(); } +    bool Pre(const parser::InternalSubprogram &) {      // Clear the labels being tracked in the previous scope      ClearLabels(); @@ -639,8 +651,7 @@ public:    bool Pre(const parser::OpenMPThreadprivate &);    void Post(const parser::OpenMPThreadprivate &) { PopContext(); } -  bool Pre(const parser::OpenMPDeclarativeAllocate &); -  void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); } +  bool Pre(const parser::OmpAllocateDirective &);    bool Pre(const parser::OpenMPAssumeConstruct &);    void Post(const parser::OpenMPAssumeConstruct &) { PopContext(); } @@ -651,9 +662,6 @@ public:    bool Pre(const parser::OpenMPDispatchConstruct &);    void Post(const parser::OpenMPDispatchConstruct &) { PopContext(); } -  bool Pre(const parser::OpenMPExecutableAllocate &); -  void Post(const parser::OpenMPExecutableAllocate &); -    bool Pre(const parser::OpenMPAllocatorsConstruct &);    void Post(const parser::OpenMPAllocatorsConstruct &); @@ -998,6 +1006,14 @@ private:        targetLabels_;    parser::CharBlock currentStatementSource_; +  enum class PartKind : int { +    // There are also other "parts", such as internal-subprogram-part, etc, +    // but we're keeping track of these two for now. +    SpecificationPart, +    ExecutionPart, +  }; +  std::vector<PartKind> partStack_; +    void AddAllocateName(const parser::Name *&object) {      allocateNames_.push_back(object);    } @@ -2558,10 +2574,24 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {    return true;  } -bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) { +bool OmpAttributeVisitor::Pre(const parser::OmpAllocateDirective &x) {    PushContext(x.source, llvm::omp::Directive::OMPD_allocate); -  const auto &list{std::get<parser::OmpObjectList>(x.t)}; -  ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective); +  assert(!partStack_.empty() && "Misplaced directive"); + +  auto ompFlag{partStack_.back() == PartKind::SpecificationPart +          ? Symbol::Flag::OmpDeclarativeAllocateDirective +          : Symbol::Flag::OmpExecutableAllocateDirective}; + +  parser::omp::OmpAllocateInfo info{parser::omp::SplitOmpAllocate(x)}; +  for (const parser::OmpAllocateDirective *ad : info.dirs) { +    for (const parser::OmpArgument &arg : ad->BeginDir().Arguments().v) { +      if (auto *object{omp::GetArgumentObject(arg)}) { +        ResolveOmpObject(*object, ompFlag); +      } +    } +  } + +  PopContext();    return false;  } @@ -2580,15 +2610,6 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) {    return true;  } -bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { -  PushContext(x.source, llvm::omp::Directive::OMPD_allocate); -  const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}; -  if (list) { -    ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective); -  } -  return true; -} -  bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) {    const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};    PushContext(x.source, dirSpec.DirId()); @@ -2660,10 +2681,6 @@ bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {    return false;  } -void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) { -  PopContext(); -} -  void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {    PopContext();  } diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 220f1c9..a2062ef 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1700,12 +1700,12 @@ public:    void Post(const parser::OpenMPDeclareTargetConstruct &) {      SkipImplicitTyping(false);    } -  bool Pre(const parser::OpenMPDeclarativeAllocate &x) { +  bool Pre(const parser::OmpAllocateDirective &x) {      AddOmpSourceRange(x.source);      SkipImplicitTyping(true);      return true;    } -  void Post(const parser::OpenMPDeclarativeAllocate &) { +  void Post(const parser::OmpAllocateDirective &) {      SkipImplicitTyping(false);      messageHandler().set_currStmtSource(std::nullopt);    }  | 
