// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_EVALUATE_TRAVERSAL_H_ #define FORTRAN_EVALUATE_TRAVERSAL_H_ #include "descender.h" #include // Implements an expression traversal utility framework. // See fold.cc to see an example of how this framework was used to // implement then detection of constant expressions. // // The bases of references (component, array, coarray, substring, & // procedures) are visited before any subscript, cosubscript, or actual // arguments. Visitors may rely on this ordering of descent. // // To use for non-mutating visitation, define one or more client visitation // classes of the form: // class MyVisitor : public virtual VisitorBase { // public: // using Result = RESULT; // explicit MyVisitor(ARGTYPE); // single-argument constructor // void Handle(const T1 &); // callback for type T1 objects // void Pre(const T2 &); // callback before visiting T2 // void Post(const T2 &); // callback after visiting T2 // ... // }; // RESULT should have some default-constructible type, and it must be // the same type in all of the visitors that you combine in the next step. // // If Handle() and Pre()/Post() are defined for the same type, // Handle() has precedence. This can arise when member function // templates are used as catch-alls. // // Then instantiate and construct a Visitor and its embedded visitors via: // Visitor v{value...}; // value is/are ARGTYPE && // and call: // RESULT result{v.Traverse(topLevelExpr)}; // Within the callback routines (Handle, Pre, Post), one may call // void Return(A &&); // to assign to the result and end traversal // void Return(); // to end traversal with current result // RESULT &result(); // to reference the result to define or update it // For any given expression object type T for which a callback is defined // in any visitor class, the callback must be distinct from all others. // Further, if there is a Handle(const T &) callback, there cannot be a // Pre(const T &) or a Post(const T &). // // For rewriting traversals, the paradigm is similar; however, the // argument types are rvalues and the non-void result types match // the arguments: // class MyRewriter : public virtual RewriterBase { // using Result = RESULT; // explicit MyRewriter(ARGTYPE); // single-argument constructor // T1 Handle(T1 &&); // rewriting callback for type T1 objects // void Pre(T2 &); // in-place mutating callback before visiting T2 // T2 Post(T2 &&); // rewriting callback after visiting T2 // ... // }; // Rewriter rw{value}; // topLevelExpr = rw.Traverse(std::move(topLevelExpr)); namespace Fortran::evaluate { template class VisitorBase { public: using Result = RESULT; Result &result() { return result_; } // These dummies prevent the "using A::Handle..., " // statements in Visitor (below) from failing, while // their odd result and argument types prevent them // from clashing with actual member function callbacks // and member function template callbacks in visitor // instances. std::nullptr_t Handle(std::nullptr_t); std::nullptr_t Pre(std::nullptr_t); std::nullptr_t Post(std::nullptr_t); void Return() { done_ = true; } template void Return(A &&x) { result_ = std::move(x); done_ = true; } protected: bool done_{false}; Result result_; }; template struct VisitorResultTypeHelper { using type = typename A::Result; static_assert(common::AreSameType); }; template using VisitorResultType = typename VisitorResultTypeHelper::type; // Some SFINAE-fu to enable detection of Handle(), Pre() and Post() // callbacks in "if constexpr ()" predicates that guard calls to them below. // These have to be declared outside Visitor because they rely on // specialization. template struct HasVisitorHandle : std::false_type {}; template struct HasVisitorHandle(nullptr)->Handle( *static_cast(nullptr)))> : std::true_type {}; template struct HasVisitorPre : std::false_type {}; template struct HasVisitorPre(nullptr)->Pre(*static_cast(nullptr)))> : std::true_type {}; template struct HasVisitorPost : std::false_type {}; template struct HasVisitorPost(nullptr)->Post(*static_cast(nullptr)))> : std::true_type {}; template class Visitor : public virtual VisitorBase>, public A... { public: using Result = VisitorResultType; using Base = VisitorBase; using A::Handle..., A::Pre..., A::Post...; private: using VisitorBase::done_, VisitorBase::result_; public: template Visitor(B... x) : A{x}... {} template Result Traverse(const B &x) { Visit(x); return std::move(result_); } template void Visit(const B &x) { if (!done_) { if constexpr ((... || HasVisitorHandle::value)) { // At least one visitor declares a member function // or member function template Handle() for B. This call // will fail if more than one visitor has done so. Handle(x); } else { if constexpr ((... || HasVisitorPre::value)) { Pre(x); } if (!done_) { descender_.Descend(x); if (!done_) { if constexpr ((... || HasVisitorPost::value)) { Post(x); } } } } } } private: friend class Descender; Descender descender_{*this}; }; class RewriterBase { public: void Return() { done_ = true; } // Dummy declarations to ensure that "using A::Handle..." &c. // do not fail in Rewriter below. std::nullptr_t Handle(std::nullptr_t); std::nullptr_t Pre(std::nullptr_t); std::nullptr_t Post(std::nullptr_t); protected: bool done_{false}; }; template struct HasMutatorHandle : std::false_type {}; template struct HasMutatorHandle(nullptr)->Handle( static_cast(*static_cast(nullptr))))> : std::true_type {}; template struct HasMutatorPre : std::false_type {}; template struct HasMutatorPre(nullptr)->Pre(*static_cast(nullptr)))> : std::true_type {}; template struct HasMutatorPost : std::false_type {}; template struct HasMutatorPost(nullptr)->Post( static_cast(*static_cast(nullptr))))> : std::true_type {}; template class Rewriter : public virtual RewriterBase, public A... { public: using A::Handle..., A::Pre..., A::Post...; template Rewriter(B... x) : A{x}... {} private: using RewriterBase::done_; public: template common::IfNoLvalue Traverse(B &&x) { if (!done_) { if constexpr ((... || HasMutatorHandle::value)) { x = Handle(std::move(x)); } else { if constexpr ((... || HasMutatorPre::value)) { Pre(x); } if (!done_) { descender_.Descend(x); if (!done_) { if constexpr ((... || HasMutatorPost::value)) { x = Post(std::move(x)); } } } } } return x; } Descender descender_{*this}; }; } #endif // FORTRAN_EVALUATE_TRAVERSAL_H_