[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:
peter klausler 2019-09-19 15:49:13 -07:00
parent f07d6bc6ba
commit fc5d127c1c
5 changed files with 70 additions and 319 deletions

View File

@ -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 &param) { Check(param.isExplicit()); }
template<typename T> void Pre(const FunctionRef<T> &call) {
bool operator()(const CoarrayRef &) const { return false; }
bool operator()(const semantics::ParamValue &param) 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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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_