[flang] add left(), right(), and comments

Original-commit: flang-compiler/f18@372fd06508
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-09-07 10:33:32 -07:00
parent 710d635cad
commit 003c8329ba
10 changed files with 460 additions and 85 deletions

View File

@ -22,6 +22,7 @@
// Intended to be as invisible as a reference, wherever possible.
#include "../common/idioms.h"
#include <memory>
#include <type_traits>
#include <utility>
@ -113,5 +114,44 @@ private:
A *p_{nullptr};
};
// A variant of Indirection suitable for use with forward-referenced types.
// These are nullable pointers, not references. Allocation is not available,
// and a single externalized destructor must be defined.
template<typename A> class OwningPointer {
public:
using element_type = A;
OwningPointer() {}
OwningPointer(OwningPointer &&that) : p_{that.release()} {}
explicit OwningPointer(std::unique_ptr<A> &&that) : p_{that.release()} {}
explicit OwningPointer(A *&&p) : p_{p} { p = nullptr; }
~OwningPointer();
OwningPointer &operator=(OwningPointer &&that) {
reset(that.release());
return *this;
}
A &operator*() { return *p_; }
const A &operator*() const { return *p_; }
A *operator->() { return p_; }
const A *operator->() const { return p_; }
A *get() const { return p_; }
A *release() {
A *result{p_};
p_ = nullptr;
return result;
}
void reset(A *p) {
this->~OwningPointer();
p_ = p;
}
private:
A *p_{nullptr};
};
} // namespace Fortran::common
#endif // FORTRAN_COMMON_INDIRECTION_H_

View File

