forked from OSchip/llvm-project
[flang] Better folding infrastructure checkpoint
Original-commit: flang-compiler/f18@85d16ace6c Reviewed-on: https://github.com/flang-compiler/f18/pull/144 Tree-same-pre-rewrite: false
This commit is contained in:
parent
e4f12b087c
commit
2eac1d0475
|
@ -26,7 +26,8 @@ namespace Fortran::evaluate {
|
|||
// An expression of some specific result type.
|
||||
template<Category CAT, int KIND> class Expr;
|
||||
|
||||
// An expression of some supported kind of a category of result type.
|
||||
// An expression whose result is of some dynamic supported kind of a
|
||||
// particular category.
|
||||
template<Category CAT> struct CategoryExpr;
|
||||
|
||||
template<int KIND> using IntegerExpr = Expr<Category::Integer, KIND>;
|
||||
|
|
|
@ -56,35 +56,36 @@ std::ostream &GenericExpr::Dump(std::ostream &o) const {
|
|||
return DumpExpr(o, u);
|
||||
}
|
||||
|
||||
template<typename A, typename CONST>
|
||||
std::ostream &Unary<A, CONST>::Dump(std::ostream &o, const char *opr) const {
|
||||
template<typename CRTP, typename RESULT, typename A, typename ASCALAR>
|
||||
std::ostream &Unary<CRTP, RESULT, A, ASCALAR>::Dump(
|
||||
std::ostream &o, const char *opr) const {
|
||||
return operand().Dump(o << opr) << ')';
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename CONST>
|
||||
std::ostream &Binary<A, B, CONST>::Dump(
|
||||
template<typename CRTP, typename RESULT, typename A, typename B,
|
||||
typename ASCALAR, typename BSCALAR>
|
||||
std::ostream &Binary<CRTP, RESULT, A, B, ASCALAR, BSCALAR>::Dump(
|
||||
std::ostream &o, const char *opr, const char *before) const {
|
||||
return right().Dump(left().Dump(o << before) << opr) << ')';
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::ostream &IntegerExpr<KIND>::Dump(std::ostream &o) const {
|
||||
std::visit(
|
||||
common::visitors{[&](const Scalar &n) { o << n.SignedDecimal(); },
|
||||
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
|
||||
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
|
||||
[&](const Parentheses &p) { p.Dump(o, "("); },
|
||||
[&](const Negate &n) { n.Dump(o, "(-"); },
|
||||
[&](const Add &a) { a.Dump(o, "+"); },
|
||||
[&](const Subtract &s) { s.Dump(o, "-"); },
|
||||
[&](const Multiply &m) { m.Dump(o, "*"); },
|
||||
[&](const Divide &d) { d.Dump(o, "/"); },
|
||||
[&](const Power &p) { p.Dump(o, "**"); },
|
||||
[&](const Max &m) { m.Dump(o, ",", "MAX("); },
|
||||
[&](const Min &m) { m.Dump(o, ",", "MIN("); },
|
||||
[&](const auto &convert) {
|
||||
DumpExprWithType(o, convert.operand().u);
|
||||
}},
|
||||
std::visit(common::visitors{[&](const Scalar &n) { o << n.SignedDecimal(); },
|
||||
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
|
||||
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
|
||||
[&](const Parentheses &p) { p.Dump(o, "("); },
|
||||
[&](const Negate &n) { n.Dump(o, "(-"); },
|
||||
[&](const Add &a) { a.Dump(o, "+"); },
|
||||
[&](const Subtract &s) { s.Dump(o, "-"); },
|
||||
[&](const Multiply &m) { m.Dump(o, "*"); },
|
||||
[&](const Divide &d) { d.Dump(o, "/"); },
|
||||
[&](const Power &p) { p.Dump(o, "**"); },
|
||||
[&](const Max &m) { m.Dump(o, ",", "MAX("); },
|
||||
[&](const Min &m) { m.Dump(o, ",", "MIN("); },
|
||||
[&](const auto &convert) {
|
||||
DumpExprWithType(o, convert.operand().u);
|
||||
}},
|
||||
u_);
|
||||
return o;
|
||||
}
|
||||
|
@ -192,8 +193,9 @@ template<int KIND> SubscriptIntegerExpr CharacterExpr<KIND>::LEN() const {
|
|||
}
|
||||
|
||||
// Rank
|
||||
template<typename A, typename B, typename SCALAR>
|
||||
int Binary<A, B, SCALAR>::Rank() const {
|
||||
template<typename CRTP, typename RESULT, typename A, typename B,
|
||||
typename ASCALAR, typename BSCALAR>
|
||||
int Binary<CRTP, RESULT, A, B, ASCALAR, BSCALAR>::Rank() const {
|
||||
int lrank{left_.Rank()};
|
||||
if (lrank > 0) {
|
||||
return lrank;
|
||||
|
@ -202,178 +204,153 @@ int Binary<A, B, SCALAR>::Rank() const {
|
|||
}
|
||||
|
||||
// Folding
|
||||
template<typename A, typename SCALAR>
|
||||
std::optional<SCALAR> Unary<A, SCALAR>::Fold(FoldingContext &context) {
|
||||
operand_->Fold(context);
|
||||
template<typename CRTP, typename RESULT, typename A, typename ASCALAR>
|
||||
auto Unary<CRTP, RESULT, A, ASCALAR>::Fold(FoldingContext &context)
|
||||
-> std::optional<Scalar> {
|
||||
if (std::optional<OperandScalar> c{operand_->Fold(context)}) {
|
||||
return static_cast<CRTP *>(this)->FoldScalar(context, *c);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename SCALAR>
|
||||
std::optional<SCALAR> Binary<A, B, SCALAR>::Fold(FoldingContext &context) {
|
||||
left_->Fold(context);
|
||||
right_->Fold(context);
|
||||
template<typename CRTP, typename RESULT, typename A, typename B,
|
||||
typename ASCALAR, typename BSCALAR>
|
||||
auto Binary<CRTP, RESULT, A, B, ASCALAR, BSCALAR>::Fold(FoldingContext &context)
|
||||
-> std::optional<Scalar> {
|
||||
std::optional<LeftScalar> lc{left_->Fold(context)};
|
||||
std::optional<RightScalar> rc{right_->Fold(context)};
|
||||
if (lc.has_value() && rc.has_value()) {
|
||||
return static_cast<CRTP *>(this)->FoldScalar(context, *lc, *rc);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::ConvertInteger::Fold(FoldingContext &context) {
|
||||
auto IntegerExpr<KIND>::ConvertInteger::FoldScalar(FoldingContext &context,
|
||||
const CategoryScalar<Category::Integer> &c) -> std::optional<Scalar> {
|
||||
return std::visit(
|
||||
[&](auto &x) -> std::optional<typename IntegerExpr<KIND>::Scalar> {
|
||||
if (auto c{x.Fold(context)}) {
|
||||
auto converted{Scalar::ConvertSigned(*c)};
|
||||
if (converted.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(
|
||||
context.at, "integer conversion overflowed"_en_US);
|
||||
}
|
||||
return {std::move(converted.value)};
|
||||
[&](auto &x) -> std::optional<Scalar> {
|
||||
auto converted{Scalar::ConvertSigned(x)};
|
||||
if (converted.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(
|
||||
context.at, "integer conversion overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
// g++ 8.1.0 choked on the legal "return {};" that should be here,
|
||||
// saying that it may be used uninitialized.
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar> result;
|
||||
return std::move(result);
|
||||
return {std::move(converted.value)};
|
||||
},
|
||||
this->operand().u);
|
||||
c.u);
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Negate::Fold(FoldingContext &context) {
|
||||
if (auto c{this->operand().Fold(context)}) {
|
||||
auto negated{c->Negate()};
|
||||
if (negated.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer negation overflowed"_en_US);
|
||||
}
|
||||
return {std::move(negated.value)};
|
||||
auto IntegerExpr<KIND>::Negate::FoldScalar(
|
||||
FoldingContext &context, const Scalar &c) -> std::optional<Scalar> {
|
||||
auto negated{c.Negate()};
|
||||
if (negated.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer negation overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
return {std::move(negated.value)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Add::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
auto sum{lc->AddSigned(*rc)};
|
||||
if (sum.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer addition overflowed"_en_US);
|
||||
}
|
||||
return {std::move(sum.value)};
|
||||
auto IntegerExpr<KIND>::Add::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
auto sum{a.AddSigned(b)};
|
||||
if (sum.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer addition overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
return {std::move(sum.value)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Subtract::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
auto diff{lc->SubtractSigned(*rc)};
|
||||
if (diff.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer subtraction overflowed"_en_US);
|
||||
}
|
||||
return {std::move(diff.value)};
|
||||
auto IntegerExpr<KIND>::Subtract::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
auto diff{a.SubtractSigned(b)};
|
||||
if (diff.overflow && context.messages != nullptr) {
|
||||
context.messages->Say(context.at, "integer subtraction overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
return {std::move(diff.value)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Multiply::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
auto product{lc->MultiplySigned(*rc)};
|
||||
if (product.SignedMultiplicationOverflowed() &&
|
||||
context.messages != nullptr) {
|
||||
context.messages->Say(
|
||||
context.at, "integer multiplication overflowed"_en_US);
|
||||
}
|
||||
return {std::move(product.lower)};
|
||||
auto IntegerExpr<KIND>::Multiply::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
auto product{a.MultiplySigned(b)};
|
||||
if (product.SignedMultiplicationOverflowed() && context.messages != nullptr) {
|
||||
context.messages->Say(
|
||||
context.at, "integer multiplication overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
return {std::move(product.lower)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Divide::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
auto qr{lc->DivideSigned(*rc)};
|
||||
if (context.messages != nullptr) {
|
||||
if (qr.divisionByZero) {
|
||||
context.messages->Say(context.at, "integer division by zero"_en_US);
|
||||
} else if (qr.overflow) {
|
||||
context.messages->Say(context.at, "integer division overflowed"_en_US);
|
||||
}
|
||||
auto IntegerExpr<KIND>::Divide::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
auto qr{a.DivideSigned(b)};
|
||||
if (context.messages != nullptr) {
|
||||
if (qr.divisionByZero) {
|
||||
context.messages->Say(context.at, "integer division by zero"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {std::move(qr.quotient)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Power::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
typename Scalar::PowerWithErrors power{lc->Power(*rc)};
|
||||
if (context.messages != nullptr) {
|
||||
if (power.divisionByZero) {
|
||||
context.messages->Say(context.at, "zero to negative power"_en_US);
|
||||
} else if (power.overflow) {
|
||||
context.messages->Say(context.at, "integer power overflowed"_en_US);
|
||||
} else if (power.zeroToZero) {
|
||||
context.messages->Say(context.at, "integer 0**0"_en_US);
|
||||
}
|
||||
if (qr.overflow) {
|
||||
context.messages->Say(context.at, "integer division overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
return {std::move(power.power)};
|
||||
}
|
||||
return {};
|
||||
return {std::move(qr.quotient)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Max::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
if (lc->CompareSigned(*rc) == Ordering::Greater) {
|
||||
return lc;
|
||||
auto IntegerExpr<KIND>::Power::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
typename Scalar::PowerWithErrors power{a.Power(b)};
|
||||
if (context.messages != nullptr) {
|
||||
if (power.divisionByZero) {
|
||||
context.messages->Say(context.at, "zero to negative power"_en_US);
|
||||
return {};
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar>
|
||||
IntegerExpr<KIND>::Min::Fold(FoldingContext &context) {
|
||||
auto lc{this->left().Fold(context)};
|
||||
auto rc{this->right().Fold(context)};
|
||||
if (lc && rc) {
|
||||
if (lc->CompareSigned(*rc) == Ordering::Less) {
|
||||
return lc;
|
||||
if (power.overflow) {
|
||||
context.messages->Say(context.at, "integer power overflowed"_en_US);
|
||||
return {};
|
||||
}
|
||||
if (power.zeroToZero) {
|
||||
context.messages->Say(context.at, "integer 0**0"_en_US);
|
||||
return {};
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
return {};
|
||||
return {std::move(power.power)};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::optional<typename IntegerExpr<KIND>::Scalar> IntegerExpr<KIND>::Fold(
|
||||
FoldingContext &context) {
|
||||
auto IntegerExpr<KIND>::Max::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
if (a.CompareSigned(b) == Ordering::Greater) {
|
||||
return {a};
|
||||
}
|
||||
return {b};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
auto IntegerExpr<KIND>::Min::FoldScalar(FoldingContext &context,
|
||||
const Scalar &a, const Scalar &b) -> std::optional<Scalar> {
|
||||
if (a.CompareSigned(b) == Ordering::Less) {
|
||||
return {a};
|
||||
}
|
||||
return {b};
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
auto IntegerExpr<KIND>::Fold(FoldingContext &context) -> std::optional<Scalar> {
|
||||
return std::visit(
|
||||
[&](auto &x) -> std::optional<Scalar> {
|
||||
using Ty = typename std::decay<decltype(x)>::type;
|
||||
if constexpr (std::is_same_v<Ty, Scalar>) {
|
||||
return {x};
|
||||
}
|
||||
if constexpr (std::is_base_of_v<Un, Ty> || std::is_base_of_v<Bin, Ty>) {
|
||||
if constexpr (evaluate::FoldableTrait<Ty>) {
|
||||
auto c{x.Fold(context)};
|
||||
if (c.has_value()) {
|
||||
u_ = *c;
|
||||
|
@ -385,20 +362,24 @@ std::optional<typename IntegerExpr<KIND>::Scalar> IntegerExpr<KIND>::Fold(
|
|||
u_);
|
||||
}
|
||||
|
||||
template<int KIND> void RealExpr<KIND>::Fold(FoldingContext &context) {
|
||||
// TODO
|
||||
template<int KIND>
|
||||
auto RealExpr<KIND>::Fold(FoldingContext &context) -> std::optional<Scalar> {
|
||||
return {}; // TODO
|
||||
}
|
||||
|
||||
template<int KIND> void ComplexExpr<KIND>::Fold(FoldingContext &context) {
|
||||
// TODO
|
||||
template<int KIND>
|
||||
auto ComplexExpr<KIND>::Fold(FoldingContext &context) -> std::optional<Scalar> {
|
||||
return {}; // TODO
|
||||
}
|
||||
|
||||
template<int KIND> void CharacterExpr<KIND>::Fold(FoldingContext &context) {
|
||||
// TODO
|
||||
template<int KIND>
|
||||
auto CharacterExpr<KIND>::Fold(FoldingContext &context)
|
||||
-> std::optional<Scalar> {
|
||||
return {}; // TODO
|
||||
}
|
||||
|
||||
void LogicalExpr::Fold(FoldingContext &context) {
|
||||
// TODO and comparisons too
|
||||
std::optional<bool> LogicalExpr::Fold(FoldingContext &context) {
|
||||
return {}; // TODO and comparisons too
|
||||
}
|
||||
|
||||
std::optional<GenericScalar> GenericExpr::ScalarValue() const {
|
||||
|
@ -413,23 +394,38 @@ std::optional<GenericScalar> GenericExpr::ScalarValue() const {
|
|||
}
|
||||
|
||||
template<Category CAT>
|
||||
std::optional<CategoryScalar<CAT>> CategoryExpr<CAT>::ScalarValue() const {
|
||||
auto CategoryExpr<CAT>::ScalarValue() const -> std::optional<Scalar> {
|
||||
return std::visit(
|
||||
[](const auto &x) -> std::optional<CategoryScalar<CAT>> {
|
||||
[](const auto &x) -> std::optional<Scalar> {
|
||||
if (auto c{x.ScalarValue()}) {
|
||||
return {CategoryScalar<CAT>{std::move(*c)}};
|
||||
return {Scalar{std::move(*c)}};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template<Category CAT> void CategoryExpr<CAT>::Fold(FoldingContext &context) {
|
||||
std::visit([&](auto &x) { x.Fold(context); }, u);
|
||||
template<Category CAT>
|
||||
auto CategoryExpr<CAT>::Fold(FoldingContext &context) -> std::optional<Scalar> {
|
||||
return std::visit(
|
||||
[&](auto &x) -> std::optional<Scalar> {
|
||||
if (auto c{x.Fold(context)}) {
|
||||
return {Scalar{std::move(*c)}};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
void GenericExpr::Fold(FoldingContext &context) {
|
||||
std::visit([&](auto &x) { x.Fold(context); }, u);
|
||||
std::optional<GenericScalar> GenericExpr::Fold(FoldingContext &context) {
|
||||
return std::visit(
|
||||
[&](auto &x) -> std::optional<GenericScalar> {
|
||||
if (auto c{x.Fold(context)}) {
|
||||
return {GenericScalar{std::move(*c)}};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template struct CategoryExpr<Category::Integer>;
|
||||
|
|
|
@ -34,17 +34,52 @@
|
|||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
CLASS_TRAIT(FoldableTrait);
|
||||
struct FoldingContext {
|
||||
const parser::CharBlock &at;
|
||||
parser::Messages *messages;
|
||||
std::size_t element;
|
||||
};
|
||||
|
||||
// Holds a scalar constant of any kind in an intrinsic type category.
|
||||
template<Category CAT> struct CategoryScalar {
|
||||
CLASS_BOILERPLATE(CategoryScalar)
|
||||
template<int KIND> using KindScalar = typename Type<CAT, KIND>::Value;
|
||||
template<typename A> CategoryScalar(const A &x) : u{x} {}
|
||||
template<typename A>
|
||||
CategoryScalar(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: u{std::move(x)} {}
|
||||
typename KindsVariant<CAT, KindScalar>::type u;
|
||||
};
|
||||
|
||||
template<> struct CategoryScalar<Category::Logical> { std::variant<bool> u; };
|
||||
|
||||
// Holds a scalar constant of any intrinsic category and size.
|
||||
struct GenericScalar {
|
||||
CLASS_BOILERPLATE(GenericScalar)
|
||||
template<Category CAT, int KIND>
|
||||
GenericScalar(const typename Type<CAT, KIND>::Value &x)
|
||||
: u{CategoryScalar<CAT>{x}} {}
|
||||
template<Category CAT, int KIND>
|
||||
GenericScalar(typename Type<CAT, KIND>::Value &&x)
|
||||
: u{CategoryScalar<CAT>{std::move(x)}} {}
|
||||
template<typename A> GenericScalar(const A &x) : u{x} {}
|
||||
template<typename A>
|
||||
GenericScalar(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: u{std::move(x)} {}
|
||||
std::variant<CategoryScalar<Category::Integer>,
|
||||
CategoryScalar<Category::Real>, CategoryScalar<Category::Complex>,
|
||||
CategoryScalar<Category::Character>, bool>
|
||||
u;
|
||||
};
|
||||
|
||||
// Helper base classes for packaging subexpressions.
|
||||
template<typename A, typename SCALAR = typename A::Scalar> class Unary {
|
||||
template<typename CRTP, typename RESULT, typename A,
|
||||
typename ASCALAR = typename A::Scalar>
|
||||
class Unary {
|
||||
public:
|
||||
using Operand = A;
|
||||
using Scalar = SCALAR;
|
||||
using Result = RESULT;
|
||||
using Scalar = typename Type<Result::category, Result::kind>::Value;
|
||||
using FoldableTrait = std::true_type;
|
||||
CLASS_BOILERPLATE(Unary)
|
||||
Unary(const A &a) : operand_{a} {}
|
||||
Unary(A &&a) : operand_{std::move(a)} {}
|
||||
|
@ -52,19 +87,24 @@ public:
|
|||
const A &operand() const { return *operand_; }
|
||||
A &operand() { return *operand_; }
|
||||
std::ostream &Dump(std::ostream &, const char *opr) const;
|
||||
std::optional<Scalar> Fold(FoldingContext &); // folds operand, no result
|
||||
int Rank() const { return operand_.Rank(); }
|
||||
std::optional<Scalar> Fold(FoldingContext &); // TODO: array result
|
||||
protected:
|
||||
using Operand = A;
|
||||
using OperandScalar = ASCALAR;
|
||||
|
||||
private:
|
||||
CopyableIndirection<A> operand_;
|
||||
CopyableIndirection<Operand> operand_;
|
||||
};
|
||||
|
||||
template<typename A, typename B = A, typename SCALAR = typename A::Scalar>
|
||||
template<typename CRTP, typename RESULT, typename A, typename B = A,
|
||||
typename ASCALAR = typename A::Scalar,
|
||||
typename BSCALAR = typename B::Scalar>
|
||||
class Binary {
|
||||
public:
|
||||
using Left = A;
|
||||
using Right = B;
|
||||
using Scalar = SCALAR;
|
||||
using Result = RESULT;
|
||||
using Scalar = typename Type<Result::category, Result::kind>::Value;
|
||||
using FoldableTrait = std::true_type;
|
||||
CLASS_BOILERPLATE(Binary)
|
||||
Binary(const A &a, const B &b) : left_{a}, right_{b} {}
|
||||
Binary(A &&a, B &&b) : left_{std::move(a)}, right_{std::move(b)} {}
|
||||
|
@ -77,63 +117,93 @@ public:
|
|||
std::ostream &Dump(
|
||||
std::ostream &, const char *opr, const char *before = "(") const;
|
||||
int Rank() const;
|
||||
std::optional<Scalar> Fold(FoldingContext &); // folds operands, no result
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
|
||||
protected:
|
||||
using Left = A;
|
||||
using Right = B;
|
||||
using LeftScalar = ASCALAR;
|
||||
using RightScalar = BSCALAR;
|
||||
|
||||
private:
|
||||
CopyableIndirection<A> left_;
|
||||
CopyableIndirection<B> right_;
|
||||
CopyableIndirection<Left> left_;
|
||||
CopyableIndirection<Right> right_;
|
||||
};
|
||||
|
||||
// Per-category expressions
|
||||
|
||||
template<int KIND> class Expr<Category::Integer, KIND> {
|
||||
public:
|
||||
using Result = Type<Category::Integer, KIND>;
|
||||
using Scalar = typename Result::Value;
|
||||
struct ConvertInteger : public Unary<GenericIntegerExpr, Scalar> {
|
||||
using Unary<GenericIntegerExpr, Scalar>::Unary;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
using FoldableTrait = std::true_type;
|
||||
|
||||
struct ConvertInteger
|
||||
: public Unary<ConvertInteger, Result, GenericIntegerExpr,
|
||||
CategoryScalar<Category::Integer>> {
|
||||
using Unary<ConvertInteger, Result, GenericIntegerExpr,
|
||||
CategoryScalar<Category::Integer>>::Unary;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const CategoryScalar<Category::Integer> &);
|
||||
};
|
||||
struct ConvertReal : public Unary<GenericRealExpr, Scalar> {
|
||||
using Unary<GenericRealExpr, Scalar>::Unary;
|
||||
|
||||
struct ConvertReal : public Unary<ConvertReal, Result, GenericRealExpr,
|
||||
CategoryScalar<Category::Real>> {
|
||||
using Unary<ConvertReal, Result, GenericRealExpr,
|
||||
CategoryScalar<Category::Real>>::Unary;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const CategoryScalar<Category::Real> &) {
|
||||
return {};
|
||||
} // TODO
|
||||
};
|
||||
using Un = Unary<Expr, Scalar>;
|
||||
using Bin = Binary<Expr, Expr, Scalar>;
|
||||
struct Parentheses : public Un {
|
||||
using Un::Un;
|
||||
std::optional<Scalar> Fold(FoldingContext &c) {
|
||||
return this->operand().Fold(c);
|
||||
|
||||
template<typename CRTP> using Un = Unary<CRTP, Result, Expr>;
|
||||
template<typename CRTP>
|
||||
using Bin = Binary<CRTP, Result, Expr, Expr, Scalar, Scalar>;
|
||||
struct Parentheses : public Un<Parentheses> {
|
||||
using Un<Parentheses>::Un;
|
||||
static std::optional<Scalar> FoldScalar(FoldingContext &, const Scalar &x) {
|
||||
return {x};
|
||||
}
|
||||
};
|
||||
struct Negate : public Un {
|
||||
using Un::Un;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Negate : public Un<Negate> {
|
||||
using Un<Negate>::Un;
|
||||
static std::optional<Scalar> FoldScalar(FoldingContext &, const Scalar &);
|
||||
};
|
||||
struct Add : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Add : public Bin<Add> {
|
||||
using Bin<Add>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Subtract : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Subtract : public Bin<Subtract> {
|
||||
using Bin<Subtract>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Multiply : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Multiply : public Bin<Multiply> {
|
||||
using Bin<Multiply>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Divide : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Divide : public Bin<Divide> {
|
||||
using Bin<Divide>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Power : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Power : public Bin<Power> {
|
||||
using Bin<Power>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Max : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Max : public Bin<Max> {
|
||||
using Bin<Max>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
struct Min : public Bin {
|
||||
using Bin::Bin;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct Min : public Bin<Min> {
|
||||
using Bin<Min>::Bin;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const Scalar &, const Scalar &);
|
||||
};
|
||||
// TODO: R916 type-param-inquiry
|
||||
|
||||
|
@ -175,54 +245,69 @@ template<int KIND> class Expr<Category::Real, KIND> {
|
|||
public:
|
||||
using Result = Type<Category::Real, KIND>;
|
||||
using Scalar = typename Result::Value;
|
||||
using FoldableTrait = std::true_type;
|
||||
|
||||
// N.B. Real->Complex and Complex->Real conversions are done with CMPLX
|
||||
// and part access operations (resp.). Conversions between kinds of
|
||||
// Complex are done via decomposition to Real and reconstruction.
|
||||
struct ConvertInteger : public Unary<GenericIntegerExpr, Scalar> {
|
||||
using Unary<GenericIntegerExpr, Scalar>::Unary;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
struct ConvertInteger
|
||||
: public Unary<ConvertInteger, Result, GenericIntegerExpr,
|
||||
CategoryScalar<Category::Integer>> {
|
||||
using Unary<ConvertInteger, Result, GenericIntegerExpr,
|
||||
CategoryScalar<Category::Integer>>::Unary;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const CategoryScalar<Category::Integer> &);
|
||||
};
|
||||
struct ConvertReal : public Unary<GenericRealExpr, Scalar> {
|
||||
using Unary<GenericRealExpr, Scalar>::Unary;
|
||||
struct ConvertReal : public Unary<ConvertReal, Result, GenericRealExpr,
|
||||
CategoryScalar<Category::Real>> {
|
||||
using Unary<ConvertReal, Result, GenericRealExpr,
|
||||
CategoryScalar<Category::Real>>::Unary;
|
||||
static std::optional<Scalar> FoldScalar(
|
||||
FoldingContext &, const CategoryScalar<Category::Real> &);
|
||||
};
|
||||
using Un = Unary<Expr, Scalar>;
|
||||
using Bin = Binary<Expr, Expr, Scalar>;
|
||||
struct Parentheses : public Un {
|
||||
using Un::Un;
|
||||
template<typename CRTP> using Un = Unary<CRTP, Result, Expr, Scalar>;
|
||||
template<typename CRTP>
|
||||
using Bin = Binary<CRTP, Result, Expr, Expr, Scalar, Scalar>;
|
||||
struct Parentheses : public Un<Parentheses> {
|
||||
using Un<Parentheses>::Un;
|
||||
};
|
||||
struct Negate : public Un {
|
||||
using Un::Un;
|
||||
struct Negate : public Un<Negate> {
|
||||
using Un<Negate>::Un;
|
||||
};
|
||||
struct Add : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Add : public Bin<Add> {
|
||||
using Bin<Add>::Bin;
|
||||
};
|
||||
struct Subtract : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Subtract : public Bin<Subtract> {
|
||||
using Bin<Subtract>::Bin;
|
||||
};
|
||||
struct Multiply : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Multiply : public Bin<Multiply> {
|
||||
using Bin<Multiply>::Bin;
|
||||
};
|
||||
struct Divide : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Divide : public Bin<Divide> {
|
||||
using Bin<Divide>::Bin;
|
||||
};
|
||||
struct Power : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Power : public Bin<Power> {
|
||||
using Bin<Power>::Bin;
|
||||
};
|
||||
struct IntPower : public Binary<Expr, GenericIntegerExpr, Scalar> {
|
||||
using Binary<Expr, GenericIntegerExpr, Scalar>::Binary;
|
||||
struct IntPower : public Binary<IntPower, Result, Expr, GenericIntegerExpr,
|
||||
Scalar, CategoryScalar<Category::Integer>> {
|
||||
using Binary<IntPower, Result, Expr, GenericIntegerExpr, Scalar,
|
||||
CategoryScalar<Category::Integer>>::Binary;
|
||||
};
|
||||
struct Max : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Max : public Bin<Max> {
|
||||
using Bin<Max>::Bin;
|
||||
};
|
||||
struct Min : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Min : public Bin<Min> {
|
||||
using Bin<Min>::Bin;
|
||||
};
|
||||
using CplxUn = Unary<ComplexExpr<KIND>, Scalar>;
|
||||
struct RealPart : public CplxUn {
|
||||
using CplxUn::CplxUn;
|
||||
template<typename CRTP>
|
||||
using CplxUn = Unary<CRTP, Result, ComplexExpr<KIND>,
|
||||
typename Type<Category::Complex, KIND>::Value>;
|
||||
struct RealPart : public CplxUn<RealPart> {
|
||||
using CplxUn<RealPart>::CplxUn;
|
||||
};
|
||||
struct AIMAG : public CplxUn {
|
||||
using CplxUn::CplxUn;
|
||||
struct AIMAG : public CplxUn<AIMAG> {
|
||||
using CplxUn<AIMAG>::CplxUn;
|
||||
};
|
||||
|
||||
CLASS_BOILERPLATE(Expr)
|
||||
|
@ -244,7 +329,7 @@ public:
|
|||
std::optional<Scalar> ScalarValue() const {
|
||||
return common::GetIf<Scalar>(u_);
|
||||
}
|
||||
void Fold(FoldingContext &c);
|
||||
std::optional<Scalar> Fold(FoldingContext &c);
|
||||
|
||||
private:
|
||||
std::variant<Scalar, CopyableIndirection<DataRef>,
|
||||
|
@ -258,34 +343,40 @@ template<int KIND> class Expr<Category::Complex, KIND> {
|
|||
public:
|
||||
using Result = Type<Category::Complex, KIND>;
|
||||
using Scalar = typename Result::Value;
|
||||
using Un = Unary<Expr, Scalar>;
|
||||
using Bin = Binary<Expr, Expr, Scalar>;
|
||||
struct Parentheses : public Un {
|
||||
using Un::Un;
|
||||
using FoldableTrait = std::true_type;
|
||||
template<typename CRTP> using Un = Unary<CRTP, Result, Expr, Scalar>;
|
||||
template<typename CRTP>
|
||||
using Bin = Binary<CRTP, Result, Expr, Expr, Scalar, Scalar>;
|
||||
struct Parentheses : public Un<Parentheses> {
|
||||
using Un<Parentheses>::Un;
|
||||
};
|
||||
struct Negate : public Un {
|
||||
using Un::Un;
|
||||
struct Negate : public Un<Negate> {
|
||||
using Un<Negate>::Un;
|
||||
};
|
||||
struct Add : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Add : public Bin<Add> {
|
||||
using Bin<Add>::Bin;
|
||||
};
|
||||
struct Subtract : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Subtract : public Bin<Subtract> {
|
||||
using Bin<Subtract>::Bin;
|
||||
};
|
||||
struct Multiply : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Multiply : public Bin<Multiply> {
|
||||
using Bin<Multiply>::Bin;
|
||||
};
|
||||
struct Divide : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Divide : public Bin<Divide> {
|
||||
using Bin<Divide>::Bin;
|
||||
};
|
||||
struct Power : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Power : public Bin<Power> {
|
||||
using Bin<Power>::Bin;
|
||||
};
|
||||
struct IntPower : public Binary<Expr, GenericIntegerExpr, Scalar> {
|
||||
using Binary<Expr, GenericIntegerExpr, Scalar>::Binary;
|
||||
struct IntPower : public Binary<IntPower, Result, Expr, GenericIntegerExpr,
|
||||
Scalar, CategoryScalar<Category::Integer>> {
|
||||
using Binary<IntPower, Result, Expr, GenericIntegerExpr, Scalar,
|
||||
CategoryScalar<Category::Integer>>::Binary;
|
||||
};
|
||||
struct CMPLX : public Binary<RealExpr<KIND>, RealExpr<KIND>, Scalar> {
|
||||
using Binary<RealExpr<KIND>, RealExpr<KIND>, Scalar>::Binary;
|
||||
struct CMPLX : public Binary<CMPLX, Result, RealExpr<KIND>, RealExpr<KIND>,
|
||||
typename Scalar::Part, typename Scalar::Part> {
|
||||
using Binary<CMPLX, Result, RealExpr<KIND>, RealExpr<KIND>,
|
||||
typename Scalar::Part, typename Scalar::Part>::Binary;
|
||||
};
|
||||
|
||||
CLASS_BOILERPLATE(Expr)
|
||||
|
@ -298,7 +389,7 @@ public:
|
|||
std::optional<Scalar> ScalarValue() const {
|
||||
return common::GetIf<Scalar>(u_);
|
||||
}
|
||||
void Fold(FoldingContext &c);
|
||||
std::optional<Scalar> Fold(FoldingContext &c);
|
||||
|
||||
private:
|
||||
std::variant<Scalar, CopyableIndirection<DataRef>,
|
||||
|
@ -311,15 +402,17 @@ template<int KIND> class Expr<Category::Character, KIND> {
|
|||
public:
|
||||
using Result = Type<Category::Character, KIND>;
|
||||
using Scalar = typename Result::Value;
|
||||
using Bin = Binary<Expr, Expr, Scalar>;
|
||||
struct Concat : public Bin {
|
||||
using Bin::Bin;
|
||||
using FoldableTrait = std::true_type;
|
||||
template<typename CRTP>
|
||||
using Bin = Binary<CRTP, Result, Expr, Expr, Scalar, Scalar>;
|
||||
struct Concat : public Bin<Concat> {
|
||||
using Bin<Concat>::Bin;
|
||||
};
|
||||
struct Max : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Max : public Bin<Max> {
|
||||
using Bin<Max>::Bin;
|
||||
};
|
||||
struct Min : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Min : public Bin<Min> {
|
||||
using Bin<Min>::Bin;
|
||||
};
|
||||
|
||||
CLASS_BOILERPLATE(Expr)
|
||||
|
@ -333,7 +426,7 @@ public:
|
|||
std::optional<Scalar> ScalarValue() const {
|
||||
return common::GetIf<Scalar>(u_);
|
||||
}
|
||||
void Fold(FoldingContext &c);
|
||||
std::optional<Scalar> Fold(FoldingContext &c);
|
||||
SubscriptIntegerExpr LEN() const;
|
||||
|
||||
private:
|
||||
|
@ -348,13 +441,17 @@ private:
|
|||
// categories and kinds of comparable operands.
|
||||
ENUM_CLASS(RelationalOperator, LT, LE, EQ, NE, GE, GT)
|
||||
|
||||
template<typename EXPR> struct Comparison : Binary<EXPR, EXPR, bool> {
|
||||
template<typename EXPR>
|
||||
struct Comparison
|
||||
: public Binary<Comparison<EXPR>, Type<Category::Logical, 1>, EXPR, EXPR> {
|
||||
using Base = Binary<Comparison<EXPR>, Type<Category::Logical, 1>, EXPR, EXPR>;
|
||||
CLASS_BOILERPLATE(Comparison)
|
||||
Comparison(RelationalOperator r, const EXPR &a, const EXPR &b)
|
||||
: Binary<EXPR, EXPR, bool>{a, b}, opr{r} {}
|
||||
: Base{a, b}, opr{r} {}
|
||||
Comparison(RelationalOperator r, EXPR &&a, EXPR &&b)
|
||||
: Binary<EXPR, EXPR, bool>{std::move(a), std::move(b)}, opr{r} {}
|
||||
std::optional<bool> Fold(FoldingContext &c);
|
||||
: Base{std::move(a), std::move(b)}, opr{r} {}
|
||||
std::optional<bool> FoldScalar(FoldingContext &c,
|
||||
const typename Base::LeftScalar &, const typename Base::RightScalar &);
|
||||
RelationalOperator opr;
|
||||
};
|
||||
|
||||
|
@ -375,8 +472,8 @@ extern template struct Comparison<ComplexExpr<10>>;
|
|||
extern template struct Comparison<ComplexExpr<16>>;
|
||||
extern template struct Comparison<CharacterExpr<1>>;
|
||||
|
||||
// Dynamically polymorphic comparisons that can hold any supported kind
|
||||
// of a specific category.
|
||||
// Dynamically polymorphic comparisons whose operands are expressions of
|
||||
// the same supported kind of a particular type category.
|
||||
template<Category CAT> struct CategoryComparison {
|
||||
CLASS_BOILERPLATE(CategoryComparison)
|
||||
template<int KIND> using KindComparison = Comparison<Expr<CAT, KIND>>;
|
||||
|
@ -390,22 +487,25 @@ template<Category CAT> struct CategoryComparison {
|
|||
// No need to distinguish the various kinds of LOGICAL expression results.
|
||||
template<> class Expr<Category::Logical, 1> {
|
||||
public:
|
||||
using Result = Type<Category::Logical, 1>;
|
||||
using Scalar = bool;
|
||||
struct Not : Unary<Expr, bool> {
|
||||
using Unary<Expr, bool>::Unary;
|
||||
using FoldableTrait = std::true_type;
|
||||
struct Not : Unary<Not, Result, Expr, bool> {
|
||||
using Unary<Not, Result, Expr, bool>::Unary;
|
||||
};
|
||||
using Bin = Binary<Expr, Expr, bool>;
|
||||
struct And : public Bin {
|
||||
using Bin::Bin;
|
||||
template<typename CRTP>
|
||||
using Bin = Binary<CRTP, Result, Expr, Expr, bool, bool>;
|
||||
struct And : public Bin<And> {
|
||||
using Bin<And>::Bin;
|
||||
};
|
||||
struct Or : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Or : public Bin<Or> {
|
||||
using Bin<Or>::Bin;
|
||||
};
|
||||
struct Eqv : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Eqv : public Bin<Eqv> {
|
||||
using Bin<Eqv>::Bin;
|
||||
};
|
||||
struct Neqv : public Bin {
|
||||
using Bin::Bin;
|
||||
struct Neqv : public Bin<Neqv> {
|
||||
using Bin<Neqv>::Bin;
|
||||
};
|
||||
|
||||
CLASS_BOILERPLATE(Expr)
|
||||
|
@ -421,7 +521,7 @@ public:
|
|||
template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
|
||||
|
||||
std::optional<bool> ScalarValue() const { return common::GetIf<bool>(u_); }
|
||||
void Fold(FoldingContext &c);
|
||||
std::optional<Scalar> Fold(FoldingContext &c);
|
||||
|
||||
private:
|
||||
std::variant<bool, CopyableIndirection<DataRef>,
|
||||
|
@ -450,51 +550,26 @@ extern template class Expr<Category::Complex, 16>;
|
|||
extern template class Expr<Category::Character, 1>;
|
||||
extern template class Expr<Category::Logical, 1>;
|
||||
|
||||
// Holds a scalar constant of any kind in an intrinsic type category.
|
||||
template<Category CAT> struct CategoryScalar {
|
||||
CLASS_BOILERPLATE(CategoryScalar)
|
||||
template<int KIND> using KindScalar = typename Expr<CAT, KIND>::Scalar;
|
||||
template<typename A> CategoryScalar(const A &x) : u{x} {}
|
||||
template<typename A>
|
||||
CategoryScalar(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: u{std::move(x)} {}
|
||||
typename KindsVariant<CAT, KindScalar>::type u;
|
||||
};
|
||||
|
||||
// Holds a scalar constant of any intrinsic category and size.
|
||||
struct GenericScalar {
|
||||
CLASS_BOILERPLATE(GenericScalar)
|
||||
template<Category CAT, int KIND>
|
||||
GenericScalar(const typename Expr<CAT, KIND>::Scalar &x)
|
||||
: u{CategoryScalar<CAT>{x}} {}
|
||||
template<Category CAT, int KIND>
|
||||
GenericScalar(typename Expr<CAT, KIND>::Scalar &&x)
|
||||
: u{CategoryScalar<CAT>{std::move(x)}} {}
|
||||
template<typename A> GenericScalar(const A &x) : u{x} {}
|
||||
template<typename A>
|
||||
GenericScalar(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: u{std::move(x)} {}
|
||||
std::variant<CategoryScalar<Category::Integer>,
|
||||
CategoryScalar<Category::Real>, CategoryScalar<Category::Complex>,
|
||||
CategoryScalar<Category::Character>, bool>
|
||||
u;
|
||||
};
|
||||
|
||||
// Dynamically polymorphic expressions that can hold any supported kind
|
||||
// of a specific intrinsic type category.
|
||||
template<Category CAT> struct CategoryExpr {
|
||||
static constexpr Category category{CAT};
|
||||
using Scalar = CategoryScalar<CAT>;
|
||||
using FoldableTrait = std::true_type;
|
||||
CLASS_BOILERPLATE(CategoryExpr)
|
||||
template<int KIND> using KindExpr = Expr<CAT, KIND>;
|
||||
template<int KIND> CategoryExpr(const KindExpr<KIND> &x) : u{x} {}
|
||||
template<int KIND> CategoryExpr(KindExpr<KIND> &&x) : u{std::move(x)} {}
|
||||
std::optional<CategoryScalar<CAT>> ScalarValue() const;
|
||||
void Fold(FoldingContext &);
|
||||
std::optional<Scalar> ScalarValue() const;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
typename KindsVariant<CAT, KindExpr>::type u;
|
||||
};
|
||||
|
||||
// A completely generic expression, polymorphic across the intrinsic type
|
||||
// categories and each of their kinds.
|
||||
struct GenericExpr {
|
||||
using Scalar = GenericScalar;
|
||||
using FoldableTrait = std::true_type;
|
||||
CLASS_BOILERPLATE(GenericExpr)
|
||||
template<Category CAT, int KIND>
|
||||
GenericExpr(const Expr<CAT, KIND> &x) : u{CategoryExpr<CAT>{x}} {}
|
||||
|
@ -504,8 +579,8 @@ struct GenericExpr {
|
|||
template<typename A>
|
||||
GenericExpr(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: u{std::move(x)} {}
|
||||
std::optional<GenericScalar> ScalarValue() const;
|
||||
void Fold(FoldingContext &);
|
||||
std::optional<Scalar> ScalarValue() const;
|
||||
std::optional<Scalar> Fold(FoldingContext &);
|
||||
int Rank() const { return 1; } // TODO
|
||||
std::variant<GenericIntegerExpr, GenericRealExpr, GenericComplexExpr,
|
||||
GenericCharacterExpr, LogicalExpr>
|
||||
|
|
Loading…
Reference in New Issue