[flang] Support coarrays in name resolution

A coarray is represented as a `Symbol` with `ObjectEntityDetails` that
has a non-empty coshape. The coshape is represented using the same type
(`ArrayShape`) as the shape is, so the fact that it is a coshape is
determined from context.

Move code for analyzing shapes to `resolve-names-utils.cc` and
generalize it for coshapes.

In `symbol.cc` add dumping of coshapes. Simplify some of the functions
by adding some `Dump*` functions to handle common cases.

In `mod-file.cc` generalize the code for writing shapes to also write
coshapes. Fix a bug in `PutShapeSpec()`.

Original-commit: flang-compiler/f18@9d2482c40c
Reviewed-on: https://github.com/flang-compiler/f18/pull/384
Tree-same-pre-rewrite: false
This commit is contained in:
Tim Keith 2019-04-04 14:46:40 -07:00
parent 924c38356c
commit b7efa8b77b
9 changed files with 391 additions and 155 deletions

View File

@ -424,7 +424,7 @@ void PutEntity(std::ostream &os, const Symbol &symbol) {
} }
void PutShapeSpec(std::ostream &os, const ShapeSpec &x) { void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
if (x.ubound().isAssumed()) { if (x.lbound().isAssumed()) {
CHECK(x.ubound().isAssumed()); CHECK(x.ubound().isAssumed());
os << ".."; os << "..";
} else { } else {
@ -437,9 +437,9 @@ void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
} }
} }
} }
void PutShape(std::ostream &os, const ArraySpec &shape) { void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) {
if (!shape.empty()) { if (!shape.empty()) {
os << '('; os << open;
bool first{true}; bool first{true};
for (const auto &shapeSpec : shape) { for (const auto &shapeSpec : shape) {
if (first) { if (first) {
@ -449,7 +449,7 @@ void PutShape(std::ostream &os, const ArraySpec &shape) {
} }
PutShapeSpec(os, shapeSpec); PutShapeSpec(os, shapeSpec);
} }
os << ')'; os << close;
} }
} }
@ -460,7 +460,8 @@ void PutObjectEntity(std::ostream &os, const Symbol &symbol) {
CHECK(type); CHECK(type);
PutLower(os, *type); PutLower(os, *type);
}); });
PutShape(os, details.shape()); PutShape(os, details.shape(), '(', ')');
PutShape(os, details.coshape(), '[', ']');
PutInit(os, details.init()); PutInit(os, details.init());
} }
@ -816,6 +817,10 @@ void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
DoBound(spec.lbound()); DoBound(spec.lbound());
DoBound(spec.ubound()); DoBound(spec.ubound());
} }
for (const ShapeSpec &spec : details.coshape()) {
DoBound(spec.lbound());
DoBound(spec.ubound());
}
if (const Symbol * commonBlock{details.commonBlock()}) { if (const Symbol * commonBlock{details.commonBlock()}) {
DoSymbol(*commonBlock); DoSymbol(*commonBlock);
} }

View File

