template<typename T, typename V> void Walk(const Statement<T> &x, V &visitor) {
if (visitor.Pre(x)) {
// N.B. the label is not traversed
+ Walk(x.source, visitor);
Walk(x.statement, visitor);
visitor.Post(x);
}
template<typename T, typename M> void Walk(Statement<T> &x, M &mutator) {
if (mutator.Pre(x)) {
// N.B. the label is not traversed
+ Walk(x.source, mutator);
Walk(x.statement, mutator);
mutator.Post(x);
}
template<typename V> void Walk(const Name &x, V &visitor) {
if (visitor.Pre(x)) {
+ Walk(x.source, visitor);
visitor.Post(x);
}
}
template<typename M> void Walk(Name &x, M &mutator) {
if (mutator.Pre(x)) {
+ Walk(x.source, mutator);
mutator.Post(x);
}
}
mutator.Post(x);
}
}
+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);
+ }
+}
+template<typename M> void Walk(Expr &x, M &mutator) {
+ if (mutator.Pre(x)) {
+ Walk(x.source, mutator);
+ Walk(x.u, mutator);
+ mutator.Post(x);
+ }
+}
template<typename V> void Walk(const PartRef &x, V &visitor) {
if (visitor.Pre(x)) {
Walk(x.name, visitor);
mutator.Post(x);
}
}
+template<typename V> void Walk(const SignedIntLiteralConstant &x, V &visitor) {
+ if (visitor.Pre(x)) {
+ Walk(x.source, visitor);
+ Walk(x.t, visitor);
+ visitor.Post(x);
+ }
+}
+template<typename M> void Walk(SignedIntLiteralConstant &x, M &mutator) {
+ if (mutator.Pre(x)) {
+ Walk(x.source, mutator);
+ Walk(x.t, mutator);
+ mutator.Post(x);
+ }
+}
template<typename V> void Walk(const RealLiteralConstant &x, V &visitor) {
if (visitor.Pre(x)) {
Walk(x.real, visitor);
}
template<typename V> void Walk(const RealLiteralConstant::Real &x, V &visitor) {
if (visitor.Pre(x)) {
+ Walk(x.source, visitor);
visitor.Post(x);
}
}
template<typename M> void Walk(RealLiteralConstant::Real &x, M &mutator) {
if (mutator.Pre(x)) {
+ Walk(x.source, mutator);
mutator.Post(x);
}
}
mutator.Post(x);
}
}
+template<typename V> void Walk(const CompilerDirective &x, V &visitor) {
+ if (visitor.Pre(x)) {
+ Walk(x.source, visitor);
+ Walk(x.u, visitor);
+ visitor.Post(x);
+ }
+}
+template<typename M> void Walk(CompilerDirective &x, M &mutator) {
+ if (mutator.Pre(x)) {
+ Walk(x.source, mutator);
+ Walk(x.u, mutator);
+ mutator.Post(x);
+ }
+}
template<typename V>
void Walk(const OmpLinearClause::WithModifier &x, V &visitor) {
if (visitor.Pre(x)) {
#include <iostream> // TODO pmk remove soon
#include <optional>
-using namespace Fortran::parser::literals;
-
// Typedef for optional generic expressions (ubiquitous in this file)
using MaybeExpr =
std::optional<Fortran::evaluate::Expr<Fortran::evaluate::SomeType>>;
+namespace Fortran::parser {
+bool SourceLocationFindingVisitor::Pre(const Expr &x) {
+ source = x.source;
+ return false;
+}
+void SourceLocationFindingVisitor::Post(const CharBlock &at) { source = at; }
+}
+
// Much of the code that implements semantic analysis of expressions is
// tightly coupled with their typed representations in lib/evaluate,
// and appears here in namespace Fortran::evaluate for convenience.
using common::TypeCategory;
-// Constraint checking
-void ExpressionAnalysisContext::CheckConstraints(MaybeExpr &expr) {
- if (inner_ != nullptr) {
- inner_->CheckConstraints(expr);
- }
- if (constraint_ != nullptr && expr.has_value()) {
- if (!(this->*constraint_)(*expr)) {
- expr.reset();
- }
- }
-}
-
-bool ExpressionAnalysisContext::ScalarConstraint(Expr<SomeType> &expr) {
- int rank{expr.Rank()};
- if (rank == 0) {
- return true;
- }
- Say("expression must be scalar, but has rank %d"_err_en_US, rank);
- return false;
-}
-
-bool ExpressionAnalysisContext::ConstantConstraint(Expr<SomeType> &expr) {
- expr = Fold(context_.foldingContext(), std::move(expr));
- if (IsConstant(expr)) {
- return true;
- }
- Say("expression must be constant"_err_en_US);
- return false;
-}
-
-bool ExpressionAnalysisContext::IntegerConstraint(Expr<SomeType> &expr) {
- if (std::holds_alternative<Expr<SomeInteger>>(expr.u)) {
- return true;
- }
- Say("expression must be INTEGER"_err_en_US);
- return false;
-}
-
-bool ExpressionAnalysisContext::LogicalConstraint(Expr<SomeType> &expr) {
- if (std::holds_alternative<Expr<SomeLogical>>(expr.u)) {
- return true;
- }
- Say("expression must be LOGICAL"_err_en_US);
- return false;
-}
-
-bool ExpressionAnalysisContext::DefaultCharConstraint(Expr<SomeType> &expr) {
- if (auto *charExpr{std::get_if<Expr<SomeCharacter>>(&expr.u)}) {
- return charExpr->GetKind() ==
- context_.defaultKinds().GetDefaultKind(TypeCategory::Character);
- }
- Say("expression must be default CHARACTER"_err_en_US);
- return false;
-}
-
// If a generic expression simply wraps a DataRef, extract it.
// TODO: put in tools.h?
template<typename A> std::optional<DataRef> ExtractDataRef(A &&) {
// Analyze the expression in a specified source position context for better
// error reporting.
auto save{context_.foldingContext().messages.SetLocation(expr.source)};
- MaybeExpr result{AnalyzeExpr(*this, expr.u)};
- CheckConstraints(result);
- return result;
+ return AnalyzeExpr(*this, expr.u);
} else {
- MaybeExpr result{AnalyzeExpr(*this, expr.u)};
- CheckConstraints(result);
- return result;
+ return AnalyzeExpr(*this, expr.u);
}
}
}
#include "semantics.h"
#include "../common/indirection.h"
#include "../evaluate/expression.h"
+#include "../evaluate/tools.h"
#include "../evaluate/type.h"
+#include "../parser/parse-tree-visitor.h"
+#include "../parser/parse-tree.h"
#include <optional>
#include <variant>
+using namespace Fortran::parser::literals;
+
namespace Fortran::parser {
-struct Expr;
-struct Program;
-template<typename> struct Scalar;
-template<typename> struct Integer;
-template<typename> struct Constant;
-template<typename> struct Logical;
-template<typename> struct DefaultChar;
+struct SourceLocationFindingVisitor {
+ template<typename A> bool Pre(const A &) { return true; }
+ template<typename A> void Post(const A &) {}
+ bool Pre(const Expr &);
+ template<typename A> bool Pre(const Statement<A> &stmt) {
+ source = stmt.source;
+ return false;
+ }
+ void Post(const CharBlock &);
+
+ CharBlock source;
+};
+
+template<typename A> CharBlock FindSourceLocation(const A &x) {
+ SourceLocationFindingVisitor visitor;
+ Walk(x, visitor);
+ return visitor.source;
+}
}
// The expression semantic analysis code has its implementation in
// The ExpressionAnalysisContext wraps a SemanticsContext reference
// and implements constraint checking on expressions using the
// parse tree node wrappers that mirror the grammar annotations used
-// in the Fortran standard (i.e., scalar-, constant-, &c.). These
-// constraint checks are performed in a deferred manner so that any
-// errors are reported on the most accurate source location available.
+// in the Fortran standard (i.e., scalar-, constant-, &c.).
namespace Fortran::evaluate {
class ExpressionAnalysisContext {
public:
- using ConstraintChecker = bool (ExpressionAnalysisContext::*)(
- Expr<SomeType> &);
-
ExpressionAnalysisContext(semantics::SemanticsContext &sc) : context_{sc} {}
ExpressionAnalysisContext(ExpressionAnalysisContext &i)
: context_{i.context_}, inner_{&i} {}
- ExpressionAnalysisContext(ExpressionAnalysisContext &i, ConstraintChecker cc)
- : context_{i.context_}, inner_{&i}, constraint_{cc} {}
semantics::SemanticsContext &context() const { return context_; }
context_.foldingContext().messages.Say(std::forward<A>(args)...);
}
- void CheckConstraints(std::optional<Expr<SomeType>> &);
- bool ScalarConstraint(Expr<SomeType> &);
- bool ConstantConstraint(Expr<SomeType> &);
- bool IntegerConstraint(Expr<SomeType> &);
- bool LogicalConstraint(Expr<SomeType> &);
- bool DefaultCharConstraint(Expr<SomeType> &);
+ template<typename T, typename... A> void SayAt(const T &parsed, A... args) {
+ context_.foldingContext().messages.Say(
+ parser::FindSourceLocation(parsed), std::forward<A>(args)...);
+ }
std::optional<Expr<SomeType>> Analyze(const parser::Expr &);
private:
ExpressionAnalysisContext *inner_{nullptr};
- ConstraintChecker constraint_{nullptr};
};
template<typename PARSED>
return AnalyzeExpr(context, *x);
}
-// These specializations create nested expression analysis contexts
-// to implement constraint checking.
+// These specializations implement constraint checking.
template<typename A>
std::optional<Expr<SomeType>> AnalyzeExpr(
- ExpressionAnalysisContext &context, const parser::Scalar<A> &expr) {
- ExpressionAnalysisContext withCheck{
- context, &ExpressionAnalysisContext::ScalarConstraint};
- return AnalyzeExpr(withCheck, expr.thing);
+ ExpressionAnalysisContext &context, const parser::Scalar<A> &x) {
+ auto result{AnalyzeExpr(context, x.thing)};
+ if (result.has_value()) {
+ if (int rank{result->Rank()}; rank != 0) {
+ context.SayAt(
+ x, "Must be a scalar value, but is a rank-%d array"_err_en_US);
+ }
+ }
+ return result;
}
template<typename A>
std::optional<Expr<SomeType>> AnalyzeExpr(
- ExpressionAnalysisContext &context, const parser::Constant<A> &expr) {
- ExpressionAnalysisContext withCheck{
- context, &ExpressionAnalysisContext::ConstantConstraint};
- return AnalyzeExpr(withCheck, expr.thing);
+ ExpressionAnalysisContext &context, const parser::Constant<A> &x) {
+ auto result{AnalyzeExpr(context, x.thing)};
+ if (result.has_value()) {
+ *result = Fold(context.context().foldingContext(), std::move(*result));
+ if (!IsConstant(*result)) {
+ context.SayAt(x, "Must be a constant value"_err_en_US);
+ }
+ }
+ return result;
}
template<typename A>
std::optional<Expr<SomeType>> AnalyzeExpr(
- ExpressionAnalysisContext &context, const parser::Integer<A> &expr) {
- ExpressionAnalysisContext withCheck{
- context, &ExpressionAnalysisContext::IntegerConstraint};
- return AnalyzeExpr(withCheck, expr.thing);
+ ExpressionAnalysisContext &context, const parser::Integer<A> &x) {
+ auto result{AnalyzeExpr(context, x.thing)};
+ if (result.has_value()) {
+ if (!std::holds_alternative<Expr<SomeInteger>>(result->u)) {
+ context.SayAt(x, "Must have INTEGER type"_err_en_US);
+ }
+ }
+ return result;
}
template<typename A>
std::optional<Expr<SomeType>> AnalyzeExpr(
- ExpressionAnalysisContext &context, const parser::Logical<A> &expr) {
- ExpressionAnalysisContext withCheck{
- context, &ExpressionAnalysisContext::LogicalConstraint};
- return AnalyzeExpr(withCheck, expr.thing);
+ ExpressionAnalysisContext &context, const parser::Logical<A> &x) {
+ auto result{AnalyzeExpr(context, x.thing)};
+ if (result.has_value()) {
+ if (!std::holds_alternative<Expr<SomeLogical>>(result->u)) {
+ context.SayAt(x, "Must have LOGICAL type"_err_en_US);
+ }
+ }
+ return result;
}
template<typename A>
std::optional<Expr<SomeType>> AnalyzeExpr(
- ExpressionAnalysisContext &context, const parser::DefaultChar<A> &expr) {
- ExpressionAnalysisContext withCheck{
- context, &ExpressionAnalysisContext::DefaultCharConstraint};
- return AnalyzeExpr(withCheck, expr.thing);
+ ExpressionAnalysisContext &context, const parser::DefaultChar<A> &x) {
+ auto result{AnalyzeExpr(context, x.thing)};
+ if (result.has_value()) {
+ if (auto *charExpr{std::get_if<Expr<SomeCharacter>>(&result->u)}) {
+ if (charExpr->GetKind() ==
+ context.context().defaultKinds().GetDefaultKind(
+ TypeCategory::Character)) {
+ return result;
+ }
+ }
+ context.SayAt(x, "Must have default CHARACTER type"_err_en_US);
+ }
+ return result;
}
}