diff --git a/flang/lib/evaluate/fold-implementation.h b/flang/lib/evaluate/fold-implementation.h index c7f0dccd32bc..8b90fbe189c8 100644 --- a/flang/lib/evaluate/fold-implementation.h +++ b/flang/lib/evaluate/fold-implementation.h @@ -45,6 +45,7 @@ namespace Fortran::evaluate { +// Utilities template class Folder { public: explicit Folder(FoldingContext &c) : context_{c} {} @@ -63,7 +64,6 @@ public: private: FoldingContext &context_; }; -FOR_EACH_SPECIFIC_TYPE(extern template class Folder, ) // FoldOperation() rewrites expression tree nodes. // If there is any possibility that the rewritten node will @@ -119,6 +119,357 @@ template Expr FoldOperation(FoldingContext &, ArrayConstructor &&); Expr FoldOperation(FoldingContext &, StructureConstructor &&); +template +std::optional> Folder::GetNamedConstantValue(const Symbol &symbol0) { + const Symbol &symbol{ResolveAssociations(symbol0).GetUltimate()}; + if (IsNamedConstant(symbol)) { + if (const auto *object{ + symbol.detailsIf()}) { + if (object->initWasValidated()) { + const auto *constant{UnwrapConstantValue(object->init())}; + return Expr{DEREF(constant)}; + } + if (const auto &init{object->init()}) { + if (auto dyType{DynamicType::From(symbol)}) { + semantics::ObjectEntityDetails *mutableObject{ + const_cast(object)}; + auto converted{ + ConvertToType(*dyType, std::move(mutableObject->init().value()))}; + // Reset expression now to prevent infinite loops if the init + // expression depends on symbol itself. + mutableObject->set_init(std::nullopt); + if (converted) { + *converted = Fold(context_, std::move(*converted)); + auto *unwrapped{UnwrapExpr>(*converted)}; + CHECK(unwrapped); + if (auto *constant{UnwrapConstantValue(*unwrapped)}) { + if (symbol.Rank() > 0) { + if (constant->Rank() == 0) { + // scalar expansion + if (auto symShape{GetShape(context_, symbol)}) { + if (auto extents{AsConstantExtents(context_, *symShape)}) { + *constant = constant->Reshape(std::move(*extents)); + CHECK(constant->Rank() == symbol.Rank()); + } + } + } + if (constant->Rank() == symbol.Rank()) { + NamedEntity base{symbol}; + if (auto lbounds{AsConstantExtents( + context_, GetLowerBounds(context_, base))}) { + constant->set_lbounds(*std::move(lbounds)); + } + } + } + mutableObject->set_init(AsGenericExpr(Expr{*constant})); + if (auto constShape{GetShape(context_, *constant)}) { + if (auto symShape{GetShape(context_, symbol)}) { + if (CheckConformance(context_.messages(), *constShape, + *symShape, "initialization expression", + "PARAMETER")) { + mutableObject->set_initWasValidated(); + return std::move(*unwrapped); + } + } else { + context_.messages().Say(symbol.name(), + "Could not determine the shape of the PARAMETER"_err_en_US); + } + } else { + context_.messages().Say(symbol.name(), + "Could not determine the shape of the initialization expression"_err_en_US); + } + mutableObject->set_init(std::nullopt); + } else { + std::stringstream ss; + unwrapped->AsFortran(ss); + context_.messages().Say(symbol.name(), + "Initialization expression for PARAMETER '%s' (%s) cannot be computed as a constant value"_err_en_US, + symbol.name(), ss.str()); + } + } else { + std::stringstream ss; + init->AsFortran(ss); + context_.messages().Say(symbol.name(), + "Initialization expression for PARAMETER '%s' (%s) cannot be converted to its type (%s)"_err_en_US, + symbol.name(), ss.str(), dyType->AsFortran()); + } + } + } + } + } + return std::nullopt; +} + +template +std::optional> Folder::GetFoldedNamedConstantValue( + const Symbol &symbol) { + if (auto value{GetNamedConstantValue(symbol)}) { + Expr folded{Fold(context_, std::move(*value))}; + if (const Constant *value{UnwrapConstantValue(folded)}) { + return *value; + } + } + return std::nullopt; +} + +static std::optional> GetConstantSubscript( + FoldingContext &context, Subscript &ss, const NamedEntity &base, int dim) { + ss = FoldOperation(context, std::move(ss)); + return std::visit( + common::visitors{ + [](IndirectSubscriptIntegerExpr &expr) + -> std::optional> { + if (auto constant{ + GetScalarConstantValue(expr.value())}) { + return Constant{*constant}; + } else { + return std::nullopt; + } + }, + [&](Triplet &triplet) -> std::optional> { + auto lower{triplet.lower()}, upper{triplet.upper()}; + std::optional stride{ToInt64(triplet.stride())}; + if (!lower) { + lower = GetLowerBound(context, base, dim); + } + if (!upper) { + upper = + ComputeUpperBound(context, GetLowerBound(context, base, dim), + GetExtent(context, base, dim)); + } + auto lbi{ToInt64(lower)}, ubi{ToInt64(upper)}; + if (lbi && ubi && stride && *stride != 0) { + std::vector values; + while ((*stride > 0 && *lbi <= *ubi) || + (*stride < 0 && *lbi >= *ubi)) { + values.emplace_back(*lbi); + *lbi += *stride; + } + return Constant{std::move(values), + ConstantSubscripts{ + static_cast(values.size())}}; + } else { + return std::nullopt; + } + }, + }, + ss.u); +} + +template +std::optional> Folder::Folding(ArrayRef &aRef) { + std::vector> subscripts; + int dim{0}; + for (Subscript &ss : aRef.subscript()) { + if (auto constant{GetConstantSubscript(context_, ss, aRef.base(), dim++)}) { + subscripts.emplace_back(std::move(*constant)); + } else { + return std::nullopt; + } + } + if (Component * component{aRef.base().UnwrapComponent()}) { + return GetConstantComponent(*component, &subscripts); + } else if (std::optional> array{ + GetFoldedNamedConstantValue(aRef.base().GetLastSymbol())}) { + return ApplySubscripts(*array, subscripts); + } else { + return std::nullopt; + } +} + +template +std::optional> Folder::ApplySubscripts(const Constant &array, + const std::vector> &subscripts) { + const auto &shape{array.shape()}; + const auto &lbounds{array.lbounds()}; + int rank{GetRank(shape)}; + CHECK(rank == static_cast(subscripts.size())); + std::size_t elements{1}; + ConstantSubscripts resultShape; + ConstantSubscripts ssLB; + for (const auto &ss : subscripts) { + CHECK(ss.Rank() <= 1); + if (ss.Rank() == 1) { + resultShape.push_back(static_cast(ss.size())); + elements *= ss.size(); + ssLB.push_back(ss.lbounds().front()); + } + } + ConstantSubscripts ssAt(rank, 0), at(rank, 0), tmp(1, 0); + std::vector> values; + while (elements-- > 0) { + bool increment{true}; + int k{0}; + for (int j{0}; j < rank; ++j) { + if (subscripts[j].Rank() == 0) { + at[j] = subscripts[j].GetScalarValue().value().ToInt64(); + } else { + CHECK(k < GetRank(resultShape)); + tmp[0] = ssLB[j] + ssAt[j]; + at[j] = subscripts[j].At(tmp).ToInt64(); + if (increment) { + if (++ssAt[j] == resultShape[k]) { + ssAt[j] = 0; + } else { + increment = false; + } + } + ++k; + } + if (at[j] < lbounds[j] || at[j] >= lbounds[j] + shape[j]) { + context_.messages().Say( + "Subscript value (%jd) is out of range on dimension %d in reference to a constant array value"_err_en_US, + static_cast(at[j]), j + 1); + return std::nullopt; + } + } + values.emplace_back(array.At(at)); + CHECK(!increment || elements == 0); + CHECK(k == GetRank(resultShape)); + } + if constexpr (T::category == TypeCategory::Character) { + return Constant{array.LEN(), std::move(values), std::move(resultShape)}; + } else if constexpr (std::is_same_v) { + return Constant{array.result().derivedTypeSpec(), std::move(values), + std::move(resultShape)}; + } else { + return Constant{std::move(values), std::move(resultShape)}; + } +} + +template +std::optional> Folder::ApplyComponent( + Constant &&structures, const Symbol &component, + const std::vector> *subscripts) { + if (auto scalar{structures.GetScalarValue()}) { + if (auto *expr{scalar->Find(component)}) { + if (const Constant *value{UnwrapConstantValue(*expr)}) { + if (!subscripts) { + return std::move(*value); + } else { + return ApplySubscripts(*value, *subscripts); + } + } + } + } else { + // A(:)%scalar_component & A(:)%array_component(subscripts) + std::unique_ptr> array; + if (structures.empty()) { + return std::nullopt; + } + ConstantSubscripts at{structures.lbounds()}; + do { + StructureConstructor scalar{structures.At(at)}; + if (auto *expr{scalar.Find(component)}) { + if (const Constant *value{UnwrapConstantValue(*expr)}) { + if (!array.get()) { + // This technique ensures that character length or derived type + // information is propagated to the array constructor. + auto *typedExpr{UnwrapExpr>(*expr)}; + CHECK(typedExpr); + array = std::make_unique>(*typedExpr); + } + if (subscripts) { + if (auto element{ApplySubscripts(*value, *subscripts)}) { + CHECK(element->Rank() == 0); + array->Push(Expr{std::move(*element)}); + } else { + return std::nullopt; + } + } else { + CHECK(value->Rank() == 0); + array->Push(Expr{*value}); + } + } else { + return std::nullopt; + } + } + } while (structures.IncrementSubscripts(at)); + // Fold the ArrayConstructor<> into a Constant<>. + CHECK(array); + Expr result{Fold(context_, Expr{std::move(*array)})}; + if (auto *constant{UnwrapConstantValue(result)}) { + return constant->Reshape(common::Clone(structures.shape())); + } + } + return std::nullopt; +} + +template +std::optional> Folder::GetConstantComponent(Component &component, + const std::vector> *subscripts) { + if (std::optional> structures{std::visit( + common::visitors{ + [&](const Symbol &symbol) { + return Folder{context_} + .GetFoldedNamedConstantValue(symbol); + }, + [&](ArrayRef &aRef) { + return Folder{context_}.Folding(aRef); + }, + [&](Component &base) { + return Folder{context_}.GetConstantComponent(base); + }, + [&](CoarrayRef &) { + return std::optional>{}; + }, + }, + component.base().u)}) { + return ApplyComponent( + std::move(*structures), component.GetLastSymbol(), subscripts); + } else { + return std::nullopt; + } +} + +template Expr Folder::Folding(Designator &&designator) { + if constexpr (T::category == TypeCategory::Character) { + if (auto *substring{common::Unwrap(designator.u)}) { + if (std::optional> folded{ + substring->Fold(context_)}) { + if (auto value{GetScalarConstantValue(*folded)}) { + return Expr{*value}; + } + } + if (auto length{ToInt64(Fold(context_, substring->LEN()))}) { + if (*length == 0) { + return Expr{Constant{Scalar{}}}; + } + } + } + } + return std::visit( + common::visitors{ + [&](SymbolRef &&symbol) { + if (auto constant{GetFoldedNamedConstantValue(*symbol)}) { + return Expr{std::move(*constant)}; + } + return Expr{std::move(designator)}; + }, + [&](ArrayRef &&aRef) { + aRef = FoldOperation(context_, std::move(aRef)); + if (auto c{Folding(aRef)}) { + return Expr{std::move(*c)}; + } else { + return Expr{Designator{std::move(aRef)}}; + } + }, + [&](Component &&component) { + component = FoldOperation(context_, std::move(component)); + if (auto c{GetConstantComponent(component)}) { + return Expr{std::move(*c)}; + } else { + return Expr{Designator{std::move(component)}}; + } + }, + [&](auto &&x) { + return Expr{ + Designator{FoldOperation(context_, std::move(x))}}; + }, + }, + std::move(designator.u)); +} + // helpers to fold intrinsic function references // Define callable types used in a common utility that // takes care of array and cast/conversion aspects for elemental intrinsics diff --git a/flang/lib/evaluate/fold.cc b/flang/lib/evaluate/fold.cc index b7260da22cb1..dd50186d0177 100644 --- a/flang/lib/evaluate/fold.cc +++ b/flang/lib/evaluate/fold.cc @@ -11,360 +11,6 @@ namespace Fortran::evaluate { -template -std::optional> Folder::GetNamedConstantValue(const Symbol &symbol0) { - const Symbol &symbol{ResolveAssociations(symbol0).GetUltimate()}; - if (IsNamedConstant(symbol)) { - if (const auto *object{ - symbol.detailsIf()}) { - if (object->initWasValidated()) { - const auto *constant{UnwrapConstantValue(object->init())}; - CHECK(constant); - return Expr{*constant}; - } - if (const auto &init{object->init()}) { - if (auto dyType{DynamicType::From(symbol)}) { - semantics::ObjectEntityDetails *mutableObject{ - const_cast(object)}; - auto converted{ - ConvertToType(*dyType, std::move(mutableObject->init().value()))}; - // Reset expression now to prevent infinite loops if the init - // expression depends on symbol itself. - mutableObject->set_init(std::nullopt); - if (converted) { - *converted = Fold(context_, std::move(*converted)); - auto *unwrapped{UnwrapExpr>(*converted)}; - CHECK(unwrapped); - if (auto *constant{UnwrapConstantValue(*unwrapped)}) { - if (symbol.Rank() > 0) { - if (constant->Rank() == 0) { - // scalar expansion - if (auto symShape{GetShape(context_, symbol)}) { - if (auto extents{AsConstantExtents(context_, *symShape)}) { - *constant = constant->Reshape(std::move(*extents)); - CHECK(constant->Rank() == symbol.Rank()); - } - } - } - if (constant->Rank() == symbol.Rank()) { - NamedEntity base{symbol}; - if (auto lbounds{AsConstantExtents( - context_, GetLowerBounds(context_, base))}) { - constant->set_lbounds(*std::move(lbounds)); - } - } - } - mutableObject->set_init(AsGenericExpr(Expr{*constant})); - if (auto constShape{GetShape(context_, *constant)}) { - if (auto symShape{GetShape(context_, symbol)}) { - if (CheckConformance(context_.messages(), *constShape, - *symShape, "initialization expression", - "PARAMETER")) { - mutableObject->set_initWasValidated(); - return std::move(*unwrapped); - } - } else { - context_.messages().Say(symbol.name(), - "Could not determine the shape of the PARAMETER"_err_en_US); - } - } else { - context_.messages().Say(symbol.name(), - "Could not determine the shape of the initialization expression"_err_en_US); - } - mutableObject->set_init(std::nullopt); - } else { - std::stringstream ss; - unwrapped->AsFortran(ss); - context_.messages().Say(symbol.name(), - "Initialization expression for PARAMETER '%s' (%s) cannot be computed as a constant value"_err_en_US, - symbol.name(), ss.str()); - } - } else { - std::stringstream ss; - init->AsFortran(ss); - context_.messages().Say(symbol.name(), - "Initialization expression for PARAMETER '%s' (%s) cannot be converted to its type (%s)"_err_en_US, - symbol.name(), ss.str(), dyType->AsFortran()); - } - } - } - } - } - return std::nullopt; -} - -template -std::optional> Folder::GetFoldedNamedConstantValue( - const Symbol &symbol) { - if (auto value{GetNamedConstantValue(symbol)}) { - Expr folded{Fold(context_, std::move(*value))}; - if (const Constant *value{UnwrapConstantValue(folded)}) { - return *value; - } - } - return std::nullopt; -} - -static std::optional> GetConstantSubscript( - FoldingContext &context, Subscript &ss, const NamedEntity &base, int dim) { - ss = FoldOperation(context, std::move(ss)); - return std::visit( - common::visitors{ - [](IndirectSubscriptIntegerExpr &expr) - -> std::optional> { - if (auto constant{ - GetScalarConstantValue(expr.value())}) { - return Constant{*constant}; - } else { - return std::nullopt; - } - }, - [&](Triplet &triplet) -> std::optional> { - auto lower{triplet.lower()}, upper{triplet.upper()}; - std::optional stride{ToInt64(triplet.stride())}; - if (!lower) { - lower = GetLowerBound(context, base, dim); - } - if (!upper) { - upper = - ComputeUpperBound(context, GetLowerBound(context, base, dim), - GetExtent(context, base, dim)); - } - auto lbi{ToInt64(lower)}, ubi{ToInt64(upper)}; - if (lbi && ubi && stride && *stride != 0) { - std::vector values; - while ((*stride > 0 && *lbi <= *ubi) || - (*stride < 0 && *lbi >= *ubi)) { - values.emplace_back(*lbi); - *lbi += *stride; - } - return Constant{std::move(values), - ConstantSubscripts{ - static_cast(values.size())}}; - } else { - return std::nullopt; - } - }, - }, - ss.u); -} - -template -std::optional> Folder::Folding(ArrayRef &aRef) { - std::vector> subscripts; - int dim{0}; - for (Subscript &ss : aRef.subscript()) { - if (auto constant{GetConstantSubscript(context_, ss, aRef.base(), dim++)}) { - subscripts.emplace_back(std::move(*constant)); - } else { - return std::nullopt; - } - } - if (Component * component{aRef.base().UnwrapComponent()}) { - return GetConstantComponent(*component, &subscripts); - } else if (std::optional> array{ - GetFoldedNamedConstantValue(aRef.base().GetLastSymbol())}) { - return ApplySubscripts(*array, subscripts); - } else { - return std::nullopt; - } -} - -template -std::optional> Folder::ApplySubscripts(const Constant &array, - const std::vector> &subscripts) { - const auto &shape{array.shape()}; - const auto &lbounds{array.lbounds()}; - int rank{GetRank(shape)}; - CHECK(rank == static_cast(subscripts.size())); - std::size_t elements{1}; - ConstantSubscripts resultShape; - ConstantSubscripts ssLB; - for (const auto &ss : subscripts) { - CHECK(ss.Rank() <= 1); - if (ss.Rank() == 1) { - resultShape.push_back(static_cast(ss.size())); - elements *= ss.size(); - ssLB.push_back(ss.lbounds().front()); - } - } - ConstantSubscripts ssAt(rank, 0), at(rank, 0), tmp(1, 0); - std::vector> values; - while (elements-- > 0) { - bool increment{true}; - int k{0}; - for (int j{0}; j < rank; ++j) { - if (subscripts[j].Rank() == 0) { - at[j] = subscripts[j].GetScalarValue().value().ToInt64(); - } else { - CHECK(k < GetRank(resultShape)); - tmp[0] = ssLB[j] + ssAt[j]; - at[j] = subscripts[j].At(tmp).ToInt64(); - if (increment) { - if (++ssAt[j] == resultShape[k]) { - ssAt[j] = 0; - } else { - increment = false; - } - } - ++k; - } - if (at[j] < lbounds[j] || at[j] >= lbounds[j] + shape[j]) { - context_.messages().Say( - "Subscript value (%jd) is out of range on dimension %d in reference to a constant array value"_err_en_US, - static_cast(at[j]), j + 1); - return std::nullopt; - } - } - values.emplace_back(array.At(at)); - CHECK(!increment || elements == 0); - CHECK(k == GetRank(resultShape)); - } - if constexpr (T::category == TypeCategory::Character) { - return Constant{array.LEN(), std::move(values), std::move(resultShape)}; - } else if constexpr (std::is_same_v) { - return Constant{array.result().derivedTypeSpec(), std::move(values), - std::move(resultShape)}; - } else { - return Constant{std::move(values), std::move(resultShape)}; - } -} - -template -std::optional> Folder::ApplyComponent( - Constant &&structures, const Symbol &component, - const std::vector> *subscripts) { - if (auto scalar{structures.GetScalarValue()}) { - if (auto *expr{scalar->Find(component)}) { - if (const Constant *value{UnwrapConstantValue(*expr)}) { - if (!subscripts) { - return std::move(*value); - } else { - return ApplySubscripts(*value, *subscripts); - } - } - } - } else { - // A(:)%scalar_component & A(:)%array_component(subscripts) - std::unique_ptr> array; - if (structures.empty()) { - return std::nullopt; - } - ConstantSubscripts at{structures.lbounds()}; - do { - StructureConstructor scalar{structures.At(at)}; - if (auto *expr{scalar.Find(component)}) { - if (const Constant *value{UnwrapConstantValue(*expr)}) { - if (!array.get()) { - // This technique ensures that character length or derived type - // information is propagated to the array constructor. - auto *typedExpr{UnwrapExpr>(*expr)}; - CHECK(typedExpr); - array = std::make_unique>(*typedExpr); - } - if (subscripts) { - if (auto element{ApplySubscripts(*value, *subscripts)}) { - CHECK(element->Rank() == 0); - array->Push(Expr{std::move(*element)}); - } else { - return std::nullopt; - } - } else { - CHECK(value->Rank() == 0); - array->Push(Expr{*value}); - } - } else { - return std::nullopt; - } - } - } while (structures.IncrementSubscripts(at)); - // Fold the ArrayConstructor<> into a Constant<>. - CHECK(array); - Expr result{Fold(context_, Expr{std::move(*array)})}; - if (auto *constant{UnwrapConstantValue(result)}) { - return constant->Reshape(common::Clone(structures.shape())); - } - } - return std::nullopt; -} - -template -std::optional> Folder::GetConstantComponent(Component &component, - const std::vector> *subscripts) { - if (std::optional> structures{std::visit( - common::visitors{ - [&](const Symbol &symbol) { - return Folder{context_} - .GetFoldedNamedConstantValue(symbol); - }, - [&](ArrayRef &aRef) { - return Folder{context_}.Folding(aRef); - }, - [&](Component &base) { - return Folder{context_}.GetConstantComponent(base); - }, - [&](CoarrayRef &) { - return std::optional>{}; - }, - }, - component.base().u)}) { - return ApplyComponent( - std::move(*structures), component.GetLastSymbol(), subscripts); - } else { - return std::nullopt; - } -} - -template Expr Folder::Folding(Designator &&designator) { - if constexpr (T::category == TypeCategory::Character) { - if (auto *substring{common::Unwrap(designator.u)}) { - if (std::optional> folded{ - substring->Fold(context_)}) { - if (auto value{GetScalarConstantValue(*folded)}) { - return Expr{*value}; - } - } - if (auto length{ToInt64(Fold(context_, substring->LEN()))}) { - if (*length == 0) { - return Expr{Constant{Scalar{}}}; - } - } - } - } - return std::visit( - common::visitors{ - [&](SymbolRef &&symbol) { - if (auto constant{GetFoldedNamedConstantValue(*symbol)}) { - return Expr{std::move(*constant)}; - } - return Expr{std::move(designator)}; - }, - [&](ArrayRef &&aRef) { - aRef = FoldOperation(context_, std::move(aRef)); - if (auto c{Folding(aRef)}) { - return Expr{std::move(*c)}; - } else { - return Expr{Designator{std::move(aRef)}}; - } - }, - [&](Component &&component) { - component = FoldOperation(context_, std::move(component)); - if (auto c{GetConstantComponent(component)}) { - return Expr{std::move(*c)}; - } else { - return Expr{Designator{std::move(component)}}; - } - }, - [&](auto &&x) { - return Expr{ - Designator{FoldOperation(context_, std::move(x))}}; - }, - }, - std::move(designator.u)); -} - -FOR_EACH_SPECIFIC_TYPE(template class Folder, ) - Expr FoldOperation( FoldingContext &context, StructureConstructor &&structure) { StructureConstructor result{structure.derivedTypeSpec()};