Skip to content

Commit

Permalink
[flang] Improve expression traversal, use it more for tools
Browse files Browse the repository at this point in the history
Original-commit: flang-compiler/f18@4980b92
Reviewed-on: flang-compiler/f18#601
Tree-same-pre-rewrite: false
  • Loading branch information
klausler committed Jul 23, 2019
1 parent daf9eb0 commit e4945e5
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 58 deletions.
49 changes: 49 additions & 0 deletions flang/lib/evaluate/tools.cc
Expand Up @@ -23,6 +23,22 @@ using namespace Fortran::parser::literals;

namespace Fortran::evaluate {

// IsVariable()
IsVariableVisitor::IsVariableVisitor(std::nullptr_t) {}
void IsVariableVisitor::Handle(const StaticDataObject &) { Return(false); }
void IsVariableVisitor::Post(const Substring &) { Return(true); }
void IsVariableVisitor::Pre(const Component &) { Return(true); }
void IsVariableVisitor::Pre(const ArrayRef &) { Return(true); }
void IsVariableVisitor::Pre(const CoarrayRef &) { Return(true); }
void IsVariableVisitor::Pre(const ComplexPart &) { Return(true); }
void IsVariableVisitor::Handle(const ProcedureDesignator &x) {
if (const semantics::Symbol * symbol{x.GetSymbol()}) {
Return(symbol->attrs().test(semantics::Attr::POINTER));
} else {
Return(false);
}
}

// Conversions of complex component expressions to REAL.
ConvertRealOperandsResult ConvertRealOperands(
parser::ContextualMessages &messages, Expr<SomeType> &&x,
Expand Down Expand Up @@ -630,4 +646,37 @@ bool IsAssumedRank(const ActualArgument &arg) {
return IsAssumedRank(*assumedTypeDummy);
}
}

// GetLastSymbol()
GetLastSymbolVisitor::GetLastSymbolVisitor(std::nullptr_t) {}
void GetLastSymbolVisitor::Handle(const semantics::Symbol &symbol) {
Return(&symbol);
}
void GetLastSymbolVisitor::Handle(const Component &x) {
Return(&x.GetLastSymbol());
}
void GetLastSymbolVisitor::Handle(const NamedEntity &x) {
Return(&x.GetLastSymbol());
}
void GetLastSymbolVisitor::Handle(const ProcedureDesignator &x) {
Return(x.GetSymbol());
}

// GetLastTarget()
GetLastTargetVisitor::GetLastTargetVisitor(std::nullptr_t) {}
void GetLastTargetVisitor::Handle(const semantics::Symbol &x) {
if (x.attrs().HasAny({semantics::Attr::POINTER, semantics::Attr::TARGET})) {
Return(&x);
} else {
Return(nullptr);
}
}
void GetLastTargetVisitor::Pre(const Component &x) {
const semantics::Symbol &symbol{x.GetLastSymbol()};
if (symbol.attrs().HasAny(
{semantics::Attr::POINTER, semantics::Attr::TARGET})) {
Return(&symbol);
}
}

}
68 changes: 45 additions & 23 deletions flang/lib/evaluate/tools.h
Expand Up @@ -57,28 +57,28 @@ std::optional<Variable<A>> AsVariable(const std::optional<Expr<A>> &expr) {
}

