forked from OSchip/llvm-project
[flang] Complete conversions, delete old visitor
Original-commit: flang-compiler/f18@6ce1da55c8 Reviewed-on: https://github.com/flang-compiler/f18/pull/755 Tree-same-pre-rewrite: false
This commit is contained in:
parent
f07d6bc6ba
commit
fc5d127c1c
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include "check-expression.h"
|
||||
#include "traversal.h" // TODO pmk
|
||||
#include "traverse.h"
|
||||
#include "../semantics/symbol.h"
|
||||
#include "../semantics/tools.h"
|
||||
|
@ -26,53 +25,49 @@ namespace Fortran::evaluate {
|
|||
// able to fold it (yet) into a known constant value; specifically,
|
||||
// the expression may reference derived type kind parameters whose values
|
||||
// are not yet known.
|
||||
class IsConstantExprVisitor : public virtual VisitorBase<bool> {
|
||||
class IsConstantExprHelper : public AllTraverse<IsConstantExprHelper> {
|
||||
public:
|
||||
using Result = bool;
|
||||
explicit IsConstantExprVisitor(int) { result() = true; }
|
||||
using Base = AllTraverse<IsConstantExprHelper>;
|
||||
IsConstantExprHelper() : Base{*this} {}
|
||||
using Base::operator();
|
||||
|
||||
template<int KIND> void Handle(const TypeParamInquiry<KIND> &inq) {
|
||||
Check(inq.parameter().template get<semantics::TypeParamDetails>().attr() ==
|
||||
common::TypeParamAttr::Kind);
|
||||
template<int KIND> bool operator()(const TypeParamInquiry<KIND> &inq) const {
|
||||
return inq.parameter().template get<semantics::TypeParamDetails>().attr() ==
|
||||
common::TypeParamAttr::Kind;
|
||||
}
|
||||
void Handle(const semantics::Symbol &symbol) {
|
||||
Check(IsNamedConstant(symbol));
|
||||
bool operator()(const semantics::Symbol &symbol) const {
|
||||
return IsNamedConstant(symbol);
|
||||
}
|
||||
void Handle(const CoarrayRef &) { Return(false); }
|
||||
void Pre(const semantics::ParamValue ¶m) { Check(param.isExplicit()); }
|
||||
template<typename T> void Pre(const FunctionRef<T> &call) {
|
||||
bool operator()(const CoarrayRef &) const { return false; }
|
||||
bool operator()(const semantics::ParamValue ¶m) const {
|
||||
return param.isExplicit() && (*this)(param.GetExplicit());
|
||||
}
|
||||
template<typename T> bool operator()(const FunctionRef<T> &call) const {
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) {
|
||||
Check(intrinsic->name == "kind");
|
||||
return intrinsic->name == "kind";
|
||||
// TODO: Obviously many other intrinsics can be allowed
|
||||
} else {
|
||||
Return(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Forbid integer division by zero in constants.
|
||||
template<int KIND>
|
||||
void Handle(const Divide<Type<TypeCategory::Integer, KIND>> &division) {
|
||||
bool operator()(
|
||||
const Divide<Type<TypeCategory::Integer, KIND>> &division) const {
|
||||
using T = Type<TypeCategory::Integer, KIND>;
|
||||
if (const auto divisor{GetScalarConstantValue<T>(division.right())}) {
|
||||
Check(!divisor->IsZero());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void Check(bool ok) {
|
||||
if (!ok) {
|
||||
Return(false);
|
||||
return !divisor->IsZero();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename A> bool IsConstantExpr(const A &x) {
|
||||
return Visitor<IsConstantExprVisitor>{0}.Traverse(x);
|
||||
}
|
||||
|
||||
bool IsConstantExpr(const Expr<SomeType> &expr) {
|
||||
return Visitor<IsConstantExprVisitor>{0}.Traverse(expr);
|
||||
return IsConstantExprHelper{}(x);
|
||||
}
|
||||
template bool IsConstantExpr(const Expr<SomeType> &);
|
||||
|
||||
// Object pointer initialization checking predicate IsInitialDataTarget().
|
||||
// This code determines whether an expression is allowable as the static
|
||||
|
|
|
@ -30,7 +30,8 @@ namespace Fortran::evaluate {
|
|||
// Predicate: true when an expression is a constant expression (in the
|
||||
// strict sense of the Fortran standard); it may not (yet) be a hard
|
||||
// constant value.
|
||||
bool IsConstantExpr(const Expr<SomeType> &);
|
||||
template<typename A> bool IsConstantExpr(const A &);
|
||||
extern template bool IsConstantExpr(const Expr<SomeType> &);
|
||||
|
||||
// Predicate: true when an expression is an object designator with
|
||||
// constant addressing and no vector-valued subscript.
|
||||
|
@ -46,6 +47,7 @@ extern template void CheckSpecificationExpr(
|
|||
extern template void CheckSpecificationExpr(
|
||||
const std::optional<Expr<SomeInteger>> &x, parser::ContextualMessages &);
|
||||
extern template void CheckSpecificationExpr(
|
||||
const std::optional<Expr<SubscriptInteger>> &x, parser::ContextualMessages &);
|
||||
const std::optional<Expr<SubscriptInteger>> &x,
|
||||
parser::ContextualMessages &);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include "tools.h"
|
||||
#include "traversal.h"
|
||||
#include "traverse.h"
|
||||
#include "../common/idioms.h"
|
||||
#include "../parser/message.h"
|
||||
|
@ -649,21 +648,23 @@ bool IsAssumedRank(const ActualArgument &arg) {
|
|||
}
|
||||
|
||||
// GetLastTarget()
|
||||
GetLastTargetVisitor::GetLastTargetVisitor(std::nullptr_t) {}
|
||||
void GetLastTargetVisitor::Handle(const semantics::Symbol &x) {
|
||||
auto GetLastTargetHelper::operator()(const semantics::Symbol &x) const
|
||||
-> Result {
|
||||
if (x.attrs().HasAny({semantics::Attr::POINTER, semantics::Attr::TARGET})) {
|
||||
Return(&x);
|
||||
return &x;
|
||||
} else {
|
||||
Return(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
void GetLastTargetVisitor::Pre(const Component &x) {
|
||||
auto GetLastTargetHelper::operator()(const Component &x) const -> Result {
|
||||
const semantics::Symbol &symbol{x.GetLastSymbol()};
|
||||
if (symbol.attrs().HasAny(
|
||||
{semantics::Attr::POINTER, semantics::Attr::TARGET})) {
|
||||
Return(&symbol);
|
||||
return &symbol;
|
||||
} else if (symbol.attrs().test(semantics::Attr::ALLOCATABLE)) {
|
||||
Return(nullptr);
|
||||
return nullptr;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "constant.h"
|
||||
#include "expression.h"
|
||||
#include "traversal.h"
|
||||
#include "traverse.h"
|
||||
#include "../common/idioms.h"
|
||||
#include "../common/template.h"
|
||||
|
@ -95,8 +94,8 @@ struct IsVariableHelper
|
|||
};
|
||||
|
||||
template<typename A> bool IsVariable(const A &x) {
|
||||
if (std::optional<bool> optional{IsVariableHelper{}(x)}) {
|
||||
return *optional;
|
||||
if (auto known{IsVariableHelper{}(x)}) {
|
||||
return *known;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -684,32 +683,36 @@ struct TypeKindVisitor {
|
|||
// GetLastSymbol() returns the rightmost symbol in an object or procedure
|
||||
// designator (which has perhaps been wrapped in an Expr<>), or a null pointer
|
||||
// when none is found.
|
||||
struct GetLastSymbolVisitor
|
||||
: public virtual VisitorBase<std::optional<const semantics::Symbol *>> {
|
||||
// std::optional<> is used because it is default-constructible.
|
||||
struct GetLastSymbolHelper : public AnyTraverse<GetLastSymbolHelper,
|
||||
std::optional<const semantics::Symbol *>> {
|
||||
using Result = std::optional<const semantics::Symbol *>;
|
||||
explicit GetLastSymbolVisitor(std::nullptr_t) {}
|
||||
void Handle(const semantics::Symbol &x) { Return(&x); }
|
||||
void Handle(const Component &x) { Return(&x.GetLastSymbol()); }
|
||||
void Handle(const NamedEntity &x) { Return(&x.GetLastSymbol()); }
|
||||
void Handle(const ProcedureDesignator &x) { Return(x.GetSymbol()); }
|
||||
template<TypeCategory CAT, int KIND>
|
||||
void Pre(const Expr<Type<CAT, KIND>> &x) {
|
||||
if (!std::holds_alternative<Designator<Type<CAT, KIND>>>(x.u)) {
|
||||
Return(nullptr);
|
||||
}
|
||||
using Base = AnyTraverse<GetLastSymbolHelper, Result>;
|
||||
GetLastSymbolHelper() : Base{*this} {}
|
||||
using Base::operator();
|
||||
Result operator()(const semantics::Symbol &x) const { return &x; }
|
||||
Result operator()(const Component &x) const { return &x.GetLastSymbol(); }
|
||||
Result operator()(const NamedEntity &x) const { return &x.GetLastSymbol(); }
|
||||
Result operator()(const ProcedureDesignator &x) const {
|
||||
return x.GetSymbol();
|
||||
}
|
||||
void Pre(const Expr<SomeDerived> &x) {
|
||||
if (!std::holds_alternative<Designator<SomeDerived>>(x.u)) {
|
||||
Return(nullptr);
|
||||
template<typename T> Result operator()(const Expr<T> &x) const {
|
||||
if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
|
||||
std::is_same_v<T, SomeDerived>) {
|
||||
if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) {
|
||||
if (auto known{(*this)(*designator)}) {
|
||||
return known;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
} else {
|
||||
return (*this)(x.u);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename A> const semantics::Symbol *GetLastSymbol(const A &x) {
|
||||
Visitor<GetLastSymbolVisitor> visitor{nullptr};
|
||||
if (auto optional{visitor.Traverse(x)}) {
|
||||
return *optional;
|
||||
if (auto known{GetLastSymbolHelper{}(x)}) {
|
||||
return *known;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -766,19 +769,19 @@ template<typename A> bool IsProcedurePointer(const std::optional<A> &x) {
|
|||
// GetLastTarget() returns the rightmost symbol in an object
|
||||
// designator (which has perhaps been wrapped in an Expr<>) that has the
|
||||
// POINTER or TARGET attribute, or a null pointer when none is found.
|
||||
struct GetLastTargetVisitor
|
||||
: public virtual VisitorBase<std::optional<const semantics::Symbol *>> {
|
||||
// std::optional<> is used because it is default-constructible.
|
||||
struct GetLastTargetHelper : public AnyTraverse<GetLastTargetHelper,
|
||||
std::optional<const semantics::Symbol *>> {
|
||||
using Result = std::optional<const semantics::Symbol *>;
|
||||
explicit GetLastTargetVisitor(std::nullptr_t);
|
||||
void Handle(const semantics::Symbol &);
|
||||
void Pre(const Component &);
|
||||
using Base = AnyTraverse<GetLastTargetHelper, Result>;
|
||||
GetLastTargetHelper() : Base{*this} {}
|
||||
using Base::operator();
|
||||
Result operator()(const semantics::Symbol &) const;
|
||||
Result operator()(const Component &) const;
|
||||
};
|
||||
|
||||
template<typename A> const semantics::Symbol *GetLastTarget(const A &x) {
|
||||
Visitor<GetLastTargetVisitor> visitor{nullptr};
|
||||
if (auto optional{visitor.Traverse(x)}) {
|
||||
return *optional;
|
||||
if (auto known{GetLastTargetHelper{}(x)}) {
|
||||
return *known;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -1,250 +0,0 @@
|
|||
// 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 <type_traits>
|
||||
|
||||
// Implements an expression traversal utility framework.
|
||||
// See fold.cc to see an example of how this framework was used to
|
||||
// implement 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<RESULT> {
|
||||
// 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<MyVisitor, ...> 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.
|
||||
//
|
||||
// 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<RESULT> {
|
||||
// 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<MyRewriter, ...> rw{value};
|
||||
// topLevelExpr = rw.Traverse(std::move(topLevelExpr));
|
||||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
template<typename RESULT> 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<typename A> common::IfNoLvalue<void, A> Return(A &&x) {
|
||||
result_ = std::move(x);
|
||||
done_ = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool done_{false};
|
||||
Result result_;
|
||||
};
|
||||
|
||||
template<typename A, typename... B> struct VisitorResultTypeHelper {
|
||||
using type = typename A::Result;
|
||||
static_assert(common::AreSameType<type, typename B::Result...>);
|
||||
};
|
||||
template<typename... A>
|
||||
using VisitorResultType = typename VisitorResultTypeHelper<A...>::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<typename A, typename B, typename = void>
|
||||
struct HasVisitorHandle : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasVisitorHandle<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Handle(
|
||||
*static_cast<const B *>(nullptr)))> : std::true_type {};
|
||||
|
||||
template<typename A, typename B, typename = void>
|
||||
struct HasVisitorPre : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasVisitorPre<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Pre(*static_cast<const B *>(nullptr)))>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename A, typename B, typename = void>
|
||||
struct HasVisitorPost : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasVisitorPost<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Post(*static_cast<const B *>(nullptr)))>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename... A>
|
||||
class Visitor : public virtual VisitorBase<VisitorResultType<A...>>,
|
||||
public A... {
|
||||
public:
|
||||
using Result = VisitorResultType<A...>;
|
||||
using Base = VisitorBase<Result>;
|
||||
using A::Handle..., A::Pre..., A::Post...;
|
||||
|
||||
private:
|
||||
using VisitorBase<Result>::done_, VisitorBase<Result>::result_;
|
||||
|
||||
public:
|
||||
template<typename... B> Visitor(B... x) : A{x}... {}
|
||||
template<typename B> Result Traverse(const B &x) {
|
||||
Visit(x);
|
||||
return std::move(result_);
|
||||
}
|
||||
|
||||
template<typename B> void Visit(const B &x) {
|
||||
if (!done_) {
|
||||
if constexpr ((... || HasVisitorHandle<A, B, void>::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<A, B, void>::value)) {
|
||||
Pre(x);
|
||||
}
|
||||
if (!done_) {
|
||||
descender_.Descend(x);
|
||||
if (!done_) {
|
||||
if constexpr ((... || HasVisitorPost<A, B, void>::value)) {
|
||||
Post(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Descender<Visitor>;
|
||||
Descender<Visitor> 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<typename A, typename B, typename = B>
|
||||
struct HasMutatorHandle : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasMutatorHandle<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Handle(
|
||||
static_cast<B &&>(*static_cast<B *>(nullptr))))> : std::true_type {};
|
||||
|
||||
template<typename A, typename B, typename = void>
|
||||
struct HasMutatorPre : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasMutatorPre<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Pre(*static_cast<const B *>(nullptr)))>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename A, typename B, typename = B>
|
||||
struct HasMutatorPost : std::false_type {};
|
||||
template<typename A, typename B>
|
||||
struct HasMutatorPost<A, B,
|
||||
decltype(static_cast<A *>(nullptr)->Post(
|
||||
static_cast<B &&>(*static_cast<B *>(nullptr))))> : std::true_type {};
|
||||
|
||||
template<typename... A>
|
||||
class Rewriter : public virtual RewriterBase, public A... {
|
||||
public:
|
||||
using A::Handle..., A::Pre..., A::Post...;
|
||||
|
||||
template<typename... B> Rewriter(B... x) : A{x}... {}
|
||||
|
||||
private:
|
||||
using RewriterBase::done_;
|
||||
|
||||
public:
|
||||
template<typename B> common::IfNoLvalue<B, B> Traverse(B &&x) {
|
||||
if (!done_) {
|
||||
if constexpr ((... || HasMutatorHandle<A, B, B>::value)) {
|
||||
x = Handle(std::move(x));
|
||||
} else {
|
||||
if constexpr ((... || HasMutatorPre<A, B, B>::value)) {
|
||||
Pre(x);
|
||||
}
|
||||
if (!done_) {
|
||||
descender_.Descend(x);
|
||||
if (!done_) {
|
||||
if constexpr ((... || HasMutatorPost<A, B, B>::value)) {
|
||||
x = Post(std::move(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
Descender<Rewriter> descender_{*this};
|
||||
};
|
||||
}
|
||||
#endif // FORTRAN_EVALUATE_TRAVERSAL_H_
|
Loading…
Reference in New Issue