forked from OSchip/llvm-project
[flang] Add Expression traversal framework, use it to reimplement IsConstantExpr()
Original-commit: flang-compiler/f18@f5d3915935 Reviewed-on: https://github.com/flang-compiler/f18/pull/316 Tree-same-pre-rewrite: false
This commit is contained in:
parent
87abe61c54
commit
dbed3cebdc
|
@ -18,6 +18,7 @@
|
|||
#include "expression.h"
|
||||
#include "int-power.h"
|
||||
#include "tools.h"
|
||||
#include "traversal.h"
|
||||
#include "type.h"
|
||||
#include "../common/indirection.h"
|
||||
#include "../common/template.h"
|
||||
|
@ -27,7 +28,6 @@
|
|||
#include "../semantics/symbol.h"
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
|
@ -799,221 +799,45 @@ FOR_EACH_TYPE_AND_KIND(template class ExpressionBase)
|
|||
// 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.
|
||||
//
|
||||
// The implementation uses mutually recursive helper function overloadings and
|
||||
// templates.
|
||||
|
||||
struct ConstExprContext {
|
||||
std::set<parser::CharBlock> constantNames;
|
||||
class IsConstantExprVisitor : public virtual TraversalBase<bool> {
|
||||
public:
|
||||
using Base = TraversalBase<bool>;
|
||||
using Base::Handle, Base::Pre, Base::Post;
|
||||
|
||||
explicit IsConstantExprVisitor(std::nullptr_t) {}
|
||||
|
||||
template<int KIND> void Handle(const TypeParamInquiry<KIND> &inq) {
|
||||
Check(inq.parameter().template get<semantics::TypeParamDetails>().attr() ==
|
||||
common::TypeParamAttr::Kind);
|
||||
}
|
||||
void Handle(const semantics::Symbol &symbol) {
|
||||
Check(symbol.attrs().test(semantics::Attr::PARAMETER));
|
||||
}
|
||||
void Handle(const CoarrayRef &) { NotConstant(); }
|
||||
void Pre(const semantics::ParamValue ¶m) { Check(param.isExplicit()); }
|
||||
template<typename T> void Pre(const FunctionRef<T> &call) {
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) {
|
||||
Check(intrinsic->name == "kind");
|
||||
// TODO: Obviously many other intrinsics can be allowed
|
||||
} else {
|
||||
NotConstant();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void NotConstant() { Return(false); }
|
||||
|
||||
void Check(bool ok) {
|
||||
if (!ok) {
|
||||
NotConstant();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Base cases
|
||||
bool IsConstExpr(ConstExprContext &, const BOZLiteralConstant &) {
|
||||
return true;
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &, const NullPointer &) { return true; }
|
||||
template<typename A> bool IsConstExpr(ConstExprContext &, const Constant<A> &) {
|
||||
return true;
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &, const StaticDataObject::Pointer) {
|
||||
return true;
|
||||
}
|
||||
template<int KIND>
|
||||
bool IsConstExpr(ConstExprContext &, const TypeParamInquiry<KIND> &inquiry) {
|
||||
return inquiry.parameter()
|
||||
.template get<semantics::TypeParamDetails>()
|
||||
.attr() == common::TypeParamAttr::Kind;
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &, const Symbol *symbol) {
|
||||
return symbol->attrs().test(semantics::Attr::PARAMETER);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &, const CoarrayRef &) { return false; }
|
||||
bool IsConstExpr(ConstExprContext &, const ImpliedDoIndex &) {
|
||||
return true; // only tested when bounds are constant
|
||||
}
|
||||
|
||||
// Prototypes for mutual recursion
|
||||
template<typename D, typename R, typename O1>
|
||||
bool IsConstExpr(ConstExprContext &, const Operation<D, R, O1> &);
|
||||
template<typename D, typename R, typename O1, typename O2>
|
||||
bool IsConstExpr(ConstExprContext &, const Operation<D, R, O1, O2> &);
|
||||
template<typename V> bool IsConstExpr(ConstExprContext &, const ImpliedDo<V> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const ArrayConstructorValue<A> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const ArrayConstructorValues<A> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const ArrayConstructor<A> &);
|
||||
bool IsConstExpr(ConstExprContext &, const semantics::DerivedTypeSpec &);
|
||||
bool IsConstExpr(ConstExprContext &, const StructureConstructor &);
|
||||
bool IsConstExpr(ConstExprContext &, const BaseObject &);
|
||||
bool IsConstExpr(ConstExprContext &, const Component &);
|
||||
bool IsConstExpr(ConstExprContext &, const Triplet &);
|
||||
bool IsConstExpr(ConstExprContext &, const Subscript &);
|
||||
bool IsConstExpr(ConstExprContext &, const ArrayRef &);
|
||||
bool IsConstExpr(ConstExprContext &, const DataRef &);
|
||||
bool IsConstExpr(ConstExprContext &, const Substring &);
|
||||
bool IsConstExpr(ConstExprContext &, const ComplexPart &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const Designator<A> &);
|
||||
bool IsConstExpr(ConstExprContext &, const ActualArgument &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const FunctionRef<A> &);
|
||||
template<typename A> bool IsConstExpr(ConstExprContext &, const Expr<A> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const CopyableIndirection<A> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const std::optional<A> &);
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &, const std::vector<A> &);
|
||||
template<typename... As>
|
||||
bool IsConstExpr(ConstExprContext &, const std::variant<As...> &);
|
||||
bool IsConstExpr(ConstExprContext &, const Relational<SomeType> &);
|
||||
|
||||
template<typename D, typename R, typename O1>
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const Operation<D, R, O1> &operation) {
|
||||
return IsConstExpr(context, operation.left());
|
||||
}
|
||||
template<typename D, typename R, typename O1, typename O2>
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const Operation<D, R, O1, O2> &operation) {
|
||||
return IsConstExpr(context, operation.left()) &&
|
||||
IsConstExpr(context, operation.right());
|
||||
}
|
||||
template<typename V>
|
||||
bool IsConstExpr(ConstExprContext &context, const ImpliedDo<V> &impliedDo) {
|
||||
if (!IsConstExpr(context, impliedDo.lower()) ||
|
||||
!IsConstExpr(context, impliedDo.upper()) ||
|
||||
!IsConstExpr(context, impliedDo.stride())) {
|
||||
return false;
|
||||
}
|
||||
ConstExprContext newContext{context};
|
||||
newContext.constantNames.insert(impliedDo.name());
|
||||
return IsConstExpr(newContext, impliedDo.values());
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const ArrayConstructorValue<A> &value) {
|
||||
return IsConstExpr(context, value.u);
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const ArrayConstructorValues<A> &values) {
|
||||
return IsConstExpr(context, values.values());
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const ArrayConstructor<A> &array) {
|
||||
const typename ArrayConstructor<A>::Base &base{array};
|
||||
return IsConstExpr(context, base);
|
||||
}
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const semantics::DerivedTypeSpec &spec) {
|
||||
for (const auto &nameValue : spec.parameters()) {
|
||||
const auto &value{nameValue.second};
|
||||
if (!value.isExplicit() || !value.GetExplicit().has_value() ||
|
||||
!IsConstExpr(context, *value.GetExplicit())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool IsConstExpr(
|
||||
ConstExprContext &context, const StructureConstructor &structure) {
|
||||
if (!IsConstExpr(context, structure.derivedTypeSpec())) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &symbolExpr : structure.values()) {
|
||||
if (!IsConstExpr(context, symbolExpr.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const BaseObject &base) {
|
||||
return IsConstExpr(context, base.u);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const Component &component) {
|
||||
return IsConstExpr(context, component.base());
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const Triplet &triplet) {
|
||||
return IsConstExpr(context, triplet.lower()) &&
|
||||
IsConstExpr(context, triplet.upper()) &&
|
||||
IsConstExpr(context, triplet.stride());
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const Subscript &subscript) {
|
||||
return IsConstExpr(context, subscript.u);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const ArrayRef &arrayRef) {
|
||||
return IsConstExpr(context, arrayRef.base()) &&
|
||||
IsConstExpr(context, arrayRef.subscript());
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const DataRef &dataRef) {
|
||||
return IsConstExpr(context, dataRef.u);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const Substring &substring) {
|
||||
if (const auto *dataRef{substring.GetParentIf<DataRef>()}) {
|
||||
if (!IsConstExpr(context, *dataRef)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return IsConstExpr(context, substring.lower()) &&
|
||||
IsConstExpr(context, substring.upper());
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const ComplexPart &complexPart) {
|
||||
return IsConstExpr(context, complexPart.complex());
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const Designator<A> &designator) {
|
||||
return IsConstExpr(context, designator.u);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const ActualArgument &arg) {
|
||||
return IsConstExpr(context, arg.value());
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const FunctionRef<A> &funcRef) {
|
||||
if (const auto *intrinsic{
|
||||
std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}) {
|
||||
if (intrinsic->name == "kind") {
|
||||
return true;
|
||||
}
|
||||
// TODO: This is a placeholder with obvious false positives
|
||||
return IsConstExpr(context, funcRef.arguments());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const Expr<A> &expr) {
|
||||
return IsConstExpr(context, expr.u);
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const CopyableIndirection<A> &x) {
|
||||
return IsConstExpr(context, x.value());
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const std::optional<A> &maybe) {
|
||||
return !maybe.has_value() || IsConstExpr(context, *maybe);
|
||||
}
|
||||
template<typename A>
|
||||
bool IsConstExpr(ConstExprContext &context, const std::vector<A> &v) {
|
||||
for (const auto &x : v) {
|
||||
if (!IsConstExpr(context, x)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template<typename... As>
|
||||
bool IsConstExpr(ConstExprContext &context, const std::variant<As...> &u) {
|
||||
return std::visit([&](const auto &x) { return IsConstExpr(context, x); }, u);
|
||||
}
|
||||
bool IsConstExpr(ConstExprContext &context, const Relational<SomeType> &rel) {
|
||||
return IsConstExpr(context, rel.u);
|
||||
}
|
||||
|
||||
bool IsConstantExpr(const Expr<SomeType> &expr) {
|
||||
ConstExprContext context;
|
||||
return IsConstExpr(context, expr);
|
||||
Traversal<bool, IsConstantExprVisitor> traverser{nullptr};
|
||||
return !traverser.Traverse(expr).has_value(); // only no news is good news
|
||||
}
|
||||
|
||||
std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) {
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
// 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 "expression.h"
|
||||
|
||||
// Implements an expression traversal utility framework.
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
template<typename RESULT>
|
||||
class TraversalBase {
|
||||
public:
|
||||
using Result = RESULT;
|
||||
template<typename A> void Handle(const A &) { defaultHandle_ = true; }
|
||||
template<typename A> void Pre(const A &) {}
|
||||
template<typename A> void Post(const A &) {}
|
||||
template<typename... A> void Return(A &&...x) {
|
||||
result_.emplace(std::move(x)...);
|
||||
}
|
||||
protected:
|
||||
std::optional<Result> result_;
|
||||
bool defaultHandle_{false};
|
||||
};
|
||||
|
||||
// Descend() is a helper function template for Traversal::Visit().
|
||||
// Do not use directly.
|
||||
namespace descend {
|
||||
template<typename VISITOR, typename EXPR> void Descend(VISITOR &, const EXPR &) {}
|
||||
template<typename V, typename A> void Descend(V &visitor, const A *p) {
|
||||
if (p != nullptr) {
|
||||
visitor.Visit(*p);
|
||||
}
|
||||
}
|
||||
template<typename V, typename A> void Descend(V &visitor, const std::optional<A> *o) {
|
||||
if (o.has_value()) {
|
||||
visitor.Visit(*o);
|
||||
}
|
||||
}
|
||||
template<typename V, typename A> void Descend(V &visitor, const CopyableIndirection<A> &p) {
|
||||
visitor.Visit(p.value());
|
||||
}
|
||||
template<typename V, typename... A> void Descend(V &visitor, const std::variant<A...> &u) {
|
||||
std::visit([&](const auto &x){ visitor.Visit(x); }, u);
|
||||
}
|
||||
template<typename V, typename A> void Descend(V &visitor, const std::vector<A> &xs) {
|
||||
for (const auto &x : xs) {
|
||||
visitor.Visit(x);
|
||||
}
|
||||
}
|
||||
template<typename V, typename T> void Descend(V &visitor, const Expr<T> &expr) {
|
||||
visitor.Visit(expr.u);
|
||||
}
|
||||
template<typename V, typename D, typename R, typename... O>
|
||||
void Descend(V &visitor, const Operation<D,R,O...> &op) {
|
||||
visitor.Visit(op.left());
|
||||
if constexpr (op.operands > 1) {
|
||||
visitor.Visit(op.right());
|
||||
}
|
||||
}
|
||||
template<typename V, typename R> void Descend(V &visitor, const ImpliedDo<R> &ido) {
|
||||
visitor.Visit(ido.lower());
|
||||
visitor.Visit(ido.upper());
|
||||
visitor.Visit(ido.stride());
|
||||
visitor.Visit(ido.values());
|
||||
}
|
||||
template<typename V, typename R> void Descend(V &visitor, const ArrayConstructorValue<R> &av) {
|
||||
visitor.Visit(av.u);
|
||||
}
|
||||
template<typename V, typename R> void Descend(V &visitor, const ArrayConstructorValues<R> &avs) {
|
||||
visitor.Visit(avs.values());
|
||||
}
|
||||
template<typename V, int KIND> void Descend(V &visitor, const ArrayConstructor<Type<TypeCategory::Character, KIND>> &ac) {
|
||||
visitor.Visit(static_cast<ArrayConstructorValues<Type<TypeCategory::Character, KIND>>>(ac));
|
||||
visitor.Visit(ac.LEN());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const semantics::ParamValue ¶m) {
|
||||
visitor.Visit(param.GetExplicit());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const semantics::DerivedTypeSpec &derived) {
|
||||
for (const auto &pair : derived.parameters()) {
|
||||
visitor.Visit(pair.second);
|
||||
}
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const StructureConstructor &sc) {
|
||||
visitor.Visit(sc.derivedTypeSpec());
|
||||
for (const auto &pair : sc.values()) {
|
||||
visitor.Visit(pair.second);
|
||||
}
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const BaseObject &object) {
|
||||
visitor.Visit(object.u);
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const Component &component) {
|
||||
visitor.Visit(component.base());
|
||||
visitor.Visit(component.GetLastSymbol());
|
||||
}
|
||||
template<typename V, int KIND> void Descend(V &visitor, const TypeParamInquiry<KIND> &inq) {
|
||||
visitor.Visit(inq.base());
|
||||
visitor.Visit(inq.parameter());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const Triplet &triplet) {
|
||||
visitor.Visit(triplet.lower());
|
||||
visitor.Visit(triplet.upper());
|
||||
visitor.Visit(triplet.stride());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const Subscript &sscript) {
|
||||
visitor.Visit(sscript.u);
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const ArrayRef &aref) {
|
||||
visitor.Visit(aref.base());
|
||||
visitor.Visit(aref.subscript());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const CoarrayRef &caref) {
|
||||
visitor.Visit(caref.base());
|
||||
visitor.Visit(caref.subscript());
|
||||
visitor.Visit(caref.cosubscript());
|
||||
visitor.Visit(caref.stat());
|
||||
visitor.Visit(caref.team());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const DataRef &data) {
|
||||
visitor.Visit(data.u);
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const ComplexPart &z) {
|
||||
visitor.Visit(z.complex());
|
||||
}
|
||||
template<typename V, typename T> void Descend(V &visitor, const Designator<T> &designator) {
|
||||
visitor.Visit(designator.u);
|
||||
}
|
||||
template<typename V, typename T> void Descend(V &visitor, const Variable<T> &var) {
|
||||
visitor.Visit(var.u);
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const ActualArgument &arg) {
|
||||
visitor.Visit(arg.value());
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const ProcedureDesignator &p) {
|
||||
visitor.Visit(p.u);
|
||||
}
|
||||
template<typename V> void Descend(V &visitor, const ProcedureRef &call) {
|
||||
visitor.Visit(call.proc());
|
||||
visitor.Visit(call.arguments());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RESULT, typename... A>
|
||||
class Traversal : public virtual TraversalBase<RESULT>, public virtual A... {
|
||||
public:
|
||||
using Result = RESULT;
|
||||
using A::Handle..., A::Pre..., A::Post...;
|
||||
private:
|
||||
using TraversalBase<Result>::result_, TraversalBase<Result>::defaultHandle_;
|
||||
public:
|
||||
template<typename... B> Traversal(B... x) : A{x}... {}
|
||||
template<typename B> std::optional<Result> Traverse(const B &x) {
|
||||
Visit(x);
|
||||
return std::move(result_);
|
||||
}
|
||||
|
||||
// TODO: make private, make Descend instances friends
|
||||
template<typename B> void Visit(const B &x) {
|
||||
if (!result_.has_value()) {
|
||||
defaultHandle_ = false;
|
||||
Handle(x);
|
||||
if (defaultHandle_) {
|
||||
Pre(x);
|
||||
if (!result_.has_value()) {
|
||||
descend::Descend(*this, x);
|
||||
if (!result_.has_value()) {
|
||||
Post(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif // FORTRAN_EVALUATE_TRAVERSAL_H_
|
|
@ -356,8 +356,8 @@ public:
|
|||
|
||||
FOR_EACH_CHARACTER_KIND(extern template class Designator)
|
||||
|
||||
template<typename A> struct Variable {
|
||||
using Result = A;
|
||||
template<typename T> struct Variable {
|
||||
using Result = T;
|
||||
static_assert(IsSpecificIntrinsicType<Result> ||
|
||||
std::is_same_v<Result, SomeKind<TypeCategory::Derived>>);
|
||||
EVALUATE_UNION_CLASS_BOILERPLATE(Variable)
|
||||
|
|
Loading…
Reference in New Issue