| //===-- 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 (const auto &binding{ | |
| std::get<std::optional<parser::LanguageBindingSpec>>(suffix->t)}) { | |
| bindingSpec = &*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 |