| //===-- lib/Semantics/program-tree.cpp ------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Semantics/program-tree.h" |
| #include "flang/Common/idioms.h" |
| #include "flang/Parser/char-block.h" |
| #include "flang/Semantics/scope.h" |
| #include "flang/Semantics/semantics.h" |
| |
| namespace Fortran::semantics { |
| |
| static void GetEntryStmts( |
| ProgramTree &node, const parser::SpecificationPart &spec) { |
| const auto &implicitPart{std::get<parser::ImplicitPart>(spec.t)}; |
| for (const parser::ImplicitPartStmt &stmt : implicitPart.v) { |
| if (const auto *entryStmt{std::get_if< |
| parser::Statement<common::Indirection<parser::EntryStmt>>>( |
| &stmt.u)}) { |
| node.AddEntry(entryStmt->statement.value()); |
| } |
| } |
| for (const auto &decl : |
| std::get<std::list<parser::DeclarationConstruct>>(spec.t)) { |
| if (const auto *entryStmt{std::get_if< |
| parser::Statement<common::Indirection<parser::EntryStmt>>>( |
| &decl.u)}) { |
| node.AddEntry(entryStmt->statement.value()); |
| } |
| } |
| } |
| |
| static void GetEntryStmts( |
| ProgramTree &node, const parser::ExecutionPart &exec) { |
| for (const auto &epConstruct : exec.v) { |
| if (const auto *entryStmt{std::get_if< |
| parser::Statement<common::Indirection<parser::EntryStmt>>>( |
| &epConstruct.u)}) { |
| node.AddEntry(entryStmt->statement.value()); |
| } |
| } |
| } |
| |
| // Collects generics that define simple names that could include |
| // identically-named subprograms as specific procedures. |
| static void GetGenerics( |
| ProgramTree &node, const parser::SpecificationPart &spec) { |
| for (const auto &decl : |
| std::get<std::list<parser::DeclarationConstruct>>(spec.t)) { |
| if (const auto *spec{ |
| std::get_if<parser::SpecificationConstruct>(&decl.u)}) { |
| if (const auto *generic{std::get_if< |
| parser::Statement<common::Indirection<parser::GenericStmt>>>( |
| &spec->u)}) { |
| const parser::GenericStmt &genericStmt{generic->statement.value()}; |
| const auto &genericSpec{std::get<parser::GenericSpec>(genericStmt.t)}; |
| node.AddGeneric(genericSpec); |
| } else if (const auto *interface{ |
| std::get_if<common::Indirection<parser::InterfaceBlock>>( |
| &spec->u)}) { |
| const parser::InterfaceBlock &interfaceBlock{interface->value()}; |
| const parser::InterfaceStmt &interfaceStmt{ |
| std::get<parser::Statement<parser::InterfaceStmt>>(interfaceBlock.t) |
| .statement}; |
| const auto *genericSpec{ |
| std::get_if<std::optional<parser::GenericSpec>>(&interfaceStmt.u)}; |
| if (genericSpec && genericSpec->has_value()) { |
| node.AddGeneric(**genericSpec); |
| } |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| static ProgramTree BuildSubprogramTree( |
| const parser::Name &name, SemanticsContext &context, const T &x) { |
| const auto &spec{std::get<parser::SpecificationPart>(x.t)}; |
| const auto &exec{std::get<parser::ExecutionPart>(x.t)}; |
| const auto &subps{ |
| std::get<std::optional<parser::InternalSubprogramPart>>(x.t)}; |
| ProgramTree node{name, spec, &exec}; |
| GetEntryStmts(node, spec); |
| GetEntryStmts(node, exec); |
| GetGenerics(node, spec); |
| if (subps) { |
| for (const auto &subp : |
| std::get<std::list<parser::InternalSubprogram>>(subps->t)) { |
| common::visit( |
| [&](const auto &y) { |
| if (auto child{ProgramTree::Build(y.value(), context)}) { |
| node.AddChild(std::move(*child)); |
| } |
| }, |
| subp.u); |
| } |
| } |
| return node; |
| } |
| |
| static ProgramTree BuildSubprogramTree( |
| const parser::Name &name, SemanticsContext &, const parser::BlockData &x) { |
| const auto &spec{std::get<parser::SpecificationPart>(x.t)}; |
| return ProgramTree{name, spec}; |
| } |
| |
| template <typename T> |
| static ProgramTree BuildModuleTree( |
| const parser::Name &name, SemanticsContext &context, const T &x) { |
| const auto &spec{std::get<parser::SpecificationPart>(x.t)}; |
| const auto &subps{std::get<std::optional<parser::ModuleSubprogramPart>>(x.t)}; |
| ProgramTree node{name, spec}; |
| GetGenerics(node, spec); |
| if (subps) { |
| for (const auto &subp : |
| std::get<std::list<parser::ModuleSubprogram>>(subps->t)) { |
| common::visit( |
| [&](const auto &y) { |
| if (auto child{ProgramTree::Build(y.value(), context)}) { |
| node.AddChild(std::move(*child)); |
| } |
| }, |
| subp.u); |
| } |
| } |
| return node; |
| } |
| |
| ProgramTree &ProgramTree::Build( |
| const parser::ProgramUnit &x, SemanticsContext &context) { |
| return common::visit( |
| [&](const auto &y) -> ProgramTree & { |
| auto node{Build(y.value(), context)}; |
| CHECK(node.has_value()); |
| return context.SaveProgramTree(std::move(*node)); |
| }, |
| x.u); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::MainProgram &x, SemanticsContext &context) { |
| const auto &stmt{ |
| std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndProgramStmt>>(x.t)}; |
| static parser::Name emptyName; |
| auto result{stmt |
| ? BuildSubprogramTree(stmt->statement.v, context, x).set_stmt(*stmt) |
| : BuildSubprogramTree(emptyName, context, x)}; |
| return std::move(result.set_endStmt(end)); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::FunctionSubprogram &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::FunctionStmt>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndFunctionStmt>>(x.t)}; |
| const auto &name{std::get<parser::Name>(stmt.statement.t)}; |
| const parser::LanguageBindingSpec *bindingSpec{}; |
| if (const auto &suffix{ |
| std::get<std::optional<parser::Suffix>>(stmt.statement.t)}) { |
| if (suffix->binding) { |
| bindingSpec = &*suffix->binding; |
| } |
| } |
| return BuildSubprogramTree(name, context, x) |
| .set_stmt(stmt) |
| .set_endStmt(end) |
| .set_bindingSpec(bindingSpec); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::SubroutineSubprogram &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::SubroutineStmt>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndSubroutineStmt>>(x.t)}; |
| const auto &name{std::get<parser::Name>(stmt.statement.t)}; |
| const parser::LanguageBindingSpec *bindingSpec{}; |
| if (const auto &binding{std::get<std::optional<parser::LanguageBindingSpec>>( |
| stmt.statement.t)}) { |
| bindingSpec = &*binding; |
| } |
| return BuildSubprogramTree(name, context, x) |
| .set_stmt(stmt) |
| .set_endStmt(end) |
| .set_bindingSpec(bindingSpec); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::SeparateModuleSubprogram &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::MpSubprogramStmt>>(x.t)}; |
| const auto &end{ |
| std::get<parser::Statement<parser::EndMpSubprogramStmt>>(x.t)}; |
| const auto &name{stmt.statement.v}; |
| return BuildSubprogramTree(name, context, x).set_stmt(stmt).set_endStmt(end); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::Module &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::ModuleStmt>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndModuleStmt>>(x.t)}; |
| const auto &name{stmt.statement.v}; |
| return BuildModuleTree(name, context, x).set_stmt(stmt).set_endStmt(end); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::Submodule &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::SubmoduleStmt>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndSubmoduleStmt>>(x.t)}; |
| const auto &name{std::get<parser::Name>(stmt.statement.t)}; |
| return BuildModuleTree(name, context, x).set_stmt(stmt).set_endStmt(end); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::BlockData &x, SemanticsContext &context) { |
| const auto &stmt{std::get<parser::Statement<parser::BlockDataStmt>>(x.t)}; |
| const auto &end{std::get<parser::Statement<parser::EndBlockDataStmt>>(x.t)}; |
| static parser::Name emptyName; |
| auto result{stmt.statement.v |
| ? BuildSubprogramTree(*stmt.statement.v, context, x) |
| : BuildSubprogramTree(emptyName, context, x)}; |
| return std::move(result.set_stmt(stmt).set_endStmt(end)); |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::CompilerDirective &x, SemanticsContext &context) { |
| if (context.ShouldWarn(common::UsageWarning::IgnoredDirective)) { |
| context.Say(x.source, "Compiler directive ignored here"_warn_en_US); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<ProgramTree> ProgramTree::Build( |
| const parser::OpenACCRoutineConstruct &, SemanticsContext &) { |
| DIE("ProgramTree::Build() called for OpenACCRoutineConstruct"); |
| } |
| |
| const parser::ParentIdentifier &ProgramTree::GetParentId() const { |
| const auto *stmt{ |
| std::get<const parser::Statement<parser::SubmoduleStmt> *>(stmt_)}; |
| return std::get<parser::ParentIdentifier>(stmt->statement.t); |
| } |
| |
| bool ProgramTree::IsModule() const { |
| auto kind{GetKind()}; |
| return kind == Kind::Module || kind == Kind::Submodule; |
| } |
| |
| Symbol::Flag ProgramTree::GetSubpFlag() const { |
| return GetKind() == Kind::Function ? Symbol::Flag::Function |
| : Symbol::Flag::Subroutine; |
| } |
| |
| bool ProgramTree::HasModulePrefix() const { |
| if (std::holds_alternative< |
| const parser::Statement<parser::MpSubprogramStmt> *>(stmt_)) { |
| return true; // MODULE PROCEDURE foo |
| } |
| using ListType = std::list<parser::PrefixSpec>; |
| const auto *prefixes{common::visit( |
| common::visitors{ |
| [](const parser::Statement<parser::FunctionStmt> *x) { |
| return &std::get<ListType>(x->statement.t); |
| }, |
| [](const parser::Statement<parser::SubroutineStmt> *x) { |
| return &std::get<ListType>(x->statement.t); |
| }, |
| [](const auto *) -> const ListType * { return nullptr; }, |
| }, |
| stmt_)}; |
| if (prefixes) { |
| for (const auto &prefix : *prefixes) { |
| if (std::holds_alternative<parser::PrefixSpec::Module>(prefix.u)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| ProgramTree::Kind ProgramTree::GetKind() const { |
| return common::visit( |
| common::visitors{ |
| [](const parser::Statement<parser::ProgramStmt> *) { |
| return Kind::Program; |
| }, |
| [](const parser::Statement<parser::FunctionStmt> *) { |
| return Kind::Function; |
| }, |
| [](const parser::Statement<parser::SubroutineStmt> *) { |
| return Kind::Subroutine; |
| }, |
| [](const parser::Statement<parser::MpSubprogramStmt> *) { |
| return Kind::MpSubprogram; |
| }, |
| [](const parser::Statement<parser::ModuleStmt> *) { |
| return Kind::Module; |
| }, |
| [](const parser::Statement<parser::SubmoduleStmt> *) { |
| return Kind::Submodule; |
| }, |
| [](const parser::Statement<parser::BlockDataStmt> *) { |
| return Kind::BlockData; |
| }, |
| }, |
| stmt_); |
| } |
| |
| void ProgramTree::set_scope(Scope &scope) { |
| scope_ = &scope; |
| CHECK(endStmt_); |
| scope.AddSourceRange(*endStmt_); |
| } |
| |
| void ProgramTree::AddChild(ProgramTree &&child) { |
| children_.emplace_back(std::move(child)); |
| } |
| |
| void ProgramTree::AddEntry(const parser::EntryStmt &entryStmt) { |
| entryStmts_.emplace_back(entryStmt); |
| } |
| |
| void ProgramTree::AddGeneric(const parser::GenericSpec &generic) { |
| genericSpecs_.emplace_back(generic); |
| } |
| |
| } // namespace Fortran::semantics |