[flang] Fix missing substring bounds (bug flang-compiler/f18#1091)

Original-commit: flang-compiler/f18@3b0c150b2e
Reviewed-on: https://github.com/flang-compiler/f18/pull/1093
This commit is contained in:
peter klausler 2020-03-27 14:17:25 -07:00
parent b2a0e4a235
commit 84a099df05
5 changed files with 240 additions and 206 deletions

View File

@ -31,7 +31,7 @@ namespace Fortran::evaluate {
// When an Expr holds something that is a Variable (i.e., a Designator // When an Expr holds something that is a Variable (i.e., a Designator
// or pointer-valued FunctionRef), return a copy of its contents in // or pointer-valued FunctionRef), return a copy of its contents in
// a Variable. // a Variable.
template<typename A> template <typename A>
std::optional<Variable<A>> AsVariable(const Expr<A> &expr) { std::optional<Variable<A>> AsVariable(const Expr<A> &expr) {
using Variant = decltype(Variable<A>::u); using Variant = decltype(Variable<A>::u);
return std::visit( return std::visit(
@ -44,7 +44,7 @@ std::optional<Variable<A>> AsVariable(const Expr<A> &expr) {
expr.u); expr.u);
} }
template<typename A> template <typename A>
std::optional<Variable<A>> AsVariable(const std::optional<Expr<A>> &expr) { std::optional<Variable<A>> AsVariable(const std::optional<Expr<A>> &expr) {
if (expr) { if (expr) {
return AsVariable(*expr); return AsVariable(*expr);
@ -58,8 +58,8 @@ std::optional<Variable<A>> AsVariable(const std::optional<Expr<A>> &expr) {
// pointer is a "variable" in Fortran (it can be the left-hand side of // pointer is a "variable" in Fortran (it can be the left-hand side of
// an assignment). // an assignment).
struct IsVariableHelper struct IsVariableHelper
: public AnyTraverse<IsVariableHelper, std::optional<bool>> { : public AnyTraverse<IsVariableHelper, std::optional<bool>> {
using Result = std::optional<bool>; // effectively tri-state using Result = std::optional<bool>; // effectively tri-state
using Base = AnyTraverse<IsVariableHelper, Result>; using Base = AnyTraverse<IsVariableHelper, Result>;
IsVariableHelper() : Base{*this} {} IsVariableHelper() : Base{*this} {}
using Base::operator(); using Base::operator();
@ -71,7 +71,7 @@ struct IsVariableHelper
Result operator()(const CoarrayRef &) const { return true; } Result operator()(const CoarrayRef &) const { return true; }
Result operator()(const ComplexPart &) const { return true; } Result operator()(const ComplexPart &) const { return true; }
Result operator()(const ProcedureDesignator &) const; Result operator()(const ProcedureDesignator &) const;
template<typename T> Result operator()(const Expr<T> &x) const { template <typename T> Result operator()(const Expr<T> &x) const {
if constexpr (common::HasMember<T, AllIntrinsicTypes> || if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
std::is_same_v<T, SomeDerived>) { std::is_same_v<T, SomeDerived>) {
// Expression with a specific type // Expression with a specific type
@ -88,7 +88,7 @@ struct IsVariableHelper
} }
}; };
template<typename A> bool IsVariable(const A &x) { template <typename A> bool IsVariable(const A &x) {
if (auto known{IsVariableHelper{}(x)}) { if (auto known{IsVariableHelper{}(x)}) {
return *known; return *known;
} else { } else {
@ -99,39 +99,39 @@ template<typename A> bool IsVariable(const A &x) {
// Predicate: true when an expression is assumed-rank // Predicate: true when an expression is assumed-rank
bool IsAssumedRank(const Symbol &); bool IsAssumedRank(const Symbol &);
bool IsAssumedRank(const ActualArgument &); bool IsAssumedRank(const ActualArgument &);
template<typename A> bool IsAssumedRank(const A &) { return false; } template <typename A> bool IsAssumedRank(const A &) { return false; }
template<typename A> bool IsAssumedRank(const Designator<A> &designator) { template <typename A> bool IsAssumedRank(const Designator<A> &designator) {
if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) { if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
return IsAssumedRank(symbol->get()); return IsAssumedRank(symbol->get());
} else { } else {
return false; return false;
} }
} }
template<typename T> bool IsAssumedRank(const Expr<T> &expr) { template <typename T> bool IsAssumedRank(const Expr<T> &expr) {
return std::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u); return std::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
} }
template<typename A> bool IsAssumedRank(const std::optional<A> &x) { template <typename A> bool IsAssumedRank(const std::optional<A> &x) {
return x && IsAssumedRank(*x); return x && IsAssumedRank(*x);
} }
// Generalizing packagers: these take operations and expressions of more // Generalizing packagers: these take operations and expressions of more
// specific types and wrap them in Expr<> containers of more abstract types. // specific types and wrap them in Expr<> containers of more abstract types.
template<typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) { template <typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) {
return Expr<ResultType<A>>{std::move(x)}; return Expr<ResultType<A>>{std::move(x)};
} }
template<typename T> Expr<T> AsExpr(Expr<T> &&x) { template <typename T> Expr<T> AsExpr(Expr<T> &&x) {
static_assert(IsSpecificIntrinsicType<T>); static_assert(IsSpecificIntrinsicType<T>);
return std::move(x); return std::move(x);
} }
template<TypeCategory CATEGORY> template <TypeCategory CATEGORY>
Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) { Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
return std::move(x); return std::move(x);
} }
template<typename A> template <typename A>
common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) { common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
if constexpr (common::HasMember<A, TypelessExpression>) { if constexpr (common::HasMember<A, TypelessExpression>) {
return Expr<SomeType>{std::move(x)}; return Expr<SomeType>{std::move(x)};
@ -140,7 +140,7 @@ common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
} }
} }
template<typename A> template <typename A>
common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr( common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr(
A &&x) { A &&x) {
return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))}; return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
@ -153,13 +153,13 @@ Expr<SomeType> Parenthesize(Expr<SomeType> &&);
Expr<SomeReal> GetComplexPart( Expr<SomeReal> GetComplexPart(
const Expr<SomeComplex> &, bool isImaginary = false); const Expr<SomeComplex> &, bool isImaginary = false);
template<int KIND> template <int KIND>
Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re, Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
Expr<Type<TypeCategory::Real, KIND>> &&im) { Expr<Type<TypeCategory::Real, KIND>> &&im) {
return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)}); return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)});
} }
template<typename A> constexpr bool IsNumericCategoryExpr() { template <typename A> constexpr bool IsNumericCategoryExpr() {
if constexpr (common::HasMember<A, TypelessExpression>) { if constexpr (common::HasMember<A, TypelessExpression>) {
return false; return false;
} else { } else {
@ -170,7 +170,7 @@ template<typename A> constexpr bool IsNumericCategoryExpr() {
// Specializing extractor. If an Expr wraps some type of object, perhaps // Specializing extractor. If an Expr wraps some type of object, perhaps
// in several layers, return a pointer to it; otherwise null. Also works // in several layers, return a pointer to it; otherwise null. Also works
// with expressions contained in ActualArgument. // with expressions contained in ActualArgument.
template<typename A, typename B> template <typename A, typename B>
auto UnwrapExpr(B &x) -> common::Constify<A, B> * { auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
using Ty = std::decay_t<B>; using Ty = std::decay_t<B>;
if constexpr (std::is_same_v<A, Ty>) { if constexpr (std::is_same_v<A, Ty>) {
@ -190,7 +190,7 @@ auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
return nullptr; return nullptr;
} }
template<typename A, typename B> template <typename A, typename B>
const A *UnwrapExpr(const std::optional<B> &x) { const A *UnwrapExpr(const std::optional<B> &x) {
if (x) { if (x) {
return UnwrapExpr<A>(*x); return UnwrapExpr<A>(*x);
@ -199,7 +199,7 @@ const A *UnwrapExpr(const std::optional<B> &x) {
} }
} }
template<typename A, typename B> A *UnwrapExpr(std::optional<B> &x) { template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
if (x) { if (x) {
return UnwrapExpr<A>(*x); return UnwrapExpr<A>(*x);
} else { } else {
@ -208,41 +208,52 @@ template<typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
} }
// If an expression simply wraps a DataRef, extract and return it. // If an expression simply wraps a DataRef, extract and return it.
template<typename A> // The Boolean argument controls the handling of Substring
common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(const A &) { // references: when true (not default), it extracts the base DataRef
return std::nullopt; // default base case // of a substring, if it has one.
template <typename A>
common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(
const A &, bool intoSubstring) {
return std::nullopt; // default base case
} }
template<typename T> template <typename T>
std::optional<DataRef> ExtractDataRef(const Designator<T> &d) { std::optional<DataRef> ExtractDataRef(
const Designator<T> &d, bool intoSubstring = false) {
return std::visit( return std::visit(
[](const auto &x) -> std::optional<DataRef> { [=](const auto &x) -> std::optional<DataRef> {
if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) { if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) {
return DataRef{x}; return DataRef{x};
} }
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) { if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) {
return ExtractDataRef(x); if (intoSubstring) {
return ExtractSubstringBase(x);
}
} }
return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning
}, },
d.u); d.u);
} }
template<typename T> template <typename T>
std::optional<DataRef> ExtractDataRef(const Expr<T> &expr) { std::optional<DataRef> ExtractDataRef(
return std::visit([](const auto &x) { return ExtractDataRef(x); }, expr.u); const Expr<T> &expr, bool intoSubstring = false) {
return std::visit(
[=](const auto &x) { return ExtractDataRef(x, intoSubstring); }, expr.u);
} }
template<typename A> template <typename A>
std::optional<DataRef> ExtractDataRef(const std::optional<A> &x) { std::optional<DataRef> ExtractDataRef(
const std::optional<A> &x, bool intoSubstring = false) {
if (x) { if (x) {
return ExtractDataRef(*x); return ExtractDataRef(*x, intoSubstring);
} else { } else {
return std::nullopt; return std::nullopt;
} }
} }
std::optional<DataRef> ExtractDataRef(const Substring &); std::optional<DataRef> ExtractSubstringBase(const Substring &);
// Predicate: is an expression is an array element reference? // Predicate: is an expression is an array element reference?
template<typename T> bool IsArrayElement(const Expr<T> &expr) { template <typename T>
if (auto dataRef{ExtractDataRef(expr)}) { bool IsArrayElement(const Expr<T> &expr, bool intoSubstring = false) {
if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) {
const DataRef *ref{&*dataRef}; const DataRef *ref{&*dataRef};
while (const Component * component{std::get_if<Component>(&ref->u)}) { while (const Component * component{std::get_if<Component>(&ref->u)}) {
ref = &component->base(); ref = &component->base();
@ -253,8 +264,9 @@ template<typename T> bool IsArrayElement(const Expr<T> &expr) {
} }
} }
template<typename A> std::optional<NamedEntity> ExtractNamedEntity(const A &x) { template <typename A>
if (auto dataRef{ExtractDataRef(x)}) { std::optional<NamedEntity> ExtractNamedEntity(const A &x) {
if (auto dataRef{ExtractDataRef(x, true)}) {
return std::visit( return std::visit(
common::visitors{ common::visitors{
[](SymbolRef &&symbol) -> std::optional<NamedEntity> { [](SymbolRef &&symbol) -> std::optional<NamedEntity> {
@ -275,11 +287,11 @@ template<typename A> std::optional<NamedEntity> ExtractNamedEntity(const A &x) {
} }
struct ExtractCoindexedObjectHelper { struct ExtractCoindexedObjectHelper {
template<typename A> std::optional<CoarrayRef> operator()(const A &) const { template <typename A> std::optional<CoarrayRef> operator()(const A &) const {
return std::nullopt; return std::nullopt;
} }
std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; } std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; }
template<typename A> template <typename A>
std::optional<CoarrayRef> operator()(const Expr<A> &expr) const { std::optional<CoarrayRef> operator()(const Expr<A> &expr) const {
return std::visit(*this, expr.u); return std::visit(*this, expr.u);
} }
@ -309,8 +321,8 @@ struct ExtractCoindexedObjectHelper {
} }
}; };
template<typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) { template <typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) {
if (auto dataRef{ExtractDataRef(x)}) { if (auto dataRef{ExtractDataRef(x, true)}) {
return ExtractCoindexedObjectHelper{}(*dataRef); return ExtractCoindexedObjectHelper{}(*dataRef);
} else { } else {
return ExtractCoindexedObjectHelper{}(x); return ExtractCoindexedObjectHelper{}(x);
@ -319,7 +331,7 @@ template<typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) {
// If an expression is simply a whole symbol data designator, // If an expression is simply a whole symbol data designator,
// extract and return that symbol, else null. // extract and return that symbol, else null.
template<typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) { template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
if (auto dataRef{ExtractDataRef(x)}) { if (auto dataRef{ExtractDataRef(x)}) {
if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) { if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
return &p->get(); return &p->get();
@ -329,8 +341,8 @@ template<typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
} }
// GetFirstSymbol(A%B%C[I]%D) -> A // GetFirstSymbol(A%B%C[I]%D) -> A
template<typename A> const Symbol *GetFirstSymbol(const A &x) { template <typename A> const Symbol *GetFirstSymbol(const A &x) {
if (auto dataRef{ExtractDataRef(x)}) { if (auto dataRef{ExtractDataRef(x, true)}) {
return &dataRef->GetFirstSymbol(); return &dataRef->GetFirstSymbol();
} else { } else {
return nullptr; return nullptr;
@ -341,7 +353,7 @@ template<typename A> const Symbol *GetFirstSymbol(const A &x) {
// 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).
template<typename TO, TypeCategory FROMCAT> template <typename TO, TypeCategory FROMCAT>
Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) { Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
static_assert(IsSpecificIntrinsicType<TO>); static_assert(IsSpecificIntrinsicType<TO>);
if constexpr (FROMCAT != TO::category) { if constexpr (FROMCAT != TO::category) {
@ -390,12 +402,12 @@ Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
} }
} }
template<typename TO, TypeCategory FROMCAT, int FROMKIND> template <typename TO, TypeCategory FROMCAT, int FROMKIND>
Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) { Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)}); return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
} }
template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) { template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
static_assert(IsSpecificIntrinsicType<TO>); static_assert(IsSpecificIntrinsicType<TO>);
if constexpr (TO::category == TypeCategory::Integer) { if constexpr (TO::category == TypeCategory::Integer) {
return Expr<TO>{ return Expr<TO>{
@ -418,13 +430,13 @@ std::optional<Expr<SomeType>> ConvertToType(
const Symbol &, std::optional<Expr<SomeType>> &&); const Symbol &, std::optional<Expr<SomeType>> &&);
// Conversions to the type of another expression // Conversions to the type of another expression
template<TypeCategory TC, int TK, typename FROM> template <TypeCategory TC, int TK, typename FROM>
common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo( common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
const Expr<Type<TC, TK>> &, FROM &&x) { const Expr<Type<TC, TK>> &, FROM &&x) {
return ConvertToType<Type<TC, TK>>(std::move(x)); return ConvertToType<Type<TC, TK>>(std::move(x));
} }
template<TypeCategory TC, typename FROM> template <TypeCategory TC, typename FROM>
common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo( common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
const Expr<SomeKind<TC>> &to, FROM &&from) { const Expr<SomeKind<TC>> &to, FROM &&from) {
return std::visit( return std::visit(
@ -436,7 +448,7 @@ common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
to.u); to.u);
} }
template<typename FROM> template <typename FROM>
common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo( common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
const Expr<SomeType> &to, FROM &&from) { const Expr<SomeType> &to, FROM &&from) {
return std::visit( return std::visit(
@ -448,11 +460,11 @@ common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
// Convert an expression of some known category to a dynamically chosen // Convert an expression of some known category to a dynamically chosen
// kind of some category (usually but not necessarily distinct). // kind of some category (usually but not necessarily distinct).
template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper { template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
using Result = std::optional<Expr<SomeKind<TOCAT>>>; using Result = std::optional<Expr<SomeKind<TOCAT>>>;
using Types = CategoryTypes<TOCAT>; using Types = CategoryTypes<TOCAT>;
ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {} ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
template<typename T> Result Test() { template <typename T> Result Test() {
if (kind == T::kind) { if (kind == T::kind) {
return std::make_optional( return std::make_optional(
AsCategoryExpr(ConvertToType<T>(std::move(value)))); AsCategoryExpr(ConvertToType<T>(std::move(value))));
@ -463,7 +475,7 @@ template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
VALUE value; VALUE value;
}; };
template<TypeCategory TOCAT, typename VALUE> template <TypeCategory TOCAT, typename VALUE>
common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind( common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
int kind, VALUE &&x) { int kind, VALUE &&x) {
return common::SearchTypes( return common::SearchTypes(
@ -474,11 +486,11 @@ common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
// Given a type category CAT, SameKindExprs<CAT, N> is a variant that // Given a type category CAT, SameKindExprs<CAT, N> is a variant that
// holds an arrays of expressions of the same supported kind in that // holds an arrays of expressions of the same supported kind in that
// category. // category.
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>;
template<int N = 2> struct SameKindExprsHelper { template <int N = 2> struct SameKindExprsHelper {
template<typename A> using SameExprs = std::array<Expr<A>, N>; template <typename A> using SameExprs = std::array<Expr<A>, N>;
}; };
template<TypeCategory CAT, int N = 2> template <TypeCategory CAT, int N = 2>
using SameKindExprs = using SameKindExprs =
common::MapTemplate<SameKindExprsHelper<N>::template SameExprs, common::MapTemplate<SameKindExprsHelper<N>::template SameExprs,
CategoryTypes<CAT>>; CategoryTypes<CAT>>;
@ -486,7 +498,7 @@ using SameKindExprs =
// Given references to two expressions of arbitrary kind in the same type // Given references to two expressions of arbitrary kind in the same type
// category, convert one to the kind of the other when it has the smaller kind, // category, convert one to the kind of the other when it has the smaller kind,
// then return them in a type-safe package. // then return them in a type-safe package.
template<TypeCategory CAT> template <TypeCategory CAT>
SameKindExprs<CAT, 2> AsSameKindExprs( SameKindExprs<CAT, 2> AsSameKindExprs(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return std::visit( return std::visit(
@ -528,7 +540,7 @@ std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&,
int defaultRealKind); int defaultRealKind);
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(
std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken"); std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
@ -538,7 +550,7 @@ template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
// Combine two expressions of the same specific numeric type with an operation // Combine two expressions of the same specific numeric type with an operation
// to produce a new expression. Implements piecewise addition and subtraction // to produce a new expression. Implements piecewise addition and subtraction
// for COMPLEX. // for COMPLEX.
template<template<typename> class OPR, typename SPECIFIC> template <template <typename> class OPR, typename SPECIFIC>
Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) { Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
static_assert(IsSpecificIntrinsicType<SPECIFIC>); static_assert(IsSpecificIntrinsicType<SPECIFIC>);
if constexpr (SPECIFIC::category == TypeCategory::Complex && if constexpr (SPECIFIC::category == TypeCategory::Complex &&
@ -560,7 +572,7 @@ Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
// category, convert one of them if necessary to the larger kind of the // category, convert one of them if necessary to the larger kind of the
// other, then combine the resulting homogenized operands with a given // other, then combine the resulting homogenized operands with a given
// operation, returning a new expression in the same type category. // operation, returning a new expression in the same type category.
template<template<typename> class OPR, TypeCategory CAT> template <template <typename> class OPR, TypeCategory CAT>
Expr<SomeKind<CAT>> PromoteAndCombine( Expr<SomeKind<CAT>> PromoteAndCombine(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return std::visit( return std::visit(
@ -577,7 +589,7 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
// one of the operands to the type of the other. Handles special cases with // one of the operands to the type of the other. Handles special cases with
// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
// powers. // powers.
template<template<typename> class OPR> template <template <typename> class OPR>
std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &, std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind); Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
@ -605,7 +617,7 @@ std::optional<Expr<SomeType>> Negation(
std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &, std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&); RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
template<int K> template <int K>
Expr<Type<TypeCategory::Logical, K>> LogicalNegation( Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
Expr<Type<TypeCategory::Logical, K>> &&x) { Expr<Type<TypeCategory::Logical, K>> &&x) {
return AsExpr(Not<K>{std::move(x)}); return AsExpr(Not<K>{std::move(x)});
@ -613,7 +625,7 @@ Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&); Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&);
template<int K> template <int K>
Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr, Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr,
Expr<Type<TypeCategory::Logical, K>> &&x, Expr<Type<TypeCategory::Logical, K>> &&x,
Expr<Type<TypeCategory::Logical, K>> &&y) { Expr<Type<TypeCategory::Logical, K>> &&y) {
@ -628,12 +640,12 @@ Expr<SomeLogical> BinaryLogicalOperation(
// emit any message. Use the more general templates (above) in other // emit any message. Use the more general templates (above) in other
// situations. // situations.
template<TypeCategory C, int K> template <TypeCategory C, int K>
Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) { Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
return AsExpr(Negate<Type<C, K>>{std::move(x)}); return AsExpr(Negate<Type<C, K>>{std::move(x)});
} }
template<int K> template <int K>
Expr<Type<TypeCategory::Complex, K>> operator-( Expr<Type<TypeCategory::Complex, K>> operator-(
Expr<Type<TypeCategory::Complex, K>> &&x) { Expr<Type<TypeCategory::Complex, K>> &&x) {
using Part = Type<TypeCategory::Real, K>; using Part = Type<TypeCategory::Real, K>;
@ -642,50 +654,50 @@ Expr<Type<TypeCategory::Complex, K>> operator-(
AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{true, x})})}); AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{true, x})})});
} }
template<TypeCategory C, int K> template <TypeCategory C, int K>
Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y))); return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
} }
template<TypeCategory C, int K> template <TypeCategory C, int K>
Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y))); return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y)));
} }
template<TypeCategory C, int K> template <TypeCategory C, int K>
Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y))); return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y)));
} }
template<TypeCategory C, int K> template <TypeCategory C, int K>
Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y))); return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y)));
} }
template<TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) { template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
return std::visit( return std::visit(
[](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u); [](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u);
} }
template<TypeCategory CAT> template <TypeCategory CAT>
Expr<SomeKind<CAT>> operator+( Expr<SomeKind<CAT>> operator+(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y)); return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y));
} }
template<TypeCategory CAT> template <TypeCategory CAT>
Expr<SomeKind<CAT>> operator-( Expr<SomeKind<CAT>> operator-(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y)); return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y));
} }
template<TypeCategory CAT> template <TypeCategory CAT>
Expr<SomeKind<CAT>> operator*( Expr<SomeKind<CAT>> operator*(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y)); return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y));
} }
template<TypeCategory CAT> template <TypeCategory CAT>
Expr<SomeKind<CAT>> operator/( Expr<SomeKind<CAT>> operator/(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y)); return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y));
@ -694,7 +706,7 @@ Expr<SomeKind<CAT>> operator/(
// A utility for use with common::SearchTypes to create generic expressions // A utility for use with common::SearchTypes to create generic expressions
// when an intrinsic type category for (say) a variable is known // when an intrinsic type category for (say) a variable is known
// but the kind parameter value is not. // but the kind parameter value is not.
template<TypeCategory CAT, template<typename> class TEMPLATE, typename VALUE> template <TypeCategory CAT, template <typename> class TEMPLATE, typename VALUE>
struct TypeKindVisitor { struct TypeKindVisitor {
using Result = std::optional<Expr<SomeType>>; using Result = std::optional<Expr<SomeType>>;
using Types = CategoryTypes<CAT>; using Types = CategoryTypes<CAT>;
@ -702,7 +714,7 @@ struct TypeKindVisitor {
TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {} TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {} TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
template<typename T> Result Test() { template <typename T> Result Test() {
if (kind == T::kind) { if (kind == T::kind) {
return AsGenericExpr(TEMPLATE<T>{std::move(value)}); return AsGenericExpr(TEMPLATE<T>{std::move(value)});
} }
@ -717,7 +729,7 @@ struct TypeKindVisitor {
// designator (which has perhaps been wrapped in an Expr<>), or a null pointer // designator (which has perhaps been wrapped in an Expr<>), or a null pointer
// when none is found. // when none is found.
struct GetLastSymbolHelper struct GetLastSymbolHelper
: public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> { : public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> {
using Result = std::optional<const Symbol *>; using Result = std::optional<const Symbol *>;
using Base = AnyTraverse<GetLastSymbolHelper, Result>; using Base = AnyTraverse<GetLastSymbolHelper, Result>;
GetLastSymbolHelper() : Base{*this} {} GetLastSymbolHelper() : Base{*this} {}
@ -728,7 +740,7 @@ struct GetLastSymbolHelper
Result operator()(const ProcedureDesignator &x) const { Result operator()(const ProcedureDesignator &x) const {
return x.GetSymbol(); return x.GetSymbol();
} }
template<typename T> Result operator()(const Expr<T> &x) const { template <typename T> Result operator()(const Expr<T> &x) const {
if constexpr (common::HasMember<T, AllIntrinsicTypes> || if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
std::is_same_v<T, SomeDerived>) { std::is_same_v<T, SomeDerived>) {
if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) { if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) {
@ -743,7 +755,7 @@ struct GetLastSymbolHelper
} }
}; };
template<typename A> const Symbol *GetLastSymbol(const A &x) { template <typename A> const Symbol *GetLastSymbol(const A &x) {
if (auto known{GetLastSymbolHelper{}(x)}) { if (auto known{GetLastSymbolHelper{}(x)}) {
return *known; return *known;
} else { } else {
@ -753,7 +765,7 @@ template<typename A> const Symbol *GetLastSymbol(const A &x) {
// Convenience: If GetLastSymbol() succeeds on the argument, return its // Convenience: If GetLastSymbol() succeeds on the argument, return its
// set of attributes, otherwise the empty set. // set of attributes, otherwise the empty set.
template<typename A> semantics::Attrs GetAttrs(const A &x) { template <typename A> semantics::Attrs GetAttrs(const A &x) {
if (const Symbol * symbol{GetLastSymbol(x)}) { if (const Symbol * symbol{GetLastSymbol(x)}) {
return symbol->attrs(); return symbol->attrs();
} else { } else {
@ -762,17 +774,18 @@ template<typename A> semantics::Attrs GetAttrs(const A &x) {
} }
// GetBaseObject() // GetBaseObject()
template<typename A> std::optional<BaseObject> GetBaseObject(const A &) { template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
return std::nullopt; return std::nullopt;
} }
template<typename T> template <typename T>
std::optional<BaseObject> GetBaseObject(const Designator<T> &x) { std::optional<BaseObject> GetBaseObject(const Designator<T> &x) {
return x.GetBaseObject(); return x.GetBaseObject();
} }
template<typename T> std::optional<BaseObject> GetBaseObject(const Expr<T> &x) { template <typename T>
std::optional<BaseObject> GetBaseObject(const Expr<T> &x) {
return std::visit([](const auto &y) { return GetBaseObject(y); }, x.u); return std::visit([](const auto &y) { return GetBaseObject(y); }, x.u);
} }
template<typename A> template <typename A>
std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) { std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
if (x) { if (x) {
return GetBaseObject(*x); return GetBaseObject(*x);
@ -782,7 +795,7 @@ std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
} }
// Predicate: IsAllocatableOrPointer() // Predicate: IsAllocatableOrPointer()
template<typename A> bool IsAllocatableOrPointer(const A &x) { template <typename A> bool IsAllocatableOrPointer(const A &x) {
return GetAttrs(x).HasAny( return GetAttrs(x).HasAny(
semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE}); semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
} }
@ -796,7 +809,7 @@ bool IsNullPointer(const Expr<SomeType> &);
// wrapped in an Expr<>, removing all of the (co)subscripts. The // wrapped in an Expr<>, removing all of the (co)subscripts. The
// base object will be the first symbol in the result vector. // base object will be the first symbol in the result vector.
struct GetSymbolVectorHelper struct GetSymbolVectorHelper
: public Traverse<GetSymbolVectorHelper, SymbolVector> { : public Traverse<GetSymbolVectorHelper, SymbolVector> {
using Result = SymbolVector; using Result = SymbolVector;
using Base = Traverse<GetSymbolVectorHelper, Result>; using Base = Traverse<GetSymbolVectorHelper, Result>;
using Base::operator(); using Base::operator();
@ -811,7 +824,7 @@ struct GetSymbolVectorHelper
Result operator()(const ArrayRef &) const; Result operator()(const ArrayRef &) const;
Result operator()(const CoarrayRef &) const; Result operator()(const CoarrayRef &) const;
}; };
template<typename A> SymbolVector GetSymbolVector(const A &x) { template <typename A> SymbolVector GetSymbolVector(const A &x) {
return GetSymbolVectorHelper{}(x); return GetSymbolVectorHelper{}(x);
} }
@ -824,7 +837,7 @@ const Symbol *GetLastTarget(const SymbolVector &);
const Symbol &ResolveAssociations(const Symbol &); const Symbol &ResolveAssociations(const Symbol &);
// Collects all of the Symbols in an expression // Collects all of the Symbols in an expression
template<typename A> semantics::SymbolSet CollectSymbols(const A &); template <typename A> semantics::SymbolSet CollectSymbols(const A &);
extern template semantics::SymbolSet CollectSymbols(const Expr<SomeType> &); extern template semantics::SymbolSet CollectSymbols(const Expr<SomeType> &);
extern template semantics::SymbolSet CollectSymbols(const Expr<SomeInteger> &); extern template semantics::SymbolSet CollectSymbols(const Expr<SomeInteger> &);
extern template semantics::SymbolSet CollectSymbols( extern template semantics::SymbolSet CollectSymbols(
@ -838,7 +851,7 @@ bool HasVectorSubscript(const Expr<SomeType> &);
// the case of USE association gracefully. // the case of USE association gracefully.
parser::Message *AttachDeclaration(parser::Message &, const Symbol &); parser::Message *AttachDeclaration(parser::Message &, const Symbol &);
parser::Message *AttachDeclaration(parser::Message *, const Symbol &); parser::Message *AttachDeclaration(parser::Message *, const Symbol &);
template<typename MESSAGES, typename... A> template <typename MESSAGES, typename... A>
parser::Message *SayWithDeclaration( parser::Message *SayWithDeclaration(
MESSAGES &messages, const Symbol &symbol, A &&... x) { MESSAGES &messages, const Symbol &symbol, A &&... x) {
return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol); return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol);
@ -851,5 +864,5 @@ std::optional<std::string> FindImpureCall(
std::optional<std::string> FindImpureCall( std::optional<std::string> FindImpureCall(
const IntrinsicProcTable &, const ProcedureRef &); const IntrinsicProcTable &, const ProcedureRef &);
} } // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_TOOLS_H_ #endif // FORTRAN_EVALUATE_TOOLS_H_

View File

@ -25,7 +25,7 @@ Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) {
using T = std::decay_t<decltype(x)>; using T = std::decay_t<decltype(x)>;
if constexpr (common::HasMember<T, TypelessExpression> || if constexpr (common::HasMember<T, TypelessExpression> ||
std::is_same_v<T, Expr<SomeDerived>>) { std::is_same_v<T, Expr<SomeDerived>>) {
return expr; // no parentheses around typeless or derived type return expr; // no parentheses around typeless or derived type
} else { } else {
return std::visit( return std::visit(
[](auto &&y) { [](auto &&y) {
@ -38,7 +38,7 @@ Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) {
std::move(expr.u)); std::move(expr.u));
} }
std::optional<DataRef> ExtractDataRef(const Substring &substring) { std::optional<DataRef> ExtractSubstringBase(const Substring &substring) {
return std::visit( return std::visit(
common::visitors{ common::visitors{
[&](const DataRef &x) -> std::optional<DataRef> { return x; }, [&](const DataRef &x) -> std::optional<DataRef> { return x; },
@ -126,7 +126,7 @@ ConvertRealOperandsResult ConvertRealOperands(
return {AsSameKindExprs<TypeCategory::Real>( return {AsSameKindExprs<TypeCategory::Real>(
ConvertTo(ry, std::move(bx)), std::move(ry))}; ConvertTo(ry, std::move(bx)), std::move(ry))};
}, },
[&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718 [&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718
messages.Say("operands must be INTEGER or REAL"_err_en_US); messages.Say("operands must be INTEGER or REAL"_err_en_US);
return std::nullopt; return std::nullopt;
}, },
@ -137,11 +137,11 @@ ConvertRealOperandsResult ConvertRealOperands(
// Helpers for NumericOperation and its subroutines below. // Helpers for NumericOperation and its subroutines below.
static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; } static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
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> template <TypeCategory CAT>
std::optional<Expr<SomeType>> Package( std::optional<Expr<SomeType>> Package(
std::optional<Expr<SomeKind<CAT>>> &&catExpr) { std::optional<Expr<SomeKind<CAT>>> &&catExpr) {
if (catExpr) { if (catExpr) {
@ -152,7 +152,7 @@ std::optional<Expr<SomeType>> Package(
// Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that // Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
// does not require conversion of the exponent expression. // does not require conversion of the exponent expression.
template<template<typename> class OPR> template <template <typename> class OPR>
std::optional<Expr<SomeType>> MixedRealLeft( std::optional<Expr<SomeType>> MixedRealLeft(
Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) { Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
return Package(std::visit( return Package(std::visit(
@ -220,7 +220,7 @@ Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
// Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way // Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
// than just converting the second operand to COMPLEX and performing the // than just converting the second operand to COMPLEX and performing the
// corresponding COMPLEX+COMPLEX operation. // corresponding COMPLEX+COMPLEX operation.
template<template<typename> class OPR, TypeCategory RCAT> template <template <typename> class OPR, TypeCategory RCAT>
std::optional<Expr<SomeType>> MixedComplexLeft( std::optional<Expr<SomeType>> MixedComplexLeft(
parser::ContextualMessages &messages, Expr<SomeComplex> &&zx, parser::ContextualMessages &messages, Expr<SomeComplex> &&zx,
Expr<SomeKind<RCAT>> &&iry, int defaultRealKind) { Expr<SomeKind<RCAT>> &&iry, int defaultRealKind) {
@ -261,7 +261,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)})); AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)}));
}, },
std::move(zx.u))); std::move(zx.u)));
} else if (defaultRealKind != 666) { // dodge unused parameter warning } else if (defaultRealKind != 666) { // dodge unused parameter warning
// (a,b) ** x -> (a,b) ** (x,0) // (a,b) ** x -> (a,b) ** (x,0)
if constexpr (RCAT == TypeCategory::Integer) { if constexpr (RCAT == TypeCategory::Integer) {
Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))}; Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
@ -279,7 +279,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
// x - (a,b) -> (x-a, -b) // x - (a,b) -> (x-a, -b)
// x * (a,b) -> (x*a, x*b) // x * (a,b) -> (x*a, x*b)
// x / (a,b) -> (x,0) / (a,b) (and **) // x / (a,b) -> (x,0) / (a,b) (and **)
template<template<typename> class OPR, TypeCategory LCAT> template <template <typename> class OPR, TypeCategory LCAT>
std::optional<Expr<SomeType>> MixedComplexRight( std::optional<Expr<SomeType>> MixedComplexRight(
parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx, parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
Expr<SomeComplex> &&zy, int defaultRealKind) { Expr<SomeComplex> &&zy, int defaultRealKind) {
@ -300,7 +300,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
return Package(ConstructComplex(messages, std::move(*rr), return Package(ConstructComplex(messages, std::move(*rr),
AsGenericExpr(-std::move(zi)), defaultRealKind)); AsGenericExpr(-std::move(zi)), defaultRealKind));
} }
} else if (defaultRealKind != 666) { // dodge unused parameter warning } else if (defaultRealKind != 666) { // dodge unused parameter warning
// x / (a,b) -> (x,0) / (a,b) // x / (a,b) -> (x,0) / (a,b)
if constexpr (LCAT == TypeCategory::Integer) { if constexpr (LCAT == TypeCategory::Integer) {
Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))}; Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
@ -316,7 +316,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
// 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 operation where one is permitted, it assumes the // the operands to a dyadic operation where one is permitted, it assumes the
// type and kind of the other operand. // type and kind of the other operand.
template<template<typename> class OPR> template <template <typename> class OPR>
std::optional<Expr<SomeType>> NumericOperation( std::optional<Expr<SomeType>> NumericOperation(
parser::ContextualMessages &messages, Expr<SomeType> &&x, parser::ContextualMessages &messages, Expr<SomeType> &&x,
Expr<SomeType> &&y, int defaultRealKind) { Expr<SomeType> &&y, int defaultRealKind) {
@ -458,7 +458,7 @@ Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&x) {
std::move(x.u)); std::move(x.u));
} }
template<typename T> template <typename T>
Expr<LogicalResult> PackageRelation( Expr<LogicalResult> PackageRelation(
RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) { RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
static_assert(IsSpecificIntrinsicType<T>); static_assert(IsSpecificIntrinsicType<T>);
@ -466,7 +466,7 @@ Expr<LogicalResult> PackageRelation(
Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}}; Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
} }
template<TypeCategory CAT> template <TypeCategory CAT>
Expr<LogicalResult> PromoteAndRelate( Expr<LogicalResult> PromoteAndRelate(
RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
return std::visit( return std::visit(
@ -576,7 +576,7 @@ Expr<SomeLogical> BinaryLogicalOperation(
AsSameKindExprs(std::move(x), std::move(y))); AsSameKindExprs(std::move(x), std::move(y)));
} }
template<TypeCategory TO> template <TypeCategory TO>
std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) { std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
static_assert(common::IsNumericTypeCategory(TO)); static_assert(common::IsNumericTypeCategory(TO));
return std::visit( return std::visit(
@ -772,7 +772,7 @@ const Symbol &ResolveAssociations(const Symbol &symbol) {
} }
struct CollectSymbolsHelper struct CollectSymbolsHelper
: public SetTraverse<CollectSymbolsHelper, semantics::SymbolSet> { : public SetTraverse<CollectSymbolsHelper, semantics::SymbolSet> {
using Base = SetTraverse<CollectSymbolsHelper, semantics::SymbolSet>; using Base = SetTraverse<CollectSymbolsHelper, semantics::SymbolSet>;
CollectSymbolsHelper() : Base{*this} {} CollectSymbolsHelper() : Base{*this} {}
using Base::operator(); using Base::operator();
@ -780,7 +780,7 @@ struct CollectSymbolsHelper
return {symbol}; return {symbol};
} }
}; };
template<typename A> semantics::SymbolSet CollectSymbols(const A &x) { template <typename A> semantics::SymbolSet CollectSymbols(const A &x) {
return CollectSymbolsHelper{}(x); return CollectSymbolsHelper{}(x);
} }
template semantics::SymbolSet CollectSymbols(const Expr<SomeType> &); template semantics::SymbolSet CollectSymbols(const Expr<SomeType> &);
@ -796,7 +796,7 @@ struct HasVectorSubscriptHelper : public AnyTraverse<HasVectorSubscriptHelper> {
return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0; return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0;
} }
bool operator()(const ProcedureRef &) const { bool operator()(const ProcedureRef &) const {
return false; // don't descend into function call arguments return false; // don't descend into function call arguments
} }
}; };
@ -841,13 +841,13 @@ parser::Message *AttachDeclaration(
} }
class FindImpureCallHelper class FindImpureCallHelper
: public AnyTraverse<FindImpureCallHelper, std::optional<std::string>> { : public AnyTraverse<FindImpureCallHelper, std::optional<std::string>> {
using Result = std::optional<std::string>; using Result = std::optional<std::string>;
using Base = AnyTraverse<FindImpureCallHelper, Result>; using Base = AnyTraverse<FindImpureCallHelper, Result>;
public: public:
explicit FindImpureCallHelper(const IntrinsicProcTable &intrinsics) explicit FindImpureCallHelper(const IntrinsicProcTable &intrinsics)
: Base{*this}, intrinsics_{intrinsics} {} : Base{*this}, intrinsics_{intrinsics} {}
using Base::operator(); using Base::operator();
Result operator()(const ProcedureRef &call) const { Result operator()(const ProcedureRef &call) const {
if (auto chars{characteristics::Procedure::Characterize( if (auto chars{characteristics::Procedure::Characterize(
@ -872,4 +872,4 @@ std::optional<std::string> FindImpureCall(
return FindImpureCallHelper{intrinsics}(proc); return FindImpureCallHelper{intrinsics}(proc);
} }
} } // namespace Fortran::evaluate

View File

@ -36,7 +36,7 @@ public:
AssignmentContext(const AssignmentContext &) = delete; AssignmentContext(const AssignmentContext &) = delete;
bool operator==(const AssignmentContext &x) const { return this == &x; } bool operator==(const AssignmentContext &x) const { return this == &x; }
template<typename A> void PushWhereContext(const A &); template <typename A> void PushWhereContext(const A &);
void PopWhereContext(); void PopWhereContext();
void Analyze(const parser::AssignmentStmt &); void Analyze(const parser::AssignmentStmt &);
void Analyze(const parser::PointerAssignmentStmt &); void Analyze(const parser::PointerAssignmentStmt &);
@ -46,7 +46,7 @@ private:
bool CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, bool CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs,
parser::CharBlock rhsSource, bool isPointerAssignment); parser::CharBlock rhsSource, bool isPointerAssignment);
void CheckShape(parser::CharBlock, const SomeExpr *); void CheckShape(parser::CharBlock, const SomeExpr *);
template<typename... A> template <typename... A>
parser::Message *Say(parser::CharBlock at, A &&... args) { parser::Message *Say(parser::CharBlock at, A &&... args) {
return &context_.Say(at, std::forward<A>(args)...); return &context_.Say(at, std::forward<A>(args)...);
} }
@ -55,7 +55,7 @@ private:
} }
SemanticsContext &context_; SemanticsContext &context_;
int whereDepth_{0}; // number of WHEREs currently nested in int whereDepth_{0}; // number of WHEREs currently nested in
// shape of masks in LHS of assignments in current WHERE: // shape of masks in LHS of assignments in current WHERE:
std::vector<std::optional<std::int64_t>> whereExtents_; std::vector<std::optional<std::int64_t>> whereExtents_;
}; };
@ -67,7 +67,7 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
auto lhsLoc{std::get<parser::Variable>(stmt.t).GetSource()}; auto lhsLoc{std::get<parser::Variable>(stmt.t).GetSource()};
auto rhsLoc{std::get<parser::Expr>(stmt.t).source}; auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
auto shape{evaluate::GetShape(foldingContext(), lhs)}; auto shape{evaluate::GetShape(foldingContext(), lhs)};
if (shape && !shape->empty() && !shape->back().has_value()) { // C1014 if (shape && !shape->empty() && !shape->back().has_value()) { // C1014
Say(lhsLoc, Say(lhsLoc,
"Left-hand side of assignment may not be a whole assumed-size array"_err_en_US); "Left-hand side of assignment may not be a whole assumed-size array"_err_en_US);
} }
@ -178,7 +178,7 @@ bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs,
"A pure subprogram may not define a coindexed object"_err_en_US); "A pure subprogram may not define a coindexed object"_err_en_US);
} else if (const Symbol * base{GetFirstSymbol(lhs)}) { } else if (const Symbol * base{GetFirstSymbol(lhs)}) {
if (const auto *assoc{base->detailsIf<AssocEntityDetails>()}) { if (const auto *assoc{base->detailsIf<AssocEntityDetails>()}) {
auto dataRef{ExtractDataRef(assoc->expr())}; auto dataRef{ExtractDataRef(assoc->expr(), true)};
// ASSOCIATE(a=>x) -- check x, not a, for "a=..." // ASSOCIATE(a=>x) -- check x, not a, for "a=..."
base = dataRef ? &dataRef->GetFirstSymbol() : nullptr; base = dataRef ? &dataRef->GetFirstSymbol() : nullptr;
} }
@ -190,7 +190,7 @@ bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs,
if (isPointerAssignment) { if (isPointerAssignment) {
if (const Symbol * base{GetFirstSymbol(rhs)}) { if (const Symbol * base{GetFirstSymbol(rhs)}) {
if (const char *why{ if (const char *why{
WhyBaseObjectIsSuspicious(*base, scope)}) { // C1594(3) WhyBaseObjectIsSuspicious(*base, scope)}) { // C1594(3)
evaluate::SayWithDeclaration(messages, *base, evaluate::SayWithDeclaration(messages, *base,
"A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US, "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
base->name(), why); base->name(), why);
@ -249,7 +249,7 @@ void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) {
} }
} }
template<typename A> void AssignmentContext::PushWhereContext(const A &x) { template <typename A> void AssignmentContext::PushWhereContext(const A &x) {
const auto &expr{std::get<parser::LogicalExpr>(x.t)}; const auto &expr{std::get<parser::LogicalExpr>(x.t)};
CheckShape(expr.thing.value().source, GetExpr(expr)); CheckShape(expr.thing.value().source, GetExpr(expr));
++whereDepth_; ++whereDepth_;
@ -265,7 +265,7 @@ void AssignmentContext::PopWhereContext() {
AssignmentChecker::~AssignmentChecker() {} AssignmentChecker::~AssignmentChecker() {}
AssignmentChecker::AssignmentChecker(SemanticsContext &context) AssignmentChecker::AssignmentChecker(SemanticsContext &context)
: context_{new AssignmentContext{context}} {} : context_{new AssignmentContext{context}} {}
void AssignmentChecker::Enter(const parser::AssignmentStmt &x) { void AssignmentChecker::Enter(const parser::AssignmentStmt &x) {
context_.value().Analyze(x); context_.value().Analyze(x);
} }
@ -291,6 +291,6 @@ void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) {
context_.value().PopWhereContext(); context_.value().PopWhereContext();
} }
} } // namespace Fortran::semantics
template class Fortran::common::Indirection< template class Fortran::common::Indirection<
Fortran::semantics::AssignmentContext>; Fortran::semantics::AssignmentContext>;

View File

@ -60,7 +60,7 @@ std::optional<Expr<SubscriptInteger>> DynamicTypeWithLength::LEN() const {
return ConvertToType<SubscriptInteger>(common::Clone(*len)); return ConvertToType<SubscriptInteger>(common::Clone(*len));
} }
} }
return std::nullopt; // assumed or deferred length return std::nullopt; // assumed or deferred length
} }
static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec( static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
@ -97,14 +97,14 @@ static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
// Wraps a object in an explicitly typed representation (e.g., Designator<> // Wraps a object in an explicitly typed representation (e.g., Designator<>
// or FunctionRef<>) that has been instantiated on a dynamically chosen type. // or FunctionRef<>) that has been instantiated on a dynamically chosen type.
template<TypeCategory CATEGORY, template<typename> typename WRAPPER, template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
typename WRAPPED> typename WRAPPED>
common::IfNoLvalue<MaybeExpr, WRAPPED> WrapperHelper(int kind, WRAPPED &&x) { common::IfNoLvalue<MaybeExpr, WRAPPED> WrapperHelper(int kind, WRAPPED &&x) {
return common::SearchTypes( return common::SearchTypes(
TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)}); TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
} }
template<template<typename> typename WRAPPER, typename WRAPPED> template <template <typename> typename WRAPPER, typename WRAPPED>
common::IfNoLvalue<MaybeExpr, WRAPPED> TypedWrapper( common::IfNoLvalue<MaybeExpr, WRAPPED> TypedWrapper(
const DynamicType &dyType, WRAPPED &&x) { const DynamicType &dyType, WRAPPED &&x) {
switch (dyType.category()) { switch (dyType.category()) {
@ -132,10 +132,11 @@ common::IfNoLvalue<MaybeExpr, WRAPPED> TypedWrapper(
class ArgumentAnalyzer { class ArgumentAnalyzer {
public: public:
explicit ArgumentAnalyzer(ExpressionAnalyzer &context) explicit ArgumentAnalyzer(ExpressionAnalyzer &context)
: context_{context}, allowAssumedType_{false} {} : context_{context}, allowAssumedType_{false} {}
ArgumentAnalyzer(ExpressionAnalyzer &context, parser::CharBlock source, ArgumentAnalyzer(ExpressionAnalyzer &context, parser::CharBlock source,
bool allowAssumedType = false) bool allowAssumedType = false)
: context_{context}, source_{source}, allowAssumedType_{allowAssumedType} {} : context_{context}, source_{source}, allowAssumedType_{
allowAssumedType} {}
bool fatalErrors() const { return fatalErrors_; } bool fatalErrors() const { return fatalErrors_; }
ActualArguments &&GetActuals() { ActualArguments &&GetActuals() {
CHECK(!fatalErrors_); CHECK(!fatalErrors_);
@ -166,7 +167,7 @@ public:
// The provided message is used if there is no such operator. // The provided message is used if there is no such operator.
MaybeExpr TryDefinedOp( MaybeExpr TryDefinedOp(
const char *, parser::MessageFixedText &&, bool isUserOp = false); const char *, parser::MessageFixedText &&, bool isUserOp = false);
template<typename E> template <typename E>
MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText &&msg) { MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText &&msg) {
return TryDefinedOp( return TryDefinedOp(
context_.context().languageFeatures().GetNames(opr), std::move(msg)); context_.context().languageFeatures().GetNames(opr), std::move(msg));
@ -355,6 +356,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
FixMisparsedSubstring(d); FixMisparsedSubstring(d);
// These checks have to be deferred to these "top level" data-refs where // These checks have to be deferred to these "top level" data-refs where
// we can be sure that there are no following subscripts (yet). // we can be sure that there are no following subscripts (yet).
// Substrings have already been run through TopLevelChecks() and
// won't be returned by ExtractDataRef().
if (MaybeExpr result{Analyze(d.u)}) { if (MaybeExpr result{Analyze(d.u)}) {
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) { if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
return TopLevelChecks(std::move(*dataRef)); return TopLevelChecks(std::move(*dataRef));
@ -366,10 +369,10 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
// A utility subroutine to repackage optional expressions of various levels // A utility subroutine to repackage optional expressions of various levels
// of type specificity as fully general MaybeExpr values. // of type specificity as fully general MaybeExpr values.
template<typename A> common::IfNoLvalue<MaybeExpr, A> AsMaybeExpr(A &&x) { template <typename A> common::IfNoLvalue<MaybeExpr, A> AsMaybeExpr(A &&x) {
return std::make_optional(AsGenericExpr(std::move(x))); return std::make_optional(AsGenericExpr(std::move(x)));
} }
template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) { template <typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
if (x) { if (x) {
return AsMaybeExpr(std::move(*x)); return AsMaybeExpr(std::move(*x));
} }
@ -405,7 +408,7 @@ int ExpressionAnalyzer::AnalyzeKindParam(
struct IntTypeVisitor { struct IntTypeVisitor {
using Result = MaybeExpr; using Result = MaybeExpr;
using Types = IntegerTypes; using Types = IntegerTypes;
template<typename T> Result Test() { template <typename T> Result Test() {
if (T::kind >= kind) { if (T::kind >= kind) {
const char *p{digits.begin()}; const char *p{digits.begin()};
auto value{T::Scalar::Read(p, 10, true /*signed*/)}; auto value{T::Scalar::Read(p, 10, true /*signed*/)};
@ -434,7 +437,7 @@ struct IntTypeVisitor {
bool isDefaultKind; bool isDefaultKind;
}; };
template<typename PARSED> template <typename PARSED>
MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) { MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) {
const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)}; const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)};
bool isDefaultKind{!kindParam}; bool isDefaultKind{!kindParam};
@ -468,7 +471,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
return IntLiteralConstant(x); return IntLiteralConstant(x);
} }
template<typename TYPE> template <typename TYPE>
Constant<TYPE> ReadRealLiteral( Constant<TYPE> ReadRealLiteral(
parser::CharBlock source, FoldingContext &context) { parser::CharBlock source, FoldingContext &context) {
const char *p{source.begin()}; const char *p{source.begin()};
@ -487,9 +490,9 @@ struct RealTypeVisitor {
using Types = RealTypes; using Types = RealTypes;
RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx) RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx)
: kind{k}, literal{lit}, context{ctx} {} : kind{k}, literal{lit}, context{ctx} {}
template<typename T> Result Test() { template <typename T> Result Test() {
if (kind == T::kind) { if (kind == T::kind) {
return {AsCategoryExpr(ReadRealLiteral<T>(literal, context))}; return {AsCategoryExpr(ReadRealLiteral<T>(literal, context))};
} }
@ -519,10 +522,17 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
if (parser::IsLetter(*p)) { if (parser::IsLetter(*p)) {
expoLetter = *p; expoLetter = *p;
switch (expoLetter) { switch (expoLetter) {
case 'e': letterKind = defaults.GetDefaultKind(TypeCategory::Real); break; case 'e':
case 'd': letterKind = defaults.doublePrecisionKind(); break; letterKind = defaults.GetDefaultKind(TypeCategory::Real);
case 'q': letterKind = defaults.quadPrecisionKind(); break; break;
default: Say("Unknown exponent letter '%c'"_err_en_US, expoLetter); case 'd':
letterKind = defaults.doublePrecisionKind();
break;
case 'q':
letterKind = defaults.quadPrecisionKind();
break;
default:
Say("Unknown exponent letter '%c'"_err_en_US, expoLetter);
} }
break; break;
} }
@ -539,7 +549,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
} }
auto result{common::SearchTypes( auto result{common::SearchTypes(
RealTypeVisitor{kind, x.real.source, GetFoldingContext()})}; RealTypeVisitor{kind, x.real.source, GetFoldingContext()})};
if (!result) { // C717 if (!result) { // C717
Say("Unsupported REAL(KIND=%d)"_err_en_US, kind); Say("Unsupported REAL(KIND=%d)"_err_en_US, kind);
} }
return AsMaybeExpr(std::move(result)); return AsMaybeExpr(std::move(result));
@ -599,7 +609,8 @@ MaybeExpr ExpressionAnalyzer::AnalyzeString(std::string &&string, int kind) {
return AsGenericExpr(Constant<Type<TypeCategory::Character, 4>>{ return AsGenericExpr(Constant<Type<TypeCategory::Character, 4>>{
parser::DecodeString<std::u32string, parser::Encoding::UTF_8>( parser::DecodeString<std::u32string, parser::Encoding::UTF_8>(
string, true)}); string, true)});
default: CRASH_NO_CASE; default:
CRASH_NO_CASE;
} }
} }
@ -626,7 +637,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) {
TypeKindVisitor<TypeCategory::Logical, Constant, bool>{ TypeKindVisitor<TypeCategory::Logical, Constant, bool>{
kind, std::move(value)})}; kind, std::move(value)})};
if (!result) { if (!result) {
Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728 Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728
} }
return result; return result;
} }
@ -636,11 +647,18 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
const char *p{x.v.c_str()}; const char *p{x.v.c_str()};
std::uint64_t base{16}; std::uint64_t base{16};
switch (*p++) { switch (*p++) {
case 'b': base = 2; break; case 'b':
case 'o': base = 8; break; base = 2;
case 'z': break; break;
case 'x': break; case 'o':
default: CRASH_NO_CASE; base = 8;
break;
case 'z':
break;
case 'x':
break;
default:
CRASH_NO_CASE;
} }
CHECK(*p == '"'); CHECK(*p == '"');
++p; ++p;
@ -662,10 +680,10 @@ struct TypeParamInquiryVisitor {
using Result = std::optional<Expr<SomeInteger>>; using Result = std::optional<Expr<SomeInteger>>;
using Types = IntegerTypes; using Types = IntegerTypes;
TypeParamInquiryVisitor(int k, NamedEntity &&b, const Symbol &param) TypeParamInquiryVisitor(int k, NamedEntity &&b, const Symbol &param)
: kind{k}, base{std::move(b)}, parameter{param} {} : kind{k}, base{std::move(b)}, parameter{param} {}
TypeParamInquiryVisitor(int k, const Symbol &param) TypeParamInquiryVisitor(int k, const Symbol &param)
: kind{k}, parameter{param} {} : kind{k}, parameter{param} {}
template<typename T> Result Test() { template <typename T> Result Test() {
if (kind == T::kind) { if (kind == T::kind) {
return Expr<SomeInteger>{ return Expr<SomeInteger>{
Expr<T>{TypeParamInquiry<T::kind>{std::move(base), parameter}}}; Expr<T>{TypeParamInquiry<T::kind>{std::move(base), parameter}}};
@ -723,7 +741,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::NamedConstant &n) {
if (IsConstantExpr(folded)) { if (IsConstantExpr(folded)) {
return {folded}; return {folded};
} }
Say(n.v.source, "must be a constant"_err_en_US); // C718 Say(n.v.source, "must be a constant"_err_en_US); // C718
} }
return std::nullopt; return std::nullopt;
} }
@ -1093,7 +1111,7 @@ int ExpressionAnalyzer::IntegerTypeSpecKind(
// Inverts a collection of generic ArrayConstructorValues<SomeType> that // Inverts a collection of generic ArrayConstructorValues<SomeType> that
// all happen to have the same actual type T into one ArrayConstructor<T>. // all happen to have the same actual type T into one ArrayConstructor<T>.
template<typename T> template <typename T>
ArrayConstructorValues<T> MakeSpecific( ArrayConstructorValues<T> MakeSpecific(
ArrayConstructorValues<SomeType> &&from) { ArrayConstructorValues<SomeType> &&from) {
ArrayConstructorValues<T> to; ArrayConstructorValues<T> to;
@ -1120,7 +1138,7 @@ class ArrayConstructorContext {
public: public:
ArrayConstructorContext( ArrayConstructorContext(
ExpressionAnalyzer &c, std::optional<DynamicTypeWithLength> &&t) ExpressionAnalyzer &c, std::optional<DynamicTypeWithLength> &&t)
: exprAnalyzer_{c}, type_{std::move(t)} {} : exprAnalyzer_{c}, type_{std::move(t)} {}
void Add(const parser::AcValue &); void Add(const parser::AcValue &);
MaybeExpr ToExpr(); MaybeExpr ToExpr();
@ -1130,7 +1148,7 @@ public:
// expression in ToExpr(). // expression in ToExpr().
using Result = MaybeExpr; using Result = MaybeExpr;
using Types = AllTypes; using Types = AllTypes;
template<typename T> Result Test() { template <typename T> Result Test() {
if (type_ && type_->category() == T::category) { if (type_ && type_->category() == T::category) {
if constexpr (T::category == TypeCategory::Derived) { if constexpr (T::category == TypeCategory::Derived) {
return AsMaybeExpr(ArrayConstructor<T>{ return AsMaybeExpr(ArrayConstructor<T>{
@ -1153,7 +1171,7 @@ public:
private: private:
void Push(MaybeExpr &&); void Push(MaybeExpr &&);
template<int KIND, typename A> template <int KIND, typename A>
std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr( std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr(
const A &x) { const A &x) {
if (MaybeExpr y{exprAnalyzer_.Analyze(x)}) { if (MaybeExpr y{exprAnalyzer_.Analyze(x)}) {
@ -1338,12 +1356,12 @@ MaybeExpr ExpressionAnalyzer::Analyze(
const auto &spec{*parsedType.derivedTypeSpec}; const auto &spec{*parsedType.derivedTypeSpec};
const Symbol &typeSymbol{spec.typeSymbol()}; const Symbol &typeSymbol{spec.typeSymbol()};
if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) { if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) {
return std::nullopt; // error recovery return std::nullopt; // error recovery
} }
const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()}; const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()};
const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())}; const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())};
if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { // C796 if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { // C796
AttachDeclaration(Say(typeName, AttachDeclaration(Say(typeName,
"ABSTRACT derived type '%s' may not be used in a " "ABSTRACT derived type '%s' may not be used in a "
"structure constructor"_err_en_US, "structure constructor"_err_en_US,
@ -1365,7 +1383,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
std::set<parser::CharBlock> unavailable; std::set<parser::CharBlock> unavailable;
bool anyKeyword{false}; bool anyKeyword{false};
StructureConstructor result{spec}; StructureConstructor result{spec};
bool checkConflicts{true}; // until we hit one bool checkConflicts{true}; // until we hit one
for (const auto &component : for (const auto &component :
std::get<std::list<parser::ComponentSpec>>(structure.t)) { std::get<std::list<parser::ComponentSpec>>(structure.t)) {
@ -1388,16 +1406,16 @@ MaybeExpr ExpressionAnalyzer::Analyze(
symbol = &*componentIter; symbol = &*componentIter;
} }
} }
if (!symbol) { // C7101 if (!symbol) { // C7101
Say(source, Say(source,
"Keyword '%s=' does not name a component of derived type '%s'"_err_en_US, "Keyword '%s=' does not name a component of derived type '%s'"_err_en_US,
source, typeName); source, typeName);
} }
} else { } else {
if (anyKeyword) { // C7100 if (anyKeyword) { // C7100
Say(source, Say(source,
"Value in structure constructor lacks a component name"_err_en_US); "Value in structure constructor lacks a component name"_err_en_US);
checkConflicts = false; // stem cascade checkConflicts = false; // stem cascade
} }
// Here's a regrettably common extension of the standard: anonymous // Here's a regrettably common extension of the standard: anonymous
// initialization of parent components, e.g., T(PT(1)) rather than // initialization of parent components, e.g., T(PT(1)) rather than
@ -1496,7 +1514,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
} }
if (IsPointer(*symbol)) { if (IsPointer(*symbol)) {
semantics::CheckPointerAssignment( semantics::CheckPointerAssignment(
GetFoldingContext(), *symbol, *value); // C7104, C7105 GetFoldingContext(), *symbol, *value); // C7104, C7105
result.Add(*symbol, Fold(std::move(*value))); result.Add(*symbol, Fold(std::move(*value)));
} else if (MaybeExpr converted{ } else if (MaybeExpr converted{
ConvertToType(*symbol, std::move(*value))}) { ConvertToType(*symbol, std::move(*value))}) {
@ -1535,7 +1553,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
symbol.detailsIf<semantics::ObjectEntityDetails>()}) { symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
if (details->init()) { if (details->init()) {
result.Add(symbol, common::Clone(*details->init())); result.Add(symbol, common::Clone(*details->init()));
} else { // C799 } else { // C799
AttachDeclaration(Say(typeName, AttachDeclaration(Say(typeName,
"Structure constructor lacks a value for " "Structure constructor lacks a value for "
"component '%s'"_err_en_US, "component '%s'"_err_en_US,
@ -1568,7 +1586,7 @@ static int GetPassIndex(const Symbol &proc) {
std::optional<parser::CharBlock> passName{GetPassName(proc)}; std::optional<parser::CharBlock> passName{GetPassName(proc)};
const auto *interface{semantics::FindInterface(proc)}; const auto *interface{semantics::FindInterface(proc)};
if (!passName || !interface) { if (!passName || !interface) {
return 0; // first argument is passed-object return 0; // first argument is passed-object
} }
const auto &subp{interface->get<semantics::SubprogramDetails>()}; const auto &subp{interface->get<semantics::SubprogramDetails>()};
int index{0}; int index{0};
@ -1744,7 +1762,7 @@ bool ExpressionAnalyzer::ResolveForward(const Symbol &symbol) {
context_.SetError(const_cast<Symbol &>(symbol)); context_.SetError(const_cast<Symbol &>(symbol));
return false; return false;
} }
} else { // 10.1.11 para 4 } else { // 10.1.11 para 4
Say("The internal function '%s' may not be referenced in a specification expression"_err_en_US, Say("The internal function '%s' may not be referenced in a specification expression"_err_en_US,
symbol.name()); symbol.name());
context_.SetError(const_cast<Symbol &>(symbol)); context_.SetError(const_cast<Symbol &>(symbol));
@ -1759,7 +1777,7 @@ bool ExpressionAnalyzer::ResolveForward(const Symbol &symbol) {
const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol, const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
const ActualArguments &actuals, const AdjustActuals &adjustActuals, const ActualArguments &actuals, const AdjustActuals &adjustActuals,
bool mightBeStructureConstructor) { bool mightBeStructureConstructor) {
const Symbol *elemental{nullptr}; // matching elemental specific proc const Symbol *elemental{nullptr}; // matching elemental specific proc
const auto &details{symbol.GetUltimate().get<semantics::GenericDetails>()}; const auto &details{symbol.GetUltimate().get<semantics::GenericDetails>()};
for (const Symbol &specific : details.specificProcs()) { for (const Symbol &specific : details.specificProcs()) {
if (!ResolveForward(specific)) { if (!ResolveForward(specific)) {
@ -1778,7 +1796,7 @@ const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
*procedure, localActuals, GetFoldingContext())) { *procedure, localActuals, GetFoldingContext())) {
if (CheckCompatibleArguments(*procedure, localActuals)) { if (CheckCompatibleArguments(*procedure, localActuals)) {
if (!procedure->IsElemental()) { if (!procedure->IsElemental()) {
return &specific; // takes priority over elemental match return &specific; // takes priority over elemental match
} }
elemental = &specific; elemental = &specific;
} }
@ -1837,7 +1855,7 @@ auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name,
bool mightBeStructureConstructor) -> std::optional<CalleeAndArguments> { bool mightBeStructureConstructor) -> std::optional<CalleeAndArguments> {
const Symbol *symbol{name.symbol}; const Symbol *symbol{name.symbol};
if (context_.HasError(symbol)) { if (context_.HasError(symbol)) {
return std::nullopt; // also handles null symbol return std::nullopt; // also handles null symbol
} }
const Symbol &ultimate{DEREF(symbol).GetUltimate()}; const Symbol &ultimate{DEREF(symbol).GetUltimate()};
if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) { if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) {
@ -1886,11 +1904,11 @@ void ExpressionAnalyzer::CheckForBadRecursion(
if (const auto *scope{proc.scope()}) { if (const auto *scope{proc.scope()}) {
if (scope->sourceRange().Contains(callSite)) { if (scope->sourceRange().Contains(callSite)) {
parser::Message *msg{nullptr}; parser::Message *msg{nullptr};
if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3) if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3)
msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US, msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US,
callSite); callSite);
} else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) { } else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) {
msg = Say( // 15.6.2.1(3) msg = Say( // 15.6.2.1(3)
"Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US, "Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US,
callSite); callSite);
} }
@ -1899,7 +1917,7 @@ void ExpressionAnalyzer::CheckForBadRecursion(
} }
} }
template<typename A> static const Symbol *AssumedTypeDummy(const A &x) { template <typename A> static const Symbol *AssumedTypeDummy(const A &x) {
if (const auto *designator{ if (const auto *designator{
std::get_if<common::Indirection<parser::Designator>>(&x.u)}) { std::get_if<common::Indirection<parser::Designator>>(&x.u)}) {
if (const auto *dataRef{ if (const auto *dataRef{
@ -2088,7 +2106,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
if (const semantics::Symbol * result{FindFunctionResult(*symbol)}) { if (const semantics::Symbol * result{FindFunctionResult(*symbol)}) {
if (semantics::IsProcedurePointer(*result)) { if (semantics::IsProcedurePointer(*result)) {
Say("A function reference that returns a procedure " Say("A function reference that returns a procedure "
"pointer may not be parenthesized"_err_en_US); // C1003 "pointer may not be parenthesized"_err_en_US); // C1003
} }
} }
} }
@ -2166,7 +2184,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
// Binary (dyadic) operations // Binary (dyadic) operations
template<template<typename> class OPR> template <template <typename> class OPR>
MaybeExpr NumericBinaryHelper(ExpressionAnalyzer &context, NumericOperator opr, MaybeExpr NumericBinaryHelper(ExpressionAnalyzer &context, NumericOperator opr,
const parser::Expr::IntrinsicBinary &x) { const parser::Expr::IntrinsicBinary &x) {
ArgumentAnalyzer analyzer{context}; ArgumentAnalyzer analyzer{context};
@ -2370,7 +2388,7 @@ static void CheckFuncRefToArrayElementRefHasSubscripts(
// A(1) as a function reference into an array reference. // A(1) as a function reference into an array reference.
// Misparse structure constructors are detected elsewhere after generic // Misparse structure constructors are detected elsewhere after generic
// function call resolution fails. // function call resolution fails.
template<typename... A> template <typename... A>
static void FixMisparsedFunctionReference( static void FixMisparsedFunctionReference(
semantics::SemanticsContext &context, const std::variant<A...> &constU) { semantics::SemanticsContext &context, const std::variant<A...> &constU) {
// The parse tree is updated in situ when resolving an ambiguous parse. // The parse tree is updated in situ when resolving an ambiguous parse.
@ -2407,12 +2425,12 @@ static void FixMisparsedFunctionReference(
} }
// Common handling of parser::Expr and parser::Variable // Common handling of parser::Expr and parser::Variable
template<typename PARSED> template <typename PARSED>
MaybeExpr ExpressionAnalyzer::ExprOrVariable(const PARSED &x) { MaybeExpr ExpressionAnalyzer::ExprOrVariable(const PARSED &x) {
if (!x.typedExpr) { if (!x.typedExpr) {
FixMisparsedFunctionReference(context_, x.u); FixMisparsedFunctionReference(context_, x.u);
MaybeExpr result; MaybeExpr result;
if (AssumedTypeDummy(x)) { // C710 if (AssumedTypeDummy(x)) { // C710
Say("TYPE(*) dummy argument may only be used as an actual argument"_err_en_US); Say("TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
} else { } else {
if constexpr (std::is_same_v<PARSED, parser::Expr>) { if constexpr (std::is_same_v<PARSED, parser::Expr>) {
@ -2495,7 +2513,7 @@ DynamicType ExpressionAnalyzer::GetDefaultKindOfType(
bool ExpressionAnalyzer::CheckIntrinsicKind( bool ExpressionAnalyzer::CheckIntrinsicKind(
TypeCategory category, std::int64_t kind) { TypeCategory category, std::int64_t kind) {
if (IsValidKindOfIntrinsicType(category, kind)) { // C712, C714, C715, C727 if (IsValidKindOfIntrinsicType(category, kind)) { // C712, C714, C715, C727
return true; return true;
} else { } else {
Say("%s(KIND=%jd) is not a supported type"_err_en_US, Say("%s(KIND=%jd) is not a supported type"_err_en_US,
@ -2544,7 +2562,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at,
const MaybeExpr &result, TypeCategory category, bool defaultKind) { const MaybeExpr &result, TypeCategory category, bool defaultKind) {
if (result) { if (result) {
if (auto type{result->GetType()}) { if (auto type{result->GetType()}) {
if (type->category() != category) { // C885 if (type->category() != category) { // C885
Say(at, "Must have %s type, but is %s"_err_en_US, Say(at, "Must have %s type, but is %s"_err_en_US,
ToUpperCase(EnumToString(category)), ToUpperCase(EnumToString(category)),
ToUpperCase(type->AsFortran())); ToUpperCase(type->AsFortran()));
@ -2683,7 +2701,7 @@ bool ArgumentAnalyzer::IsIntrinsicNumeric(NumericOperator opr) const {
if (IsBOZLiteral(0) && type1) { if (IsBOZLiteral(0) && type1) {
auto cat1{type1->category()}; auto cat1{type1->category()};
return cat1 == TypeCategory::Integer || cat1 == TypeCategory::Real; return cat1 == TypeCategory::Integer || cat1 == TypeCategory::Real;
} else if (IsBOZLiteral(1) && type0) { // Integer/Real opr BOZ } else if (IsBOZLiteral(1) && type0) { // Integer/Real opr BOZ
auto cat0{type0->category()}; auto cat0{type0->category()};
return cat0 == TypeCategory::Integer || cat0 == TypeCategory::Real; return cat0 == TypeCategory::Integer || cat0 == TypeCategory::Real;
} else { } else {
@ -2782,7 +2800,7 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
Tristate isDefined{ Tristate isDefined{
semantics::IsDefinedAssignment(lhsType, lhsRank, rhsType, rhsRank)}; semantics::IsDefinedAssignment(lhsType, lhsRank, rhsType, rhsRank)};
if (isDefined == Tristate::No) { if (isDefined == Tristate::No) {
return std::nullopt; // user-defined assignment not allowed for these args return std::nullopt; // user-defined assignment not allowed for these args
} }
auto restorer{context_.GetContextualMessages().SetLocation(source_)}; auto restorer{context_.GetContextualMessages().SetLocation(source_)};
if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) { if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) {
@ -2979,7 +2997,7 @@ bool ArgumentAnalyzer::AnyUntypedOperand() {
return false; return false;
} }
} // namespace Fortran::evaluate } // namespace Fortran::evaluate
namespace Fortran::semantics { namespace Fortran::semantics {
evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector( evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
@ -3024,4 +3042,4 @@ bool ExprChecker::Pre(const parser::DataStmtConstant &x) {
return false; return false;
} }
} } // namespace Fortran::semantics

View File

@ -56,8 +56,10 @@ const Scope *FindProgramUnitContaining(const Scope &start) {
case Scope::Kind::Module: case Scope::Kind::Module:
case Scope::Kind::MainProgram: case Scope::Kind::MainProgram:
case Scope::Kind::Subprogram: case Scope::Kind::Subprogram:
case Scope::Kind::BlockData: return true; case Scope::Kind::BlockData:
default: return false; return true;
default:
return false;
} }
}); });
} }
@ -82,7 +84,7 @@ Tristate IsDefinedAssignment(
const std::optional<evaluate::DynamicType> &lhsType, int lhsRank, const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) { const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
if (!lhsType || !rhsType) { if (!lhsType || !rhsType) {
return Tristate::No; // error or rhs is untyped return Tristate::No; // error or rhs is untyped
} }
TypeCategory lhsCat{lhsType->category()}; TypeCategory lhsCat{lhsType->category()};
TypeCategory rhsCat{rhsType->category()}; TypeCategory rhsCat{rhsType->category()};
@ -95,8 +97,8 @@ Tristate IsDefinedAssignment(
const auto *lhsDerived{evaluate::GetDerivedTypeSpec(lhsType)}; const auto *lhsDerived{evaluate::GetDerivedTypeSpec(lhsType)};
const auto *rhsDerived{evaluate::GetDerivedTypeSpec(rhsType)}; const auto *rhsDerived{evaluate::GetDerivedTypeSpec(rhsType)};
if (lhsDerived && rhsDerived && *lhsDerived == *rhsDerived) { if (lhsDerived && rhsDerived && *lhsDerived == *rhsDerived) {
return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or
// intrinsic // intrinsic
} else { } else {
return Tristate::Yes; return Tristate::Yes;
} }
@ -412,7 +414,8 @@ bool ExprTypeKindIsDefault(
} }
// If an analyzed expr or assignment is missing, dump the node and die. // If an analyzed expr or assignment is missing, dump the node and die.
template<typename T> static void CheckMissingAnalysis(bool absent, const T &x) { template <typename T>
static void CheckMissingAnalysis(bool absent, const T &x) {
if (absent) { if (absent) {
std::string buf; std::string buf;
llvm::raw_string_ostream ss{buf}; llvm::raw_string_ostream ss{buf};
@ -611,7 +614,7 @@ bool IsSaved(const Symbol &symbol) {
if (scopeKind == Scope::Kind::Module || scopeKind == Scope::Kind::BlockData) { if (scopeKind == Scope::Kind::Module || scopeKind == Scope::Kind::BlockData) {
return true; return true;
} else if (scopeKind == Scope::Kind::DerivedType) { } else if (scopeKind == Scope::Kind::DerivedType) {
return false; // this is a component return false; // this is a component
} else if (IsNamedConstant(symbol)) { } else if (IsNamedConstant(symbol)) {
return false; return false;
} else if (symbol.attrs().test(Attr::SAVE)) { } else if (symbol.attrs().test(Attr::SAVE)) {
@ -816,7 +819,7 @@ std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) { const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
if (!evaluate::IsVariable(expr)) { if (!evaluate::IsVariable(expr)) {
return parser::Message{at, "Expression is not a variable"_en_US}; return parser::Message{at, "Expression is not a variable"_en_US};
} else if (auto dataRef{evaluate::ExtractDataRef(expr)}) { } else if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) { if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
return parser::Message{at, "Variable has a vector subscript"_en_US}; return parser::Message{at, "Variable has a vector subscript"_en_US};
} }
@ -839,10 +842,10 @@ class ImageControlStmtHelper {
parser::SyncTeamStmt, parser::UnlockStmt>; parser::SyncTeamStmt, parser::UnlockStmt>;
public: public:
template<typename T> bool operator()(const T &) { template <typename T> bool operator()(const T &) {
return common::HasMember<T, ImageControlStmts>; return common::HasMember<T, ImageControlStmts>;
} }
template<typename T> bool operator()(const common::Indirection<T> &x) { template <typename T> bool operator()(const common::Indirection<T> &x) {
return (*this)(x.value()); return (*this)(x.value());
} }
bool operator()(const parser::AllocateStmt &stmt) { bool operator()(const parser::AllocateStmt &stmt) {
@ -980,7 +983,7 @@ bool IsPolymorphicAllocatable(const Symbol &symbol) {
std::optional<parser::MessageFormattedText> CheckAccessibleComponent( std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
const Scope &scope, const Symbol &symbol) { const Scope &scope, const Symbol &symbol) {
CHECK(symbol.owner().IsDerivedType()); // symbol must be a component CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
if (symbol.attrs().test(Attr::PRIVATE)) { if (symbol.attrs().test(Attr::PRIVATE)) {
if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) { if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) {
if (!moduleScope->Contains(scope)) { if (!moduleScope->Contains(scope)) {
@ -1047,17 +1050,17 @@ const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
// ComponentIterator implementation // ComponentIterator implementation
template<ComponentKind componentKind> template <ComponentKind componentKind>
typename ComponentIterator<componentKind>::const_iterator typename ComponentIterator<componentKind>::const_iterator
ComponentIterator<componentKind>::const_iterator::Create( ComponentIterator<componentKind>::const_iterator::Create(
const DerivedTypeSpec &derived) { const DerivedTypeSpec &derived) {
const_iterator it{}; const_iterator it{};
it.componentPath_.emplace_back(derived); it.componentPath_.emplace_back(derived);
it.Increment(); // cue up first relevant component, if any it.Increment(); // cue up first relevant component, if any
return it; return it;
} }
template<ComponentKind componentKind> template <ComponentKind componentKind>
const DerivedTypeSpec * const DerivedTypeSpec *
ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal( ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
const Symbol &component) const { const Symbol &component) const {
@ -1091,12 +1094,12 @@ ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
return derived; return derived;
} }
} }
} // intrinsic & unlimited polymorphic not traversable } // intrinsic & unlimited polymorphic not traversable
} }
return nullptr; return nullptr;
} }
template<ComponentKind componentKind> template <ComponentKind componentKind>
static bool StopAtComponentPre(const Symbol &component) { static bool StopAtComponentPre(const Symbol &component) {
if constexpr (componentKind == ComponentKind::Ordered) { if constexpr (componentKind == ComponentKind::Ordered) {
// Parent components need to be iterated upon after their // Parent components need to be iterated upon after their
@ -1114,13 +1117,13 @@ static bool StopAtComponentPre(const Symbol &component) {
} }
} }
template<ComponentKind componentKind> template <ComponentKind componentKind>
static bool StopAtComponentPost(const Symbol &component) { static bool StopAtComponentPost(const Symbol &component) {
return componentKind == ComponentKind::Ordered && return componentKind == ComponentKind::Ordered &&
component.test(Symbol::Flag::ParentComp); component.test(Symbol::Flag::ParentComp);
} }
template<ComponentKind componentKind> template <ComponentKind componentKind>
void ComponentIterator<componentKind>::const_iterator::Increment() { void ComponentIterator<componentKind>::const_iterator::Increment() {
while (!componentPath_.empty()) { while (!componentPath_.empty()) {
ComponentPathNode &deepest{componentPath_.back()}; ComponentPathNode &deepest{componentPath_.back()};
@ -1134,7 +1137,7 @@ void ComponentIterator<componentKind>::const_iterator::Increment() {
} }
} else if (!deepest.visited()) { } else if (!deepest.visited()) {
deepest.set_visited(true); deepest.set_visited(true);
return; // this is the next component to visit, after descending return; // this is the next component to visit, after descending
} }
} }
auto &nameIterator{deepest.nameIterator()}; auto &nameIterator{deepest.nameIterator()};
@ -1144,7 +1147,7 @@ void ComponentIterator<componentKind>::const_iterator::Increment() {
deepest.set_component(*nameIterator++->second); deepest.set_component(*nameIterator++->second);
deepest.set_descended(false); deepest.set_descended(false);
deepest.set_visited(true); deepest.set_visited(true);
return; // this is the next component to visit, before descending return; // this is the next component to visit, before descending
} else { } else {
const Scope &scope{deepest.GetScope()}; const Scope &scope{deepest.GetScope()};
auto scopeIter{scope.find(*nameIterator++)}; auto scopeIter{scope.find(*nameIterator++)};
@ -1154,7 +1157,7 @@ void ComponentIterator<componentKind>::const_iterator::Increment() {
deepest.set_descended(false); deepest.set_descended(false);
if (StopAtComponentPre<componentKind>(component)) { if (StopAtComponentPre<componentKind>(component)) {
deepest.set_visited(true); deepest.set_visited(true);
return; // this is the next component to visit, before descending return; // this is the next component to visit, before descending
} else { } else {
deepest.set_visited(!StopAtComponentPost<componentKind>(component)); deepest.set_visited(!StopAtComponentPost<componentKind>(component));
} }
@ -1163,7 +1166,7 @@ void ComponentIterator<componentKind>::const_iterator::Increment() {
} }
} }
template<ComponentKind componentKind> template <ComponentKind componentKind>
std::string std::string
ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName() ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
const { const {
@ -1353,4 +1356,4 @@ void LabelEnforce::SayWithConstruct(SemanticsContext &context,
context.Say(stmtLocation, message) context.Say(stmtLocation, message)
.Attach(constructLocation, GetEnclosingConstructMsg()); .Attach(constructLocation, GetEnclosingConstructMsg());
} }
} } // namespace Fortran::semantics