[flang] Improve shape & length characterization

Analyze the shape of the result of TRANSFER(ptr,array) correctly
when "ptr" is an array of deferred shape.  Fixing this bug led to
some refactoring and concentration of common code in TypeAndShape
member functions with code in general shape and character length
analysis, and this led to some regression test failures that have
all been cleaned up.

Differential Revision: https://reviews.llvm.org/D95744
This commit is contained in:
peter klausler 2021-01-30 10:14:07 -08:00
parent 3fdf2a56dd
commit 6f3d322f25
8 changed files with 113 additions and 96 deletions

View File

@ -78,8 +78,6 @@ public:
static std::optional<TypeAndShape> Characterize(
const semantics::Symbol &, FoldingContext &);
static std::optional<TypeAndShape> Characterize(
const semantics::ObjectEntityDetails &, FoldingContext &);
static std::optional<TypeAndShape> Characterize(
const semantics::ProcInterface &, FoldingContext &);
static std::optional<TypeAndShape> Characterize(
@ -87,26 +85,25 @@ public:
static std::optional<TypeAndShape> Characterize(
const ActualArgument &, FoldingContext &);
// Handle Expr<T> & Designator<T>
template <typename A>
static std::optional<TypeAndShape> Characterize(
const A &x, FoldingContext &context) {
if (const auto *symbol{UnwrapWholeSymbolDataRef(x)}) {
if (const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)}) {
if (auto result{Characterize(*symbol, context)}) {
return result;
}
}
if (auto type{x.GetType()}) {
if (auto shape{GetShape(context, x)}) {
TypeAndShape result{*type, std::move(*shape)};
if (type->category() == TypeCategory::Character) {
if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
if (auto length{chExpr->LEN()}) {
result.set_LEN(std::move(*length));
}
TypeAndShape result{*type, GetShape(context, x)};
if (type->category() == TypeCategory::Character) {
if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
if (auto length{chExpr->LEN()}) {
result.set_LEN(std::move(*length));
}
}
return std::move(result.Rewrite(context));
}
return std::move(result.Rewrite(context));
}
return std::nullopt;
}
@ -162,8 +159,9 @@ private:
const semantics::AssocEntityDetails &, FoldingContext &);
static std::optional<TypeAndShape> Characterize(
const semantics::ProcEntityDetails &, FoldingContext &);
void AcquireShape(const semantics::ObjectEntityDetails &);
void AcquireAttrs(const semantics::Symbol &);
void AcquireLEN();
void AcquireLEN(const semantics::Symbol &);
protected:
DynamicType type_;

View File