@ -13,10 +13,12 @@
// limitations under the License. // limitations under the License.
#include "resolve-names-utils.h" #include "resolve-names-utils.h"
#include "expression.h"
#include "semantics.h" #include "semantics.h"
#include "symbol.h"
#include "type.h"
#include "../common/idioms.h" #include "../common/idioms.h"
#include "../evaluate/fold.h"
#include "../evaluate/tools.h"
#include "../evaluate/type.h"
#include "../parser/char-block.h" #include "../parser/char-block.h"
#include "../parser/features.h" #include "../parser/features.h"
#include "../parser/parse-tree.h" #include "../parser/parse-tree.h"
@ -163,4 +165,114 @@ static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
} }
} }
class ArraySpecAnalyzer {
public:
ArraySpecAnalyzer(ArraySpec &arraySpec, SemanticsContext &context)
: context_{context}, arraySpec_{arraySpec} {
CHECK(arraySpec.empty());
}
void Analyze(const parser::ArraySpec &);
void Analyze(const parser::CoarraySpec &);
private:
SemanticsContext &context_;
ArraySpec &arraySpec_;
template<typename T> void Analyze(const std::list<T> &list) {
for (const auto &elem : list) {
Analyze(elem);
}
}
void Analyze(const parser::AssumedShapeSpec &);
void Analyze(const parser::ExplicitShapeSpec &);
void Analyze(const parser::AssumedImpliedSpec &);
void Analyze(const parser::AssumedRankSpec &);
void MakeExplicit(const std::optional<parser::SpecificationExpr> &,
const parser::SpecificationExpr &);
void MakeImplied(const std::optional<parser::SpecificationExpr> &);
void MakeDeferred(int);
Bound GetBound(const std::optional<parser::SpecificationExpr> &);
Bound GetBound(const parser::SpecificationExpr &);
};
void AnalyzeArraySpec(ArraySpec &result, SemanticsContext &context,
const parser::ArraySpec &arraySpec) {
ArraySpecAnalyzer{result, context}.Analyze(arraySpec);
}
void AnalyzeCoarraySpec(ArraySpec &result, SemanticsContext &context,
const parser::CoarraySpec &coarraySpec) {
ArraySpecAnalyzer{result, context}.Analyze(coarraySpec);
}
void ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
std::visit(
common::visitors{
[&](const parser::DeferredShapeSpecList &y) { MakeDeferred(y.v); },
[&](const parser::AssumedSizeSpec &y) {
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
},
[&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
[&](const auto &y) { Analyze(y); },
},
x.u);
}
void ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
std::visit(
common::visitors{
[&](const parser::DeferredCoshapeSpecList &y) { MakeDeferred(y.v); },
[&](const parser::ExplicitCoshapeSpec &y) {
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
MakeImplied(
std::get<std::optional<parser::SpecificationExpr>>(y.t));
},
},
x.u);
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
arraySpec_.push_back(ShapeSpec::MakeAssumed(GetBound(x.v)));
}
void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
std::get<parser::SpecificationExpr>(x.t));
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
MakeImplied(x.v);
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedRankSpec &) {
arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
}
void ArraySpecAnalyzer::MakeExplicit(
const std::optional<parser::SpecificationExpr> &lb,
const parser::SpecificationExpr &ub) {
arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub)));
}
void ArraySpecAnalyzer::MakeImplied(
const std::optional<parser::SpecificationExpr> &lb) {
arraySpec_.push_back(ShapeSpec::MakeImplied(GetBound(lb)));
}
void ArraySpecAnalyzer::MakeDeferred(int n) {
for (int i = 0; i < n; ++i) {
arraySpec_.push_back(ShapeSpec::MakeDeferred());
}
}
Bound ArraySpecAnalyzer::GetBound(
const std::optional<parser::SpecificationExpr> &x) {
return x ? GetBound(*x) : Bound{1};
}
Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
MaybeSubscriptIntExpr expr;
if (MaybeExpr maybeExpr{AnalyzeExpr(context_, x.v)}) {
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
expr = evaluate::Fold(context_.foldingContext(),
evaluate::ConvertToType<evaluate::SubscriptInteger>(
std::move(*intExpr)));
}
}
return Bound{std::move(expr)};
}
} }

View File

@ -18,10 +18,13 @@
// Utility functions and class for use in resolve-names.cc. // Utility functions and class for use in resolve-names.cc.
#include "symbol.h" #include "symbol.h"
#include "type.h"
#include "../parser/message.h" #include "../parser/message.h"
namespace Fortran::parser { namespace Fortran::parser {
class CharBlock; class CharBlock;
struct ArraySpec;
struct CoarraySpec;
struct DefinedOpName; struct DefinedOpName;
struct GenericSpec; struct GenericSpec;
struct Name; struct Name;
@ -30,6 +33,7 @@ struct Name;
namespace Fortran::semantics { namespace Fortran::semantics {
using SourceName = parser::CharBlock; using SourceName = parser::CharBlock;
class SemanticsContext;
// Record that a Name has been resolved to a Symbol // Record that a Name has been resolved to a Symbol
Symbol &Resolve(const parser::Name &, Symbol &); Symbol &Resolve(const parser::Name &, Symbol &);
@ -64,6 +68,12 @@ private:
void Analyze(const parser::GenericSpec &); void Analyze(const parser::GenericSpec &);
}; };
// Analyze a parser::ArraySpec or parser::CoarraySpec into the provide ArraySpec
void AnalyzeArraySpec(
ArraySpec &, SemanticsContext &, const parser::ArraySpec &);
void AnalyzeCoarraySpec(
ArraySpec &, SemanticsContext &, const parser::CoarraySpec &);
} }
#endif // FORTRAN_SEMANTICS_RESOLVE_NAMES_H_ #endif // FORTRAN_SEMANTICS_RESOLVE_NAMES_H_

View File

