#include <tuple>
#include <utility>
#include <variant>
+#include <vector>
/// Parse tree visitor
/// Call Walk(x, visitor) to visit x and, by default, each node under x.
mutator.Post(x);
}
}
+
+// Expr traversal uses iteration rather than recursion to avoid
+// blowing out the stack on very deep expression parse trees.
+// It replaces implementations that looked like:
+// template <typename V> void Walk(const Expr &x, V visitor) {
+// if (visitor.Pre(x)) { // Pre on the Expr
+// Walk(x.source, visitor);
+// // Pre on the operator, walk the operands, Post on operator
+// Walk(x.u, visitor);
+// visitor.Post(x); // Post on the Expr
+// }
+// }
+template <typename A, typename V, typename UNARY, typename BINARY>
+static void IterativeWalk(A &start, V &visitor) {
+ struct ExprWorkList {
+ ExprWorkList(A &x) : expr(&x) {}
+ bool doPostExpr{false}, doPostOpr{false};
+ A *expr;
+ };
+ std::vector<ExprWorkList> stack;
+ stack.emplace_back(start);
+ do {
+ A &expr{*stack.back().expr};
+ if (stack.back().doPostOpr) {
+ stack.back().doPostOpr = false;
+ common::visit([&visitor](auto &y) { visitor.Post(y); }, expr.u);
+ } else if (stack.back().doPostExpr) {
+ visitor.Post(expr);
+ stack.pop_back();
+ } else if (!visitor.Pre(expr)) {
+ stack.pop_back();
+ } else {
+ stack.back().doPostExpr = true;
+ Walk(expr.source, visitor);
+ UNARY *unary{nullptr};
+ BINARY *binary{nullptr};
+ common::visit(
+ [&unary, &binary](auto &y) {
+ if constexpr (std::is_convertible_v<decltype(&y), UNARY *>) {
+ unary = &y;
+ } else if constexpr (std::is_convertible_v<decltype(&y),
+ BINARY *>) {
+ binary = &y;
+ }
+ },
+ expr.u);
+ if (!unary && !binary) {
+ Walk(expr.u, visitor);
+ } else if (common::visit(
+ [&visitor](auto &y) { return visitor.Pre(y); }, expr.u)) {
+ stack.back().doPostOpr = true;
+ if (unary) {
+ stack.emplace_back(unary->v.value());
+ } else {
+ stack.emplace_back(std::get<1>(binary->t).value());
+ stack.emplace_back(std::get<0>(binary->t).value());
+ }
+ }
+ }
+ } while (!stack.empty());
+}
template <typename V> void Walk(const Expr &x, V &visitor) {
- if (visitor.Pre(x)) {
- Walk(x.source, visitor);
- Walk(x.u, visitor);
- visitor.Post(x);
- }
+ IterativeWalk<const Expr, V, const Expr::IntrinsicUnary,
+ const Expr::IntrinsicBinary>(x, visitor);
}
template <typename M> void Walk(Expr &x, M &mutator) {
- if (mutator.Pre(x)) {
- Walk(x.source, mutator);
- Walk(x.u, mutator);
- mutator.Post(x);
- }
+ IterativeWalk<Expr, M, Expr::IntrinsicUnary, Expr::IntrinsicBinary>(
+ x, mutator);
}
+
template <typename V> void Walk(const Designator &x, V &visitor) {
if (visitor.Pre(x)) {
Walk(x.source, visitor);
bool CheckIsValidForwardReference(const semantics::DerivedTypeSpec &);
MaybeExpr AnalyzeComplex(MaybeExpr &&re, MaybeExpr &&im, const char *what);
+ MaybeExpr IterativelyAnalyzeSubexpressions(const parser::Expr &);
+
semantics::SemanticsContext &context_;
FoldingContext &foldingContext_{context_.foldingContext()};
std::map<parser::CharBlock, int> impliedDos_; // values are INTEGER kinds
bool inDataStmtObject_{false};
bool inDataStmtConstant_{false};
bool inStmtFunctionDefinition_{false};
+ bool iterativelyAnalyzingSubexpressions_{false};
friend class ArgumentAnalyzer;
};
#include <functional>
#include <optional>
#include <set>
+#include <vector>
// Typedef for optional generic expressions (ubiquitous in this file)
using MaybeExpr =
result = Analyze(x.u);
}
if (result) {
+ if constexpr (std::is_same_v<PARSED, parser::Expr>) {
+ if (!isNullPointerOk_ && IsNullPointer(*result)) {
+ Say(source,
+ "NULL() may not be used as an expression in this context"_err_en_US);
+ }
+ }
SetExpr(x, Fold(std::move(*result)));
return x.typedExpr->v;
} else {
}
}
+// This is an optional preliminary pass over parser::Expr subtrees.
+// Given an expression tree, iteratively traverse it in a bottom-up order
+// to analyze all of its subexpressions. A later normal top-down analysis
+// will then be able to use the results that will have been saved in the
+// parse tree without having to recurse deeply. This technique keeps
+// absurdly deep expression parse trees from causing the analyzer to overflow
+// its stack.
+MaybeExpr ExpressionAnalyzer::IterativelyAnalyzeSubexpressions(
+ const parser::Expr &top) {
+ std::vector<const parser::Expr *> queue, finish;
+ queue.push_back(&top);
+ do {
+ const parser::Expr &expr{*queue.back()};
+ queue.pop_back();
+ if (!expr.typedExpr) {
+ const parser::Expr::IntrinsicUnary *unary{nullptr};
+ const parser::Expr::IntrinsicBinary *binary{nullptr};
+ common::visit(
+ [&unary, &binary](auto &y) {
+ if constexpr (std::is_convertible_v<decltype(&y),
+ decltype(unary)>) {
+ // Don't evaluate a constant operand to Negate
+ if (!std::holds_alternative<parser::LiteralConstant>(
+ y.v.value().u)) {
+ unary = &y;
+ }
+ } else if constexpr (std::is_convertible_v<decltype(&y),
+ decltype(binary)>) {
+ binary = &y;
+ }
+ },
+ expr.u);
+ if (unary) {
+ queue.push_back(&unary->v.value());
+ } else if (binary) {
+ queue.push_back(&std::get<0>(binary->t).value());
+ queue.push_back(&std::get<1>(binary->t).value());
+ }
+ finish.push_back(&expr);
+ }
+ } while (!queue.empty());
+ // Analyze the collected subexpressions in bottom-up order.
+ // On an error, bail out and leave partial results in place.
+ MaybeExpr result;
+ for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) {
+ const parser::Expr &expr{**riter};
+ result = ExprOrVariable(expr, expr.source);
+ if (!result) {
+ return result;
+ }
+ }
+ return result; // last value was from analysis of "top"
+}
+
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
- if (useSavedTypedExprs_ && expr.typedExpr) {
- return expr.typedExpr->v;
+ bool wasIterativelyAnalyzing{iterativelyAnalyzingSubexpressions_};
+ MaybeExpr result;
+ if (useSavedTypedExprs_) {
+ if (expr.typedExpr) {
+ return expr.typedExpr->v;
+ }
+ if (!wasIterativelyAnalyzing) {
+ iterativelyAnalyzingSubexpressions_ = true;
+ result = IterativelyAnalyzeSubexpressions(expr);
+ }
}
- MaybeExpr result{ExprOrVariable(expr, expr.source)};
- if (!isNullPointerOk_ && result && IsNullPointer(*result)) {
- Say(expr.source,
- "NULL() may not be used as an expression in this context"_err_en_US);
+ if (!result) {
+ result = ExprOrVariable(expr, expr.source);
}
+ iterativelyAnalyzingSubexpressions_ = wasIterativelyAnalyzing;
return result;
}
const parser::Expr &expr) {
source_.ExtendToCover(expr.source);
if (const Symbol *assumedTypeDummy{AssumedTypeDummy(expr)}) {
- expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter);
+ ResetExpr(expr);
if (isProcedureCall_) {
ActualArgument arg{ActualArgument::AssumedType{*assumedTypeDummy}};
SetArgSourceLocation(arg, expr.source);
--- /dev/null
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Exercise parsing, expression analysis, and folding on a very tall expression tree
+! 32*32 = 1024 repetitions
+#define M0(x) x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x
+#define M1(x) x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x
+module m
+ logical, parameter :: test_1 = 32**2 .EQ. M1(M0(1))
+end module