@ -33,16 +33,16 @@ namespace Fortran::evaluate {
template<typename D, typename R, typename... O>
auto Operation<D, R, O...>::Fold(FoldingContext &context)
-> std::optional<Constant<Result>> {
auto c0{operand<0>().Fold(context)};
if constexpr (operands() == 1) {
auto c0{left().Fold(context)};
if constexpr (operands == 1) {
if (c0.has_value()) {
if (auto scalar{derived().FoldScalar(context, c0->value)}) {
return {Constant<Result>{std::move(*scalar)}};
}
}
} else {
static_assert(operands() == 2); // TODO: generalize to N operands?
auto c1{operand<1>().Fold(context)};
static_assert(operands == 2); // TODO: generalize to N operands?
auto c1{right().Fold(context)};
if (c0.has_value() && c1.has_value()) {
if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
return {Constant<Result>{std::move(*scalar)}};
@ -399,15 +399,30 @@ auto LogicalOperation<KIND>::FoldScalar(FoldingContext &context,
template<typename D, typename R, typename... O>
std::ostream &Operation<D, R, O...>::Dump(std::ostream &o) const {
operand<0>().Dump(o << derived().prefix());
if constexpr (operands() > 1) {
operand<1>().Dump(o << derived().infix());
left().Dump(derived().Prefix(o));
if constexpr (operands > 1) {
right().Dump(derived().Infix(o));
}
return o << derived().suffix();
return derived().Suffix(o);
}
template<typename A> std::string Relational<A>::infix() const {
return "."s + EnumToString(opr) + '.';
template<typename TO, TypeCategory FROMCAT>
std::ostream &Convert<TO, FROMCAT>::Dump(std::ostream &o) const {
static_assert(TO::category == TypeCategory::Integer ||
TO::category == TypeCategory::Real ||
TO::category == TypeCategory::Logical || !"Convert<> to bad category!");
if constexpr (TO::category == TypeCategory::Integer) {
o << "INT";
} else if constexpr (TO::category == TypeCategory::Real) {
o << "REAL";
} else if constexpr (TO::category == TypeCategory::Logical) {
o << "LOGICAL";
}
return this->left().Dump(o << '(') << ",KIND=" << TO::kind << ')';
}
template<typename A> std::ostream &Relational<A>::Infix(std::ostream &o) const {
return o << '.' << EnumToString(opr) << '.';
}
std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
@ -415,15 +430,15 @@ std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
return o;
}
template<int KIND> const char *LogicalOperation<KIND>::infix() const {
const char *result{nullptr};
template<int KIND>
std::ostream &LogicalOperation<KIND>::Infix(std::ostream &o) const {
switch (logicalOperator) {
case LogicalOperator::And: result = ".AND."; break;
case LogicalOperator::Or: result = ".OR."; break;
case LogicalOperator::Eqv: result = ".EQV."; break;
case LogicalOperator::Neqv: result = ".NEQV."; break;
case LogicalOperator::And: o << ".AND."; break;
case LogicalOperator::Or: o << ".OR."; break;
case LogicalOperator::Eqv: o << ".EQV."; break;
case LogicalOperator::Neqv: o << ".NEQV."; break;
}
return result;
return o;
}
template<typename T> std::ostream &Constant<T>::Dump(std::ostream &o) const {
@ -470,12 +485,11 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
static_cast<std::uint64_t>(c.value.size())});
},
[](const Concat<KIND> &c) {
return c.template operand<0>().LEN() +
c.template operand<1>().LEN();
return c.left().LEN() + c.template right().LEN();
},
[](const Extremum<Result> &c) {
return Expr<SubscriptInteger>{Extremum<SubscriptInteger>{
c.template operand<0>().LEN(), c.template operand<1>().LEN()}};
return Expr<SubscriptInteger>{
Extremum<SubscriptInteger>{c.left().LEN(), c.right().LEN()}};
},
[](const DataReference<Result> &dr) { return dr.reference->LEN(); },
[](const CopyableIndirection<Substring> &ss) { return ss->LEN(); },
@ -496,7 +510,7 @@ auto ExpressionBase<RESULT>::ScalarValue() const
if constexpr (common::HasMember<Parentheses<Result>,
decltype(derived().u)>) {
if (auto p{common::GetIf<Parentheses<Result>>(derived().u)}) {
return p->template operand<0>().ScalarValue();
return p->left().ScalarValue();
}
}
} else if constexpr (std::is_same_v<Result, SomeType>) {
@ -526,6 +540,8 @@ auto ExpressionBase<RESULT>::ScalarValue() const
return std::nullopt;
}
Expr<SomeType>::~Expr() {}
// Template instantiations to resolve the "extern template" declarations
// in expression.h.
@ -602,3 +618,15 @@ template struct ExpressionBase<SomeLogical>;
template struct ExpressionBase<SomeType>;
} // namespace Fortran::evaluate
// For reclamation of analyzed expressions to which owning pointers have
// been embedded in the parse tree. This destructor appears here, where
// definitions for all the necessary types are available, to obviate a
// need to include lib/evaluate/*.h headers in the parser proper.
namespace Fortran::common {
template<> OwningPointer<evaluate::GenericExprWrapper>::~OwningPointer() {
delete p_;
p_ = nullptr;
}
template class OwningPointer<evaluate::GenericExprWrapper>;
} // namespace Fortran::common

View File

@ -86,18 +86,27 @@ template<typename T> struct FunctionReference {
CopyableIndirection<FunctionRef> reference;
};
// Abstract Operation<> base class. The first type parameter is a "CRTP"
// reference to the specific operation class; e.g., Add is defined with
// struct Add : public Operation<Add, ...>.
// Operations always have specific Fortran result types (i.e., with known
// intrinsic type category and kind parameter value). The classes that
// represent the operations all inherit from this Operation<> base class
// template. Note that Operation has as its first type parameter (DERIVED) a
// "curiously reoccurring template pattern (CRTP)" reference to the specific
// operation class being derived from Operation; e.g., Add is defined with
// struct Add : public Operation<Add, ...>. Uses of instances of Operation<>,
// including its own member functions, can access each specific class derived
// from it via its derived() member function with compile-time type safety.
template<typename DERIVED, typename RESULT, typename... OPERANDS>
class Operation {
using OperandTypes = std::tuple<OPERANDS...>;
static_assert(RESULT::kind > 0 || !"bad result Type");
static_assert(RESULT::isSpecificType || !"bad result Type");
// The extra "int" member is a dummy that allows a safe unused reference
// to element 1 to arise indirectly in the definition of "right()" below
// when the operation has but a single operand.
using OperandTypes = std::tuple<OPERANDS..., int>;
public:
using Derived = DERIVED;
using Result = RESULT;
static constexpr auto operands() { return std::tuple_size_v<OperandTypes>; }
static constexpr std::size_t operands{sizeof...(OPERANDS)};
template<int J> using Operand = std::tuple_element_t<J, OperandTypes>;
using IsFoldableTrait = std::true_type;
@ -105,7 +114,7 @@ public:
// Binary operations wrap a tuple of CopyableIndirections to Exprs.
private:
using Container =
std::conditional_t<operands() == 1, CopyableIndirection<Expr<Operand<0>>>,
std::conditional_t<operands == 1, CopyableIndirection<Expr<Operand<0>>>,
std::tuple<CopyableIndirection<Expr<OPERANDS>>...>>;
public:
@ -117,8 +126,13 @@ public:
Derived &derived() { return *static_cast<Derived *>(this); }
const Derived &derived() const { return *static_cast<const Derived *>(this); }
// References to operand expressions from member functions of derived
// classes for specific operators can be made by index, e.g. operand<0>(),
// which must be spelled like "this->template operand<0>()" when
// inherited in a derived class template. There are convenience aliases
// left() and right() that are not templates.
template<int J> Expr<Operand<J>> &operand() {
if constexpr (operands() == 1) {
if constexpr (operands == 1) {
static_assert(J == 0);
return *operand_;
} else {
@ -126,7 +140,7 @@ public:
}
}
template<int J> const Expr<Operand<J>> &operand() const {
if constexpr (operands() == 1) {
if constexpr (operands == 1) {
static_assert(J == 0);
return *operand_;
} else {
@ -134,14 +148,29 @@ public:
}
}
Expr<Operand<0>> &left() { return operand<0>(); }
const Expr<Operand<0>> &left() const { return operand<0>(); }
std::conditional_t<(operands > 1), Expr<Operand<1>> &, void> right() {
if constexpr (operands > 1) {
return operand<1>();
}
}
std::conditional_t<(operands > 1), const Expr<Operand<1>> &, void>
right() const {
if constexpr (operands > 1) {
return operand<1>();
}
}
std::ostream &Dump(std::ostream &) const;
std::optional<Constant<Result>> Fold(FoldingContext &);
protected:
// Overridable string functions for Dump()
static const char *prefix() { return "("; }
static const char *infix() { return ","; }
static const char *suffix() { return ")"; }
// Overridable functions for Dump()
static std::ostream &Prefix(std::ostream &o) { return o << '('; }
static std::ostream &Infix(std::ostream &o) { return o << ','; }
static std::ostream &Suffix(std::ostream &o) { return o << ')'; }
private:
Container operand_;
@ -149,14 +178,25 @@ private:
// Unary operations
// Conversions to specific types from expressions of known category and
// dynamic kind.
template<typename TO, TypeCategory FROMCAT>
struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
// Fortran doesn't have conversions between kinds of CHARACTER.
// Conversions between kinds of COMPLEX are represented piecewise.
static_assert(((TO::category == TypeCategory::Integer ||
TO::category == TypeCategory::Real) &&
(FROMCAT == TypeCategory::Integer ||
FROMCAT == TypeCategory::Real)) ||
(TO::category == TypeCategory::Logical &&
FROMCAT == TypeCategory::Logical));
using Result = TO;
using Operand = SomeKind<FROMCAT>;
using Base = Operation<Convert, Result, Operand>;
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &);
std::ostream &Dump(std::ostream &) const;
};
template<typename A>
@ -178,7 +218,7 @@ template<typename A> struct Negate : public Operation<Negate<A>, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &);
static const char *prefix() { return "(-"; }
static std::ostream &Prefix(std::ostream &o) { return o << "(-"; }
};
template<int KIND>
@ -196,7 +236,9 @@ struct ComplexComponent
std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &) const;
const char *suffix() const { return isImaginaryPart ? "%IM)" : "%RE)"; }
std::ostream &Suffix(std::ostream &o) const {
return o << (isImaginaryPart ? "%IM)" : "%RE)");
}
bool isImaginaryPart{true};
};
@ -210,7 +252,7 @@ struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &);
static const char *prefix() { return "(.NOT."; }
static std::ostream &Prefix(std::ostream &o) { return o << "(.NOT."; }
};
// Binary operations
@ -222,7 +264,7 @@ template<typename A> struct Add : public Operation<Add<A>, A, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "+"; }
static std::ostream &Infix(std::ostream &o) { return o << '+'; }
};
template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
@ -232,7 +274,7 @@ template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "-"; }
static std::ostream &Infix(std::ostream &o) { return o << '-'; }
};
template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
@ -242,7 +284,7 @@ template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "*"; }
static std::ostream &Infix(std::ostream &o) { return o << '*'; }
};
template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
@ -252,7 +294,7 @@ template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "/"; }
static std::ostream &Infix(std::ostream &o) { return o << '/'; }
};
template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
@ -262,7 +304,7 @@ template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "**"; }
static std::ostream &Infix(std::ostream &o) { return o << "**"; }
};
template<typename A>
@ -274,7 +316,7 @@ struct RealToIntPower : public Operation<RealToIntPower<A>, A, A, SomeInteger> {
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(FoldingContext &,
const Scalar<BaseOperand> &, const Scalar<ExponentOperand> &);
static constexpr const char *infix() { return "**"; }
static std::ostream &Infix(std::ostream &o) { return o << "**"; }
};
template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
@ -291,8 +333,8 @@ template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
const char *prefix() const {
return ordering == Ordering::Less ? "MIN(" : "MAX(";
std::ostream &Prefix(std::ostream &o) const {
return o << (ordering == Ordering::Less ? "MIN(" : "MAX(");
}
Ordering ordering{Ordering::Greater};
@ -322,7 +364,7 @@ struct Concat
using Base::Base;
static std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
static constexpr const char *infix() { return "//"; }
static std::ostream &Infix(std::ostream &o) { return o << "//"; }
};
ENUM_CLASS(LogicalOperator, And, Or, Eqv, Neqv)
@ -343,7 +385,7 @@ struct LogicalOperation
std::optional<Scalar<Result>> FoldScalar(
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
const char *infix() const;
std::ostream &Infix(std::ostream &) const;
LogicalOperator logicalOperator;
};
@ -532,7 +574,7 @@ struct Relational : public Operation<Relational<A>, LogicalResult, A, A> {
std::optional<Scalar<Result>> FoldScalar(
FoldingContext &c, const Scalar<Operand> &, const Scalar<Operand> &);
std::string infix() const;
std::ostream &Infix(std::ostream &) const;
RelationalOperator opr;
};
@ -622,6 +664,11 @@ public:
using IsFoldableTrait = std::true_type;
CLASS_BOILERPLATE(Expr)
// Owning references to these generic expressions can appear in other
// compiler data structures (viz., the parse tree and symbol table), so
// its destructor is externalized to reduce redundant default instances.
~Expr();
template<typename A> Expr(const A &x) : u{x} {}
template<typename A>
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u{std::move(x)} {}
@ -649,6 +696,14 @@ public:
common::CombineVariants<Others, Categories> u;
};
// This wrapper class is used, by means of a forward reference with
// OwningPointer, to implement owning pointers to analyzed expressions
// from parse tree nodes.
struct GenericExprWrapper {
GenericExprWrapper(Expr<SomeType> &&x) : v{std::move(x)} {}
Expr<SomeType> v;
};
extern template class Expr<SomeInteger>;
extern template class Expr<SomeReal>;
extern template class Expr<SomeComplex>;