// Predicate: true when an expression is a variable reference
template<typename A> bool IsVariable(const A &) { return false; }
template<typename A> bool IsVariable(const Designator<A> &designator) {
if constexpr (common::HasMember<Substring, decltype(Designator<A>::u)>) {
if (const auto *substring{std::get_if<Substring>(&designator.u)}) {
return substring->GetLastSymbol() != nullptr;
}
}
return true;
}
template<typename A> bool IsVariable(const FunctionRef<A> &funcRef) {
if (const semantics::Symbol * symbol{funcRef.proc().GetSymbol()}) {
return symbol->attrs().test(semantics::Attr::POINTER);
struct IsVariableVisitor : public virtual VisitorBase<std::optional<bool>> {
// std::optional<> is used because it is default-constructible.
using Result = std::optional<bool>;
explicit IsVariableVisitor(std::nullptr_t);
void Handle(const StaticDataObject &); // constant Substring base -> false
void Post(const Substring &);
void Pre(const Component &);
void Pre(const ArrayRef &);
void Pre(const CoarrayRef &);
void Pre(const ComplexPart &);
void Handle(const ProcedureDesignator &);
template<typename A> void Post(const A &) { Return(false); }
};

template<typename A> bool IsVariable(const A &x) {
Visitor<IsVariableVisitor> visitor{nullptr};
if (auto optional{visitor.Traverse(x)}) {
return *optional;
} else {
return false;
}
}
template<typename T> bool IsVariable(const Expr<T> &expr) {
return std::visit([](const auto &x) { return IsVariable(x); }, expr.u);
}
template<typename A> bool IsVariable(const std::optional<A> &x) {
return x.has_value() && IsVariable(*x);
}

// Predicate: true when an expression is assumed-rank
bool IsAssumedRank(const semantics::Symbol &);
Expand Down Expand Up @@ -614,11 +614,11 @@ struct GetLastSymbolVisitor
: public virtual VisitorBase<std::optional<const semantics::Symbol *>> {
// std::optional<> is used because it is default-constructible.
using Result = std::optional<const semantics::Symbol *>;
explicit GetLastSymbolVisitor(std::nullptr_t) {}
void Handle(const semantics::Symbol &symbol) { Return(&symbol); }
void Handle(const Component &x) { Return(&x.GetLastSymbol()); }
void Handle(const NamedEntity &x) { Return(&x.GetLastSymbol()); }
void Handle(const ProcedureDesignator &x) { Return(x.GetSymbol()); }
explicit GetLastSymbolVisitor(std::nullptr_t);
void Handle(const semantics::Symbol &);
void Handle(const Component &);
void Handle(const NamedEntity &);
void Handle(const ProcedureDesignator &);
};

template<typename A> const semantics::Symbol *GetLastSymbol(const A &x) {
Expand Down Expand Up @@ -677,5 +677,27 @@ inline bool IsProcedurePointer(const Expr<SomeType> &expr) {
template<typename A> bool IsProcedurePointer(const std::optional<A> &x) {
return x.has_value() && IsProcedurePointer(*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.
using Result = std::optional<const semantics::Symbol *>;
explicit GetLastTargetVisitor(std::nullptr_t);
void Handle(const semantics::Symbol &);
void Pre(const Component &);
};

template<typename A> const semantics::Symbol *GetLastTarget(const A &x) {
Visitor<GetLastTargetVisitor> visitor{nullptr};
if (auto optional{visitor.Traverse(x)}) {
return *optional;
} else {
return nullptr;
}
}

}
#endif // FORTRAN_EVALUATE_TOOLS_H_
104 changes: 70 additions & 34 deletions flang/lib/evaluate/traversal.h
Expand Up @@ -75,11 +75,9 @@ template<typename RESULT> class VisitorBase {

Result &result() { return result_; }

// Note the odd return type; it distinguishes these default callbacks
// from any void-valued client callback.
template<typename A> std::nullptr_t Handle(const A &) { return nullptr; }
template<typename A> std::nullptr_t Pre(const A &) { return nullptr; }
template<typename A> std::nullptr_t Post(const A &) { return nullptr; }
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; }
void Return(RESULT &&x) {
Expand All @@ -99,13 +97,35 @@ template<typename A, typename... B> struct VisitorResultTypeHelper {
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.
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 Base::Handle, Base::Pre, Base::Post;
using A::Handle..., A::Pre..., A::Post...;

private:
Expand All @@ -121,22 +141,20 @@ class Visitor : public virtual VisitorBase<VisitorResultType<A...>>,
private:
template<typename B> void Visit(const B &x) {
if (!done_) {
if constexpr (std::is_same_v<std::decay_t<decltype(Handle(x))>,
std::nullptr_t>) {
// No visitation class defines Handle(B), so try Pre()/Post().
Pre(x);
if constexpr ((... || HasVisitorHandle<A, B, void>::value)) {
Handle(x);
} else {
if constexpr ((... || HasVisitorPre<A, B, void>::value)) {
Pre(x);
}
if (!done_) {
descender_.Descend(x);
if (!done_) {
Post(x);
if constexpr ((... || HasVisitorPost<A, B, void>::value)) {
Post(x);
}
}
}
} else {
static_assert(
std::is_same_v<std::decay_t<decltype(Pre(x))>, std::nullptr_t>);
static_assert(
std::is_same_v<std::decay_t<decltype(Post(x))>, std::nullptr_t>);
Handle(x);
}
}
}
Expand All @@ -147,44 +165,62 @@ class Visitor : public virtual VisitorBase<VisitorResultType<A...>>,

class RewriterBase {
public:
template<typename A> common::IfNoLvalue<A, A> Handle(A &&x) {
defaultHandleCalled_ = true;
return std::move(x);
}
template<typename A> void Pre(const A &) {}
template<typename A> common::IfNoLvalue<A, A> Post(A &&x) {
return std::move(x);
}

void Return() { done_ = true; }

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};
bool defaultHandleCalled_{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 RewriterBase::Handle, RewriterBase::Pre, RewriterBase::Post;
using A::Handle..., A::Pre..., A::Post...;

template<typename... B> Rewriter(B... x) : A{x}... {}

private:
using RewriterBase::done_, RewriterBase::defaultHandleCalled_;
using RewriterBase::done_;

public:
template<typename B> common::IfNoLvalue<B, B> Traverse(B &&x) {
if (!done_) {
defaultHandleCalled_ = false;
x = Handle(std::move(x));
if (defaultHandleCalled_) {
Pre(x);
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_) {
x = Post(std::move(x));
if constexpr ((... || HasMutatorPost<A, B, B>::value)) {
x = Post(std::move(x));
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion flang/test/semantics/symbol11.f90
Expand Up @@ -42,7 +42,7 @@ subroutine s2
!DEF: /s2/x ObjectEntity CHARACTER(4_4,1)
!DEF: /s2/y ObjectEntity CHARACTER(4_4,1)
character(len=4) x, y
!DEF: /s2/Block1/z AssocEntity CHARACTER(4_4,1)
!DEF: /s2/Block1/z AssocEntity CHARACTER(4_8,1)
!REF: /s2/x
associate (z => x)
!REF: /s2/Block1/z
Expand Down

0 comments on commit e4945e5

Please sign in to comment.