2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Evaluate/fold.cpp ---------------------------------------------===//
|
2018-10-27 06:10:24 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-10-27 06:10:24 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-10-27 06:10:24 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Evaluate/fold.h"
|
2020-01-01 04:20:42 +08:00
|
|
|
#include "fold-implementation.h"
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
#include "flang/Evaluate/characteristics.h"
|
2019-07-25 04:55:29 +08:00
|
|
|
|
2018-10-27 06:10:24 +08:00
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
characteristics::TypeAndShape Fold(
|
|
|
|
FoldingContext &context, characteristics::TypeAndShape &&x) {
|
|
|
|
x.Rewrite(context);
|
|
|
|
return std::move(x);
|
|
|
|
}
|
|
|
|
|
2020-01-04 03:34:16 +08:00
|
|
|
std::optional<Constant<SubscriptInteger>> 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<Constant<SubscriptInteger>> {
|
|
|
|
if (const auto *constant{
|
|
|
|
UnwrapConstantValue<SubscriptInteger>(expr.value())}) {
|
|
|
|
return *constant;
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](Triplet &triplet) -> std::optional<Constant<SubscriptInteger>> {
|
|
|
|
auto lower{triplet.lower()}, upper{triplet.upper()};
|
|
|
|
std::optional<ConstantSubscript> 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<SubscriptInteger::Scalar> values;
|
|
|
|
while ((*stride > 0 && *lbi <= *ubi) ||
|
|
|
|
(*stride < 0 && *lbi >= *ubi)) {
|
|
|
|
values.emplace_back(*lbi);
|
|
|
|
*lbi += *stride;
|
|
|
|
}
|
|
|
|
return Constant<SubscriptInteger>{std::move(values),
|
|
|
|
ConstantSubscripts{
|
|
|
|
static_cast<ConstantSubscript>(values.size())}};
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ss.u);
|
|
|
|
}
|
|
|
|
|
2020-01-01 04:20:42 +08:00
|
|
|
Expr<SomeDerived> FoldOperation(
|
|
|
|
FoldingContext &context, StructureConstructor &&structure) {
|
2020-06-19 08:17:04 +08:00
|
|
|
StructureConstructor ctor{structure.derivedTypeSpec()};
|
2021-12-10 05:00:54 +08:00
|
|
|
bool isConstant{true};
|
2020-01-01 04:20:42 +08:00
|
|
|
for (auto &&[symbol, value] : std::move(structure)) {
|
2020-06-19 08:17:04 +08:00
|
|
|
auto expr{Fold(context, std::move(value.value()))};
|
2021-12-10 05:00:54 +08:00
|
|
|
if (IsPointer(symbol)) {
|
|
|
|
if (IsProcedure(symbol)) {
|
|
|
|
isConstant &= IsInitialProcedureTarget(expr);
|
|
|
|
} else {
|
|
|
|
isConstant &= IsInitialDataTarget(expr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
isConstant &= IsActuallyConstant(expr);
|
2020-06-19 08:17:04 +08:00
|
|
|
if (auto valueShape{GetConstantExtents(context, expr)}) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (auto componentShape{GetConstantExtents(context, symbol)}) {
|
|
|
|
if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0) {
|
|
|
|
expr = ScalarConstantExpander{std::move(*componentShape)}.Expand(
|
|
|
|
std::move(expr));
|
2021-12-10 05:00:54 +08:00
|
|
|
isConstant &= expr.Rank() > 0;
|
2020-06-19 08:17:04 +08:00
|
|
|
} else {
|
2021-12-10 05:00:54 +08:00
|
|
|
isConstant &= *valueShape == *componentShape;
|
2020-06-19 08:17:04 +08:00
|
|
|
}
|
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
}
|
2021-12-10 05:00:54 +08:00
|
|
|
ctor.Add(symbol, std::move(expr));
|
2020-06-19 08:17:04 +08:00
|
|
|
}
|
2021-12-10 05:00:54 +08:00
|
|
|
if (isConstant) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return Expr<SomeDerived>{Constant<SomeDerived>{std::move(ctor)}};
|
|
|
|
} else {
|
|
|
|
return Expr<SomeDerived>{std::move(ctor)};
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
2018-10-27 06:10:24 +08:00
|
|
|
}
|
|
|
|
|
2019-01-08 02:15:27 +08:00
|
|
|
Component FoldOperation(FoldingContext &context, Component &&component) {
|
|
|
|
return {FoldOperation(context, std::move(component.base())),
|
|
|
|
component.GetLastSymbol()};
|
|
|
|
}
|
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
NamedEntity FoldOperation(FoldingContext &context, NamedEntity &&x) {
|
|
|
|
if (Component * c{x.UnwrapComponent()}) {
|
|
|
|
return NamedEntity{FoldOperation(context, std::move(*c))};
|
|
|
|
} else {
|
|
|
|
return std::move(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-26 07:02:36 +08:00
|
|
|
Triplet FoldOperation(FoldingContext &context, Triplet &&triplet) {
|
2019-05-22 07:58:46 +08:00
|
|
|
MaybeExtentExpr lower{triplet.lower()};
|
|
|
|
MaybeExtentExpr upper{triplet.upper()};
|
|
|
|
return {Fold(context, std::move(lower)), Fold(context, std::move(upper)),
|
|
|
|
Fold(context, triplet.stride())};
|
2019-01-08 02:15:27 +08:00
|
|
|
}
|
|
|
|
|
2019-06-26 07:02:36 +08:00
|
|
|
Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[&](IndirectSubscriptIntegerExpr &&expr) {
|
|
|
|
expr.value() = Fold(context, std::move(expr.value()));
|
|
|
|
return Subscript(std::move(expr));
|
|
|
|
},
|
|
|
|
[&](Triplet &&triplet) {
|
|
|
|
return Subscript(
|
|
|
|
FoldOperation(context, std::move(triplet)));
|
|
|
|
},
|
|
|
|
},
|
2019-01-08 02:15:27 +08:00
|
|
|
std::move(subscript.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
|
2019-06-26 04:07:32 +08:00
|
|
|
NamedEntity base{FoldOperation(context, std::move(arrayRef.base()))};
|
2019-02-01 08:04:17 +08:00
|
|
|
for (Subscript &subscript : arrayRef.subscript()) {
|
2019-06-26 07:02:36 +08:00
|
|
|
subscript = FoldOperation(context, std::move(subscript));
|
2019-01-08 02:15:27 +08:00
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
return ArrayRef{std::move(base), std::move(arrayRef.subscript())};
|
2019-01-08 02:15:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) {
|
2019-04-04 03:57:24 +08:00
|
|
|
std::vector<Subscript> subscript;
|
|
|
|
for (Subscript x : coarrayRef.subscript()) {
|
2019-06-26 07:02:36 +08:00
|
|
|
subscript.emplace_back(FoldOperation(context, std::move(x)));
|
2019-01-08 02:15:27 +08:00
|
|
|
}
|
2019-04-02 06:22:45 +08:00
|
|
|
std::vector<Expr<SubscriptInteger>> cosubscript;
|
2019-01-08 02:15:27 +08:00
|
|
|
for (Expr<SubscriptInteger> x : coarrayRef.cosubscript()) {
|
|
|
|
cosubscript.emplace_back(Fold(context, std::move(x)));
|
|
|
|
}
|
2019-04-04 03:57:24 +08:00
|
|
|
CoarrayRef folded{std::move(coarrayRef.base()), std::move(subscript),
|
|
|
|
std::move(cosubscript)};
|
2019-01-08 02:15:27 +08:00
|
|
|
if (std::optional<Expr<SomeInteger>> stat{coarrayRef.stat()}) {
|
|
|
|
folded.set_stat(Fold(context, std::move(*stat)));
|
|
|
|
}
|
|
|
|
if (std::optional<Expr<SomeInteger>> team{coarrayRef.team()}) {
|
|
|
|
folded.set_team(
|
|
|
|
Fold(context, std::move(*team)), coarrayRef.teamIsTeamNumber());
|
|
|
|
}
|
|
|
|
return folded;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataRef FoldOperation(FoldingContext &context, DataRef &&dataRef) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[&](SymbolRef symbol) { return DataRef{*symbol}; },
|
|
|
|
[&](auto &&x) {
|
|
|
|
return DataRef{FoldOperation(context, std::move(x))};
|
|
|
|
},
|
|
|
|
},
|
2019-01-08 02:15:27 +08:00
|
|
|
std::move(dataRef.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
Substring FoldOperation(FoldingContext &context, Substring &&substring) {
|
2019-05-09 01:22:56 +08:00
|
|
|
auto lower{Fold(context, substring.lower())};
|
|
|
|
auto upper{Fold(context, substring.upper())};
|
2019-01-08 02:15:27 +08:00
|
|
|
if (const DataRef * dataRef{substring.GetParentIf<DataRef>()}) {
|
|
|
|
return Substring{FoldOperation(context, DataRef{*dataRef}),
|
|
|
|
std::move(lower), std::move(upper)};
|
|
|
|
} else {
|
|
|
|
auto p{*substring.GetParentIf<StaticDataObject::Pointer>()};
|
|
|
|
return Substring{std::move(p), std::move(lower), std::move(upper)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ComplexPart FoldOperation(FoldingContext &context, ComplexPart &&complexPart) {
|
|
|
|
DataRef complex{complexPart.complex()};
|
|
|
|
return ComplexPart{
|
|
|
|
FoldOperation(context, std::move(complex)), complexPart.part()};
|
|
|
|
}
|
|
|
|
|
2020-01-01 04:20:42 +08:00
|
|
|
std::optional<std::int64_t> GetInt64Arg(
|
2019-05-22 07:58:46 +08:00
|
|
|
const std::optional<ActualArgument> &arg) {
|
|
|
|
if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(arg)}) {
|
|
|
|
return ToInt64(*intExpr);
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
2019-04-19 05:11:15 +08:00
|
|
|
}
|
2019-03-27 01:29:46 +08:00
|
|
|
}
|
|
|
|
|
2020-01-01 04:20:42 +08:00
|
|
|
std::optional<std::int64_t> GetInt64ArgOr(
|
2019-05-22 07:58:46 +08:00
|
|
|
const std::optional<ActualArgument> &arg, std::int64_t defaultValue) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!arg) {
|
2019-05-22 07:58:46 +08:00
|
|
|
return defaultValue;
|
|
|
|
} else if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(arg)}) {
|
|
|
|
return ToInt64(*intExpr);
|
2019-04-19 05:11:15 +08:00
|
|
|
} else {
|
2019-05-22 07:58:46 +08:00
|
|
|
return std::nullopt;
|
2019-04-19 05:11:15 +08:00
|
|
|
}
|
2019-03-27 01:29:46 +08:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:56:50 +08:00
|
|
|
Expr<ImpliedDoIndex::Result> FoldOperation(
|
|
|
|
FoldingContext &context, ImpliedDoIndex &&iDo) {
|
2019-06-08 00:57:10 +08:00
|
|
|
if (std::optional<ConstantSubscript> value{context.GetImpliedDo(iDo.name)}) {
|
2019-02-01 08:04:17 +08:00
|
|
|
return Expr<ImpliedDoIndex::Result>{*value};
|
2019-02-01 01:58:40 +08:00
|
|
|
} else {
|
|
|
|
return Expr<ImpliedDoIndex::Result>{std::move(iDo)};
|
|
|
|
}
|
2019-01-29 08:56:50 +08:00
|
|
|
}
|
|
|
|
|
2020-01-01 04:20:42 +08:00
|
|
|
template class ExpressionBase<SomeDerived>;
|
|
|
|
template class ExpressionBase<SomeType>;
|
2019-06-22 05:09:51 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::evaluate
|