View File

@ -57,7 +57,9 @@ ConvertRealOperandsResult ConvertRealOperands(
std::move(x.u), std::move(y.u));
}
// A helper template for NumericOperation and its subroutines.
// Helpers for NumericOperation and its subroutines below.
static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
template<TypeCategory CAT>
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
return {AsGenericExpr(std::move(catExpr))};
@ -68,7 +70,7 @@ std::optional<Expr<SomeType>> Package(
if (catExpr.has_value()) {
return {AsGenericExpr(std::move(*catExpr))};
}
return std::nullopt;
return NoExpr();
}
std::optional<Expr<SomeComplex>> ConstructComplex(
@ -141,7 +143,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
}
return std::nullopt;
return NoExpr();
}
// Mixed COMPLEX operations with the COMPLEX operand on the right.
@ -173,7 +175,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
}
return std::nullopt;
return NoExpr();
}
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
@ -254,8 +256,9 @@ std::optional<Expr<SomeType>> NumericOperation(
},
// Default case
[&](auto &&, auto &&) {
// TODO: defined operator
messages.Say("non-numeric operands to numeric operation"_err_en_US);
return std::optional<Expr<SomeType>>{std::nullopt};
return NoExpr();
}},
std::move(x.u), std::move(y.u));
}
@ -269,4 +272,28 @@ template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
template std::optional<Expr<SomeType>> NumericOperation<Divide>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
std::optional<Expr<SomeType>> Negation(
parser::ContextualMessages &messages, Expr<SomeType> &&x) {
return std::visit(
common::visitors{[&](BOZLiteralConstant &&) {
messages.Say(
"BOZ literal cannot be negated"_err_en_US);
return NoExpr();
},
[&](Expr<SomeInteger> &&x) { return Package(std::move(x)); },
[&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
[&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
[&](Expr<SomeCharacter> &&x) {
// TODO: defined operator
messages.Say("CHARACTER cannot be negated"_err_en_US);
return NoExpr();
},
[&](Expr<SomeLogical> &&x) {
// TODO: defined operator
messages.Say("LOGICAL cannot be negated"_err_en_US);
return NoExpr();
}},
std::move(x.u));
}
} // namespace Fortran::evaluate

View File

@ -299,6 +299,9 @@ extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
std::optional<Expr<SomeType>> Negation(
parser::ContextualMessages &, Expr<SomeType> &&);
// Convenience functions and operator overloadings for expression construction.
// These interfaces are defined only for those situations that cannot possibly
// need to emit any messages. Use the more general NumericOperation<>
@ -309,6 +312,14 @@ Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
return {Negate<Type<C, K>>{std::move(x)}};
}
template<int K>
Expr<Type<TypeCategory::Complex, K>> operator-(
Expr<Type<TypeCategory::Complex, K>> &&x) {
using Part = Type<TypeCategory::Real, K>;
return {ComplexConstructor<K>{Negate<Part>{ComplexComponent<K>{false, x}},
Negate<Part>{ComplexComponent<K>{true, x}}}};
}
template<TypeCategory C, int K>
Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return {Add<Type<C, K>>{std::move(x), std::move(y)}};