@ -330,6 +330,22 @@ template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
return nullptr;
}
// If an expression is a whole symbol or a whole component desginator,
// extract and return that symbol, else null.
template <typename A>
const Symbol *UnwrapWholeSymbolOrComponentDataRef(const A &x) {
if (auto dataRef{ExtractDataRef(x)}) {
if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
return &p->get();
} else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
if (c->base().Rank() == 0) {
return &c->GetLastSymbol();
}
}
}
return nullptr;
}
// GetFirstSymbol(A%B%C[I]%D) -> A
template <typename A> const Symbol *GetFirstSymbol(const A &x) {
if (auto dataRef{ExtractDataRef(x, true)}) {

View File

@ -68,17 +68,20 @@ TypeAndShape &TypeAndShape::Rewrite(FoldingContext &context) {
std::optional<TypeAndShape> TypeAndShape::Characterize(
const semantics::Symbol &symbol, FoldingContext &context) {
const auto &ultimate{symbol.GetUltimate()};
return std::visit(
common::visitors{
[&](const semantics::ObjectEntityDetails &object) {
auto result{Characterize(object, context)};
if (result &&
result->type().category() == TypeCategory::Character) {
if (auto len{DataRef{symbol}.LEN()}) {
result->set_LEN(Fold(context, std::move(*len)));
}
[&](const semantics::ObjectEntityDetails &object)
-> std::optional<TypeAndShape> {
if (auto type{DynamicType::From(object.type())}) {
TypeAndShape result{
std::move(*type), GetShape(context, ultimate)};
result.AcquireAttrs(ultimate);
result.AcquireLEN(ultimate);
return std::move(result.Rewrite(context));
} else {
return std::nullopt;
}
return result;
},
[&](const semantics::ProcEntityDetails &proc) {
const semantics::ProcInterface &interface{proc.interface()};
@ -108,18 +111,7 @@ std::optional<TypeAndShape> TypeAndShape::Characterize(
// GetUltimate() used here, not ResolveAssociations(), because
// we need the type/rank of an associate entity from TYPE IS,
// CLASS IS, or RANK statement.
symbol.GetUltimate().details());
}
std::optional<TypeAndShape> TypeAndShape::Characterize(
const semantics::ObjectEntityDetails &object, FoldingContext &context) {
if (auto type{DynamicType::From(object.type())}) {
TypeAndShape result{std::move(*type)};
result.AcquireShape(object);
return Fold(context, std::move(result));
} else {
return std::nullopt;
}
ultimate.details());
}
std::optional<TypeAndShape> TypeAndShape::Characterize(
@ -196,35 +188,24 @@ std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureSizeInBytes(
return std::nullopt;
}
void TypeAndShape::AcquireShape(const semantics::ObjectEntityDetails &object) {
CHECK(shape_.empty() && !attrs_.test(Attr::AssumedRank));
corank_ = object.coshape().Rank();
if (object.IsAssumedRank()) {
attrs_.set(Attr::AssumedRank);
return;
}
if (object.IsAssumedShape()) {
attrs_.set(Attr::AssumedShape);
}
if (object.IsAssumedSize()) {
attrs_.set(Attr::AssumedSize);
}
if (object.IsDeferredShape()) {
attrs_.set(Attr::DeferredShape);
}
if (object.IsCoarray()) {
attrs_.set(Attr::Coarray);
}
for (const semantics::ShapeSpec &dim : object.shape()) {
if (dim.ubound().GetExplicit()) {
Expr<SubscriptInteger> extent{*dim.ubound().GetExplicit()};
if (auto lbound{dim.lbound().GetExplicit()}) {
extent =
std::move(extent) + Expr<SubscriptInteger>{1} - std::move(*lbound);
}
shape_.emplace_back(std::move(extent));
} else {
shape_.push_back(std::nullopt);
void TypeAndShape::AcquireAttrs(const semantics::Symbol &symbol) {
if (const auto *object{
symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) {
corank_ = object->coshape().Rank();
if (object->IsAssumedRank()) {
attrs_.set(Attr::AssumedRank);
}
if (object->IsAssumedShape()) {
attrs_.set(Attr::AssumedShape);
}
if (object->IsAssumedSize()) {
attrs_.set(Attr::AssumedSize);
}
if (object->IsDeferredShape()) {
attrs_.set(Attr::DeferredShape);
}
if (object->IsCoarray()) {
attrs_.set(Attr::Coarray);
}
}
}
@ -239,6 +220,14 @@ void TypeAndShape::AcquireLEN() {
}
}
void TypeAndShape::AcquireLEN(const semantics::Symbol &symbol) {
if (type_.category() == TypeCategory::Character) {
if (auto len{DataRef{symbol}.LEN()}) {
LEN_ = std::move(*len);
}
}
}
llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const {
o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
attrs_.Dump(o, EnumToString);
@ -278,8 +267,8 @@ static common::Intent GetIntent(const semantics::Attrs &attrs) {
std::optional<DummyDataObject> DummyDataObject::Characterize(
const semantics::Symbol &symbol, FoldingContext &context) {
if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
if (auto type{TypeAndShape::Characterize(*obj, context)}) {
if (symbol.has<semantics::ObjectEntityDetails>()) {
if (auto type{TypeAndShape::Characterize(symbol, context)}) {
std::optional<DummyDataObject> result{std::move(*type)};
using semantics::Attr;
CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result,
@ -522,8 +511,8 @@ bool FunctionResult::operator==(const FunctionResult &that) const {
std::optional<FunctionResult> FunctionResult::Characterize(
const Symbol &symbol, FoldingContext &context) {
if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
if (auto type{TypeAndShape::Characterize(*object, context)}) {
if (symbol.has<semantics::ObjectEntityDetails>()) {
if (auto type{TypeAndShape::Characterize(symbol, context)}) {
FunctionResult result{std::move(*type)};
CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result,
{

View File

@ -296,27 +296,31 @@ MaybeExtentExpr GetExtent(const NamedEntity &base, int dimension) {
CHECK(dimension >= 0);
const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())};
if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
if (IsImpliedShape(symbol)) {
Shape shape{GetShape(symbol).value()};
return std::move(shape.at(dimension));
}
int j{0};
for (const auto &shapeSpec : details->shape()) {
if (j++ == dimension) {
if (shapeSpec.ubound().isExplicit()) {
if (const auto &ubound{shapeSpec.ubound().GetExplicit()}) {
if (const auto &lbound{shapeSpec.lbound().GetExplicit()}) {
return common::Clone(ubound.value()) -
common::Clone(lbound.value()) + ExtentExpr{1};
} else {
return ubound.value();
if (IsImpliedShape(symbol) && details->init()) {
if (auto shape{GetShape(symbol)}) {
if (dimension < static_cast<int>(shape->size())) {
return std::move(shape->at(dimension));
}
}
} else {
int j{0};
for (const auto &shapeSpec : details->shape()) {
if (j++ == dimension) {
if (shapeSpec.ubound().isExplicit()) {
if (const auto &ubound{shapeSpec.ubound().GetExplicit()}) {
if (const auto &lbound{shapeSpec.lbound().GetExplicit()}) {
return common::Clone(ubound.value()) -
common::Clone(lbound.value()) + ExtentExpr{1};
} else {
return ubound.value();
}
}
} else if (details->IsAssumedSize() && j == symbol.Rank()) {
return std::nullopt;
} else if (semantics::IsDescriptor(symbol)) {
return ExtentExpr{DescriptorInquiry{NamedEntity{base},
DescriptorInquiry::Field::Extent, dimension}};
}
} else if (details->IsAssumedSize() && j == symbol.Rank()) {
return std::nullopt;
} else if (semantics::IsDescriptor(symbol)) {
return ExtentExpr{DescriptorInquiry{
NamedEntity{base}, DescriptorInquiry::Field::Extent, dimension}};
}
}
}
@ -449,7 +453,7 @@ auto GetShapeHelper::operator()(const Symbol &symbol) const -> Result {
return std::visit(
common::visitors{
[&](const semantics::ObjectEntityDetails &object) {
if (IsImpliedShape(symbol)) {
if (IsImpliedShape(symbol) && object.init()) {
return (*this)(object.init());
} else {
int n{object.shape().Rank()};

View File

@ -14,6 +14,7 @@
#include "flang/Parser/char-block.h"
#include "flang/Parser/characters.h"
#include "flang/Parser/message.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/symbol.h"
#include <type_traits>
@ -257,8 +258,13 @@ DescriptorInquiry::DescriptorInquiry(NamedEntity &&base, Field field, int dim)
}
// LEN()
static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &sym) {
if (auto dyType{DynamicType::From(sym)}) {
static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {
const Symbol &ultimate{symbol.GetUltimate()};
if (const auto *assoc{ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc->expr())}) {
return chExpr->LEN();
}
} else if (auto dyType{DynamicType::From(ultimate)}) {
if (const semantics::ParamValue * len{dyType->charLength()}) {
if (len->isExplicit()) {
if (auto intExpr{len->GetExplicit()}) {
@ -267,8 +273,10 @@ static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &sym) {
}
}
}
return Expr<SubscriptInteger>{
DescriptorInquiry{NamedEntity{sym}, DescriptorInquiry::Field::Len}};
if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
return Expr<SubscriptInteger>{DescriptorInquiry{
NamedEntity{ultimate}, DescriptorInquiry::Field::Len}};
}
}
}
return std::nullopt;

View File

@ -272,6 +272,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
: nullptr};
int actualRank{evaluate::GetRank(actualType.shape())};
bool actualIsPointer{evaluate::IsObjectPointer(actual, context)};
bool dummyIsAssumedRank{dummy.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedRank)};
if (dummy.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedShape)) {
// 15.5.2.4(16)
@ -295,7 +297,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
if (!IsArrayElement(actual) &&
!(actualType.type().category() == TypeCategory::Character &&
actualType.type().kind() == 1) &&
!(dummy.type.type().IsAssumedType() && dummyIsAssumedSize)) {
!(dummy.type.type().IsAssumedType() && dummyIsAssumedSize) &&
!dummyIsAssumedRank) {
messages.Say(
"Whole scalar actual argument may not be associated with a %s array"_err_en_US,
dummyName);
@ -355,8 +358,6 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
bool dummyIsContiguous{
dummy.attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
bool actualIsContiguous{IsSimplyContiguous(actual, context)};
bool dummyIsAssumedRank{dummy.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedRank)};
bool dummyIsAssumedShape{dummy.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedShape)};
if ((actualIsAsynchronous || actualIsVolatile) &&

View File

@ -1847,11 +1847,12 @@ static bool CheckCompatibleArgument(bool isElemental,
return std::visit(
common::visitors{
[&](const characteristics::DummyDataObject &x) {
characteristics::TypeAndShape dummyTypeAndShape{x.type};
if (!isElemental && actual.Rank() != dummyTypeAndShape.Rank()) {
if (!isElemental && actual.Rank() != x.type.Rank() &&
!x.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedRank)) {
return false;
} else if (auto actualType{actual.GetType()}) {
return dummyTypeAndShape.type().IsTkCompatibleWith(*actualType);
return x.type.type().IsTkCompatibleWith(*actualType);
} else {
return false;
}

View File

@ -616,7 +616,7 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
const std::string &distinctName, const SymbolVector *parameters) {
evaluate::StructureConstructorValues values;
auto typeAndShape{evaluate::characteristics::TypeAndShape::Characterize(
object, context_.foldingContext())};
symbol, context_.foldingContext())};
CHECK(typeAndShape.has_value());
auto dyType{typeAndShape->type()};
const auto &shape{typeAndShape->shape()};