// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_SEMANTICS_EXPRESSION_H_ #define FORTRAN_SEMANTICS_EXPRESSION_H_ #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 #include using namespace Fortran::parser::literals; namespace Fortran::parser { struct SourceLocationFindingVisitor { template bool Pre(const A &) { return true; } template void Post(const A &) {} bool Pre(const Expr &); template bool Pre(const Statement &stmt) { source = stmt.source; return false; } void Post(const CharBlock &); CharBlock source; }; template CharBlock FindSourceLocation(const A &x) { SourceLocationFindingVisitor visitor; Walk(x, visitor); return visitor.source; } } // The expression semantic analysis code has its implementation in // namespace Fortran::evaluate, but the exposed API to it is in the // namespace Fortran::semantics (below). // // The template function AnalyzeExpr is an internal interface // between the implementation and the API used by semantic analysis. // This template function has a few specializations here in the header // file to handle what semantics might want to pass in as a top-level // expression; other specializations appear in the implementation. // // 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.). namespace Fortran::evaluate { class ExpressionAnalysisContext { public: explicit ExpressionAnalysisContext(semantics::SemanticsContext &sc) : context_{sc} {} semantics::SemanticsContext &context() const { return context_; } template void Say(A... args) { context_.foldingContext().messages.Say(std::forward(args)...); } template void SayAt(const T &parsed, A... args) { Say(parser::FindSourceLocation(parsed), std::forward(args)...); } std::optional> Analyze(const parser::Expr &); private: semantics::SemanticsContext &context_; }; template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const PARSED &); template<> inline std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::Expr &expr) { return context.Analyze(expr); } // Forward declarations of exposed specializations template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const common::Indirection &); template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const parser::Scalar &); template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const parser::Constant &); template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const parser::Integer &); template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const parser::Logical &); template std::optional> AnalyzeExpr( ExpressionAnalysisContext &, const parser::DefaultChar &); // Indirections are silently traversed by AnalyzeExpr(). template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const common::Indirection &x) { return AnalyzeExpr(context, *x); } // These specializations implement constraint checking. template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::Scalar &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, rank); } } return result; } template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::Constant &x) { auto result{AnalyzeExpr(context, x.thing)}; if (result.has_value()) { *result = Fold(context.context().foldingContext(), std::move(*result)); if (!IsConstantExpr(*result)) { context.SayAt(x, "Must be a constant value"_err_en_US); } } return result; } template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::Integer &x) { auto result{AnalyzeExpr(context, x.thing)}; if (result.has_value()) { if (!std::holds_alternative>(result->u)) { context.SayAt(x, "Must have INTEGER type"_err_en_US); } } return result; } template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::Logical &x) { auto result{AnalyzeExpr(context, x.thing)}; if (result.has_value()) { if (!std::holds_alternative>(result->u)) { context.SayAt(x, "Must have LOGICAL type"_err_en_US); } } return result; } template std::optional> AnalyzeExpr( ExpressionAnalysisContext &context, const parser::DefaultChar &x) { auto result{AnalyzeExpr(context, x.thing)}; if (result.has_value()) { if (auto *charExpr{std::get_if>(&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; } } namespace Fortran::semantics { // Semantic analysis of one expression. template std::optional> AnalyzeExpr( SemanticsContext &context, const A &expr) { evaluate::ExpressionAnalysisContext exprContext{context}; return AnalyzeExpr(exprContext, expr); } // Semantic analysis of all expressions in a parse tree, which is // decorated with typed representations for top-level expressions. void AnalyzeExpressions(parser::Program &, SemanticsContext &); } #endif // FORTRAN_SEMANTICS_EXPRESSION_H_