View File

@ -62,6 +62,17 @@ namespace Fortran::semantics {
class Symbol;
} // namespace Fortran::semantics
// Expressions in the parse tree have owning pointers that can be set to
// type-checked generic expression representations by semantic analysis.
// OwningPointer<> is used for leak safety without having to include
// the bulk of lib/evaluate/*.h headers into the parser proper.
namespace Fortran::evaluate {
struct GenericExprWrapper; // forward definition, wraps Expr<SomeType>
} // namespace Fortran::evaluate
namespace Fortran::common {
extern template class OwningPointer<evaluate::GenericExprWrapper>;
} // namespace Fortran::common
// Most non-template classes in this file use these default definitions
// for their move constructor and move assignment operator=, and disable
// their copy constructor and copy assignment operator=.
@ -1684,6 +1695,9 @@ struct Expr {
explicit Expr(Designator &&);
explicit Expr(FunctionReference &&);
// Filled in later during semantic analysis of the expression.
common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
std::variant<common::Indirection<CharLiteralConstantSubstring>,
LiteralConstant, common::Indirection<Designator>, ArrayConstructor,
StructureConstructor, common::Indirection<TypeParamInquiry>,

View File

@ -13,10 +13,13 @@
// limitations under the License.
#include "expression.h"
#include "dump-parse-tree.h" // TODO pmk temporary
#include "symbol.h"
#include "../common/idioms.h"
#include "../evaluate/common.h"
#include "../evaluate/tools.h"
#include "../parser/parse-tree-visitor.h"
#include "../parser/parse-tree.h"
#include <functional>
#include <optional>
@ -63,7 +66,9 @@ struct ExprAnalyzer {
int Analyze(
const std::optional<parser::KindParam> &, int defaultKind, int kanjiKind);
MaybeExpr Analyze(const parser::Expr &);
MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &);
MaybeExpr Analyze(const parser::LiteralConstant &);
MaybeExpr Analyze(const parser::HollerithLiteralConstant &);
MaybeExpr Analyze(const parser::IntLiteralConstant &);
@ -77,32 +82,36 @@ struct ExprAnalyzer {
MaybeExpr Analyze(const parser::Name &);
MaybeExpr Analyze(const parser::NamedConstant &);
MaybeExpr Analyze(const parser::ComplexPart &);
MaybeExpr Analyze(const parser::Designator &);
MaybeExpr Analyze(const parser::ArrayConstructor &);
MaybeExpr Analyze(const parser::StructureConstructor &);
MaybeExpr Analyze(const parser::TypeParamInquiry &);
MaybeExpr Analyze(const parser::FunctionReference &);
MaybeExpr Analyze(const parser::Expr::Parentheses &);
MaybeExpr Analyze(const parser::Expr::UnaryPlus &); // TODO
MaybeExpr Analyze(const parser::Expr::Negate &); // TODO
MaybeExpr Analyze(const parser::Expr::NOT &); // TODO
MaybeExpr Analyze(const parser::Expr::DefinedUnary &); // TODO
MaybeExpr Analyze(const parser::Expr::Power &); // TODO
MaybeExpr Analyze(const parser::Expr::UnaryPlus &);
MaybeExpr Analyze(const parser::Expr::Negate &);
MaybeExpr Analyze(const parser::Expr::NOT &);
MaybeExpr Analyze(const parser::Expr::PercentLoc &);
MaybeExpr Analyze(const parser::Expr::DefinedUnary &);
MaybeExpr Analyze(const parser::Expr::Power &);
MaybeExpr Analyze(const parser::Expr::Multiply &);
MaybeExpr Analyze(const parser::Expr::Divide &);
MaybeExpr Analyze(const parser::Expr::Add &);
MaybeExpr Analyze(const parser::Expr::Subtract &);
MaybeExpr Analyze(const parser::Expr::Concat &); // TODO
MaybeExpr Analyze(const parser::Expr::LT &); // TODO
MaybeExpr Analyze(const parser::Expr::LE &); // TODO
MaybeExpr Analyze(const parser::Expr::EQ &); // TODO
MaybeExpr Analyze(const parser::Expr::NE &); // TODO
MaybeExpr Analyze(const parser::Expr::GE &); // TODO
MaybeExpr Analyze(const parser::Expr::GT &); // TODO
MaybeExpr Analyze(const parser::Expr::AND &); // TODO
MaybeExpr Analyze(const parser::Expr::OR &); // TODO
MaybeExpr Analyze(const parser::Expr::EQV &); // TODO
MaybeExpr Analyze(const parser::Expr::NEQV &); // TODO
MaybeExpr Analyze(const parser::Expr::XOR &); // TODO
MaybeExpr Analyze(const parser::Expr::Concat &);
MaybeExpr Analyze(const parser::Expr::LT &);
MaybeExpr Analyze(const parser::Expr::LE &);
MaybeExpr Analyze(const parser::Expr::EQ &);
MaybeExpr Analyze(const parser::Expr::NE &);
MaybeExpr Analyze(const parser::Expr::GE &);
MaybeExpr Analyze(const parser::Expr::GT &);
MaybeExpr Analyze(const parser::Expr::AND &);
MaybeExpr Analyze(const parser::Expr::OR &);
MaybeExpr Analyze(const parser::Expr::EQV &);
MaybeExpr Analyze(const parser::Expr::NEQV &);
MaybeExpr Analyze(const parser::Expr::XOR &);
MaybeExpr Analyze(const parser::Expr::ComplexConstructor &);
MaybeExpr Analyze(const parser::Expr::DefinedBinary &); // TODO
// TODO more remain
MaybeExpr Analyze(const parser::Expr::DefinedBinary &);
FoldingContext &context;
const semantics::IntrinsicTypeDefaultKinds &defaults;
@ -149,13 +158,14 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const std::variant<As...> &u) {
return std::visit([&](const auto &x) { return AnalyzeHelper(ea, x); }, u);
}
template<typename A>
MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
return AnalyzeHelper(ea, *x);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr &expr) {
return std::visit(common::visitors{[&](const parser::LiteralConstant &c) {
return AnalyzeHelper(*this, c);
},
// TODO: remaining cases
[&](const auto &) { return MaybeExpr{}; }},
expr.u);
return std::visit(
[&](const auto &x) { return AnalyzeHelper(*this, x); }, expr.u);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::LiteralConstant &x) {
@ -295,9 +305,9 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
for (const char *p{x.real.source.begin()}; p < end; ++p) {
if (parser::IsLetter(*p)) {
switch (*p) {
case 'e': letterKind = 4; break;
case 'd': letterKind = 8; break;
case 'q': letterKind = 16; break;
case 'e': letterKind = defaults.defaultRealKind; break;
case 'd': letterKind = defaults.defaultDoublePrecisionKind; break;
case 'q': letterKind = defaults.defaultQuadPrecisionKind; break;
default: ctxMsgs.Say("unknown exponent letter '%c'"_err_en_US, *p);
}
break;
@ -407,6 +417,37 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
return Analyze(n.v);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::CharLiteralConstantSubstring &) {
context.messages.Say(
"pmk: CharLiteralConstantSubstring unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Designator &) {
context.messages.Say("pmk: Designator unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayConstructor &) {
context.messages.Say("pmk: ArrayConstructor unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::StructureConstructor &) {
context.messages.Say("pmk: StructureConstructor unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::TypeParamInquiry &) {
context.messages.Say("pmk: TypeParamInquiry unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
context.messages.Say("pmk: FunctionReference unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
return std::visit(
@ -433,7 +474,33 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
return std::nullopt;
}
// TODO: defined operators for illegal intrinsic operator cases
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) {
return AnalyzeHelper(*this, *x.v);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Negate &x) {
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
return Negation(context.messages, std::move(operand->u));
}
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NOT &) {
context.messages.Say("pmk: NOT unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::PercentLoc &) {
context.messages.Say("pmk: %LOC unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedUnary &) {
context.messages.Say("pmk: DefinedUnary unimplemented\n"_err_en_US);
return std::nullopt;
}
// TODO: check defined operators for illegal intrinsic operator cases
template<template<typename> class OPR, typename PARSED>
MaybeExpr BinaryOperationHelper(ExprAnalyzer &ea, const PARSED &x) {
if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
@ -466,6 +533,76 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
AnalyzeHelper(*this, *std::get<1>(x.t))));
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &) {
context.messages.Say("pmk: Power unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Concat &) {
context.messages.Say("pmk: Concat unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &) {
context.messages.Say("pmk: .LT. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &) {
context.messages.Say("pmk: .LE. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &) {
context.messages.Say("pmk: .EQ. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &) {
context.messages.Say("pmk: .NE. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &) {
context.messages.Say("pmk: .GT. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &) {
context.messages.Say("pmk: .GE. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &) {
context.messages.Say("pmk: .AND. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &) {
context.messages.Say("pmk: .OR. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &) {
context.messages.Say("pmk: .EQV. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &) {
context.messages.Say("pmk: .NEQV. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &) {
context.messages.Say("pmk: .XOR. unimplemented\n"_err_en_US);
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
context.messages.Say("pmk: DefinedBinary unimplemented\n"_err_en_US);
return std::nullopt;
}
} // namespace Fortran::evaluate
namespace Fortran::semantics {
@ -475,4 +612,40 @@ MaybeExpr AnalyzeExpr(evaluate::FoldingContext &context,
return evaluate::ExprAnalyzer{context, defaults}.Analyze(expr);
}
class Mutator {
public:
Mutator(evaluate::FoldingContext &context,
const IntrinsicTypeDefaultKinds &defaults, std::ostream &o)
: context_{context}, defaults_{defaults}, out_{o} {}
template<typename A> bool Pre(A &) { return true /* visit children */; }
template<typename A> void Post(A &) {}
bool Pre(parser::Expr &expr) {
if (expr.typedExpr.get() == nullptr) {
if (MaybeExpr checked{AnalyzeExpr(context_, defaults_, expr)}) {
checked->Dump(out_ << "pmk checked: ") << '\n';
expr.typedExpr.reset(
new evaluate::GenericExprWrapper{std::move(*checked)});
} else {
out_ << "pmk: expression analysis failed for an expression: ";
DumpTree(out_, expr);
}
}
return false;
}
private:
evaluate::FoldingContext &context_;
const IntrinsicTypeDefaultKinds &defaults_;
std::ostream &out_;
};
void AnalyzeExpressions(parser::Program &program,
evaluate::FoldingContext &context,
const IntrinsicTypeDefaultKinds &defaults, std::ostream &o) {
Mutator mutator{context, defaults, o};
parser::Walk(program, mutator);
}
} // namespace Fortran::semantics

View File

@ -21,6 +21,7 @@
#include "../parser/parse-tree.h"
#include <cinttypes>
#include <optional>
#include <ostream> // TODO pmk
namespace Fortran::semantics {
@ -29,6 +30,8 @@ using MaybeExpr = std::optional<evaluate::Expr<evaluate::SomeType>>;
struct IntrinsicTypeDefaultKinds {
int defaultIntegerKind{evaluate::DefaultInteger::kind};
int defaultRealKind{evaluate::DefaultReal::kind};
int defaultDoublePrecisionKind{evaluate::DefaultDoublePrecision::kind};
int defaultQuadPrecisionKind{evaluate::DefaultDoublePrecision::kind};
int defaultCharacterKind{evaluate::DefaultCharacter::kind};
int defaultLogicalKind{evaluate::DefaultLogical::kind};
};
@ -36,5 +39,8 @@ struct IntrinsicTypeDefaultKinds {
MaybeExpr AnalyzeExpr(evaluate::FoldingContext &,
const IntrinsicTypeDefaultKinds &, const parser::Expr &);
void AnalyzeExpressions(parser::Program &, evaluate::FoldingContext &,
const IntrinsicTypeDefaultKinds &, std::ostream &);
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_EXPRESSION_H_

View File

@ -20,4 +20,5 @@ add_executable(f18
target_link_libraries(f18
FortranParser
FortranSemantics
FortranEvaluate
)

View File

@ -23,6 +23,7 @@
#include "../../lib/parser/provenance.h"
#include "../../lib/parser/unparse.h"
#include "../../lib/semantics/dump-parse-tree.h"
#include "../../lib/semantics/expression.h"
#include "../../lib/semantics/mod-file.h"
#include "../../lib/semantics/resolve-labels.h"
#include "../../lib/semantics/resolve-names.h"
@ -92,6 +93,7 @@ struct DriverOptions {
bool dumpUnparseWithSymbols{false};
bool dumpParseTree{false};
bool dumpSymbols{false};
bool debugExpressions{false};
bool debugResolveNames{false};
bool measureTree{false};
std::vector<std::string> pgf90Args;
@ -208,7 +210,7 @@ std::string CompileFortran(
MeasureParseTree(parseTree);
}
if (driver.debugResolveNames || driver.dumpSymbols ||
driver.dumpUnparseWithSymbols) {
driver.dumpUnparseWithSymbols || driver.debugExpressions) {
std::vector<std::string> directories{options.searchDirectories};
directories.insert(directories.begin(), "."s);
if (driver.moduleDirectory != "."s) {
@ -236,6 +238,22 @@ std::string CompileFortran(
return {};
}
}
if (driver.debugExpressions) {
Fortran::parser::CharBlock whole{parsing.cooked().data()};
Fortran::parser::Messages messages;
Fortran::parser::ContextualMessages contextualMessages{whole, &messages};
Fortran::evaluate::FoldingContext context{contextualMessages};
Fortran::semantics::IntrinsicTypeDefaultKinds defaults;
Fortran::semantics::AnalyzeExpressions(
parseTree, context, defaults, std::cout);
messages.Emit(std::cerr, parsing.cooked());
if (!messages.empty() &&
(driver.warningsAreErrors || messages.AnyFatalError())) {
std::cerr << driver.prefix << "semantic errors in " << path << '\n';
exitStatus = EXIT_FAILURE;
return {};
}
}
if (driver.dumpParseTree) {
Fortran::semantics::DumpTree(std::cout, parseTree);
}
@ -394,6 +412,8 @@ int main(int argc, char *const argv[]) {
driver.dumpParseTree = true;
} else if (arg == "-fdebug-dump-symbols") {
driver.dumpSymbols = true;
} else if (arg == "-fdebug-expressions") {
driver.debugExpressions = true;
} else if (arg == "-fdebug-resolve-names") {
driver.debugResolveNames = true;
} else if (arg == "-fdebug-measure-parse-tree") {