@ -353,30 +353,29 @@ private:
// 6. TODO: BasedPointerStmt // 6. TODO: BasedPointerStmt
class ArraySpecVisitor : public virtual BaseVisitor { class ArraySpecVisitor : public virtual BaseVisitor {
public: public:
bool Pre(const parser::ArraySpec &); void Post(const parser::ArraySpec &);
void Post(const parser::CoarraySpec &);
void Post(const parser::AttrSpec &) { PostAttrSpec(); } void Post(const parser::AttrSpec &) { PostAttrSpec(); }
void Post(const parser::ComponentAttrSpec &) { PostAttrSpec(); } void Post(const parser::ComponentAttrSpec &) { PostAttrSpec(); }
void Post(const parser::DeferredShapeSpecList &);
void Post(const parser::AssumedShapeSpec &);
void Post(const parser::ExplicitShapeSpec &);
void Post(const parser::AssumedImpliedSpec &);
void Post(const parser::AssumedRankSpec &);
protected: protected:
const ArraySpec &arraySpec(); const ArraySpec &arraySpec();
const ArraySpec &coarraySpec();
void BeginArraySpec(); void BeginArraySpec();
void EndArraySpec(); void EndArraySpec();
void ClearArraySpec() { arraySpec_.clear(); } void ClearArraySpec() { arraySpec_.clear(); }
void ClearCoarraySpec() { coarraySpec_.clear(); }
private: private:
// arraySpec_ is populated by any ArraySpec // arraySpec_/coarraySpec_ are populated from any ArraySpec/CoarraySpec
ArraySpec arraySpec_; ArraySpec arraySpec_;
ArraySpec coarraySpec_;
// When an ArraySpec is under an AttrSpec or ComponentAttrSpec, it is moved // When an ArraySpec is under an AttrSpec or ComponentAttrSpec, it is moved
// into attrArraySpec_ // into attrArraySpec_
ArraySpec attrArraySpec_; ArraySpec attrArraySpec_;
ArraySpec attrCoarraySpec_;
void PostAttrSpec(); void PostAttrSpec();
Bound GetBound(const parser::SpecificationExpr &);
}; };
// Manage a stack of Scopes // Manage a stack of Scopes
@ -649,7 +648,6 @@ class DeclarationVisitor : public ArraySpecVisitor,
public virtual ScopeHandler { public virtual ScopeHandler {
public: public:
using ArraySpecVisitor::Post; using ArraySpecVisitor::Post;
using ArraySpecVisitor::Pre;
using ScopeHandler::Post; using ScopeHandler::Post;
using ScopeHandler::Pre; using ScopeHandler::Pre;
@ -681,6 +679,7 @@ public:
} }
void Post(const parser::TargetStmt &) { objectDeclAttr_ = std::nullopt; } void Post(const parser::TargetStmt &) { objectDeclAttr_ = std::nullopt; }
void Post(const parser::DimensionStmt::Declaration &); void Post(const parser::DimensionStmt::Declaration &);
void Post(const parser::CodimensionDecl &);
bool Pre(const parser::TypeDeclarationStmt &) { return BeginDecl(); } bool Pre(const parser::TypeDeclarationStmt &) { return BeginDecl(); }
void Post(const parser::TypeDeclarationStmt &) { EndDecl(); } void Post(const parser::TypeDeclarationStmt &) { EndDecl(); }
void Post(const parser::IntegerTypeSpec &); void Post(const parser::IntegerTypeSpec &);
@ -952,7 +951,6 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
public ConstructVisitor { public ConstructVisitor {
public: public:
using ArraySpecVisitor::Post; using ArraySpecVisitor::Post;
using ArraySpecVisitor::Pre;
using ConstructVisitor::Post; using ConstructVisitor::Post;
using ConstructVisitor::Pre; using ConstructVisitor::Pre;
using DeclarationVisitor::Post; using DeclarationVisitor::Post;
@ -1420,65 +1418,42 @@ bool ImplicitRulesVisitor::HandleImplicitNone(
// ArraySpecVisitor implementation // ArraySpecVisitor implementation
bool ArraySpecVisitor::Pre(const parser::ArraySpec &x) { void ArraySpecVisitor::Post(const parser::ArraySpec &x) {
CHECK(arraySpec_.empty()); AnalyzeArraySpec(arraySpec_, context(), x);
return true;
} }
void ArraySpecVisitor::Post(const parser::CoarraySpec &x) {
void ArraySpecVisitor::Post(const parser::DeferredShapeSpecList &x) { AnalyzeCoarraySpec(coarraySpec_, context(), x);
for (int i = 0; i < x.v; ++i) {
arraySpec_.push_back(ShapeSpec::MakeDeferred());
}
}
void ArraySpecVisitor::Post(const parser::AssumedShapeSpec &x) {
const auto &lb{x.v};
arraySpec_.push_back(
lb ? ShapeSpec::MakeAssumed(GetBound(*lb)) : ShapeSpec::MakeAssumed());
}
void ArraySpecVisitor::Post(const parser::ExplicitShapeSpec &x) {
auto &&ub{GetBound(std::get<parser::SpecificationExpr>(x.t))};
if (const auto &lb{std::get<std::optional<parser::SpecificationExpr>>(x.t)}) {
arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(*lb), std::move(ub)));
} else {
arraySpec_.push_back(ShapeSpec::MakeExplicit(Bound{1}, std::move(ub)));
}
}
void ArraySpecVisitor::Post(const parser::AssumedImpliedSpec &x) {
const auto &lb{x.v};
arraySpec_.push_back(
lb ? ShapeSpec::MakeImplied(GetBound(*lb)) : ShapeSpec::MakeImplied());
}
void ArraySpecVisitor::Post(const parser::AssumedRankSpec &) {
arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
} }
const ArraySpec &ArraySpecVisitor::arraySpec() { const ArraySpec &ArraySpecVisitor::arraySpec() {
return !arraySpec_.empty() ? arraySpec_ : attrArraySpec_; return !arraySpec_.empty() ? arraySpec_ : attrArraySpec_;
} }
const ArraySpec &ArraySpecVisitor::coarraySpec() {
return !coarraySpec_.empty() ? coarraySpec_ : attrCoarraySpec_;
}
void ArraySpecVisitor::BeginArraySpec() { void ArraySpecVisitor::BeginArraySpec() {
CHECK(arraySpec_.empty()); CHECK(arraySpec_.empty());
CHECK(coarraySpec_.empty());
CHECK(attrArraySpec_.empty()); CHECK(attrArraySpec_.empty());
CHECK(attrCoarraySpec_.empty());
} }
void ArraySpecVisitor::EndArraySpec() { void ArraySpecVisitor::EndArraySpec() {
CHECK(arraySpec_.empty()); CHECK(arraySpec_.empty());
CHECK(coarraySpec_.empty());
attrArraySpec_.clear(); attrArraySpec_.clear();
attrCoarraySpec_.clear();
} }
void ArraySpecVisitor::PostAttrSpec() { void ArraySpecVisitor::PostAttrSpec() {
// Save dimension/codimension from attrs so we can process array/coarray-spec
// on the entity-decl
if (!arraySpec_.empty()) { if (!arraySpec_.empty()) {
// Example: integer, dimension(<1>) :: x(<2>)
// This saves <1> in attrArraySpec_ so we can process <2> into arraySpec_
CHECK(attrArraySpec_.empty()); CHECK(attrArraySpec_.empty());
attrArraySpec_.splice(attrArraySpec_.cbegin(), arraySpec_); attrArraySpec_.splice(attrArraySpec_.cbegin(), arraySpec_);
CHECK(arraySpec_.empty());
} }
} if (!coarraySpec_.empty()) {
CHECK(attrCoarraySpec_.empty());
Bound ArraySpecVisitor::GetBound(const parser::SpecificationExpr &x) { attrCoarraySpec_.splice(attrCoarraySpec_.cbegin(), coarraySpec_);
return Bound{EvaluateSubscriptIntExpr(x.v)}; }
} }
// ScopeHandler implementation // ScopeHandler implementation
@ -2554,6 +2529,11 @@ void DeclarationVisitor::Post(const parser::DimensionStmt::Declaration &x) {
const auto &name{std::get<parser::Name>(x.t)}; const auto &name{std::get<parser::Name>(x.t)};
DeclareObjectEntity(name, Attrs{}); DeclareObjectEntity(name, Attrs{});
} }
void DeclarationVisitor::Post(const parser::CodimensionDecl &x) {
const auto &name{std::get<parser::Name>(x.t)};
DeclareObjectEntity(name, Attrs{});
}
//TODO: ChangeTeamStmt also uses CodimensionDecl
void DeclarationVisitor::Post(const parser::EntityDecl &x) { void DeclarationVisitor::Post(const parser::EntityDecl &x) {
// TODO: may be under StructureStmt // TODO: may be under StructureStmt
@ -2703,7 +2683,7 @@ void DeclarationVisitor::Post(const parser::ObjectDecl &x) {
// Declare an entity not yet known to be an object or proc. // Declare an entity not yet known to be an object or proc.
Symbol &DeclarationVisitor::DeclareUnknownEntity( Symbol &DeclarationVisitor::DeclareUnknownEntity(
const parser::Name &name, Attrs attrs) { const parser::Name &name, Attrs attrs) {
if (!arraySpec().empty()) { if (!arraySpec().empty() || !coarraySpec().empty()) {
return DeclareObjectEntity(name, attrs); return DeclareObjectEntity(name, attrs);
} else { } else {
Symbol &symbol{DeclareEntity<EntityDetails>(name, attrs)}; Symbol &symbol{DeclareEntity<EntityDetails>(name, attrs)};
@ -2752,6 +2732,15 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
} }
ClearArraySpec(); ClearArraySpec();
} }
if (!coarraySpec().empty()) {
if (details->IsCoarray()) {
Say(name,
"The codimensions of '%s' have already been declared"_err_en_US);
} else {
details->set_coshape(coarraySpec());
}
ClearCoarraySpec();
}
SetBindNameOn(symbol); SetBindNameOn(symbol);
} }
return symbol; return symbol;
@ -3110,6 +3099,7 @@ void DeclarationVisitor::Post(const parser::ComponentDecl &x) {
currScope().symbol()->get<DerivedTypeDetails>().add_component(symbol); currScope().symbol()->get<DerivedTypeDetails>().add_component(symbol);
} }
ClearArraySpec(); ClearArraySpec();
ClearCoarraySpec();
} }
bool DeclarationVisitor::Pre(const parser::ProcedureDeclarationStmt &) { bool DeclarationVisitor::Pre(const parser::ProcedureDeclarationStmt &) {
CHECK(!interfaceName_); CHECK(!interfaceName_);
@ -3414,6 +3404,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
const auto &name{std::get<parser::Name>(x.t)}; const auto &name{std::get<parser::Name>(x.t)};
auto &symbol{DeclareObjectEntity(name, Attrs{})}; auto &symbol{DeclareObjectEntity(name, Attrs{})};
ClearArraySpec(); ClearArraySpec();
ClearCoarraySpec();
auto *details{symbol.detailsIf<ObjectEntityDetails>()}; auto *details{symbol.detailsIf<ObjectEntityDetails>()};
if (!details) { if (!details) {
return; // error was reported return; // error was reported

View File

@ -26,6 +26,52 @@ std::ostream &operator<<(std::ostream &os, const parser::CharBlock &name) {
return os << name.ToString(); return os << name.ToString();
} }
template<typename T>
static void DumpOptional(
std::ostream &os, const char *label, const std::optional<T> &x) {
if (x) {
os << ' ' << label << ':' << *x;
}
}
static void DumpBool(std::ostream &os, const char *label, bool x) {
if (x) {
os << ' ' << label;
}
}
static void DumpSymbolList(std::ostream &os, const SymbolList &list) {
char sep{' '};
for (const auto *elem : list) {
os << sep << elem->name();
sep = ',';
}
}
static void DumpType(std::ostream &os, const Symbol &symbol) {
if (const auto *type{symbol.GetType()}) {
os << *type << ' ';
}
}
static void DumpType(std::ostream &os, const DeclTypeSpec *type) {
if (type) {
os << ' ' << *type;
}
}
template<typename T>
static void DumpList(
std::ostream &os, const char *label, const std::list<T> &list) {
if (!list.empty()) {
os << ' ' << label << ':';
char sep{' '};
for (const auto &elem : list) {
os << sep << elem;
sep = ',';
}
}
}
const Scope *ModuleDetails::parent() const { const Scope *ModuleDetails::parent() const {
return isSubmodule_ && scope_ ? &scope_->parent() : nullptr; return isSubmodule_ && scope_ ? &scope_->parent() : nullptr;
} }
@ -49,12 +95,8 @@ void ModuleDetails::set_scope(const Scope *scope) {
} }
std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) { std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) {
if (x.isInterface_) { DumpBool(os, "isInterface", x.isInterface_);
os << " isInterface"; DumpOptional(os, "bindName", x.bindName_);
}
if (x.bindName_) {
os << " bindName:" << x.bindName_;
}
if (x.result_) { if (x.result_) {
os << " result:" << x.result_->name(); os << " result:" << x.result_->name();
if (!x.result_->attrs().empty()) { if (!x.result_->attrs().empty()) {
@ -86,6 +128,12 @@ void ObjectEntityDetails::set_shape(const ArraySpec &shape) {
shape_.push_back(shapeSpec); shape_.push_back(shapeSpec);
} }
} }
void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) {
CHECK(coshape_.empty());
for (const auto &shapeSpec : coshape) {
coshape_.push_back(shapeSpec);
}
}
ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) { ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) {
if (type()) { if (type()) {
@ -268,75 +316,43 @@ ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
: EntityDetails(d) {} : EntityDetails(d) {}
std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { std::ostream &operator<<(std::ostream &os, const EntityDetails &x) {
if (x.isDummy()) { DumpBool(os, "dummy", x.isDummy());
os << " dummy"; DumpBool(os, "funcResult", x.isFuncResult());
}
if (x.isFuncResult()) {
os << " funcResult";
}
if (x.type()) { if (x.type()) {
os << " type: " << *x.type(); os << " type: " << *x.type();
} }
if (x.bindName_) { DumpOptional(os, "bindName", x.bindName_);
os << " bindName:" << x.bindName_;
}
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) {
os << *static_cast<const EntityDetails *>(&x); os << *static_cast<const EntityDetails *>(&x);
if (!x.shape().empty()) { DumpList(os, "shape", x.shape());
os << " shape:"; DumpList(os, "coshape", x.coshape());
for (const auto &s : x.shape()) { DumpOptional(os, "init", x.init_);
os << ' ' << s;
}
}
if (x.init_) {
os << " init:" << x.init_;
}
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) { std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) {
os << *static_cast<const EntityDetails *>(&x); os << *static_cast<const EntityDetails *>(&x);
if (x.expr().has_value()) { DumpOptional(os, "expr", x.expr());
os << ' ' << x.expr();
}
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) {
if (auto *symbol{x.interface_.symbol()}) { if (auto *symbol{x.interface_.symbol()}) {
os << ' ' << symbol->name(); os << ' ' << symbol->name();
} else if (auto *type{x.interface_.type()}) { } else {
os << ' ' << *type; DumpType(os, x.interface_.type());
}
if (x.bindName()) {
os << " bindName:" << x.bindName();
}
if (x.passName_) {
os << " passName:" << *x.passName_;
} }
DumpOptional(os, "bindName", x.bindName());
DumpOptional(os, "passName", x.passName());
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) { std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) {
if (x.sequence_) { DumpBool(os, "sequence", x.sequence_);
os << " sequence"; DumpList(os, "components", x.componentNames_);
}
if (!x.componentNames_.empty()) {
os << " components:";
for (auto name : x.componentNames_) {
os << ' ' << name.ToString();
}
}
return os;
}
static std::ostream &DumpType(std::ostream &os, const Symbol &symbol) {
if (const auto *type{symbol.GetType()}) {
os << *type << ' ';
}
return os; return os;
} }
@ -371,17 +387,13 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
os << dummy->name(); os << dummy->name();
} }
os << ')'; os << ')';
if (x.bindName()) { DumpOptional(os, "bindName", x.bindName());
os << " bindName:" << x.bindName();
}
if (x.isFunction()) { if (x.isFunction()) {
os << " result("; os << " result(";
DumpType(os, x.result()); DumpType(os, x.result());
os << x.result().name() << ')'; os << x.result().name() << ')';
} }
if (x.isInterface()) { DumpBool(os, "interface", x.isInterface());
os << " interface";
}
}, },
[&](const SubprogramNameDetails &x) { [&](const SubprogramNameDetails &x) {
os << ' ' << EnumToString(x.kind()); os << ' ' << EnumToString(x.kind());
@ -398,29 +410,19 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
[](const HostAssocDetails &) {}, [](const HostAssocDetails &) {},
[&](const GenericDetails &x) { [&](const GenericDetails &x) {
os << ' ' << EnumToString(x.kind()); os << ' ' << EnumToString(x.kind());
for (const auto *proc : x.specificProcs()) { DumpSymbolList(os, x.specificProcs());
os << ' ' << proc->name();
}
}, },
[&](const ProcBindingDetails &x) { [&](const ProcBindingDetails &x) {
os << " => " << x.symbol().name(); os << " => " << x.symbol().name();
if (x.passName()) { DumpOptional(os, "passName", x.passName());
os << " passName:" << *x.passName();
}
}, },
[&](const GenericBindingDetails &x) { [&](const GenericBindingDetails &x) {
os << " =>"; os << " =>";
char sep{' '}; DumpSymbolList(os, x.specificProcs());
for (const auto *proc : x.specificProcs()) {
os << sep << proc->name();
sep = ',';
}
}, },
[&](const NamelistDetails &x) { [&](const NamelistDetails &x) {
os << ':'; os << ':';
for (const auto *object : x.objects()) { DumpSymbolList(os, x.objects());
os << ' ' << object->name();
}
}, },
[&](const CommonBlockDetails &x) { [&](const CommonBlockDetails &x) {
os << ':'; os << ':';
@ -434,9 +436,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
os << ' ' << *x.type(); os << ' ' << *x.type();
} }
os << ' ' << common::EnumToString(x.attr()); os << ' ' << common::EnumToString(x.attr());
if (x.init()) { DumpOptional(os, "init", x.init());
os << " init:" << x.init();
}
}, },
[&](const MiscDetails &x) { [&](const MiscDetails &x) {
os << ' ' << MiscDetails::EnumToString(x.kind()); os << ' ' << MiscDetails::EnumToString(x.kind());
@ -512,22 +512,12 @@ std::ostream &DumpForUnparse(
if (!symbol.attrs().empty()) { if (!symbol.attrs().empty()) {
os << ' ' << symbol.attrs(); os << ' ' << symbol.attrs();
} }
if (symbol.test(Symbol::Flag::Implicit)) { DumpBool(os, "(implicit)", symbol.test(Symbol::Flag::Implicit));
os << " (implicit)"; DumpBool(os, "(local)", symbol.test(Symbol::Flag::LocalityLocal));
} DumpBool(os, "(local_init)", symbol.test(Symbol::Flag::LocalityLocalInit));
if (symbol.test(Symbol::Flag::LocalityLocal)) { DumpBool(os, "(shared)", symbol.test(Symbol::Flag::LocalityShared));
os << " (local)";
}
if (symbol.test(Symbol::Flag::LocalityLocalInit)) {
os << " (local_init)";
}
if (symbol.test(Symbol::Flag::LocalityShared)) {
os << " (shared)";
}
os << ' ' << symbol.GetDetailsName(); os << ' ' << symbol.GetDetailsName();
if (const auto *type{symbol.GetType()}) { DumpType(os, symbol.GetType());
os << ' ' << *type;
}
} }
return os; return os;
} }
@ -591,7 +581,16 @@ Symbol &Symbol::Instantiate(
foldingContext, std::move(dim.ubound().GetExplicit()))); foldingContext, std::move(dim.ubound().GetExplicit())));
} }
} }
// TODO: fold cobounds too once we can represent them for (ShapeSpec &dim : details.coshape()) {
if (dim.lbound().isExplicit()) {
dim.lbound().SetExplicit(Fold(
foldingContext, std::move(dim.lbound().GetExplicit())));
}
if (dim.ubound().isExplicit()) {
dim.ubound().SetExplicit(Fold(
foldingContext, std::move(dim.ubound().GetExplicit())));
}
}
}, },
[&](const ProcBindingDetails &that) { symbol.details_ = that; }, [&](const ProcBindingDetails &that) { symbol.details_ = that; },
[&](const GenericBindingDetails &that) { symbol.details_ = that; }, [&](const GenericBindingDetails &that) { symbol.details_ = that; },
@ -698,4 +697,5 @@ void TypeParamDetails::set_type(const DeclTypeSpec &type) {
CHECK(type_ == nullptr); CHECK(type_ == nullptr);
type_ = &type; type_ = &type;
} }
} }

View File

@ -150,10 +150,14 @@ public:
void set_init(MaybeExpr &&expr) { init_ = std::move(expr); } void set_init(MaybeExpr &&expr) { init_ = std::move(expr); }
ArraySpec &shape() { return shape_; } ArraySpec &shape() { return shape_; }
const ArraySpec &shape() const { return shape_; } const ArraySpec &shape() const { return shape_; }
void set_shape(const ArraySpec &shape); ArraySpec &coshape() { return coshape_; }
const ArraySpec &coshape() const { return coshape_; }
void set_shape(const ArraySpec &);
void set_coshape(const ArraySpec &);
const Symbol *commonBlock() const { return commonBlock_; } const Symbol *commonBlock() const { return commonBlock_; }
void set_commonBlock(const Symbol &commonBlock) { commonBlock_ = &commonBlock; } void set_commonBlock(const Symbol &commonBlock) { commonBlock_ = &commonBlock; }
bool IsArray() const { return !shape_.empty(); } bool IsArray() const { return !shape_.empty(); }
bool IsCoarray() const { return !coshape_.empty(); }
bool IsAssumedShape() const { bool IsAssumedShape() const {
return isDummy() && IsArray() && shape_.back().ubound().isDeferred() && return isDummy() && IsArray() && shape_.back().ubound().isDeferred() &&
!shape_.back().lbound().isDeferred(); !shape_.back().lbound().isDeferred();
@ -174,6 +178,7 @@ public:
private: private:
MaybeExpr init_; MaybeExpr init_;
ArraySpec shape_; ArraySpec shape_;
ArraySpec coshape_;
const Symbol *commonBlock_{nullptr}; // common block this object is in const Symbol *commonBlock_{nullptr}; // common block this object is in
friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &); friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &);
}; };
@ -529,11 +534,7 @@ public:
return std::visit( return std::visit(
common::visitors{ common::visitors{
[](const SubprogramDetails &sd) { [](const SubprogramDetails &sd) {
if (sd.isFunction()) { return sd.isFunction() ? sd.result().Rank() : 0;
return sd.result().Rank();
} else {
return 0;
}
}, },
[](const GenericDetails &) { [](const GenericDetails &) {
return 0; /*TODO*/ return 0; /*TODO*/
@ -548,6 +549,25 @@ public:
details_); details_);
} }
int Corank() const {
return std::visit(
common::visitors{
[](const SubprogramDetails &sd) {
return sd.isFunction() ? sd.result().Corank() : 0;
},
[](const GenericDetails &) {
return 0; /*TODO*/
},
[](const UseDetails &x) { return x.symbol().Corank(); },
[](const HostAssocDetails &x) { return x.symbol().Corank(); },
[](const ObjectEntityDetails &oed) {
return static_cast<int>(oed.coshape().size());
},
[](const auto &) { return 0; },
},
details_);
}
// Clones the Symbol in the context of a parameterized derived type instance // Clones the Symbol in the context of a parameterized derived type instance
Symbol &Instantiate(Scope &, SemanticsContext &) const; Symbol &Instantiate(Scope &, SemanticsContext &) const;

View File

@ -134,6 +134,7 @@ set(MODFILE_TESTS
modfile21.f90 modfile21.f90
modfile22.f90 modfile22.f90
modfile23.f90 modfile23.f90
modfile24.f90
) )
set(LABEL_TESTS set(LABEL_TESTS

View File

@ -0,0 +1,88 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! Test declarations with coarray-spec
! Different ways of declaring the same coarray.
module m1
real :: a(1:5)[1:10,1:*]
real, dimension(5) :: b[1:10,1:*]
real, codimension[1:10,1:*] :: c(5)
real, codimension[1:10,1:*], dimension(5) :: d
codimension :: e[1:10,1:*]
dimension :: e(5)
real :: e
end
!Expect: m1.mod
!module m1
! real(4)::a(1_8:5_8)[1_8:10_8,1_8:*]
! real(4)::b(1_8:5_8)[1_8:10_8,1_8:*]
! real(4)::c(1_8:5_8)[1_8:10_8,1_8:*]
! real(4)::d(1_8:5_8)[1_8:10_8,1_8:*]
! real(4)::e(1_8:5_8)[1_8:10_8,1_8:*]
!end
! coarray-spec in codimension and target statements.
module m2
codimension :: a[10,*], b[*]
target :: c[10,*], d[*]
end
!Expect: m2.mod
!module m2
! real(4)::a[1_8:10_8,1_8:*]
! real(4)::b[1_8:*]
! real(4),target::c[1_8:10_8,1_8:*]
! real(4),target::d[1_8:*]
!end
! coarray-spec in components and with non-constants bounds
module m3
type t
real :: c(1:5)[1:10,1:*]
complex, codimension[5,*] :: d
end type
real, allocatable :: e[:,:,:]
contains
subroutine s(a, b, n)
integer(8) :: n
real :: a[1:n,2:*]
real, codimension[1:n,2:*] :: b
end
end
!Expect: m3.mod
!module m3
! type::t
! real(4)::c[1_8:10_8,1_8:*]
! complex(4)::d[1_8:5_8,1_8:*]
! end type
! real(4),allocatable::e[:,:,:]
!contains
! subroutine s(a,b,n)
! integer(8)::n
! real(4)::a[1_8:n,2_8:*]
! real(4)::b[1_8:n,2_8:*]
! end
!end
! coarray-spec in both attributes and entity-decl
module m4
real, codimension[2:*], dimension(2:5) :: a, b(4,4), c[10,*], d(4,4)[10,*]
end
!Expect: m4.mod
!module m4
! real(4)::a(2_8:5_8)[2_8:*]
! real(4)::b(1_8:4_8,1_8:4_8)[2_8:*]
! real(4)::c(2_8:5_8)[1_8:10_8,1_8:*]
! real(4)::d(1_8:4_8,1_8:4_8)[1_8:10_8,1_8:*]
!end

View File

@ -16,18 +16,27 @@ subroutine s1
integer :: x(2) integer :: x(2)
!ERROR: The dimensions of 'x' have already been declared !ERROR: The dimensions of 'x' have already been declared
allocatable :: x(:) allocatable :: x(:)
real :: y[1:*]
!ERROR: The codimensions of 'y' have already been declared
allocatable :: y[:]
end end
subroutine s2 subroutine s2
target :: x(1) target :: x(1)
!ERROR: The dimensions of 'x' have already been declared !ERROR: The dimensions of 'x' have already been declared
integer :: x(2) integer :: x(2)
target :: y[1:*]
!ERROR: The codimensions of 'y' have already been declared
integer :: y[2:*]
end end
subroutine s3 subroutine s3
dimension :: x(4), y(8) dimension :: x(4), x2(8)
!ERROR: The dimensions of 'x' have already been declared !ERROR: The dimensions of 'x' have already been declared
allocatable :: x(:) allocatable :: x(:)
codimension :: y[*], y2[1:2,2:*]
!ERROR: The codimensions of 'y' have already been declared
allocatable :: y[:]
end end
subroutine s4 subroutine s4