@@ -61,8 +61,7 @@ template <common::TypeCategory C, int K>
6161struct IsIntegral <evaluate::Type<C, K>> {
6262 static constexpr bool value{//
6363 C == common::TypeCategory::Integer ||
64- C == common::TypeCategory::Unsigned ||
65- C == common::TypeCategory::Logical};
64+ C == common::TypeCategory::Unsigned};
6665};
6766
6867template <typename T> constexpr bool is_integral_v{IsIntegral<T>::value};
@@ -83,10 +82,25 @@ constexpr bool is_floating_point_v{IsFloatingPoint<T>::value};
8382template <typename T>
8483constexpr bool is_numeric_v{is_integral_v<T> || is_floating_point_v<T>};
8584
85+ template <typename ...> struct IsLogical {
86+ static constexpr bool value{false };
87+ };
88+
89+ template <common::TypeCategory C, int K>
90+ struct IsLogical <evaluate::Type<C, K>> {
91+ static constexpr bool value{C == common::TypeCategory::Logical};
92+ };
93+
94+ template <typename T> constexpr bool is_logical_v{IsLogical<T>::value};
95+
8696template <typename T, typename Op0, typename Op1>
8797using ReassocOpBase = evaluate::match::AnyOfPattern< //
8898 evaluate::match::Add<T, Op0, Op1>, //
89- evaluate::match::Mul<T, Op0, Op1>>;
99+ evaluate::match::Mul<T, Op0, Op1>, //
100+ evaluate::match::LogicalOp<common::LogicalOperator::And, T, Op0, Op1>,
101+ evaluate::match::LogicalOp<common::LogicalOperator::Or, T, Op0, Op1>,
102+ evaluate::match::LogicalOp<common::LogicalOperator::Eqv, T, Op0, Op1>,
103+ evaluate::match::LogicalOp<common::LogicalOperator::Neqv, T, Op0, Op1>>;
90104
91105template <typename T, typename Op0, typename Op1>
92106struct ReassocOp : public ReassocOpBase <T, Op0, Op1> {
@@ -110,16 +124,16 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
110124 // Try to find cases where the input expression is of the form
111125 // (1) (a . b) . c, or
112126 // (2) a . (b . c),
113- // where . denotes an associative operation (currently + or *) , and a, b, c
114- // are some subexpresions.
127+ // where . denotes an associative operation, and a, b, c are some
128+ // subexpresions.
115129 // If one of the operands in the nested operation is the atomic variable
116130 // (with some possible type conversions applied to it), bring it to the
117131 // top-level operation, and move the top-level operand into the nested
118132 // operation.
119133 // For example, assuming x is the atomic variable:
120134 // (a + x) + b -> (a + b) + x, i.e. (conceptually) swap x and b.
121135 template <typename T, typename U,
122- typename = std::enable_if_t <is_numeric_v<T>>>
136+ typename = std::enable_if_t <is_numeric_v<T> || is_logical_v<T> >>
123137 evaluate::Expr<T> operator ()(evaluate::Expr<T> &&x, const U &u) {
124138 if constexpr (is_floating_point_v<T>) {
125139 if (!context_.langOptions ().AssociativeMath ) {
@@ -133,8 +147,8 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
133147 // some order) from the example above.
134148 evaluate::match::Expr<T> sub[3 ];
135149 auto inner{reassocOp<T>(sub[0 ], sub[1 ])};
136- auto outer1{reassocOp<T>(inner, sub[2 ])}; // inner + something
137- auto outer2{reassocOp<T>(sub[2 ], inner)}; // something + inner
150+ auto outer1{reassocOp<T>(inner, sub[2 ])}; // inner . something
151+ auto outer2{reassocOp<T>(sub[2 ], inner)}; // something . inner
138152#if !defined(__clang__) && !defined(_MSC_VER) && \
139153 (__GNUC__ < 8 || (__GNUC__ == 8 && __GNUC_MINOR__ < 5 ))
140154 // If GCC version < 8.5, use this definition. For the other definition
@@ -167,37 +181,53 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
167181 }
168182 return common::visit (
169183 [&](auto &&s) {
170- using Expr = evaluate::Expr<T>;
171- using TypeS = llvm::remove_cvref_t <decltype (s)>;
172- // This visitor has to be semantically correct for all possible
173- // types of s even though at runtime s will only be one of the
174- // matched types.
175- // Limit the construction to the operation types that we tried
176- // to match (otherwise TypeS(op1, op2) would fail for non-binary
177- // operations).
178- if constexpr (common::HasMember<TypeS, MatchTypes>) {
179- Expr atom{*sub[atomIdx].ref };
180- Expr op1{*sub[(atomIdx + 1 ) % 3 ].ref };
181- Expr op2{*sub[(atomIdx + 2 ) % 3 ].ref };
182- return Expr (
183- TypeS (atom, Expr (TypeS (std::move (op1), std::move (op2)))));
184- } else {
185- return Expr (TypeS (s));
186- }
184+ // Build the new expression from the matched components.
185+ return Reconstruct<T, MatchTypes>(s, *sub[atomIdx].ref ,
186+ *sub[(atomIdx + 1 ) % 3 ].ref , *sub[(atomIdx + 2 ) % 3 ].ref );
187187 },
188188 evaluate::match::deparen (x).u );
189189 }
190190 return Id::operator ()(std::move (x), u);
191191 }
192192
193193 template <typename T, typename U,
194- typename = std::enable_if_t <!is_numeric_v<T>>>
194+ typename = std::enable_if_t <!is_numeric_v<T> && !is_logical_v<T> >>
195195 evaluate::Expr<T> operator ()(
196196 evaluate::Expr<T> &&x, const U &u, NonIntegralTag = {}) {
197197 return Id::operator ()(std::move (x), u);
198198 }
199199
200200private:
201+ template <typename T, typename MatchTypes, typename S>
202+ evaluate::Expr<T> Reconstruct (const S &op, evaluate::Expr<T> atom,
203+ evaluate::Expr<T> op1, evaluate::Expr<T> op2) {
204+ using TypeS = llvm::remove_cvref_t <decltype (op)>;
205+ // This function has to be semantically correct for all possible types
206+ // of S even though at runtime s will only be one of the matched types.
207+ // Limit the construction to the operation types that we tried to match
208+ // (otherwise TypeS(op1, op2) would fail for non-binary operations).
209+ if constexpr (!common::HasMember<TypeS, MatchTypes>) {
210+ return evaluate::Expr<T>(TypeS (op));
211+ } else if constexpr (is_logical_v<T>) {
212+ constexpr int K{T::kind};
213+ if constexpr (std::is_same_v<TypeS, evaluate::LogicalOperation<K>>) {
214+ // Logical operators take an extra argument in their constructor,
215+ // so they need their own reconstruction code.
216+ common::LogicalOperator opCode{op.logicalOperator };
217+ return evaluate::Expr<T>(TypeS ( //
218+ opCode, std::move (atom),
219+ evaluate::Expr<T>(TypeS ( //
220+ opCode, std::move (op1), std::move (op2)))));
221+ }
222+ } else {
223+ // Generic reconstruction.
224+ return evaluate::Expr<T>(TypeS ( //
225+ std::move (atom),
226+ evaluate::Expr<T>(TypeS ( //
227+ std::move (op1), std::move (op2)))));
228+ }
229+ }
230+
201231 template <typename T> bool IsAtom (const evaluate::Expr<T> &x) const {
202232 return IsSameOrConvertOf (evaluate::AsGenericExpr (AsRvalue (x)), atom_);
203233 }
0 commit comments