[flang] mixed z+i, z+r expressions

Original-commit: flang-compiler/f18@5c5d11c1f7
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-09-04 16:42:32 -07:00
parent 0b2d90bc00
commit 63a26fc7dc
4 changed files with 133 additions and 49 deletions

View File

@ -68,13 +68,13 @@ template<typename T> struct Constant {
Value value;
};
// BOZ literal constants need to be wide enough to hold an integer or real
// value of any supported kind. They also need to be distinguishable from
// BOZ literal "typeless" constants must be wide enough to hold a numeric
// value of any supported kind. They must also be distinguishable from
// other integer constants, since they are permitted to be used in only a
// few situations.
using BOZLiteralConstant = value::Integer<128>;
using BOZLiteralConstant = value::Integer<8 * 2 * DefaultComplex::kind>;
// "Typeless" operands to INTEGER and REAL operations.
// "Typeless" operands to INTEGER, REAL, and COMPLEX operations.
template<typename T> struct BOZConstant {
using Result = T;
using Value = BOZLiteralConstant;
@ -469,8 +469,8 @@ public:
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
using Others = std::variant<Constant<Result>, DataReference<Result>,
FunctionReference<Result>>;
using Others = std::variant<Constant<Result>, BOZConstant<Result>,
DataReference<Result>, FunctionReference<Result>>;
public:
common::CombineVariants<Operations, Others> u;

View File

@ -57,11 +57,85 @@ ConvertRealOperandsResult ConvertRealOperands(
std::move(x.u), std::move(y.u));
}
// A helper template for NumericOperation below.
// A helper template for NumericOperation and its subroutines.
template<TypeCategory CAT>
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
return {AsGenericExpr(std::move(catExpr))};
}
template<TypeCategory CAT>
std::optional<Expr<SomeType>> Package(
std::optional<Expr<SomeKind<CAT>>> &&catExpr) {
if (catExpr.has_value()) {
return {AsGenericExpr(std::move(*catExpr))};
}
return std::nullopt;
}
std::optional<Expr<SomeComplex>> ConstructComplex(
parser::ContextualMessages &messages, Expr<SomeType> &&real,
Expr<SomeType> &&imaginary) {
if (auto converted{ConvertRealOperands(
messages, std::move(real), std::move(imaginary))}) {
return {std::visit(
[](auto &&pair) {
return MakeComplex(std::move(pair[0]), std::move(pair[1]));
},
std::move(*converted))};
}
return std::nullopt;
}
std::optional<Expr<SomeComplex>> ConstructComplex(
parser::ContextualMessages &messages, std::optional<Expr<SomeType>> &&real,
std::optional<Expr<SomeType>> &&imaginary) {
if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
return ConstructComplex(messages, std::move(std::get<0>(*parts)),
std::move(std::get<1>(*parts)));
}
return std::nullopt;
}
Expr<SomeReal> GetComplexPart(const Expr<SomeComplex> &z, bool isImaginary) {
return std::visit(
[&](const auto &zk) {
static constexpr int kind{ResultType<decltype(zk)>::kind};
return AsCategoryExpr(AsExpr(ComplexComponent<kind>{isImaginary, zk}));
},
z.u);
}
template<template<typename> class OPR>
std::optional<Expr<SomeType>> MixedComplex(parser::ContextualMessages &messages,
Expr<SomeComplex> &&zx, Expr<SomeType> &&iry) {
Expr<SomeReal> zr{GetComplexPart(zx, false)};
Expr<SomeReal> zi{GetComplexPart(zx, true)};
if constexpr (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
std::is_same_v<OPR<DefaultReal>, Subtract<DefaultReal>>) {
// Addition and subtraction: apply the operation to the real part of the
// complex operand, and a transfer/convert its imaginary part.
// i.e., (a,b) + c = (a+c, b)
if (std::optional<Expr<SomeType>> rr{
NumericOperation<OPR>(messages, std::move(zr), std::move(iry))}) {
return Package(ConstructComplex(messages, AsGenericExpr(std::move(*rr)),
AsGenericExpr(std::move(zi))));
}
} else if constexpr (std::is_same_v<OPR<DefaultReal>,
Multiply<DefaultReal>> ||
std::is_same_v<OPR<DefaultReal>, Divide<DefaultReal>>) {
// Multiplication and division of a COMPLEX value by an INTEGER or REAL
// operand: apply the operation to both components of the COMPLEX value,
// then convert and recombine them.
// i.e., (a,b) * c = (a*c, b*c)
auto copy{iry};
auto rr{NumericOperation<OPR>(messages, std::move(zr), std::move(iry))};
auto ri{NumericOperation<OPR>(messages, std::move(zi), std::move(copy))};
if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
return Package(ConstructComplex(messages, std::move(std::get<0>(*parts)),
std::move(std::get<1>(*parts))));
}
}
return std::nullopt;
}
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
// the operands to a dyadic INTEGER or REAL operation, it assumes the type
@ -93,8 +167,9 @@ std::optional<Expr<SomeType>> NumericOperation(
return Package(std::visit(
[&](auto &&ryk) -> Expr<SomeReal> {
using resultType = ResultType<decltype(ryk)>;
return AsCategoryExpr(AsExpr(OPR<resultType>{
ConvertToType<resultType>(std::move(ix)), std::move(ryk)}));
return AsCategoryExpr(AsExpr(
OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
std::move(ryk)}));
},
std::move(ry.u)));
},
@ -102,19 +177,30 @@ std::optional<Expr<SomeType>> NumericOperation(
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
std::move(zx), std::move(zy)));
},
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&zy) {
return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
},
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&zy) {
return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
},
// TODO pmk: mixed r+complex, &c.; r/z is tricky
// TODO pmk: mixed complex + boz? yes but what about COMPLEX*16?
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
return NumericOperation<OPR>(messages, ConvertTo(iy, std::move(bx)), std::move(y));
return NumericOperation<OPR>(
messages, ConvertTo(iy, std::move(bx)), std::move(y));
},
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
return NumericOperation<OPR>(messages, ConvertTo(ry, std::move(bx)), std::move(y));
return NumericOperation<OPR>(
messages, ConvertTo(ry, std::move(bx)), std::move(y));
},
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
return NumericOperation<OPR>(messages, std::move(x), ConvertTo(ix, std::move(by)));
return NumericOperation<OPR>(
messages, std::move(x), ConvertTo(ix, std::move(by)));
},
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
return NumericOperation<OPR>(messages, std::move(x), ConvertTo(rx, std::move(by)));
return NumericOperation<OPR>(
messages, std::move(x), ConvertTo(rx, std::move(by)));
},
// TODO pmk mixed complex; Add/Sub different from Mult/Div
[&](auto &&, auto &&) {
messages.Say("non-numeric operands to numeric operation"_err_en_US);
return std::optional<Expr<SomeType>>{std::nullopt};

View File

@ -89,6 +89,16 @@ template<> inline Expr<SomeType> AsGenericExpr(GenericScalar &&x) {
x.u);
}
Expr<SomeReal> GetComplexPart(
const Expr<SomeComplex> &, bool isImaginary = false);
template<int KIND>
Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
Expr<Type<TypeCategory::Real, KIND>> &&im) {
return AsCategoryExpr(
AsExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)}));
}
// Creation of conversion expressions can be done to either a known
// specific intrinsic type with ConvertToType<T>(x) or by converting
// one arbitrary expression to the type of another with ConvertTo(to, from).
@ -123,9 +133,9 @@ Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
}
}
template<typename TO>
Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
// TODO: check rank == 0
// TODO: pmk: truncation warnings
static_assert(TO::isSpecificType);
return Expr<TO>{BOZConstant<TO>{std::move(x)}};
}
@ -170,11 +180,14 @@ Expr<SomeType> ConvertTo(const Expr<SomeType> &to, Expr<FT> &&from) {
}
template<TypeCategory CAT>
Expr<SomeType> ConvertTo(const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
return AsGenericExpr(std::visit([&](const auto &tok) {
using Ty = ResultType<decltype(tok)>;
return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
}, to.u));
Expr<SomeType> ConvertTo(
const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
return AsGenericExpr(std::visit(
[&](const auto &tok) {
using Ty = ResultType<decltype(tok)>;
return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
},
to.u));
}
template<typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
@ -225,6 +238,15 @@ using ConvertRealOperandsResult =
ConvertRealOperandsResult ConvertRealOperands(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
// Per F'2018 R718, if both components are INTEGER, they are both converted
// to default REAL and the result is default COMPLEX. Otherwise, the
// kind of the result is the kind of most precise REAL component, and the other
// component is converted if necessary to its type.
std::optional<Expr<SomeComplex>> ConstructComplex(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&);
template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
using Ty = TypeOf<A>;
static_assert(

View File

@ -104,8 +104,6 @@ struct ExprAnalyzer {
MaybeExpr Analyze(const parser::Expr::DefinedBinary &); // TODO
// TODO more remain
std::optional<Expr<SomeComplex>> ConstructComplex(MaybeExpr &&, MaybeExpr &&);
FoldingContext &context;
const semantics::IntrinsicTypeDefaultKinds &defaults;
};
@ -337,32 +335,9 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexPart &x) {
return AnalyzeHelper(*this, x.u);
}
// Per F'2018 R718, if both components are INTEGER, they are both converted
// to default REAL and the result is default COMPLEX. Otherwise, the
// kind of the result is the kind of most precise REAL component, and the other
// component is converted if necessary to its type.
std::optional<Expr<SomeComplex>> ExprAnalyzer::ConstructComplex(
MaybeExpr &&real, MaybeExpr &&imaginary) {
if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
if (auto converted{ConvertRealOperands(context.messages,
std::move(std::get<0>(*parts)), std::move(std::get<1>(*parts)))}) {
return {std::visit(
[](auto &&pair) -> std::optional<Expr<SomeComplex>> {
using realType = ResultType<decltype(pair[0])>;
using zType = SameKind<TypeCategory::Complex, realType>;
auto cmplx{ComplexConstructor<zType::kind>{
std::move(pair[0]), std::move(pair[1])}};
return {AsCategoryExpr(AsExpr(std::move(cmplx)))};
},
std::move(*converted))};
}
}
return std::nullopt;
}
MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) {
return AsMaybeExpr(
ConstructComplex(Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t))));
return AsMaybeExpr(ConstructComplex(
context.messages, Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t))));
}
MaybeExpr ExprAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
@ -486,7 +461,8 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Divide &x) {
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
return AsMaybeExpr(ConstructComplex(AnalyzeHelper(*this, *std::get<0>(x.t)),
return AsMaybeExpr(ConstructComplex(context.messages,
AnalyzeHelper(*this, *std::get<0>(x.t)),
AnalyzeHelper(*this, *std::get<1>(x.t))));
}