496 lines · cpp
1//===-- lib/Parser/parse-tree.cpp -----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Parser/parse-tree.h"10 11#include "flang/Common/idioms.h"12#include "flang/Common/indirection.h"13#include "flang/Parser/openmp-utils.h"14#include "flang/Parser/tools.h"15#include "flang/Parser/user-state.h"16#include "llvm/ADT/ArrayRef.h"17#include "llvm/Frontend/OpenMP/OMP.h"18#include "llvm/Support/raw_ostream.h"19#include <algorithm>20 21namespace Fortran::parser {22 23// R86724ImportStmt::ImportStmt(common::ImportKind &&k, std::list<Name> &&n)25 : kind{k}, names(std::move(n)) {26 CHECK(kind == common::ImportKind::Default ||27 kind == common::ImportKind::Only || names.empty());28}29 30// R87331CommonStmt::CommonStmt(std::optional<Name> &&name,32 std::list<CommonBlockObject> &&objects, std::list<Block> &&others) {33 blocks.emplace_front(std::move(name), std::move(objects));34 blocks.splice(blocks.end(), std::move(others));35}36 37// R901 designator38bool Designator::EndsInBareName() const {39 return common::visit(40 common::visitors{41 [](const DataRef &dr) {42 return std::holds_alternative<Name>(dr.u) ||43 std::holds_alternative<common::Indirection<StructureComponent>>(44 dr.u);45 },46 [](const Substring &) { return false; },47 },48 u);49}50 51// R911 data-ref -> part-ref [% part-ref]...52DataRef::DataRef(std::list<PartRef> &&prl) : u{std::move(prl.front().name)} {53 for (bool first{true}; !prl.empty(); first = false, prl.pop_front()) {54 PartRef &pr{prl.front()};55 if (!first) {56 u = common::Indirection<StructureComponent>::Make(57 std::move(*this), std::move(pr.name));58 }59 if (!pr.subscripts.empty()) {60 u = common::Indirection<ArrayElement>::Make(61 std::move(*this), std::move(pr.subscripts));62 }63 if (pr.imageSelector) {64 u = common::Indirection<CoindexedNamedObject>::Make(65 std::move(*this), std::move(*pr.imageSelector));66 }67 }68}69 70// R1001 - R1022 expression71Expr::Expr(Designator &&x)72 : u{common::Indirection<Designator>::Make(std::move(x))} {}73Expr::Expr(FunctionReference &&x)74 : u{common::Indirection<FunctionReference>::Make(std::move(x))} {}75 76const std::optional<LoopControl> &DoConstruct::GetLoopControl() const {77 const NonLabelDoStmt &doStmt{78 std::get<Statement<NonLabelDoStmt>>(t).statement};79 const std::optional<LoopControl> &control{80 std::get<std::optional<LoopControl>>(doStmt.t)};81 return control;82}83 84bool DoConstruct::IsDoNormal() const {85 const std::optional<LoopControl> &control{GetLoopControl()};86 return control && std::holds_alternative<LoopControl::Bounds>(control->u);87}88 89bool DoConstruct::IsDoWhile() const {90 const std::optional<LoopControl> &control{GetLoopControl()};91 return control && std::holds_alternative<ScalarLogicalExpr>(control->u);92}93 94bool DoConstruct::IsDoConcurrent() const {95 const std::optional<LoopControl> &control{GetLoopControl()};96 return control && std::holds_alternative<LoopControl::Concurrent>(control->u);97}98 99static Designator MakeArrayElementRef(100 const Name &name, std::list<Expr> &&subscripts) {101 ArrayElement arrayElement{DataRef{Name{name}}, std::list<SectionSubscript>{}};102 for (Expr &expr : subscripts) {103 arrayElement.subscripts.push_back(104 SectionSubscript{Integer{common::Indirection{std::move(expr)}}});105 }106 return Designator{DataRef{common::Indirection{std::move(arrayElement)}}};107}108 109static Designator MakeArrayElementRef(110 StructureComponent &&sc, std::list<Expr> &&subscripts) {111 ArrayElement arrayElement{DataRef{common::Indirection{std::move(sc)}},112 std::list<SectionSubscript>{}};113 for (Expr &expr : subscripts) {114 arrayElement.subscripts.push_back(115 SectionSubscript{Integer{common::Indirection{std::move(expr)}}});116 }117 return Designator{DataRef{common::Indirection{std::move(arrayElement)}}};118}119 120// Set source in any type of node that has it.121template <typename T> T WithSource(CharBlock source, T &&x) {122 x.source = source;123 return std::move(x);124}125 126static Expr ActualArgToExpr(ActualArgSpec &arg) {127 return common::visit(128 common::visitors{129 [&](common::Indirection<Expr> &y) { return std::move(y.value()); },130 [&](common::Indirection<Variable> &y) {131 return common::visit(132 common::visitors{133 [&](common::Indirection<Designator> &z) {134 return WithSource(135 z.value().source, Expr{std::move(z.value())});136 },137 [&](common::Indirection<FunctionReference> &z) {138 return WithSource(139 z.value().source, Expr{std::move(z.value())});140 },141 },142 y.value().u);143 },144 [&](auto &) -> Expr { common::die("unexpected type"); },145 },146 std::get<ActualArg>(arg.t).u);147}148 149Designator FunctionReference::ConvertToArrayElementRef() {150 std::list<Expr> args;151 for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) {152 args.emplace_back(ActualArgToExpr(arg));153 }154 return common::visit(155 common::visitors{156 [&](const Name &name) {157 return WithSource(158 source, MakeArrayElementRef(name, std::move(args)));159 },160 [&](ProcComponentRef &pcr) {161 return WithSource(source,162 MakeArrayElementRef(std::move(pcr.v.thing), std::move(args)));163 },164 },165 std::get<ProcedureDesignator>(v.t).u);166}167 168StructureConstructor FunctionReference::ConvertToStructureConstructor(169 const semantics::DerivedTypeSpec &derived) {170 Name name{std::get<parser::Name>(std::get<ProcedureDesignator>(v.t).u)};171 std::list<ComponentSpec> components;172 for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) {173 std::optional<Keyword> keyword;174 if (auto &kw{std::get<std::optional<Keyword>>(arg.t)}) {175 keyword.emplace(Keyword{Name{kw->v}});176 }177 components.emplace_back(178 std::move(keyword), ComponentDataSource{ActualArgToExpr(arg)});179 }180 DerivedTypeSpec spec{std::move(name), std::list<TypeParamSpec>{}};181 spec.derivedTypeSpec = &derived;182 return StructureConstructor{std::move(spec), std::move(components)};183}184 185StructureConstructor ArrayElement::ConvertToStructureConstructor(186 const semantics::DerivedTypeSpec &derived) {187 Name name{std::get<parser::Name>(base.u)};188 std::list<ComponentSpec> components;189 for (auto &subscript : subscripts) {190 components.emplace_back(std::optional<Keyword>{},191 ComponentDataSource{std::move(UnwrapRef<Expr>(subscript))});192 }193 DerivedTypeSpec spec{std::move(name), std::list<TypeParamSpec>{}};194 spec.derivedTypeSpec = &derived;195 return StructureConstructor{std::move(spec), std::move(components)};196}197 198Substring ArrayElement::ConvertToSubstring() {199 auto iter{subscripts.begin()};200 CHECK(iter != subscripts.end());201 auto &triplet{std::get<SubscriptTriplet>(iter->u)};202 CHECK(!std::get<2>(triplet.t));203 CHECK(++iter == subscripts.end());204 return Substring{std::move(base),205 SubstringRange{std::get<0>(std::move(triplet.t)),206 std::get<1>(std::move(triplet.t))}};207}208 209// R1544 stmt-function-stmt210// Convert this stmt-function-stmt to an assignment to the result of a211// pointer-valued function call -- which itself will be converted to a212// much more likely array element assignment statement if it needs213// to be.214Statement<ActionStmt> StmtFunctionStmt::ConvertToAssignment() {215 auto &funcName{std::get<Name>(t)};216 auto &funcArgs{std::get<std::list<Name>>(t)};217 auto &funcExpr{std::get<Scalar<Expr>>(t).thing};218 CharBlock source{funcName.source};219 // Extend source to include closing parenthesis220 if (funcArgs.empty()) {221 CHECK(*source.end() == '(');222 source = CharBlock{source.begin(), source.end() + 1};223 }224 std::list<ActualArgSpec> actuals;225 for (const Name &arg : funcArgs) {226 actuals.emplace_back(std::optional<Keyword>{},227 ActualArg{Expr{WithSource(228 arg.source, Designator{DataRef{Name{arg.source, arg.symbol}}})}});229 source.ExtendToCover(arg.source);230 }231 CHECK(*source.end() == ')');232 source = CharBlock{source.begin(), source.end() + 1};233 FunctionReference funcRef{234 Call{ProcedureDesignator{Name{funcName.source, funcName.symbol}},235 std::move(actuals)}};236 funcRef.source = source;237 auto variable{Variable{common::Indirection{std::move(funcRef)}}};238 return Statement{std::nullopt,239 ActionStmt{common::Indirection{240 AssignmentStmt{std::move(variable), std::move(funcExpr)}}}};241}242 243CharBlock Variable::GetSource() const {244 return common::visit(245 common::visitors{246 [&](const common::Indirection<Designator> &des) {247 return des.value().source;248 },249 [&](const common::Indirection<parser::FunctionReference> &call) {250 return call.value().source;251 },252 },253 u);254}255 256llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Name &x) {257 return os << x.ToString();258}259 260OmpDirectiveName::OmpDirectiveName(const Verbatim &name) {261 std::string_view nameView{name.source.begin(), name.source.size()};262 std::string nameLower{ToLowerCaseLetters(nameView)};263 // The function getOpenMPDirectiveKind will return OMPD_unknown in two cases:264 // (1) if the given string doesn't match any actual directive, or265 // (2) if the given string was "unknown".266 // The Verbatim(<token>) parser will succeed as long as the given token267 // matches the source.268 // Since using "construct<OmpDirectiveName>(verbatim(...))" will succeed269 // if the verbatim parser succeeds, in order to get OMPD_unknown the270 // token given to Verbatim must be invalid. Because it's an internal issue271 // asserting is ok.272 v = llvm::omp::getOpenMPDirectiveKind(nameLower);273 assert(v != llvm::omp::Directive::OMPD_unknown && "Invalid directive name");274 source = name.source;275}276 277OmpDependenceType::Value OmpDoacross::GetDepType() const {278 return common::visit( //279 common::visitors{280 [](const OmpDoacross::Sink &) {281 return OmpDependenceType::Value::Sink;282 },283 [](const OmpDoacross::Source &) {284 return OmpDependenceType::Value::Source;285 },286 },287 u);288}289 290OmpTaskDependenceType::Value OmpDependClause::TaskDep::GetTaskDepType() const {291 using Modifier = OmpDependClause::TaskDep::Modifier;292 auto &modifiers{std::get<std::optional<std::list<Modifier>>>(t)};293 if (modifiers) {294 for (auto &m : *modifiers) {295 if (auto *dep{std::get_if<OmpTaskDependenceType>(&m.u)}) {296 return dep->v;297 }298 }299 llvm_unreachable("expecting OmpTaskDependenceType in TaskDep");300 } else {301 llvm_unreachable("expecting modifiers on OmpDependClause::TaskDep");302 }303}304 305std::string OmpTraitSelectorName::ToString() const {306 return common::visit( //307 common::visitors{308 [&](Value v) { //309 return std::string(EnumToString(v));310 },311 [&](llvm::omp::Directive d) {312 return llvm::omp::getOpenMPDirectiveName(313 d, llvm::omp::FallbackVersion)314 .str();315 },316 [&](const std::string &s) { //317 return s;318 },319 },320 u);321}322 323std::string OmpTraitSetSelectorName::ToString() const {324 return std::string(EnumToString(v));325}326 327llvm::omp::Clause OpenMPAtomicConstruct::GetKind() const {328 const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)};329 for (auto &clause : dirSpec.Clauses().v) {330 switch (clause.Id()) {331 case llvm::omp::Clause::OMPC_read:332 case llvm::omp::Clause::OMPC_write:333 case llvm::omp::Clause::OMPC_update:334 return clause.Id();335 default:336 break;337 }338 }339 return llvm::omp::Clause::OMPC_update;340}341 342bool OpenMPAtomicConstruct::IsCapture() const {343 const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)};344 return llvm::any_of(dirSpec.Clauses().v, [](auto &clause) {345 return clause.Id() == llvm::omp::Clause::OMPC_capture;346 });347}348 349bool OpenMPAtomicConstruct::IsCompare() const {350 const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)};351 return llvm::any_of(dirSpec.Clauses().v, [](auto &clause) {352 return clause.Id() == llvm::omp::Clause::OMPC_compare;353 });354}355} // namespace Fortran::parser356 357template <typename C> static llvm::omp::Clause getClauseIdForClass(C &&) {358 using namespace Fortran;359 using A = llvm::remove_cvref_t<C>; // A is referenced in OMP.inc360 // The code included below contains a sequence of checks like the following361 // for each OpenMP clause362 // if constexpr (std::is_same_v<A, parser::OmpClause::AcqRel>)363 // return llvm::omp::Clause::OMPC_acq_rel;364 // [...]365#define GEN_FLANG_CLAUSE_PARSER_KIND_MAP366#include "llvm/Frontend/OpenMP/OMP.inc"367}368 369namespace Fortran::parser {370llvm::omp::Clause OmpClause::Id() const {371 return std::visit([](auto &&s) { return getClauseIdForClass(s); }, u);372}373 374bool OmpDirectiveName::IsExecutionPart() const {375 // Can the directive appear in the execution part of the program.376 llvm::omp::Directive id{v};377 switch (llvm::omp::getDirectiveCategory(id)) {378 case llvm::omp::Category::Executable:379 return true;380 case llvm::omp::Category::Declarative:381 switch (id) {382 case llvm::omp::Directive::OMPD_allocate:383 return true;384 default:385 return false;386 }387 break;388 case llvm::omp::Category::Informational:389 switch (id) {390 case llvm::omp::Directive::OMPD_assume:391 return true;392 default:393 return false;394 }395 break;396 case llvm::omp::Category::Meta:397 return true;398 case llvm::omp::Category::Subsidiary:399 switch (id) {400 // TODO: case llvm::omp::Directive::OMPD_task_iteration:401 case llvm::omp::Directive::OMPD_section:402 case llvm::omp::Directive::OMPD_scan:403 return true;404 default:405 return false;406 }407 break;408 case llvm::omp::Category::Utility:409 switch (id) {410 case llvm::omp::Directive::OMPD_error:411 case llvm::omp::Directive::OMPD_nothing:412 return true;413 default:414 return false;415 }416 break;417 }418 return false;419}420 421const OmpArgumentList &OmpDirectiveSpecification::Arguments() const {422 static OmpArgumentList empty{decltype(OmpArgumentList::v){}};423 if (auto &arguments = std::get<std::optional<OmpArgumentList>>(t)) {424 return *arguments;425 }426 return empty;427}428 429const OmpClauseList &OmpDirectiveSpecification::Clauses() const {430 static OmpClauseList empty{decltype(OmpClauseList::v){}};431 if (auto &clauses = std::get<std::optional<OmpClauseList>>(t)) {432 return *clauses;433 }434 return empty;435}436 437const DoConstruct *OpenMPLoopConstruct::GetNestedLoop() const {438 auto getFromBlock{[](const Block &body, auto self) -> const DoConstruct * {439 for (auto &stmt : body) {440 if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {441 return self(std::get<Block>(block->t), self);442 }443 if (auto *loop{Unwrap<DoConstruct>(&stmt)}) {444 return loop;445 }446 }447 return nullptr;448 }};449 450 return getFromBlock(std::get<Block>(t), getFromBlock);451}452 453const OpenMPLoopConstruct *OpenMPLoopConstruct::GetNestedConstruct() const {454 auto getFromBlock{455 [](const Block &body, auto self) -> const OpenMPLoopConstruct * {456 for (auto &stmt : body) {457 if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {458 return self(std::get<Block>(block->t), self);459 }460 if (auto *omp{Unwrap<OpenMPLoopConstruct>(&stmt)}) {461 return omp;462 }463 }464 return nullptr;465 }};466 467 return getFromBlock(std::get<Block>(t), getFromBlock);468}469 470static bool InitCharBlocksFromStrings(llvm::MutableArrayRef<CharBlock> blocks,471 llvm::ArrayRef<std::string> strings) {472 for (auto [i, n] : llvm::enumerate(strings)) {473 blocks[i] = CharBlock(n);474 }475 return true;476}477 478// The names should have static storage duration. Keep these names479// in a sigle place.480llvm::ArrayRef<CharBlock> OmpCombinerExpression::Variables() {481 static std::string names[]{"omp_in", "omp_out"};482 static CharBlock vars[std::size(names)];483 484 [[maybe_unused]] static bool init = InitCharBlocksFromStrings(vars, names);485 return vars;486}487 488llvm::ArrayRef<CharBlock> OmpInitializerExpression::Variables() {489 static std::string names[]{"omp_orig", "omp_priv"};490 static CharBlock vars[std::size(names)];491 492 [[maybe_unused]] static bool init = InitCharBlocksFromStrings(vars, names);493 return vars;494}495} // namespace Fortran::parser496