//===-- lib/Semantics/canonicalize-directives.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 "canonicalize-directives.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Semantics/tools.h" namespace Fortran::semantics { using namespace parser::literals; // Check that directives are associated with the correct constructs. // Directives that need to be associated with other constructs in the execution // part are moved to the execution part so they can be checked there. class CanonicalizationOfDirectives { public: CanonicalizationOfDirectives(parser::Messages &messages) : messages_{messages} {} template bool Pre(T &) { return true; } template void Post(T &) {} // Move directives that must appear in the Execution part out of the // Specification part. void Post(parser::SpecificationPart &spec); bool Pre(parser::ExecutionPart &x); // Ensure that directives associated with constructs appear accompanying the // construct. void Post(parser::Block &block); private: // Ensure that loop directives appear immediately before a loop. void CheckLoopDirective(parser::CompilerDirective &dir, parser::Block &block, std::list::iterator it); parser::Messages &messages_; // Directives to be moved to the Execution part from the Specification part. std::list> directivesToConvert_; }; bool CanonicalizeDirectives( parser::Messages &messages, parser::Program &program) { CanonicalizationOfDirectives dirs{messages}; Walk(program, dirs); return !messages.AnyFatalError(); } static bool IsExecutionDirective(const parser::CompilerDirective &dir) { return std::holds_alternative( dir.u) || std::holds_alternative(dir.u) || std::holds_alternative(dir.u) || std::holds_alternative(dir.u) || std::holds_alternative(dir.u) || std::holds_alternative(dir.u); } void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) { auto &list{ std::get>>( spec.t)}; for (auto it{list.begin()}; it != list.end();) { if (IsExecutionDirective(it->value())) { directivesToConvert_.emplace_back(std::move(*it)); it = list.erase(it); } else { ++it; } } } bool CanonicalizationOfDirectives::Pre(parser::ExecutionPart &x) { auto origFirst{x.v.begin()}; for (auto &dir : directivesToConvert_) { x.v.insert(origFirst, parser::ExecutionPartConstruct{ parser::ExecutableConstruct{std::move(dir)}}); } directivesToConvert_.clear(); return true; } void CanonicalizationOfDirectives::CheckLoopDirective( parser::CompilerDirective &dir, parser::Block &block, std::list::iterator it) { // Skip over this and other compiler directives while (it != block.end() && parser::Unwrap(*it)) { ++it; } if (it == block.end() || (!parser::Unwrap(*it) && !parser::Unwrap(*it) && !parser::Unwrap(*it))) { std::string s{parser::ToUpperCaseLetters(dir.source.ToString())}; s.pop_back(); // Remove trailing newline from source string messages_.Say( dir.source, "A DO loop must follow the %s directive"_warn_en_US, s); } } void CanonicalizationOfDirectives::Post(parser::Block &block) { for (auto it{block.begin()}; it != block.end(); ++it) { if (auto *dir{parser::Unwrap(*it)}) { std::visit( common::visitors{[&](parser::CompilerDirective::VectorAlways &) { CheckLoopDirective(*dir, block, it); }, [&](parser::CompilerDirective::Unroll &) { CheckLoopDirective(*dir, block, it); }, [&](parser::CompilerDirective::UnrollAndJam &) { CheckLoopDirective(*dir, block, it); }, [&](parser::CompilerDirective::NoVector &) { CheckLoopDirective(*dir, block, it); }, [&](parser::CompilerDirective::NoUnroll &) { CheckLoopDirective(*dir, block, it); }, [&](parser::CompilerDirective::NoUnrollAndJam &) { CheckLoopDirective(*dir, block, it); }, [&](auto &) {}}, dir->u); } } } } // namespace Fortran::semantics