forked from OSchip/llvm-project
[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:
parent
0b2d90bc00
commit
63a26fc7dc
|
@ -68,13 +68,13 @@ template<typename T> struct Constant {
|
||||||
Value value;
|
Value value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// BOZ literal constants need to be wide enough to hold an integer or real
|
// BOZ literal "typeless" constants must be wide enough to hold a numeric
|
||||||
// value of any supported kind. They also need to be distinguishable from
|
// value of any supported kind. They must also be distinguishable from
|
||||||
// other integer constants, since they are permitted to be used in only a
|
// other integer constants, since they are permitted to be used in only a
|
||||||
// few situations.
|
// 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 {
|
template<typename T> struct BOZConstant {
|
||||||
using Result = T;
|
using Result = T;
|
||||||
using Value = BOZLiteralConstant;
|
using Value = BOZLiteralConstant;
|
||||||
|
@ -469,8 +469,8 @@ public:
|
||||||
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
|
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
|
||||||
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
|
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
|
||||||
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
|
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
|
||||||
using Others = std::variant<Constant<Result>, DataReference<Result>,
|
using Others = std::variant<Constant<Result>, BOZConstant<Result>,
|
||||||
FunctionReference<Result>>;
|
DataReference<Result>, FunctionReference<Result>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
common::CombineVariants<Operations, Others> u;
|
common::CombineVariants<Operations, Others> u;
|
||||||
|
|
|
@ -57,11 +57,85 @@ ConvertRealOperandsResult ConvertRealOperands(
|
||||||
std::move(x.u), std::move(y.u));
|
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>
|
template<TypeCategory CAT>
|
||||||
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
|
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
|
||||||
return {AsGenericExpr(std::move(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
|
// 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
|
// 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(
|
return Package(std::visit(
|
||||||
[&](auto &&ryk) -> Expr<SomeReal> {
|
[&](auto &&ryk) -> Expr<SomeReal> {
|
||||||
using resultType = ResultType<decltype(ryk)>;
|
using resultType = ResultType<decltype(ryk)>;
|
||||||
return AsCategoryExpr(AsExpr(OPR<resultType>{
|
return AsCategoryExpr(AsExpr(
|
||||||
ConvertToType<resultType>(std::move(ix)), std::move(ryk)}));
|
OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
|
||||||
|
std::move(ryk)}));
|
||||||
},
|
},
|
||||||
std::move(ry.u)));
|
std::move(ry.u)));
|
||||||
},
|
},
|
||||||
|
@ -102,19 +177,30 @@ std::optional<Expr<SomeType>> NumericOperation(
|
||||||
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
||||||
std::move(zx), std::move(zy)));
|
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) {
|
[&](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) {
|
[&](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) {
|
[&](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) {
|
[&](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 &&) {
|
[&](auto &&, auto &&) {
|
||||||
messages.Say("non-numeric operands to numeric operation"_err_en_US);
|
messages.Say("non-numeric operands to numeric operation"_err_en_US);
|
||||||
return std::optional<Expr<SomeType>>{std::nullopt};
|
return std::optional<Expr<SomeType>>{std::nullopt};
|
||||||
|
|
|
@ -89,6 +89,16 @@ template<> inline Expr<SomeType> AsGenericExpr(GenericScalar &&x) {
|
||||||
x.u);
|
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
|
// Creation of conversion expressions can be done to either a known
|
||||||
// specific intrinsic type with ConvertToType<T>(x) or by converting
|
// specific intrinsic type with ConvertToType<T>(x) or by converting
|
||||||
// one arbitrary expression to the type of another with ConvertTo(to, from).
|
// 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>
|
template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
|
||||||
Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
|
|
||||||
// TODO: check rank == 0
|
// TODO: check rank == 0
|
||||||
|
// TODO: pmk: truncation warnings
|
||||||
static_assert(TO::isSpecificType);
|
static_assert(TO::isSpecificType);
|
||||||
return Expr<TO>{BOZConstant<TO>{std::move(x)}};
|
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>
|
template<TypeCategory CAT>
|
||||||
Expr<SomeType> ConvertTo(const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
|
Expr<SomeType> ConvertTo(
|
||||||
return AsGenericExpr(std::visit([&](const auto &tok) {
|
const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
|
||||||
using Ty = ResultType<decltype(tok)>;
|
return AsGenericExpr(std::visit(
|
||||||
return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
|
[&](const auto &tok) {
|
||||||
}, to.u));
|
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>;
|
template<typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
|
||||||
|
@ -225,6 +238,15 @@ using ConvertRealOperandsResult =
|
||||||
ConvertRealOperandsResult ConvertRealOperands(
|
ConvertRealOperandsResult ConvertRealOperands(
|
||||||
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
|
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) {
|
template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
|
||||||
using Ty = TypeOf<A>;
|
using Ty = TypeOf<A>;
|
||||||
static_assert(
|
static_assert(
|
||||||
|
|
|
@ -104,8 +104,6 @@ struct ExprAnalyzer {
|
||||||
MaybeExpr Analyze(const parser::Expr::DefinedBinary &); // TODO
|
MaybeExpr Analyze(const parser::Expr::DefinedBinary &); // TODO
|
||||||
// TODO more remain
|
// TODO more remain
|
||||||
|
|
||||||
std::optional<Expr<SomeComplex>> ConstructComplex(MaybeExpr &&, MaybeExpr &&);
|
|
||||||
|
|
||||||
FoldingContext &context;
|
FoldingContext &context;
|
||||||
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
||||||
};
|
};
|
||||||
|
@ -337,32 +335,9 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexPart &x) {
|
||||||
return AnalyzeHelper(*this, x.u);
|
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) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) {
|
||||||
return AsMaybeExpr(
|
return AsMaybeExpr(ConstructComplex(
|
||||||
ConstructComplex(Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t))));
|
context.messages, Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t))));
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
|
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) {
|
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))));
|
AnalyzeHelper(*this, *std::get<1>(x.t))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue