From: Tim Keith Date: Thu, 4 Apr 2019 21:46:40 +0000 (-0700) Subject: [flang] Support coarrays in name resolution X-Git-Tag: llvmorg-12-init~9537^2~1449 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b7efa8b77b11d99dce6afd98227e676594f4cd6e;p=platform%2Fupstream%2Fllvm.git [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@9d2482c40c78cad55701a9cbc52a2294cae94d44 Reviewed-on: https://github.com/flang-compiler/f18/pull/384 Tree-same-pre-rewrite: false --- diff --git a/flang/lib/semantics/mod-file.cc b/flang/lib/semantics/mod-file.cc index d0c0500..2fc07a8 100644 --- a/flang/lib/semantics/mod-file.cc +++ b/flang/lib/semantics/mod-file.cc @@ -424,7 +424,7 @@ void PutEntity(std::ostream &os, const Symbol &symbol) { } void PutShapeSpec(std::ostream &os, const ShapeSpec &x) { - if (x.ubound().isAssumed()) { + if (x.lbound().isAssumed()) { CHECK(x.ubound().isAssumed()); os << ".."; } 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()) { - os << '('; + os << open; bool first{true}; for (const auto &shapeSpec : shape) { if (first) { @@ -449,7 +449,7 @@ void PutShape(std::ostream &os, const ArraySpec &shape) { } PutShapeSpec(os, shapeSpec); } - os << ')'; + os << close; } } @@ -460,7 +460,8 @@ void PutObjectEntity(std::ostream &os, const Symbol &symbol) { CHECK(type); PutLower(os, *type); }); - PutShape(os, details.shape()); + PutShape(os, details.shape(), '(', ')'); + PutShape(os, details.coshape(), '[', ']'); PutInit(os, details.init()); } @@ -816,6 +817,10 @@ void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) { DoBound(spec.lbound()); DoBound(spec.ubound()); } + for (const ShapeSpec &spec : details.coshape()) { + DoBound(spec.lbound()); + DoBound(spec.ubound()); + } if (const Symbol * commonBlock{details.commonBlock()}) { DoSymbol(*commonBlock); } diff --git a/flang/lib/semantics/resolve-names-utils.cc b/flang/lib/semantics/resolve-names-utils.cc index ef30339..3056f9d 100644 --- a/flang/lib/semantics/resolve-names-utils.cc +++ b/flang/lib/semantics/resolve-names-utils.cc @@ -13,10 +13,12 @@ // limitations under the License. #include "resolve-names-utils.h" +#include "expression.h" #include "semantics.h" -#include "symbol.h" -#include "type.h" #include "../common/idioms.h" +#include "../evaluate/fold.h" +#include "../evaluate/tools.h" +#include "../evaluate/type.h" #include "../parser/char-block.h" #include "../parser/features.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 void Analyze(const std::list &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 &, + const parser::SpecificationExpr &); + void MakeImplied(const std::optional &); + void MakeDeferred(int); + Bound GetBound(const std::optional &); + 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>(y.t)); + Analyze(std::get(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>(y.t)); + MakeImplied( + std::get>(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>(x.t), + std::get(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 &lb, + const parser::SpecificationExpr &ub) { + arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub))); +} +void ArraySpecAnalyzer::MakeImplied( + const std::optional &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 &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(*maybeExpr)}) { + expr = evaluate::Fold(context_.foldingContext(), + evaluate::ConvertToType( + std::move(*intExpr))); + } + } + return Bound{std::move(expr)}; +} + } diff --git a/flang/lib/semantics/resolve-names-utils.h b/flang/lib/semantics/resolve-names-utils.h index a660667..0f210d2 100644 --- a/flang/lib/semantics/resolve-names-utils.h +++ b/flang/lib/semantics/resolve-names-utils.h @@ -18,10 +18,13 @@ // Utility functions and class for use in resolve-names.cc. #include "symbol.h" +#include "type.h" #include "../parser/message.h" namespace Fortran::parser { class CharBlock; +struct ArraySpec; +struct CoarraySpec; struct DefinedOpName; struct GenericSpec; struct Name; @@ -30,6 +33,7 @@ struct Name; namespace Fortran::semantics { using SourceName = parser::CharBlock; +class SemanticsContext; // Record that a Name has been resolved to a Symbol Symbol &Resolve(const parser::Name &, Symbol &); @@ -64,6 +68,12 @@ private: 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_ diff --git a/flang/lib/semantics/resolve-names.cc b/flang/lib/semantics/resolve-names.cc index 2cd93d2..47328a4 100644 --- a/flang/lib/semantics/resolve-names.cc +++ b/flang/lib/semantics/resolve-names.cc @@ -353,30 +353,29 @@ private: // 6. TODO: BasedPointerStmt class ArraySpecVisitor : public virtual BaseVisitor { 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::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: const ArraySpec &arraySpec(); + const ArraySpec &coarraySpec(); void BeginArraySpec(); void EndArraySpec(); void ClearArraySpec() { arraySpec_.clear(); } + void ClearCoarraySpec() { coarraySpec_.clear(); } private: - // arraySpec_ is populated by any ArraySpec + // arraySpec_/coarraySpec_ are populated from any ArraySpec/CoarraySpec ArraySpec arraySpec_; + ArraySpec coarraySpec_; // When an ArraySpec is under an AttrSpec or ComponentAttrSpec, it is moved // into attrArraySpec_ ArraySpec attrArraySpec_; + ArraySpec attrCoarraySpec_; void PostAttrSpec(); - Bound GetBound(const parser::SpecificationExpr &); }; // Manage a stack of Scopes @@ -649,7 +648,6 @@ class DeclarationVisitor : public ArraySpecVisitor, public virtual ScopeHandler { public: using ArraySpecVisitor::Post; - using ArraySpecVisitor::Pre; using ScopeHandler::Post; using ScopeHandler::Pre; @@ -681,6 +679,7 @@ public: } void Post(const parser::TargetStmt &) { objectDeclAttr_ = std::nullopt; } void Post(const parser::DimensionStmt::Declaration &); + void Post(const parser::CodimensionDecl &); bool Pre(const parser::TypeDeclarationStmt &) { return BeginDecl(); } void Post(const parser::TypeDeclarationStmt &) { EndDecl(); } void Post(const parser::IntegerTypeSpec &); @@ -952,7 +951,6 @@ class ResolveNamesVisitor : public virtual ScopeHandler, public ConstructVisitor { public: using ArraySpecVisitor::Post; - using ArraySpecVisitor::Pre; using ConstructVisitor::Post; using ConstructVisitor::Pre; using DeclarationVisitor::Post; @@ -1420,65 +1418,42 @@ bool ImplicitRulesVisitor::HandleImplicitNone( // ArraySpecVisitor implementation -bool ArraySpecVisitor::Pre(const parser::ArraySpec &x) { - CHECK(arraySpec_.empty()); - return true; +void ArraySpecVisitor::Post(const parser::ArraySpec &x) { + AnalyzeArraySpec(arraySpec_, context(), x); } - -void ArraySpecVisitor::Post(const parser::DeferredShapeSpecList &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(x.t))}; - if (const auto &lb{std::get>(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()); +void ArraySpecVisitor::Post(const parser::CoarraySpec &x) { + AnalyzeCoarraySpec(coarraySpec_, context(), x); } const ArraySpec &ArraySpecVisitor::arraySpec() { return !arraySpec_.empty() ? arraySpec_ : attrArraySpec_; } +const ArraySpec &ArraySpecVisitor::coarraySpec() { + return !coarraySpec_.empty() ? coarraySpec_ : attrCoarraySpec_; +} void ArraySpecVisitor::BeginArraySpec() { CHECK(arraySpec_.empty()); + CHECK(coarraySpec_.empty()); CHECK(attrArraySpec_.empty()); + CHECK(attrCoarraySpec_.empty()); } void ArraySpecVisitor::EndArraySpec() { CHECK(arraySpec_.empty()); + CHECK(coarraySpec_.empty()); attrArraySpec_.clear(); + attrCoarraySpec_.clear(); } void ArraySpecVisitor::PostAttrSpec() { + // Save dimension/codimension from attrs so we can process array/coarray-spec + // on the entity-decl if (!arraySpec_.empty()) { - // Example: integer, dimension(<1>) :: x(<2>) - // This saves <1> in attrArraySpec_ so we can process <2> into arraySpec_ CHECK(attrArraySpec_.empty()); attrArraySpec_.splice(attrArraySpec_.cbegin(), arraySpec_); - CHECK(arraySpec_.empty()); } -} - -Bound ArraySpecVisitor::GetBound(const parser::SpecificationExpr &x) { - return Bound{EvaluateSubscriptIntExpr(x.v)}; + if (!coarraySpec_.empty()) { + CHECK(attrCoarraySpec_.empty()); + attrCoarraySpec_.splice(attrCoarraySpec_.cbegin(), coarraySpec_); + } } // ScopeHandler implementation @@ -2554,6 +2529,11 @@ void DeclarationVisitor::Post(const parser::DimensionStmt::Declaration &x) { const auto &name{std::get(x.t)}; DeclareObjectEntity(name, Attrs{}); } +void DeclarationVisitor::Post(const parser::CodimensionDecl &x) { + const auto &name{std::get(x.t)}; + DeclareObjectEntity(name, Attrs{}); +} +//TODO: ChangeTeamStmt also uses CodimensionDecl void DeclarationVisitor::Post(const parser::EntityDecl &x) { // 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. Symbol &DeclarationVisitor::DeclareUnknownEntity( const parser::Name &name, Attrs attrs) { - if (!arraySpec().empty()) { + if (!arraySpec().empty() || !coarraySpec().empty()) { return DeclareObjectEntity(name, attrs); } else { Symbol &symbol{DeclareEntity(name, attrs)}; @@ -2752,6 +2732,15 @@ Symbol &DeclarationVisitor::DeclareObjectEntity( } 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); } return symbol; @@ -3110,6 +3099,7 @@ void DeclarationVisitor::Post(const parser::ComponentDecl &x) { currScope().symbol()->get().add_component(symbol); } ClearArraySpec(); + ClearCoarraySpec(); } bool DeclarationVisitor::Pre(const parser::ProcedureDeclarationStmt &) { CHECK(!interfaceName_); @@ -3414,6 +3404,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) { const auto &name{std::get(x.t)}; auto &symbol{DeclareObjectEntity(name, Attrs{})}; ClearArraySpec(); + ClearCoarraySpec(); auto *details{symbol.detailsIf()}; if (!details) { return; // error was reported diff --git a/flang/lib/semantics/symbol.cc b/flang/lib/semantics/symbol.cc index b474670..f7cdd27 100644 --- a/flang/lib/semantics/symbol.cc +++ b/flang/lib/semantics/symbol.cc @@ -26,6 +26,52 @@ std::ostream &operator<<(std::ostream &os, const parser::CharBlock &name) { return os << name.ToString(); } +template +static void DumpOptional( + std::ostream &os, const char *label, const std::optional &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 +static void DumpList( + std::ostream &os, const char *label, const std::list &list) { + if (!list.empty()) { + os << ' ' << label << ':'; + char sep{' '}; + for (const auto &elem : list) { + os << sep << elem; + sep = ','; + } + } +} + const Scope *ModuleDetails::parent() const { 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) { - if (x.isInterface_) { - os << " isInterface"; - } - if (x.bindName_) { - os << " bindName:" << x.bindName_; - } + DumpBool(os, "isInterface", x.isInterface_); + DumpOptional(os, "bindName", x.bindName_); if (x.result_) { os << " result:" << x.result_->name(); if (!x.result_->attrs().empty()) { @@ -86,6 +128,12 @@ void ObjectEntityDetails::set_shape(const ArraySpec &shape) { 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) { if (type()) { @@ -268,75 +316,43 @@ ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d) : EntityDetails(d) {} std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { - if (x.isDummy()) { - os << " dummy"; - } - if (x.isFuncResult()) { - os << " funcResult"; - } + DumpBool(os, "dummy", x.isDummy()); + DumpBool(os, "funcResult", x.isFuncResult()); if (x.type()) { os << " type: " << *x.type(); } - if (x.bindName_) { - os << " bindName:" << x.bindName_; - } + DumpOptional(os, "bindName", x.bindName_); return os; } std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { os << *static_cast(&x); - if (!x.shape().empty()) { - os << " shape:"; - for (const auto &s : x.shape()) { - os << ' ' << s; - } - } - if (x.init_) { - os << " init:" << x.init_; - } + DumpList(os, "shape", x.shape()); + DumpList(os, "coshape", x.coshape()); + DumpOptional(os, "init", x.init_); return os; } std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) { os << *static_cast(&x); - if (x.expr().has_value()) { - os << ' ' << x.expr(); - } + DumpOptional(os, "expr", x.expr()); return os; } std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { if (auto *symbol{x.interface_.symbol()}) { os << ' ' << symbol->name(); - } else if (auto *type{x.interface_.type()}) { - os << ' ' << *type; - } - if (x.bindName()) { - os << " bindName:" << x.bindName(); - } - if (x.passName_) { - os << " passName:" << *x.passName_; + } else { + DumpType(os, x.interface_.type()); } + DumpOptional(os, "bindName", x.bindName()); + DumpOptional(os, "passName", x.passName()); return os; } std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) { - if (x.sequence_) { - os << " sequence"; - } - 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 << ' '; - } + DumpBool(os, "sequence", x.sequence_); + DumpList(os, "components", x.componentNames_); return os; } @@ -371,17 +387,13 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { os << dummy->name(); } os << ')'; - if (x.bindName()) { - os << " bindName:" << x.bindName(); - } + DumpOptional(os, "bindName", x.bindName()); if (x.isFunction()) { os << " result("; DumpType(os, x.result()); os << x.result().name() << ')'; } - if (x.isInterface()) { - os << " interface"; - } + DumpBool(os, "interface", x.isInterface()); }, [&](const SubprogramNameDetails &x) { os << ' ' << EnumToString(x.kind()); @@ -398,29 +410,19 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { [](const HostAssocDetails &) {}, [&](const GenericDetails &x) { os << ' ' << EnumToString(x.kind()); - for (const auto *proc : x.specificProcs()) { - os << ' ' << proc->name(); - } + DumpSymbolList(os, x.specificProcs()); }, [&](const ProcBindingDetails &x) { os << " => " << x.symbol().name(); - if (x.passName()) { - os << " passName:" << *x.passName(); - } + DumpOptional(os, "passName", x.passName()); }, [&](const GenericBindingDetails &x) { os << " =>"; - char sep{' '}; - for (const auto *proc : x.specificProcs()) { - os << sep << proc->name(); - sep = ','; - } + DumpSymbolList(os, x.specificProcs()); }, [&](const NamelistDetails &x) { os << ':'; - for (const auto *object : x.objects()) { - os << ' ' << object->name(); - } + DumpSymbolList(os, x.objects()); }, [&](const CommonBlockDetails &x) { os << ':'; @@ -434,9 +436,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { os << ' ' << *x.type(); } os << ' ' << common::EnumToString(x.attr()); - if (x.init()) { - os << " init:" << x.init(); - } + DumpOptional(os, "init", x.init()); }, [&](const MiscDetails &x) { os << ' ' << MiscDetails::EnumToString(x.kind()); @@ -512,22 +512,12 @@ std::ostream &DumpForUnparse( if (!symbol.attrs().empty()) { os << ' ' << symbol.attrs(); } - if (symbol.test(Symbol::Flag::Implicit)) { - os << " (implicit)"; - } - if (symbol.test(Symbol::Flag::LocalityLocal)) { - os << " (local)"; - } - if (symbol.test(Symbol::Flag::LocalityLocalInit)) { - os << " (local_init)"; - } - if (symbol.test(Symbol::Flag::LocalityShared)) { - os << " (shared)"; - } + DumpBool(os, "(implicit)", symbol.test(Symbol::Flag::Implicit)); + DumpBool(os, "(local)", symbol.test(Symbol::Flag::LocalityLocal)); + DumpBool(os, "(local_init)", symbol.test(Symbol::Flag::LocalityLocalInit)); + DumpBool(os, "(shared)", symbol.test(Symbol::Flag::LocalityShared)); os << ' ' << symbol.GetDetailsName(); - if (const auto *type{symbol.GetType()}) { - os << ' ' << *type; - } + DumpType(os, symbol.GetType()); } return os; } @@ -591,7 +581,16 @@ Symbol &Symbol::Instantiate( 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 GenericBindingDetails &that) { symbol.details_ = that; }, @@ -698,4 +697,5 @@ void TypeParamDetails::set_type(const DeclTypeSpec &type) { CHECK(type_ == nullptr); type_ = &type; } + } diff --git a/flang/lib/semantics/symbol.h b/flang/lib/semantics/symbol.h index f94fd9f..6a7f28c 100644 --- a/flang/lib/semantics/symbol.h +++ b/flang/lib/semantics/symbol.h @@ -150,10 +150,14 @@ public: void set_init(MaybeExpr &&expr) { init_ = std::move(expr); } ArraySpec &shape() { 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_; } void set_commonBlock(const Symbol &commonBlock) { commonBlock_ = &commonBlock; } bool IsArray() const { return !shape_.empty(); } + bool IsCoarray() const { return !coshape_.empty(); } bool IsAssumedShape() const { return isDummy() && IsArray() && shape_.back().ubound().isDeferred() && !shape_.back().lbound().isDeferred(); @@ -174,6 +178,7 @@ public: private: MaybeExpr init_; ArraySpec shape_; + ArraySpec coshape_; const Symbol *commonBlock_{nullptr}; // common block this object is in friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &); }; @@ -529,11 +534,7 @@ public: return std::visit( common::visitors{ [](const SubprogramDetails &sd) { - if (sd.isFunction()) { - return sd.result().Rank(); - } else { - return 0; - } + return sd.isFunction() ? sd.result().Rank() : 0; }, [](const GenericDetails &) { return 0; /*TODO*/ @@ -548,6 +549,25 @@ public: 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(oed.coshape().size()); + }, + [](const auto &) { return 0; }, + }, + details_); + } + // Clones the Symbol in the context of a parameterized derived type instance Symbol &Instantiate(Scope &, SemanticsContext &) const; diff --git a/flang/test/semantics/CMakeLists.txt b/flang/test/semantics/CMakeLists.txt index 352f4a9..c63b9b1 100644 --- a/flang/test/semantics/CMakeLists.txt +++ b/flang/test/semantics/CMakeLists.txt @@ -134,6 +134,7 @@ set(MODFILE_TESTS modfile21.f90 modfile22.f90 modfile23.f90 + modfile24.f90 ) set(LABEL_TESTS diff --git a/flang/test/semantics/modfile24.f90 b/flang/test/semantics/modfile24.f90 new file mode 100644 index 0000000..bdd814d --- /dev/null +++ b/flang/test/semantics/modfile24.f90 @@ -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 diff --git a/flang/test/semantics/resolve07.f90 b/flang/test/semantics/resolve07.f90 index 96e51de..eb456ef 100644 --- a/flang/test/semantics/resolve07.f90 +++ b/flang/test/semantics/resolve07.f90 @@ -16,18 +16,27 @@ subroutine s1 integer :: x(2) !ERROR: The dimensions of 'x' have already been declared allocatable :: x(:) + real :: y[1:*] + !ERROR: The codimensions of 'y' have already been declared + allocatable :: y[:] end subroutine s2 target :: x(1) !ERROR: The dimensions of 'x' have already been declared integer :: x(2) + target :: y[1:*] + !ERROR: The codimensions of 'y' have already been declared + integer :: y[2:*] end subroutine s3 - dimension :: x(4), y(8) + dimension :: x(4), x2(8) !ERROR: The dimensions of 'x' have already been declared allocatable :: x(:) + codimension :: y[*], y2[1:2,2:*] + !ERROR: The codimensions of 'y' have already been declared + allocatable :: y[:] end subroutine s4