From 903420268a5aabbab9acdf8788b4344c395ee597 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Sun, 12 Jan 2020 11:24:13 -0800 Subject: [PATCH 001/345] Checklist to precede pull requests I added a checklist to the C++ Style document for things to check before submitting a pull request or when responding to a request for comments on a pull request. --- documentation/PullRequestChecklist.md | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 documentation/PullRequestChecklist.md diff --git a/documentation/PullRequestChecklist.md b/documentation/PullRequestChecklist.md new file mode 100644 index 000000000000..782c3b61ca0e --- /dev/null +++ b/documentation/PullRequestChecklist.md @@ -0,0 +1,44 @@ + + +# Pull request checklist +Please review the following items before submitting a pull request. This list +can also be used when reviewing pull requests. +* Verify that new files have a license with correct file name. +* Run `git diff` on all modified files to look for spurious changes such as + `#include `. +* If you added code that causes the compiler to emit a new error message, make + sure that you also added a test that causes that error message to appear + and verifies its correctness. +* Annotate the code and tests with appropriate references to constraint and + requirement numbers from the Fortran standard. +* Check dereferences of pointers and optionals where necessary. +* Ensure that the scopes of all functions and variables are as local as + possible. +* Try to make all functions fit on a screen (40 lines). +* Build and test with both GNU and clang compilers. +* When submitting an update to a pull request, review previous pull request + comments and make sure that you've actually made all of the changes that + were requested. + +## Follow the style guide +The following items are taken from the [C++ style guide](C++style.md). But +even though I've read the style guide, they regularly trip me up. +* Run clang-format version 7 on all .cc and .h files. +* Make sure that all source lines have 80 or fewer characters. Note that + clang-format will do this for most code. But you may need to break up long + strings. +* Review declarations for proper use of `constexpr` and `const`. +* Follow the C++ naming guidelines. Ensure that the names evoke their + purpose and are consistent with existing code. +* Review pointer and reference types to make sure that you're using them + appropriately. Note that the [C++ style guide](C++style.md) contains a + section that describes all of the pointer types along with their + characteristics. +* Declare non-member functions ```static``` when possible. Prefer + ```static``` functions over functions in anonymous namespaces. From 25dc49b6e9cf5bce61d6d655ab242609cbd28e13 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 14 Jan 2020 17:31:25 -0800 Subject: [PATCH 002/345] Add `std::string ExpressionBase::AsFortran()` This is easier to use when including an expression in an error message and also useful when debugging for dumping expressions. Fix up several places that no longer need to use a temporary std::stringstream. Also change some references to `operator<<` in `formatting.cc` and `symbol.cc` that became ambiguous with this change. --- lib/evaluate/characteristics.cc | 13 +++---------- lib/evaluate/expression.h | 1 + lib/evaluate/fold-implementation.h | 9 ++------- lib/evaluate/formatting.cc | 28 ++++++++++++++++------------ lib/semantics/check-call.cc | 7 ++----- lib/semantics/symbol.cc | 22 +++++++++++++++------- lib/semantics/type.cc | 10 +++------- test/evaluate/expression.cc | 23 ++++++++--------------- 8 files changed, 50 insertions(+), 63 deletions(-) diff --git a/lib/evaluate/characteristics.cc b/lib/evaluate/characteristics.cc index de03d7d03174..83c79461b792 100644 --- a/lib/evaluate/characteristics.cc +++ b/lib/evaluate/characteristics.cc @@ -155,13 +155,10 @@ bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages, bool isElemental) const { const auto &len{that.LEN()}; if (!type_.IsTypeCompatibleWith(that.type_)) { - std::stringstream lenstr; - if (len) { - len->AsFortran(lenstr); - } messages.Say( "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US, - thatIs, that.type_.AsFortran(lenstr.str()), thisIs, type_.AsFortran()); + thatIs, that.type_.AsFortran(len ? len->AsFortran() : ""), thisIs, + type_.AsFortran()); return false; } return isElemental || @@ -213,11 +210,7 @@ void TypeAndShape::AcquireLEN() { } std::ostream &TypeAndShape::Dump(std::ostream &o) const { - std::stringstream LENstr; - if (LEN_) { - LEN_->AsFortran(LENstr); - } - o << type_.AsFortran(LENstr.str()); + o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""); attrs_.Dump(o, EnumToString); if (!shape_.empty()) { o << " dimension("; diff --git a/lib/evaluate/expression.h b/lib/evaluate/expression.h index c5003ca6e20e..f10ba21e9224 100644 --- a/lib/evaluate/expression.h +++ b/lib/evaluate/expression.h @@ -89,6 +89,7 @@ template class ExpressionBase { std::optional GetType() const; int Rank() const; + std::string AsFortran() const; std::ostream &AsFortran(std::ostream &) const; static Derived Rewrite(FoldingContext &, Derived &&); }; diff --git a/lib/evaluate/fold-implementation.h b/lib/evaluate/fold-implementation.h index 688dee505401..d78c52785276 100644 --- a/lib/evaluate/fold-implementation.h +++ b/lib/evaluate/fold-implementation.h @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -185,18 +184,14 @@ std::optional> Folder::GetNamedConstantValue(const Symbol &symbol0) { } mutableObject->set_init(std::nullopt); } else { - std::stringstream ss; - unwrapped->AsFortran(ss); context_.messages().Say(symbol.name(), "Initialization expression for PARAMETER '%s' (%s) cannot be computed as a constant value"_err_en_US, - symbol.name(), ss.str()); + symbol.name(), unwrapped->AsFortran()); } } else { - std::stringstream ss; - init->AsFortran(ss); context_.messages().Say(symbol.name(), "Initialization expression for PARAMETER '%s' (%s) cannot be converted to its type (%s)"_err_en_US, - symbol.name(), ss.str(), dyType->AsFortran()); + symbol.name(), init->AsFortran(), dyType->AsFortran()); } } } diff --git a/lib/evaluate/formatting.cc b/lib/evaluate/formatting.cc index 73b087881d79..67320ac7a826 100644 --- a/lib/evaluate/formatting.cc +++ b/lib/evaluate/formatting.cc @@ -14,6 +14,7 @@ #include "tools.h" #include "../parser/characters.h" #include "../semantics/symbol.h" +#include namespace Fortran::evaluate { @@ -314,24 +315,24 @@ std::ostream &Operation::AsFortran(std::ostream &o) const { Precedence thisPrec{ToPrecedence(derived())}; if constexpr (operands == 1) { if (thisPrec != Precedence::Top && lhsPrec < thisPrec) { - o << '(' << left() << ')'; + left().AsFortran(o << '(') << ')'; } else { - o << left(); + left().AsFortran(o); } } else { if (thisPrec != Precedence::Top && (lhsPrec < thisPrec || (lhsPrec == Precedence::Power && thisPrec == Precedence::Power))) { - o << '(' << left() << ')'; + left().AsFortran(o << '(') << ')'; } else { - o << left(); + left().AsFortran(o); } o << spelling.infix; Precedence rhsPrec{ToPrecedence(right())}; if (thisPrec != Precedence::Top && rhsPrec < thisPrec) { - o << '(' << right() << ')'; + right().AsFortran(o << '(') << ')'; } else { - o << right(); + right().AsFortran(o); } } return o << spelling.suffix; @@ -403,9 +404,7 @@ std::ostream &ArrayConstructor::AsFortran(std::ostream &o) const { template std::ostream &ArrayConstructor>::AsFortran( std::ostream &o) const { - std::stringstream len; - LEN().AsFortran(len); - o << '[' << GetType().AsFortran(len.str()) << "::"; + o << '[' << GetType().AsFortran(LEN().AsFortran()) << "::"; EmitArray(o, *this); return o << ']'; } @@ -416,6 +415,13 @@ std::ostream &ArrayConstructor::AsFortran(std::ostream &o) const { return o << ']'; } +template +std::string ExpressionBase::AsFortran() const { + std::ostringstream ss; + AsFortran(ss); + return ss.str(); +} + template std::ostream &ExpressionBase::AsFortran(std::ostream &o) const { std::visit( @@ -459,9 +465,7 @@ std::string DynamicType::AsFortran() const { } else if (charLength_->isDeferred()) { result += ':'; } else if (const auto &length{charLength_->GetExplicit()}) { - std::stringstream ss; - length->AsFortran(ss); - result += ss.str(); + result += length->AsFortran(); } return result + ')'; } else if (IsUnlimitedPolymorphic()) { diff --git a/lib/semantics/check-call.cc b/lib/semantics/check-call.cc index 1888249a063d..a643c384d589 100644 --- a/lib/semantics/check-call.cc +++ b/lib/semantics/check-call.cc @@ -160,13 +160,10 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy, "dummy argument", "actual argument"); } } else { - std::stringstream lenStr; - if (const auto &len{actualType.LEN()}) { - len->AsFortran(lenStr); - } + const auto &len{actualType.LEN()}; messages.Say( "Actual argument type '%s' is not compatible with dummy argument type '%s'"_err_en_US, - actualType.type().AsFortran(lenStr.str()), + actualType.type().AsFortran(len ? len->AsFortran() : ""), dummy.type.type().AsFortran()); } diff --git a/lib/semantics/symbol.cc b/lib/semantics/symbol.cc index 9646059c0dba..4015cf754103 100644 --- a/lib/semantics/symbol.cc +++ b/lib/semantics/symbol.cc @@ -11,6 +11,7 @@ #include "semantics.h" #include "tools.h" #include "../common/idioms.h" +#include "../evaluate/expression.h" #include #include @@ -22,6 +23,13 @@ static void DumpOptional(std::ostream &os, const char *label, const T &x) { os << ' ' << label << ':' << *x; } } +template +static void DumpExpr(std::ostream &os, const char *label, + const std::optional> &x) { + if (x) { + x->AsFortran(os << ' ' << label << ':'); + } +} static void DumpBool(std::ostream &os, const char *label, bool x) { if (x) { @@ -84,7 +92,7 @@ void ModuleDetails::set_scope(const Scope *scope) { std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) { DumpBool(os, "isInterface", x.isInterface_); - DumpOptional(os, "bindName", x.bindName_); + DumpExpr(os, "bindName", x.bindName_); if (x.result_) { os << " result:" << x.result_->name(); if (!x.result_->attrs().empty()) { @@ -336,7 +344,7 @@ std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { if (x.type()) { os << " type: " << *x.type(); } - DumpOptional(os, "bindName", x.bindName_); + DumpExpr(os, "bindName", x.bindName_); return os; } @@ -344,13 +352,13 @@ std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { os << *static_cast(&x); DumpList(os, "shape", x.shape()); DumpList(os, "coshape", x.coshape()); - DumpOptional(os, "init", x.init_); + DumpExpr(os, "init", x.init_); return os; } std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) { os << *static_cast(&x); - DumpOptional(os, "expr", x.expr()); + DumpExpr(os, "expr", x.expr()); return os; } @@ -360,7 +368,7 @@ std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { } else { DumpType(os, x.interface_.type()); } - DumpOptional(os, "bindName", x.bindName()); + DumpExpr(os, "bindName", x.bindName()); DumpOptional(os, "passName", x.passName()); if (x.init()) { if (const Symbol * target{*x.init()}) { @@ -409,7 +417,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { os << dummy->name(); } os << ')'; - DumpOptional(os, "bindName", x.bindName()); + DumpExpr(os, "bindName", x.bindName()); if (x.isFunction()) { os << " result("; DumpType(os, x.result()); @@ -455,7 +463,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { [&](const TypeParamDetails &x) { DumpOptional(os, "type", x.type()); os << ' ' << common::EnumToString(x.attr()); - DumpOptional(os, "init", x.init()); + DumpExpr(os, "init", x.init()); }, [&](const MiscDetails &x) { os << ' ' << MiscDetails::EnumToString(x.kind()); diff --git a/lib/semantics/type.cc b/lib/semantics/type.cc index e680697b8e49..3e96186f6563 100644 --- a/lib/semantics/type.cc +++ b/lib/semantics/type.cc @@ -122,11 +122,9 @@ void DerivedTypeSpec::EvaluateParameters( continue; } } - std::stringstream fortran; - expr->AsFortran(fortran); evaluate::SayWithDeclaration(messages, symbol, "Value of type parameter '%s' (%s) is not convertible to its type"_err_en_US, - name, fortran.str()); + name, expr->AsFortran()); } } } @@ -243,12 +241,10 @@ void DerivedTypeSpec::Instantiate( if (expr->Rank() == 0 && maybeDynamicType->category() == TypeCategory::Integer) { if (!evaluate::ToInt64(*expr)) { - std::stringstream fortran; - fortran << *expr; if (auto *msg{foldingContext.messages().Say( "Value of kind type parameter '%s' (%s) is not " "a scalar INTEGER constant"_err_en_US, - name, fortran.str())}) { + name, expr->AsFortran())}) { msg->Attach(name, "declared here"_en_US); } } @@ -322,7 +318,7 @@ std::ostream &operator<<(std::ostream &o, const Bound &x) { } else if (x.isDeferred()) { o << ':'; } else if (x.expr_) { - o << x.expr_; + x.expr_->AsFortran(o); } else { o << ""; } diff --git a/test/evaluate/expression.cc b/test/evaluate/expression.cc index 3145769e175b..24a6e0f5f5d7 100644 --- a/test/evaluate/expression.cc +++ b/test/evaluate/expression.cc @@ -6,37 +6,30 @@ #include "../../lib/parser/message.h" #include #include -#include #include using namespace Fortran::evaluate; -template std::string AsFortran(const A &x) { - std::stringstream ss; - ss << x; - return ss.str(); -} - int main() { using DefaultIntegerExpr = Expr>; TEST(DefaultIntegerExpr::Result::AsFortran() == "INTEGER(4)"); - MATCH("666_4", AsFortran(DefaultIntegerExpr{666})); - MATCH("-1_4", AsFortran(-DefaultIntegerExpr{1})); + MATCH("666_4", DefaultIntegerExpr{666}.AsFortran()); + MATCH("-1_4", (-DefaultIntegerExpr{1}).AsFortran()); auto ex1{ DefaultIntegerExpr{2} + DefaultIntegerExpr{3} * -DefaultIntegerExpr{4}}; - MATCH("2_4+3_4*(-4_4)", AsFortran(ex1)); + MATCH("2_4+3_4*(-4_4)", ex1.AsFortran()); Fortran::common::IntrinsicTypeDefaultKinds defaults; auto intrinsics{Fortran::evaluate::IntrinsicProcTable::Configure(defaults)}; FoldingContext context{ Fortran::parser::ContextualMessages{nullptr}, defaults, intrinsics}; ex1 = Fold(context, std::move(ex1)); - MATCH("-10_4", AsFortran(ex1)); - MATCH("1_4/2_4", AsFortran(DefaultIntegerExpr{1} / DefaultIntegerExpr{2})); + MATCH("-10_4", ex1.AsFortran()); + MATCH("1_4/2_4", (DefaultIntegerExpr{1} / DefaultIntegerExpr{2}).AsFortran()); DefaultIntegerExpr a{1}; DefaultIntegerExpr b{2}; - MATCH("1_4", AsFortran(a)); + MATCH("1_4", a.AsFortran()); a = b; - MATCH("2_4", AsFortran(a)); - MATCH("2_4", AsFortran(b)); + MATCH("2_4", a.AsFortran()); + MATCH("2_4", b.AsFortran()); return testing::Complete(); } From 3b865d7703f53099cd491ed1aa9b80b46fee9f58 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 14 Jan 2020 17:39:29 -0800 Subject: [PATCH 003/345] Make GenericAssignmentWrapper more like GenericExprWrapper Have it wrap an optional Assignment so that we can distinguish between unanalyzed and analyzed with error. Change analysis of PointerAssignmentStmt to proceed with bounds even if the DataRef or Expr has an error. Otherwise any bounds expressions won't be analyzed in that case. In GetExpr() and GetAssignment() if we get an internal error due to an unanalyzed expression, dump the parse tree for the expression so we have some context for the error. They should only be called after the expression analysis phase. At that point, every expression and assignment should be analyzed, though some may have resulted in errors(indicated by returning `nullptr`). --- lib/evaluate/expression.h | 3 +- lib/semantics/expression.cc | 81 ++++++++++++++++++++----------------- lib/semantics/tools.cc | 29 +++++++++++-- lib/semantics/tools.h | 8 +--- tools/f18/f18.cc | 6 ++- 5 files changed, 78 insertions(+), 49 deletions(-) diff --git a/lib/evaluate/expression.h b/lib/evaluate/expression.h index f10ba21e9224..00363b523d5d 100644 --- a/lib/evaluate/expression.h +++ b/lib/evaluate/expression.h @@ -847,9 +847,10 @@ struct GenericExprWrapper { // Like GenericExprWrapper but for analyzed assignments struct GenericAssignmentWrapper { + GenericAssignmentWrapper() {} explicit GenericAssignmentWrapper(Assignment &&x) : v{std::move(x)} {} ~GenericAssignmentWrapper(); - Assignment v; + std::optional v; // vacant if error }; FOR_EACH_CATEGORY_TYPE(extern template class Expr, ) diff --git a/lib/semantics/expression.cc b/lib/semantics/expression.cc index a5eb00b7b6f4..e24b31045184 100644 --- a/lib/semantics/expression.cc +++ b/lib/semantics/expression.cc @@ -1915,7 +1915,9 @@ const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { ArgumentAnalyzer analyzer{*this}; analyzer.Analyze(std::get(x.t)); analyzer.Analyze(std::get(x.t)); - if (!analyzer.fatalErrors()) { + if (analyzer.fatalErrors()) { + x.typedAssignment.reset(new GenericAssignmentWrapper{}); + } else { std::optional procRef{analyzer.TryDefinedAssignment()}; x.typedAssignment.reset(new GenericAssignmentWrapper{procRef ? Assignment{std::move(*procRef)} @@ -1923,50 +1925,55 @@ const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { Fold(analyzer.MoveExpr(0)), Fold(analyzer.MoveExpr(1))}}}); } } - return x.typedAssignment ? &x.typedAssignment->v : nullptr; + return common::GetPtrFromOptional(x.typedAssignment->v); } const Assignment *ExpressionAnalyzer::Analyze( const parser::PointerAssignmentStmt &x) { - MaybeExpr lhs{Analyze(std::get(x.t))}; - MaybeExpr rhs{Analyze(std::get(x.t))}; - if (!lhs || !rhs) { - return nullptr; - } - Assignment::PointerAssignment assignment{ - Fold(std::move(*lhs)), Fold(std::move(*rhs))}; - std::visit( - common::visitors{ - [&](const std::list &list) { - if (!list.empty()) { - Assignment::PointerAssignment::BoundsRemapping bounds; - for (const auto &elem : list) { - auto lower{AsSubscript(Analyze(std::get<0>(elem.t)))}; - auto upper{AsSubscript(Analyze(std::get<1>(elem.t)))}; - if (lower && upper) { - bounds.emplace_back( - Fold(std::move(*lower)), Fold(std::move(*upper))); + if (!x.typedAssignment) { + MaybeExpr lhs{Analyze(std::get(x.t))}; + MaybeExpr rhs{Analyze(std::get(x.t))}; + decltype(Assignment::PointerAssignment::bounds) pointerBounds; + std::visit( + common::visitors{ + [&](const std::list &list) { + if (!list.empty()) { + Assignment::PointerAssignment::BoundsRemapping bounds; + for (const auto &elem : list) { + auto lower{AsSubscript(Analyze(std::get<0>(elem.t)))}; + auto upper{AsSubscript(Analyze(std::get<1>(elem.t)))}; + if (lower && upper) { + bounds.emplace_back( + Fold(std::move(*lower)), Fold(std::move(*upper))); + } } + pointerBounds = bounds; } - assignment.bounds = bounds; - } - }, - [&](const std::list &list) { - if (!list.empty()) { - Assignment::PointerAssignment::BoundsSpec bounds; - for (const auto &bound : list) { - if (auto lower{AsSubscript(Analyze(bound.v))}) { - bounds.emplace_back(Fold(std::move(*lower))); + }, + [&](const std::list &list) { + if (!list.empty()) { + Assignment::PointerAssignment::BoundsSpec bounds; + for (const auto &bound : list) { + if (auto lower{AsSubscript(Analyze(bound.v))}) { + bounds.emplace_back(Fold(std::move(*lower))); + } } + pointerBounds = bounds; } - assignment.bounds = bounds; - } - }, - }, - std::get(x.t).u); - x.typedAssignment.reset( - new GenericAssignmentWrapper{Assignment{std::move(assignment)}}); - return &x.typedAssignment->v; + }, + }, + std::get(x.t).u); + if (!lhs || !rhs) { + x.typedAssignment.reset(new GenericAssignmentWrapper{}); + } else { + Assignment::PointerAssignment assignment{ + Fold(std::move(*lhs)), Fold(std::move(*rhs))}; + assignment.bounds = pointerBounds; + x.typedAssignment.reset( + new GenericAssignmentWrapper{Assignment{std::move(assignment)}}); + } + } + return common::GetPtrFromOptional(x.typedAssignment->v); } static bool IsExternalCalledImplicitly( diff --git a/lib/semantics/tools.cc b/lib/semantics/tools.cc index 18c5c827987d..8342e4126259 100644 --- a/lib/semantics/tools.cc +++ b/lib/semantics/tools.cc @@ -13,11 +13,13 @@ #include "type.h" #include "../common/Fortran.h" #include "../common/indirection.h" +#include "../parser/dump-parse-tree.h" #include "../parser/message.h" #include "../parser/parse-tree.h" #include "../parser/tools.h" #include #include +#include #include namespace Fortran::semantics { @@ -383,14 +385,33 @@ bool ExprTypeKindIsDefault( dynamicType->kind() == context.GetDefaultKind(dynamicType->category()); } +// If an analyzed expr or assignment is missing, dump the node and die. +template static void CheckMissingAnalysis(bool absent, const T &x) { + if (absent) { + std::ostringstream ss; + ss << "node has not been analyzed:\n"; + parser::DumpTree(ss, x); + common::die(ss.str().c_str()); + } +} + +const SomeExpr *GetExprHelper::Get(const parser::Expr &x) { + CheckMissingAnalysis(!x.typedExpr, x); + return common::GetPtrFromOptional(x.typedExpr->v); +} +const SomeExpr *GetExprHelper::Get(const parser::Variable &x) { + CheckMissingAnalysis(!x.typedExpr, x); + return common::GetPtrFromOptional(x.typedExpr->v); +} + const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) { - const auto &typed{x.typedAssignment}; - return typed ? &typed->v : nullptr; + CheckMissingAnalysis(!x.typedAssignment, x); + return common::GetPtrFromOptional(x.typedAssignment->v); } const evaluate::Assignment *GetAssignment( const parser::PointerAssignmentStmt &x) { - const auto &typed{x.typedAssignment}; - return typed ? &typed->v : nullptr; + CheckMissingAnalysis(!x.typedAssignment, x); + return common::GetPtrFromOptional(x.typedAssignment->v); } const Symbol *FindInterface(const Symbol &symbol) { diff --git a/lib/semantics/tools.h b/lib/semantics/tools.h index d857654f0576..83741bdddafe 100644 --- a/lib/semantics/tools.h +++ b/lib/semantics/tools.h @@ -226,12 +226,8 @@ bool ExprTypeKindIsDefault( const SomeExpr &expr, const SemanticsContext &context); struct GetExprHelper { - const SomeExpr *Get(const parser::Expr::TypedExpr &x) { - CHECK(x); - return x && x->v ? &*x->v : nullptr; - } - const SomeExpr *Get(const parser::Expr &x) { return Get(x.typedExpr); } - const SomeExpr *Get(const parser::Variable &x) { return Get(x.typedExpr); } + const SomeExpr *Get(const parser::Expr &); + const SomeExpr *Get(const parser::Variable &); template const SomeExpr *Get(const common::Indirection &x) { return Get(x.value()); } diff --git a/tools/f18/f18.cc b/tools/f18/f18.cc index f23f6e4f9b93..6e6854051b4c 100644 --- a/tools/f18/f18.cc +++ b/tools/f18/f18.cc @@ -172,7 +172,11 @@ static Fortran::parser::AnalyzedObjectsAsFortran asFortran{ } }, [](std::ostream &o, const Fortran::evaluate::GenericAssignmentWrapper &x) { - x.v.AsFortran(o); + if (x.v) { + x.v->AsFortran(o); + } else { + o << "(bad assignment)"; + } }, [](std::ostream &o, const Fortran::evaluate::ProcedureRef &x) { x.AsFortran(o << "CALL "); From bdf3d3a292b5f9ce4241e3d63b759bd61838cf18 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 16 Jan 2020 12:43:48 -0800 Subject: [PATCH 004/345] Refactor Analyze(PointerAssignmentStmt) Use early returns to reduce the indentation. Check LHS is a pointer as early as possible. A PointerAssignmentStmt can only have a typedAssignment that represents a PointerAssignment. So assert that is the case and don't worry about the other cases. --- lib/semantics/assignment.cc | 112 +++++++++++++--------------- lib/semantics/pointer-assignment.cc | 22 +++--- 2 files changed, 62 insertions(+), 72 deletions(-) diff --git a/lib/semantics/assignment.cc b/lib/semantics/assignment.cc index 064d6f86dbe5..53724af627d7 100644 --- a/lib/semantics/assignment.cc +++ b/lib/semantics/assignment.cc @@ -158,66 +158,60 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { } void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { + using PointerAssignment = evaluate::Assignment::PointerAssignment; CHECK(!where_); - if (const evaluate::Assignment * asst{GetAssignment(stmt)}) { - bool hasBounds{false}; - auto [lhs, rhs]{std::visit( - common::visitors{ - [&](const evaluate::Assignment::IntrinsicAssignment &x) { - return std::make_pair(&x.lhs, &x.rhs); - }, - [&](const evaluate::ProcedureRef &x) { - return std::make_pair(x.arguments()[0]->UnwrapExpr(), - x.arguments()[1]->UnwrapExpr()); - }, - [&](const evaluate::Assignment::PointerAssignment &x) { - std::visit( - common::visitors{ - [&](const evaluate::Assignment::PointerAssignment:: - BoundsSpec &bounds) { - hasBounds = !bounds.empty(); - for (const auto &bound : bounds) { - CheckForImpureCall(SomeExpr{bound}); - } - }, - [&](const evaluate::Assignment::PointerAssignment:: - BoundsRemapping &bounds) { - hasBounds = !bounds.empty(); - for (const auto &bound : bounds) { - CheckForImpureCall(SomeExpr{bound.first}); - CheckForImpureCall(SomeExpr{bound.second}); - } - }, - }, - x.bounds); - return std::make_pair(&x.lhs, &x.rhs); - }, - }, - asst->u)}; - CheckForImpureCall(lhs); - CheckForImpureCall(rhs); - if (forall_) { - // TODO: Warn if some name in forall_->activeNames or its outer - // contexts does not appear on LHS - } - if (lhs && rhs) { - CheckForPureContext( - *lhs, *rhs, std::get(stmt.t).source, true /* => */); - const Symbol *pointer{GetLastSymbol(lhs)}; - if (pointer && pointer->has() && - evaluate::ExtractCoarrayRef(*lhs)) { - context_.Say( // C1027 - "Procedure pointer may not be a coindexed object"_err_en_US); - } - if (hasBounds) { - // TODO cases with bounds-spec and bounds-remapping - } else { - auto &foldingContext{context_.foldingContext()}; - auto restorer{ - foldingContext.messages().SetLocation(context_.location().value())}; - CheckPointerAssignment(foldingContext, *pointer, *rhs); - } - } + const evaluate::Assignment *assign{GetAssignment(stmt)}; + if (!assign) { + return; + } + const auto &ptrAssign{std::get(assign->u)}; + const SomeExpr &lhs{ptrAssign.lhs}; + const SomeExpr &rhs{ptrAssign.rhs}; + std::size_t numBounds{std::visit( + common::visitors{ + [&](const PointerAssignment::BoundsSpec &bounds) { + for (const auto &bound : bounds) { + CheckForImpureCall(SomeExpr{bound}); + } + return bounds.size(); + }, + [&](const PointerAssignment::BoundsRemapping &bounds) { + for (const auto &bound : bounds) { + CheckForImpureCall(SomeExpr{bound.first}); + CheckForImpureCall(SomeExpr{bound.second}); + } + return bounds.size(); + }, + }, + ptrAssign.bounds)}; + const Symbol *pointer{GetLastSymbol(lhs)}; + if (!pointer) { + return; // error was reported + } + auto &foldingContext{context_.foldingContext()}; + auto restorer{ + foldingContext.messages().SetLocation(context_.location().value())}; + if (!IsPointer(*pointer)) { + evaluate::SayWithDeclaration(foldingContext.messages(), *pointer, + "'%s' is not a pointer"_err_en_US, pointer->name()); + return; + } + CheckForImpureCall(lhs); + CheckForImpureCall(rhs); + if (forall_) { + // TODO: Warn if some name in forall_->activeNames or its outer + // contexts does not appear on LHS + } + CheckForPureContext(lhs, rhs, std::get(stmt.t).source, + true /* isPointerAssignment */); + if (pointer->has() && evaluate::ExtractCoarrayRef(lhs)) { + context_.Say( // C1027 + "Procedure pointer may not be a coindexed object"_err_en_US); + } + if (numBounds > 0) { + // TODO cases with bounds-spec and bounds-remapping + } else { + CheckPointerAssignment(foldingContext, *pointer, rhs); } } diff --git a/lib/semantics/pointer-assignment.cc b/lib/semantics/pointer-assignment.cc index 57b7d8cb4acc..88a4b065c65f 100644 --- a/lib/semantics/pointer-assignment.cc +++ b/lib/semantics/pointer-assignment.cc @@ -326,19 +326,15 @@ void CheckPointerAssignment( evaluate::FoldingContext &context, const Symbol &lhs, const SomeExpr &rhs) { // TODO: Acquire values of deferred type parameters &/or array bounds // from the RHS. - if (!IsPointer(lhs)) { - evaluate::SayWithDeclaration( - context.messages(), lhs, "'%s' is not a pointer"_err_en_US, lhs.name()); - } else { - std::string description{"pointer '"s + lhs.name().ToString() + '\''}; - PointerAssignmentChecker{lhs.name(), description, context} - .set_lhsType(TypeAndShape::Characterize(lhs, context)) - .set_procedure(Procedure::Characterize(lhs, context.intrinsics())) - .set_lhs(lhs) - .set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS)) - .set_isVolatile(lhs.attrs().test(Attr::VOLATILE)) - .Check(rhs); - } + CHECK(IsPointer(lhs)); + std::string description{"pointer '"s + lhs.name().ToString() + '\''}; + PointerAssignmentChecker{lhs.name(), description, context} + .set_lhsType(TypeAndShape::Characterize(lhs, context)) + .set_procedure(Procedure::Characterize(lhs, context.intrinsics())) + .set_lhs(lhs) + .set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS)) + .set_isVolatile(lhs.attrs().test(Attr::VOLATILE)) + .Check(rhs); } void CheckPointerAssignment(evaluate::FoldingContext &context, From 7489b3539224f8ad7a55873916e5854510236218 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 21 Jan 2020 17:15:21 -0800 Subject: [PATCH 005/345] Check bounds on pointer assignment Perform checks on bounds-spec and bounds-remapping in a pointer assignment statement: - check that the rank of the bounds specified matches the rank of the pointer - for bounds-spec, check that the pointer rank matches the target rank - for bounds-remapping: - check that the target is rank 1 or simply contiguous - check that there are sufficient elements on the RHS for the bounds specified, when it can be determined at compile time Move more of the pointer-specific checking from `assignment.cc` to `pointer-assignment.cc`. --- lib/semantics/assignment.cc | 34 ++---- lib/semantics/pointer-assignment.cc | 169 +++++++++++++++++++++------- lib/semantics/pointer-assignment.h | 3 + test/semantics/assign02.f90 | 10 +- test/semantics/assign03.f90 | 26 +++++ 5 files changed, 172 insertions(+), 70 deletions(-) diff --git a/lib/semantics/assignment.cc b/lib/semantics/assignment.cc index 53724af627d7..d7b9d91ed739 100644 --- a/lib/semantics/assignment.cc +++ b/lib/semantics/assignment.cc @@ -167,52 +167,32 @@ void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { const auto &ptrAssign{std::get(assign->u)}; const SomeExpr &lhs{ptrAssign.lhs}; const SomeExpr &rhs{ptrAssign.rhs}; - std::size_t numBounds{std::visit( + CheckForImpureCall(lhs); + CheckForImpureCall(rhs); + std::visit( common::visitors{ [&](const PointerAssignment::BoundsSpec &bounds) { for (const auto &bound : bounds) { CheckForImpureCall(SomeExpr{bound}); } - return bounds.size(); }, [&](const PointerAssignment::BoundsRemapping &bounds) { for (const auto &bound : bounds) { CheckForImpureCall(SomeExpr{bound.first}); CheckForImpureCall(SomeExpr{bound.second}); } - return bounds.size(); }, }, - ptrAssign.bounds)}; - const Symbol *pointer{GetLastSymbol(lhs)}; - if (!pointer) { - return; // error was reported - } - auto &foldingContext{context_.foldingContext()}; - auto restorer{ - foldingContext.messages().SetLocation(context_.location().value())}; - if (!IsPointer(*pointer)) { - evaluate::SayWithDeclaration(foldingContext.messages(), *pointer, - "'%s' is not a pointer"_err_en_US, pointer->name()); - return; - } - CheckForImpureCall(lhs); - CheckForImpureCall(rhs); + ptrAssign.bounds); if (forall_) { // TODO: Warn if some name in forall_->activeNames or its outer // contexts does not appear on LHS } CheckForPureContext(lhs, rhs, std::get(stmt.t).source, true /* isPointerAssignment */); - if (pointer->has() && evaluate::ExtractCoarrayRef(lhs)) { - context_.Say( // C1027 - "Procedure pointer may not be a coindexed object"_err_en_US); - } - if (numBounds > 0) { - // TODO cases with bounds-spec and bounds-remapping - } else { - CheckPointerAssignment(foldingContext, *pointer, rhs); - } + auto restorer{context_.foldingContext().messages().SetLocation( + context_.location().value())}; + CheckPointerAssignment(context_.foldingContext(), ptrAssign); } void AssignmentContext::Analyze(const parser::WhereStmt &stmt) { diff --git a/lib/semantics/pointer-assignment.cc b/lib/semantics/pointer-assignment.cc index 88a4b065c65f..017d0e80e033 100644 --- a/lib/semantics/pointer-assignment.cc +++ b/lib/semantics/pointer-assignment.cc @@ -33,21 +33,31 @@ using evaluate::characteristics::DummyDataObject; using evaluate::characteristics::FunctionResult; using evaluate::characteristics::Procedure; using evaluate::characteristics::TypeAndShape; +using parser::MessageFixedText; +using parser::MessageFormattedText; +using PointerAssignment = evaluate::Assignment::PointerAssignment; class PointerAssignmentChecker { public: - PointerAssignmentChecker(parser::CharBlock source, - const std::string &description, evaluate::FoldingContext &context) - : source_{source}, description_{description}, context_{context} {} - PointerAssignmentChecker &set_lhs(const Symbol &); + PointerAssignmentChecker(evaluate::FoldingContext &context, + parser::CharBlock source, const std::string &description) + : context_{context}, source_{source}, description_{description} {} + PointerAssignmentChecker(evaluate::FoldingContext &context, const Symbol &lhs) + : context_{context}, source_{lhs.name()}, + description_{"pointer '"s + lhs.name().ToString() + '\''}, lhs_{&lhs}, + procedure_{Procedure::Characterize(lhs, context.intrinsics())} { + set_lhsType(TypeAndShape::Characterize(lhs, context)); + set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS)); + set_isVolatile(lhs.attrs().test(Attr::VOLATILE)); + } PointerAssignmentChecker &set_lhsType(std::optional &&); - PointerAssignmentChecker &set_procedure(std::optional &&); PointerAssignmentChecker &set_isContiguous(bool); PointerAssignmentChecker &set_isVolatile(bool); + PointerAssignmentChecker &set_isBoundsRemapping(bool); void Check(const SomeExpr &); private: - template void Check(const A &); + template void Check(const T &); template void Check(const evaluate::Expr &); template void Check(const evaluate::FunctionRef &); template void Check(const evaluate::Designator &); @@ -60,33 +70,23 @@ class PointerAssignmentChecker { bool LhsOkForUnlimitedPoly() const; template parser::Message *Say(A &&...); - const parser::CharBlock source_; - const std::string &description_; evaluate::FoldingContext &context_; + const parser::CharBlock source_; + const std::string description_; const Symbol *lhs_{nullptr}; std::optional lhsType_; std::optional procedure_; bool isContiguous_{false}; bool isVolatile_{false}; + bool isBoundsRemapping_{false}; }; -PointerAssignmentChecker &PointerAssignmentChecker::set_lhs(const Symbol &lhs) { - lhs_ = &lhs; - return *this; -} - PointerAssignmentChecker &PointerAssignmentChecker::set_lhsType( std::optional &&lhsType) { lhsType_ = std::move(lhsType); return *this; } -PointerAssignmentChecker &PointerAssignmentChecker::set_procedure( - std::optional &&procedure) { - procedure_ = std::move(procedure); - return *this; -} - PointerAssignmentChecker &PointerAssignmentChecker::set_isContiguous( bool isContiguous) { isContiguous_ = isContiguous; @@ -99,7 +99,13 @@ PointerAssignmentChecker &PointerAssignmentChecker::set_isVolatile( return *this; } -template void PointerAssignmentChecker::Check(const A &) { +PointerAssignmentChecker &PointerAssignmentChecker::set_isBoundsRemapping( + bool isBoundsRemapping) { + isBoundsRemapping_ = isBoundsRemapping; + return *this; +} + +template void PointerAssignmentChecker::Check(const T &) { // Catch-all case for really bad target expression Say("Target associated with %s must be a designator or a call to a" " pointer-valued function"_err_en_US, @@ -138,7 +144,7 @@ void PointerAssignmentChecker::Check(const evaluate::FunctionRef &f) { if (!proc) { return; } - std::optional msg; + std::optional msg; const auto &funcResult{proc->functionResult}; // C1025 if (!funcResult) { msg = "%s is associated with the non-existent result of reference to" @@ -180,7 +186,7 @@ void PointerAssignmentChecker::Check(const evaluate::Designator &d) { context_.messages().Say("Pointer target is not a named entity"_err_en_US); return; } - std::optional msg; + std::optional> msg; if (procedure_) { // Shouldn't be here in this function unless lhs is an object pointer. msg = "In assignment to procedure %s, the target is not a procedure or" @@ -208,14 +214,31 @@ void PointerAssignmentChecker::Check(const evaluate::Designator &d) { " derived type when target is unlimited polymorphic"_err_en_US; } } else { - lhsType_->IsCompatibleWith(context_.messages(), *rhsType); + if (!lhsType_->type().IsTypeCompatibleWith(rhsType->type())) { + msg = MessageFormattedText{ + "Target type %s is not compatible with pointer type %s"_err_en_US, + rhsType->type().AsFortran(), lhsType_->type().AsFortran()}; + + } else if (!isBoundsRemapping_) { + std::size_t lhsRank{lhsType_->shape().size()}; + std::size_t rhsRank{rhsType->shape().size()}; + if (lhsRank != rhsRank) { + msg = MessageFormattedText{ + "Pointer has rank %d but target has rank %d"_err_en_US, lhsRank, + rhsRank}; + } + } } } if (msg) { - std::ostringstream ss; - d.AsFortran(ss); auto restorer{common::ScopedSet(lhs_, last)}; - Say(*msg, description_, ss.str()); + if (auto *m{std::get_if(&*msg)}) { + std::ostringstream ss; + d.AsFortran(ss); + Say(*m, description_, ss.str()); + } else { + Say(std::get(*msg)); + } } } @@ -235,7 +258,7 @@ static bool CharacteristicsMatch(const Procedure &lhs, const Procedure &rhs) { // Common handling for procedure pointer right-hand sides void PointerAssignmentChecker::Check( parser::CharBlock rhsName, bool isCall, const Procedure *rhsProcedure) { - std::optional msg; + std::optional msg; if (!procedure_) { msg = "In assignment to object %s, the target '%s' is a procedure" " designator"_err_en_US; @@ -322,25 +345,95 @@ parser::Message *PointerAssignmentChecker::Say(A &&... x) { return msg; } +// Verify that any bounds on the LHS of a pointer assignment are valid. +// Return true if it is a bound-remapping so we can perform further checks. +static bool CheckPointerBounds( + evaluate::FoldingContext &context, const PointerAssignment &assignment) { + auto &messages{context.messages()}; + const SomeExpr &lhs{assignment.lhs}; + const SomeExpr &rhs{assignment.rhs}; + bool isBoundsRemapping{false}; + std::size_t numBounds{std::visit( + common::visitors{ + [&](const PointerAssignment::BoundsSpec &bounds) { + return bounds.size(); + }, + [&](const PointerAssignment::BoundsRemapping &bounds) { + isBoundsRemapping = true; + evaluate::ExtentExpr lhsSizeExpr{1}; + for (const auto &bound : bounds) { + lhsSizeExpr = std::move(lhsSizeExpr) * + (common::Clone(bound.second) - common::Clone(bound.first) + + evaluate::ExtentExpr{1}); + } + if (std::optional lhsSize{evaluate::ToInt64( + evaluate::Fold(context, std::move(lhsSizeExpr)))}) { + if (auto shape{evaluate::GetShape(context, rhs)}) { + if (std::optional rhsSize{ + evaluate::ToInt64(evaluate::Fold( + context, evaluate::GetSize(std::move(*shape))))}) { + if (*lhsSize > *rhsSize) { + messages.Say( + "Pointer bounds require %d elements but target has" + " only %d"_err_en_US, + *lhsSize, *rhsSize); // 10.2.2.3(9) + } + } + } + } + return bounds.size(); + }, + }, + assignment.bounds)}; + if (numBounds > 0) { + if (lhs.Rank() != static_cast(numBounds)) { + messages.Say("Pointer '%s' has rank %d but the number of bounds specified" + " is %d"_err_en_US, + lhs.AsFortran(), lhs.Rank(), numBounds); // C1018 + } + } + if (isBoundsRemapping && rhs.Rank() != 1 && + !evaluate::IsSimplyContiguous(rhs, context.intrinsics())) { + messages.Say("Pointer bounds remapping target must have rank 1 or be" + " simply contiguous"_err_en_US); // 10.2.2.3(9) + } + return isBoundsRemapping; +} + +void CheckPointerAssignment( + evaluate::FoldingContext &context, const PointerAssignment &assignment) { + const SomeExpr &lhs{assignment.lhs}; + const SomeExpr &rhs{assignment.rhs}; + const Symbol *pointer{GetLastSymbol(lhs)}; + if (!pointer) { + return; // error was reported + } + if (!IsPointer(*pointer)) { + evaluate::SayWithDeclaration(context.messages(), *pointer, + "'%s' is not a pointer"_err_en_US, pointer->name()); + return; + } + if (pointer->has() && evaluate::ExtractCoarrayRef(lhs)) { + context.messages().Say( // C1027 + "Procedure pointer may not be a coindexed object"_err_en_US); + return; + } + bool isBoundsRemapping{CheckPointerBounds(context, assignment)}; + PointerAssignmentChecker{context, *pointer} + .set_isBoundsRemapping(isBoundsRemapping) + .Check(rhs); +} + void CheckPointerAssignment( evaluate::FoldingContext &context, const Symbol &lhs, const SomeExpr &rhs) { - // TODO: Acquire values of deferred type parameters &/or array bounds - // from the RHS. CHECK(IsPointer(lhs)); - std::string description{"pointer '"s + lhs.name().ToString() + '\''}; - PointerAssignmentChecker{lhs.name(), description, context} - .set_lhsType(TypeAndShape::Characterize(lhs, context)) - .set_procedure(Procedure::Characterize(lhs, context.intrinsics())) - .set_lhs(lhs) - .set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS)) - .set_isVolatile(lhs.attrs().test(Attr::VOLATILE)) - .Check(rhs); + PointerAssignmentChecker{context, lhs}.Check(rhs); } void CheckPointerAssignment(evaluate::FoldingContext &context, parser::CharBlock source, const std::string &description, const DummyDataObject &lhs, const SomeExpr &rhs) { - PointerAssignmentChecker{source, description, context} + PointerAssignmentChecker{context, source, description} .set_lhsType(common::Clone(lhs.type)) .set_isContiguous(lhs.attrs.test(DummyDataObject::Attr::Contiguous)) .set_isVolatile(lhs.attrs.test(DummyDataObject::Attr::Volatile)) diff --git a/lib/semantics/pointer-assignment.h b/lib/semantics/pointer-assignment.h index 268a5592a59d..83652aef550c 100644 --- a/lib/semantics/pointer-assignment.h +++ b/lib/semantics/pointer-assignment.h @@ -10,6 +10,7 @@ #define FORTRAN_SEMANTICS_POINTER_ASSIGNMENT_H_ #include "type.h" +#include "../evaluate/expression.h" #include "../parser/char-block.h" #include @@ -25,6 +26,8 @@ namespace Fortran::semantics { class Symbol; +void CheckPointerAssignment(evaluate::FoldingContext &, + const evaluate::Assignment::PointerAssignment &); void CheckPointerAssignment( evaluate::FoldingContext &, const Symbol &lhs, const SomeExpr &rhs); void CheckPointerAssignment(evaluate::FoldingContext &, diff --git a/test/semantics/assign02.f90 b/test/semantics/assign02.f90 index 60f560b69e7f..5b3fa4f6da2b 100644 --- a/test/semantics/assign02.f90 +++ b/test/semantics/assign02.f90 @@ -30,18 +30,18 @@ subroutine s1 logical, target :: l real, pointer :: p p => r - !ERROR: TARGET type 'REAL(8)' is not compatible with POINTER type 'REAL(4)' + !ERROR: Target type REAL(8) is not compatible with pointer type REAL(4) p => r8 - !ERROR: TARGET type 'LOGICAL(4)' is not compatible with POINTER type 'REAL(4)' + !ERROR: Target type LOGICAL(4) is not compatible with pointer type REAL(4) p => l end - ! C1015 + ! C1019 subroutine s2 real, target :: r1(4), r2(4,4) real, pointer :: p(:) p => r1 - !ERROR: Rank of POINTER is 1, but TARGET has rank 2 + !ERROR: Pointer has rank 1 but target has rank 2 p => r2 end @@ -51,7 +51,7 @@ subroutine s3 type(t(2)), target :: x2 type(t(1)), pointer :: p p => x1 - !ERROR: TARGET type 't(k=2_4)' is not compatible with POINTER type 't(k=1_4)' + !ERROR: Target type t(k=2_4) is not compatible with pointer type t(k=1_4) p => x2 end diff --git a/test/semantics/assign03.f90 b/test/semantics/assign03.f90 index da8ffcb70590..54112f4f4097 100644 --- a/test/semantics/assign03.f90 +++ b/test/semantics/assign03.f90 @@ -108,4 +108,30 @@ subroutine s7 p_f => s_external end + ! C1017: bounds-spec + subroutine s8 + real, target :: x(10, 10) + real, pointer :: p(:, :) + p(2:,3:) => x + !ERROR: Pointer 'p' has rank 2 but the number of bounds specified is 1 + p(2:) => x + end + + ! bounds-remapping + subroutine s9 + real, target :: x(10, 10), y(100) + real, pointer :: p(:, :) + ! C1018 + !ERROR: Pointer 'p' has rank 2 but the number of bounds specified is 1 + p(1:100) => x + ! 10.2.2.3(9) + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + p(1:5,1:5) => x(1:10,::2) + ! 10.2.2.3(9) + !ERROR: Pointer bounds require 25 elements but target has only 20 + p(1:5,1:5) => x(:,1:2) + !OK - rhs has rank 1 and enough elements + p(1:5,1:5) => y(1:100:2) + end + end From 195d807ff221e4247ec5609e8e816e81dab5df94 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Fri, 24 Jan 2020 12:08:24 -0800 Subject: [PATCH 006/345] More checklist items I added more items when reviewing some actual pull request comments. --- documentation/PullRequestChecklist.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/documentation/PullRequestChecklist.md b/documentation/PullRequestChecklist.md index 782c3b61ca0e..46cef0c4c778 100644 --- a/documentation/PullRequestChecklist.md +++ b/documentation/PullRequestChecklist.md @@ -16,7 +16,9 @@ can also be used when reviewing pull requests. sure that you also added a test that causes that error message to appear and verifies its correctness. * Annotate the code and tests with appropriate references to constraint and - requirement numbers from the Fortran standard. + requirement numbers from the Fortran standard. Do not include the text of + the constraint or requirement, just its number. +* Alphabetize arbitrary lists of names. * Check dereferences of pointers and optionals where necessary. * Ensure that the scopes of all functions and variables are as local as possible. @@ -34,8 +36,9 @@ even though I've read the style guide, they regularly trip me up. clang-format will do this for most code. But you may need to break up long strings. * Review declarations for proper use of `constexpr` and `const`. -* Follow the C++ naming guidelines. Ensure that the names evoke their - purpose and are consistent with existing code. +* Follow the C++ [naming guidelines](C++style.md#naming). +* Ensure that the names evoke their purpose and are consistent with existing code. +* Used braced initializers. * Review pointer and reference types to make sure that you're using them appropriately. Note that the [C++ style guide](C++style.md) contains a section that describes all of the pointer types along with their From 839a91f1d699cd839767407bcdb1e384f2d2b730 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 16 Jan 2020 13:51:25 -0800 Subject: [PATCH 007/345] Drill down to a working implementation of the APIs for an internal formatted WRITE with no data list items. Improve argument names in io-api.h Bump up error number to not conflict with errno values Use Fortran::runtime::io namespace Add wrapper around malloc/free, allow use of unique_ptr with wrapper IoErrorHandler Revamp FormatContext, use virtual member functions Update comment syntax, allow for old C 12HHELLO, WORLD Remove files not yet ready for review Use std::forward Fix gcc build warnings Fix redundant filename in license boilerplate Reduce runtime dependence on compiler binary libraries, fixing shared lib builds --- include/flang/ISO_Fortran_binding.h | 5 +- runtime/CMakeLists.txt | 7 +- runtime/entry-names.h | 6 +- runtime/format.cc | 173 ++++++++++++++-------------- runtime/format.h | 44 ++++--- runtime/io-api.cc | 31 +++++ runtime/io-api.h | 61 +++++----- runtime/io-error.cc | 67 +++++++++++ runtime/io-error.h | 50 ++++++++ runtime/io-stmt.cc | 88 ++++++++++++++ runtime/io-stmt.h | 64 ++++++++++ runtime/magic-numbers.h | 4 +- runtime/main.cc | 40 +++++-- runtime/main.h | 14 ++- runtime/memory.cc | 33 ++++++ runtime/memory.h | 43 +++++++ runtime/terminator.cc | 6 + runtime/terminator.h | 8 ++ runtime/transformational.cc | 15 +-- test/runtime/CMakeLists.txt | 10 ++ test/runtime/format.cc | 87 ++++++++------ test/runtime/hello.cc | 33 ++++++ 22 files changed, 686 insertions(+), 203 deletions(-) create mode 100644 runtime/io-api.cc create mode 100644 runtime/io-error.cc create mode 100644 runtime/io-error.h create mode 100644 runtime/io-stmt.cc create mode 100644 runtime/io-stmt.h create mode 100644 runtime/memory.cc create mode 100644 runtime/memory.h create mode 100644 test/runtime/hello.cc diff --git a/include/flang/ISO_Fortran_binding.h b/include/flang/ISO_Fortran_binding.h index 0a014817db0e..b54f778fdec8 100644 --- a/include/flang/ISO_Fortran_binding.h +++ b/include/flang/ISO_Fortran_binding.h @@ -30,8 +30,9 @@ inline namespace Fortran_2018 { #define CFI_MAX_RANK 15 typedef unsigned char CFI_rank_t; -// This type is probably larger than a default Fortran INTEGER -// and should be used for all array indexing and loop bound calculations. +/* This type is probably larger than a default Fortran INTEGER + * and should be used for all array indexing and loop bound calculations. + */ typedef ptrdiff_t CFI_index_t; typedef unsigned char CFI_attribute_t; diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 523c7aac9ba9..3253f4b30899 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -11,7 +11,11 @@ add_library(FortranRuntime derived-type.cc descriptor.cc format.cc + io-api.cc + io-error.cc + io-stmt.cc main.cc + memory.cc stop.cc terminator.cc transformational.cc @@ -19,5 +23,6 @@ add_library(FortranRuntime ) target_link_libraries(FortranRuntime - FortranEvaluate + FortranCommon + FortranDecimal ) diff --git a/runtime/entry-names.h b/runtime/entry-names.h index db80fee10525..e2581ca33c9d 100644 --- a/runtime/entry-names.h +++ b/runtime/entry-names.h @@ -16,7 +16,7 @@ // runtime library must change in some way that breaks backward compatibility. #ifndef RTNAME -#define PREFIX _Fortran -#define REVISION A -#define RTNAME(name) PREFIX##REVISION##name +#define NAME_WITH_PREFIX_AND_REVISION(prefix, revision, name) \ + prefix##revision##name +#define RTNAME(name) NAME_WITH_PREFIX_AND_REVISION(_Fortran, A, name) #endif diff --git a/runtime/format.cc b/runtime/format.cc index b7a175dd220b..9324d1528a65 100644 --- a/runtime/format.cc +++ b/runtime/format.cc @@ -7,23 +7,43 @@ //===----------------------------------------------------------------------===// #include "format.h" +#include "io-stmt.h" #include "../lib/common/format.h" #include "../lib/decimal/decimal.h" #include -namespace Fortran::runtime { +namespace Fortran::runtime::io { + +// Default FormatContext virtual member functions +void FormatContext::Emit(const char *, std::size_t) { + Crash("Cannot emit data from this FORMAT string"); +} +void FormatContext::Emit(const char16_t *, std::size_t) { + Crash("Cannot emit data from this FORMAT string"); +} +void FormatContext::Emit(const char32_t *, std::size_t) { + Crash("Cannot emit data from this FORMAT string"); +} +void FormatContext::HandleSlash(int) { + Crash("A / control edit descriptor may not appear in this FORMAT string"); +} +void FormatContext::HandleAbsolutePosition(int) { + Crash("A Tn control edit descriptor may not appear in this FORMAT string"); +} +void FormatContext::HandleRelativePosition(int) { + Crash("An nX, TLn, or TRn control edit descriptor may not appear in this " + "FORMAT string"); +} template -FormatControl::FormatControl(FormatContext &context, const CHAR *format, - std::size_t formatLength, const MutableModes &modes, int maxHeight) - : context_{context}, modes_{modes}, maxHeight_{static_cast( - maxHeight)}, - format_{format}, formatLength_{static_cast(formatLength)} { +FormatControl::FormatControl(Terminator &terminator, const CHAR *format, + std::size_t formatLength, int maxHeight) + : maxHeight_{static_cast(maxHeight)}, format_{format}, + formatLength_{static_cast(formatLength)} { // The additional two items are for the whole string and a // repeated non-parenthesized edit descriptor. if (maxHeight > std::numeric_limits::max()) { - context_.terminator.Crash( - "internal Fortran runtime error: maxHeight %d", maxHeight); + terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); } stack_[0].start = offset_; stack_[0].remaining = Iteration::unlimited; // 13.4(8) @@ -43,38 +63,23 @@ int FormatControl::GetMaxParenthesisNesting( return validator.maxNesting(); } -static void HandleCharacterLiteral( - FormatContext &context, const char *str, std::size_t chars) { - if (context.handleCharacterLiteral1) { - context.handleCharacterLiteral1(str, chars); - } -} - -static void HandleCharacterLiteral( - FormatContext &context, const char16_t *str, std::size_t chars) { - if (context.handleCharacterLiteral2) { - context.handleCharacterLiteral2(str, chars); - } -} - -static void HandleCharacterLiteral( - FormatContext &context, const char32_t *str, std::size_t chars) { - if (context.handleCharacterLiteral4) { - context.handleCharacterLiteral4(str, chars); - } -} - -template int FormatControl::GetIntField(CHAR firstCh) { +template +int FormatControl::GetIntField(Terminator &terminator, CHAR firstCh) { CHAR ch{firstCh ? firstCh : PeekNext()}; - if (ch < '0' || ch > '9') { - context_.terminator.Crash( + if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) { + terminator.Crash( "Invalid FORMAT: integer expected at '%c'", static_cast(ch)); } int result{0}; + bool negate{ch == '-'}; + if (negate) { + firstCh = '\0'; + ch = PeekNext(); + } while (ch >= '0' && ch <= '9') { if (result > std::numeric_limits::max() / 10 - (static_cast(ch) - '0')) { - context_.terminator.Crash("FORMAT integer field out of range"); + terminator.Crash("FORMAT integer field out of range"); } result = 10 * result + ch - '0'; if (firstCh) { @@ -84,11 +89,15 @@ template int FormatControl::GetIntField(CHAR firstCh) { } ch = PeekNext(); } + if (negate && (result *= -1) > 0) { + terminator.Crash("FORMAT integer field out of range"); + } return result; } -static void HandleControl(MutableModes &modes, std::uint16_t &scale, - FormatContext &context, char ch, char next, int n) { +static void HandleControl( + FormatContext &context, std::uint16_t &scale, char ch, char next, int n) { + MutableModes &modes{context.mutableModes()}; switch (ch) { case 'B': if (next == 'Z') { @@ -130,9 +139,7 @@ static void HandleControl(MutableModes &modes, std::uint16_t &scale, break; case 'X': if (!next) { - if (context.handleRelativePosition) { - context.handleRelativePosition(n); - } + context.HandleRelativePosition(n); return; } break; @@ -148,25 +155,20 @@ static void HandleControl(MutableModes &modes, std::uint16_t &scale, break; case 'T': { if (!next) { // Tn - if (context.handleAbsolutePosition) { - context.handleAbsolutePosition(n); - } + context.HandleAbsolutePosition(n); return; } if (next == 'L' || next == 'R') { // TLn & TRn - if (context.handleRelativePosition) { - context.handleRelativePosition(next == 'L' ? -n : n); - } + context.HandleRelativePosition(next == 'L' ? -n : n); return; } } break; default: break; } if (next) { - context.terminator.Crash( - "Unknown '%c%c' edit descriptor in FORMAT", ch, next); + context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next); } else { - context.terminator.Crash("Unknown '%c' edit descriptor in FORMAT", ch); + context.Crash("Unknown '%c' edit descriptor in FORMAT", ch); } } @@ -174,35 +176,34 @@ static void HandleControl(MutableModes &modes, std::uint16_t &scale, // Handles all repetition counts and control edit descriptors. // Generally assumes that the format string has survived the common // format validator gauntlet. -template int FormatControl::CueUpNextDataEdit(bool stop) { +template +int FormatControl::CueUpNextDataEdit(FormatContext &context, bool stop) { int unlimitedLoopCheck{-1}; while (true) { std::optional repeat; bool unlimited{false}; - CHAR ch{Capitalize(GetNextChar())}; + CHAR ch{Capitalize(GetNextChar(context))}; while (ch == ',' || ch == ':') { // Skip commas, and don't complain if they're missing; the format // validator does that. if (stop && ch == ':') { return 0; } - ch = Capitalize(GetNextChar()); + ch = Capitalize(GetNextChar(context)); } - if (ch >= '0' && ch <= '9') { // repeat count - repeat = GetIntField(ch); - ch = GetNextChar(); + if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { + repeat = GetIntField(context, ch); + ch = GetNextChar(context); } else if (ch == '*') { unlimited = true; - ch = GetNextChar(); + ch = GetNextChar(context); if (ch != '(') { - context_.terminator.Crash( - "Invalid FORMAT: '*' may appear only before '('"); + context.Crash("Invalid FORMAT: '*' may appear only before '('"); } } if (ch == '(') { if (height_ >= maxHeight_) { - context_.terminator.Crash( - "FORMAT stack overflow: too many nested parentheses"); + context.Crash("FORMAT stack overflow: too many nested parentheses"); } stack_[height_].start = offset_ - 1; // the '(' if (unlimited || height_ == 0) { @@ -218,15 +219,18 @@ template int FormatControl::CueUpNextDataEdit(bool stop) { } ++height_; } else if (height_ == 0) { - context_.terminator.Crash("FORMAT lacks initial '('"); + context.Crash("FORMAT lacks initial '('"); } else if (ch == ')') { - if (height_ == 1 && stop) { - return 0; // end of FORMAT and no data items remain + if (height_ == 1) { + if (stop) { + return 0; // end of FORMAT and no data items remain + } + context.HandleSlash(); // implied / before rightmost ) } if (stack_[height_ - 1].remaining == Iteration::unlimited) { offset_ = stack_[height_ - 1].start + 1; if (offset_ == unlimitedLoopCheck) { - context_.terminator.Crash( + context.Crash( "Unlimited repetition in FORMAT lacks data edit descriptors"); } } else if (stack_[height_ - 1].remaining-- > 0) { @@ -242,8 +246,7 @@ template int FormatControl::CueUpNextDataEdit(bool stop) { ++offset_; } if (offset_ >= formatLength_) { - context_.terminator.Crash( - "FORMAT missing closing quote on character literal"); + context.Crash("FORMAT missing closing quote on character literal"); } ++offset_; std::size_t chars{ @@ -255,14 +258,13 @@ template int FormatControl::CueUpNextDataEdit(bool stop) { } else { --chars; } - HandleCharacterLiteral(context_, format_ + start, chars); + context.Emit(format_ + start, chars); } else if (ch == 'H') { // 9HHOLLERITH if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { - context_.terminator.Crash("Invalid width on Hollerith in FORMAT"); + context.Crash("Invalid width on Hollerith in FORMAT"); } - HandleCharacterLiteral( - context_, format_ + offset_, static_cast(*repeat)); + context.Emit(format_ + offset_, static_cast(*repeat)); offset_ += *repeat; } else if (ch >= 'A' && ch <= 'Z') { int start{offset_ - 1}; @@ -276,35 +278,33 @@ template int FormatControl::CueUpNextDataEdit(bool stop) { ch == 'F' || ch == 'D' || ch == 'G'))) { // Data edit descriptor found offset_ = start; - return repeat ? *repeat : 1; + return repeat && *repeat > 0 ? *repeat : 1; } else { // Control edit descriptor if (ch == 'T') { // Tn, TLn, TRn - repeat = GetIntField(); + repeat = GetIntField(context); } - HandleControl(modes_, scale_, context_, static_cast(ch), - static_cast(next), repeat ? *repeat : 1); + HandleControl(context, scale_, static_cast(ch), + static_cast(next), repeat && *repeat > 0 ? *repeat : 1); } } else if (ch == '/') { - if (context_.handleSlash) { - context_.handleSlash(); - } + context.HandleSlash(repeat && *repeat > 0 ? *repeat : 1); } else { - context_.terminator.Crash( - "Invalid character '%c' in FORMAT", static_cast(ch)); + context.Crash("Invalid character '%c' in FORMAT", static_cast(ch)); } } } template -void FormatControl::GetNext(DataEdit &edit, int maxRepeat) { +void FormatControl::GetNext( + FormatContext &context, DataEdit &edit, int maxRepeat) { // TODO: DT editing // Return the next data edit descriptor - int repeat{CueUpNextDataEdit()}; + int repeat{CueUpNextDataEdit(context)}; auto start{offset_}; - edit.descriptor = static_cast(Capitalize(GetNextChar())); + edit.descriptor = static_cast(Capitalize(GetNextChar(context))); if (edit.descriptor == 'E') { edit.variation = static_cast(Capitalize(PeekNext())); if (edit.variation >= 'A' && edit.variation <= 'Z') { @@ -316,15 +316,15 @@ void FormatControl::GetNext(DataEdit &edit, int maxRepeat) { edit.variation = '\0'; } - edit.width = GetIntField(); - edit.modes = modes_; + edit.width = GetIntField(context); + edit.modes = context.mutableModes(); if (PeekNext() == '.') { ++offset_; - edit.digits = GetIntField(); + edit.digits = GetIntField(context); CHAR ch{PeekNext()}; if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { ++offset_; - edit.expoDigits = GetIntField(); + edit.expoDigits = GetIntField(context); } else { edit.expoDigits.reset(); } @@ -355,8 +355,9 @@ void FormatControl::GetNext(DataEdit &edit, int maxRepeat) { } } -template void FormatControl::FinishOutput() { - CueUpNextDataEdit(true /* stop at colon or end of FORMAT */); +template +void FormatControl::FinishOutput(FormatContext &context) { + CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); } template class FormatControl; diff --git a/runtime/format.h b/runtime/format.h index 94025f6760db..1f576d24bb46 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -16,7 +16,7 @@ #include #include -namespace Fortran::runtime { +namespace Fortran::runtime::io { enum EditingFlags { blankZero = 1, // BLANK=ZERO or BZ edit @@ -27,6 +27,8 @@ enum EditingFlags { struct MutableModes { std::uint8_t editingFlags{0}; // BN, DP, SS common::RoundingMode roundingMode{common::RoundingMode::TiesToEven}; // RN + bool pad{false}; // PAD= mode on READ + char delim{'\0'}; // DELIM= }; // A single edit descriptor extracted from a FORMAT @@ -40,14 +42,20 @@ struct DataEdit { int repeat{1}; }; -struct FormatContext { - Terminator &terminator; - void (*handleCharacterLiteral1)(const char *, std::size_t){nullptr}; - void (*handleCharacterLiteral2)(const char16_t *, std::size_t){nullptr}; - void (*handleCharacterLiteral4)(const char32_t *, std::size_t){nullptr}; - void (*handleSlash)(){nullptr}; - void (*handleAbsolutePosition)(int){nullptr}; // Tn - void (*handleRelativePosition)(int){nullptr}; // nX, TRn, TLn (negated) +class FormatContext : virtual public Terminator { +public: + FormatContext() {} + explicit FormatContext(const MutableModes &modes) : mutableModes_{modes} {} + virtual void Emit(const char *, std::size_t); + virtual void Emit(const char16_t *, std::size_t); + virtual void Emit(const char32_t *, std::size_t); + virtual void HandleSlash(int = 1); + virtual void HandleRelativePosition(int); + virtual void HandleAbsolutePosition(int); + MutableModes &mutableModes() { return mutableModes_; } + +private: + MutableModes mutableModes_; }; // Generates a sequence of DataEdits from a FORMAT statement or @@ -55,8 +63,8 @@ struct FormatContext { // Errors are fatal. See clause 13.4 in Fortran 2018 for background. template class FormatControl { public: - FormatControl(FormatContext &, const CHAR *format, std::size_t formatLength, - const MutableModes &initialModes, int maxHeight = maxMaxHeight); + FormatControl(Terminator &, const CHAR *format, std::size_t formatLength, + int maxHeight = maxMaxHeight); // Determines the max parenthesis nesting level by scanning and validating // the FORMAT string. @@ -71,10 +79,10 @@ template class FormatControl { // Extracts the next data edit descriptor, handling control edit descriptors // along the way. - void GetNext(DataEdit &, int maxRepeat = 1); + void GetNext(FormatContext &, DataEdit &, int maxRepeat = 1); // Emit any remaining character literals after the last data item. - void FinishOutput(); + void FinishOutput(FormatContext &); private: static constexpr std::uint8_t maxMaxHeight{100}; @@ -94,21 +102,21 @@ template class FormatControl { SkipBlanks(); return offset_ < formatLength_ ? format_[offset_] : '\0'; } - CHAR GetNextChar() { + CHAR GetNextChar(Terminator &terminator) { SkipBlanks(); if (offset_ >= formatLength_) { - context_.terminator.Crash("FORMAT missing at least one ')'"); + terminator.Crash("FORMAT missing at least one ')'"); } return format_[offset_++]; } - int GetIntField(CHAR firstCh = '\0'); + int GetIntField(Terminator &, CHAR firstCh = '\0'); // Advances through the FORMAT until the next data edit // descriptor has been found; handles control edit descriptors // along the way. Returns the repeat count that appeared // before the descriptor (defaulting to 1) and leaves offset_ // pointing to the data edit. - int CueUpNextDataEdit(bool stop = false); + int CueUpNextDataEdit(FormatContext &, bool stop = false); static constexpr CHAR Capitalize(CHAR ch) { return ch >= 'a' && ch <= 'z' ? ch + 'A' - 'a' : ch; @@ -117,8 +125,6 @@ template class FormatControl { // Data members are arranged and typed so as to reduce size. // This structure may be allocated in stack space loaned by the // user program for internal I/O. - FormatContext &context_; - MutableModes modes_; std::uint16_t scale_{0}; // kP const std::uint8_t maxHeight_{maxMaxHeight}; std::uint8_t height_{0}; diff --git a/runtime/io-api.cc b/runtime/io-api.cc new file mode 100644 index 000000000000..a140e0e8cf99 --- /dev/null +++ b/runtime/io-api.cc @@ -0,0 +1,31 @@ +//===-- runtime/io.cc -------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "io-api.h" +#include "format.h" +#include "io-stmt.h" +#include "memory.h" +#include "terminator.h" +#include +#include + +namespace Fortran::runtime::io { + +Cookie IONAME(BeginInternalFormattedOutput)(char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + void ** /*scratchArea*/, std::size_t /*scratchBytes*/, + const char *sourceFile, int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return &New>{}(oom, internal, + internalLength, format, formatLength, sourceFile, sourceLine); +} + +enum Iostat IONAME(EndIoStatement)(Cookie io) { + return static_cast(io->EndIoStatement()); +} +} diff --git a/runtime/io-api.h b/runtime/io-api.h index f41308592a44..20f0f218e041 100644 --- a/runtime/io-api.h +++ b/runtime/io-api.h @@ -19,7 +19,7 @@ namespace Fortran::runtime { class Descriptor; class NamelistGroup; -}; +} namespace Fortran::runtime::io { @@ -60,30 +60,32 @@ Cookie IONAME(BeginInternalArrayListInput)(const Descriptor &, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &, - const char *format, std::size_t formatBytes, void **scratchArea = nullptr, + const char *format, std::size_t formatLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &, - const char *format, std::size_t formatBytes, void **scratchArea = nullptr, + const char *format, std::size_t formatLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); // Internal I/O to/from a default-kind character scalar can avoid a // descriptor. -Cookie IONAME(BeginInternalListOutput)(char *internal, std::size_t bytes, - void **scratchArea = nullptr, std::size_t scratchBytes = 0, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInternalListInput)(char *internal, std::size_t bytes, - void **scratchArea = nullptr, std::size_t scratchBytes = 0, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInternalFormattedOutput)(char *internal, std::size_t bytes, - const char *format, std::size_t formatBytes, void **scratchArea = nullptr, +Cookie IONAME(BeginInternalListOutput)(char *internal, + std::size_t internalLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInternalFormattedInput)(char *internal, std::size_t bytes, - const char *format, std::size_t formatBytes, void **scratchArea = nullptr, +Cookie IONAME(BeginInternalListInput)(char *internal, + std::size_t internalLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IONAME(BeginInternalFormattedOutput)(char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + void **scratchArea = nullptr, std::size_t scratchBytes = 0, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IONAME(BeginInternalFormattedInput)(char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + void **scratchArea = nullptr, std::size_t scratchBytes = 0, + const char *sourceFile = nullptr, int sourceLine = 0); // Internal namelist I/O Cookie IONAME(BeginInternalNamelistOutput)(const Descriptor &, @@ -110,10 +112,10 @@ Cookie IONAME(BeginUnformattedOutput)(ExternalUnit = DefaultUnit, const char *sourceFile = nullptr, int sourceLine = 0); Cookie IONAME(BeginUnformattedInput)(ExternalUnit = DefaultUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginNamelistOutput)(const NamelistGroup &, +Cookie IONAME(BeginExternalNamelistOutput)(const NamelistGroup &, ExternalUnit = DefaultUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginNamelistInput)(const NamelistGroup &, +Cookie IONAME(BeginExternalNamelistInput)(const NamelistGroup &, ExternalUnit = DefaultUnit, const char *sourceFile = nullptr, int sourceLine = 0); @@ -150,7 +152,8 @@ Cookie IONAME(BeginInquireUnit)( ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); Cookie IONAME(BeginInquireFile)(const char *, std::size_t, int kind = 1, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInquireIoLength(const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IONAME(BeginInquireIoLength)( + const char *sourceFile = nullptr, int sourceLine = 0); // If an I/O statement has any IOSTAT=, ERR=, END=, or EOR= specifiers, // call EnableHandlers() immediately after the Begin...() call. @@ -228,28 +231,28 @@ bool IONAME(InputLogical)(Cookie, bool &); // SetDelim(), GetIoMsg(), SetPad(), SetRound(), & SetSign() // are also acceptable for OPEN. // ACCESS=SEQUENTIAL, DIRECT, STREAM -bool IONAME(SetAccess, Cookie, const char *, std::size_t); +bool IONAME(SetAccess)(Cookie, const char *, std::size_t); // ACTION=READ, WRITE, or READWRITE -bool IONAME(SetAction, Cookie, const char *, std::size_t); +bool IONAME(SetAction)(Cookie, const char *, std::size_t); // ASYNCHRONOUS=YES, NO -bool IONAME(SetAsynchronous, Cookie, const char *, std::size_t); +bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t); // ENCODING=UTF-8, DEFAULT -bool IONAME(SetEncoding, Cookie, const char *, std::size_t); +bool IONAME(SetEncoding)(Cookie, const char *, std::size_t); // FORM=FORMATTED, UNFORMATTED -bool IONAME(SetForm, Cookie, const char *, std::size_t); +bool IONAME(SetForm)(Cookie, const char *, std::size_t); // POSITION=ASIS, REWIND, APPEND -bool IONAME(SetPosition, Cookie, const char *, std::size_t); -bool IONAME(SetRecl, Cookie, std::size_t); // RECL= +bool IONAME(SetPosition)(Cookie, const char *, std::size_t); +bool IONAME(SetRecl)(Cookie, std::size_t); // RECL= // STATUS can be set during an OPEN or CLOSE statement. // For OPEN: STATUS=OLD, NEW, SCRATCH, REPLACE, UNKNOWN // For CLOSE: STATUS=KEEP, DELETE -bool IONAME(SetStatus, Cookie, const char *, std::size_t); +bool IONAME(SetStatus)(Cookie, const char *, std::size_t); // SetFile() may pass a CHARACTER argument of non-default kind, // and such filenames are converted to UTF-8 before being // presented to the filesystem. -bool IONAME(SetFile, Cookie, const char *, std::size_t, int kind = 1); +bool IONAME(SetFile)(Cookie, const char *, std::size_t, int kind = 1); // GetNewUnit() must not be called until after all Set...() // connection list specifiers have been called after @@ -271,13 +274,15 @@ void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG= // ACCESS, ACTION, ASYNCHRONOUS, BLANK, DECIMAL, DELIM, DIRECT, ENCODING, // FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND, // SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE: -bool IONAME(InquireCharacter)(Cookie, const char *specifier, char *, std::size_t); +bool IONAME(InquireCharacter)( + Cookie, const char *specifier, char *, std::size_t); // EXIST, NAMED, OPENED, and PENDING (without ID): bool IONAME(InquireLogical)(Cookie, const char *specifier, bool &); // PENDING with ID bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &); // NEXTREC, NUMBER, POS, RECL, SIZE -bool IONAME(InquireInteger64)(Cookie, const char *specifier, std::int64_t &, int kind = 8); +bool IONAME(InquireInteger64)( + Cookie, const char *specifier, std::int64_t &, int kind = 8); // The value of IOSTAT= is zero when no error, end-of-record, // or end-of-file condition has arisen; errors are positive values. @@ -307,6 +312,6 @@ enum Iostat { // rather than by terminating the image. enum Iostat IONAME(EndIoStatement)(Cookie); -}; // extern "C" +} // extern "C" } #endif diff --git a/runtime/io-error.cc b/runtime/io-error.cc new file mode 100644 index 000000000000..74dcef8f3c3e --- /dev/null +++ b/runtime/io-error.cc @@ -0,0 +1,67 @@ +//===-- runtime/io-error.cc -------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "io-error.h" +#include "magic-numbers.h" +#include +#include +#include + +namespace Fortran::runtime::io { + +void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) { + flags_ = 0; + ioStat_ = 0; + hitEnd_ = false; + hitEor_ = false; + SetLocation(sourceFileName, sourceLine); +} + +void IoErrorHandler::SignalError(int iostatOrErrno) { + if (iostatOrErrno != 0) { + if (flags_ & hasIoStat) { + if (!ioStat_) { + ioStat_ = iostatOrErrno; + } + } else if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT) { + Crash("INQUIRE on internal unit"); + } else { + Crash("I/O error %d: %s", iostatOrErrno, std::strerror(iostatOrErrno)); + } + } +} + +void IoErrorHandler::SignalEnd() { + if (flags_ & hasEnd) { + hitEnd_ = true; + } else { + Crash("End of file"); + } +} + +void IoErrorHandler::SignalEor() { + if (flags_ & hasEor) { + hitEor_ = true; + } else { + Crash("End of record"); + } +} + +int IoErrorHandler::GetIoStat() const { + if (ioStat_) { + return ioStat_; + } else if (hitEnd_) { + return FORTRAN_RUNTIME_IOSTAT_END; + } else if (hitEor_) { + return FORTRAN_RUNTIME_IOSTAT_EOR; + } else { + return 0; + } +} + +} diff --git a/runtime/io-error.h b/runtime/io-error.h new file mode 100644 index 000000000000..08aea4e9a506 --- /dev/null +++ b/runtime/io-error.h @@ -0,0 +1,50 @@ +//===-- runtime/io-error.h --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Distinguishes I/O error conditions; fatal ones lead to termination, +// and those that the user program has chosen to handle are recorded +// so that the highest-priority one can be returned as IOSTAT=. + +#ifndef FORTRAN_RUNTIME_IO_ERROR_H_ +#define FORTRAN_RUNTIME_IO_ERROR_H_ + +#include "terminator.h" +#include + +namespace Fortran::runtime::io { + +class IoErrorHandler : virtual public Terminator { +public: + using Terminator::Terminator; + void Begin(const char *sourceFileName, int sourceLine); + void HasIoStat() { flags_ |= hasIoStat; } + void HasErrLabel() { flags_ |= hasErr; } + void HasEndLabel() { flags_ |= hasEnd; } + void HasEorLabel() { flags_ |= hasEor; } + + void SignalError(int iostatOrErrno); + void SignalEnd(); + void SignalEor(); + + int GetIoStat() const; + +private: + enum Flag : std::uint8_t { + hasIoStat = 1, // IOSTAT= + hasErr = 2, // ERR= + hasEnd = 4, // END= + hasEor = 8, // EOR= + }; + std::uint8_t flags_{0}; + bool hitEnd_{false}; + bool hitEor_{false}; + int ioStat_{0}; +}; + +} +#endif // FORTRAN_RUNTIME_IO_ERROR_H_ diff --git a/runtime/io-stmt.cc b/runtime/io-stmt.cc new file mode 100644 index 000000000000..221cd2de9c97 --- /dev/null +++ b/runtime/io-stmt.cc @@ -0,0 +1,88 @@ +//===-- runtime/io-stmt.cc --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "io-stmt.h" +#include "memory.h" +#include +#include + +namespace Fortran::runtime::io { + +int IoStatementState::EndIoStatement() { return GetIoStat(); } + +int InternalIoStatementState::EndIoStatement() { + auto result{GetIoStat()}; + if (free_) { + FreeMemory(this); + } + return result; +} + +InternalIoStatementState::InternalIoStatementState( + const char *sourceFile, int sourceLine) + : IoStatementState(sourceFile, sourceLine) {} + +template +InternalFormattedIoStatementState::InternalFormattedIoStatementState(Buffer internal, + std::size_t internalLength, const CHAR *format, std::size_t formatLength, + const char *sourceFile, int sourceLine) + : InternalIoStatementState{sourceFile, sourceLine}, FormatContext{}, + internal_{internal}, internalLength_{internalLength}, format_{*this, format, + formatLength} { + std::fill_n(internal_, internalLength_, static_cast(' ')); +} + +template +void InternalFormattedIoStatementState::Emit( + const CHAR *data, std::size_t chars) { + if constexpr (isInput) { + FormatContext::Emit(data, chars); // default Crash() + } else if (at_ + chars > internalLength_) { + SignalEor(); + } else { + std::memcpy(internal_ + at_, data, chars * sizeof(CHAR)); + at_ += chars; + } +} + +template +void InternalFormattedIoStatementState::HandleAbsolutePosition( + int n) { + if (n < 0 || static_cast(n) >= internalLength_) { + Crash("T%d control edit descriptor is out of range", n); + } else { + at_ = n; + } +} + +template +void InternalFormattedIoStatementState::HandleRelativePosition( + int n) { + if (n < 0) { + at_ -= std::min(at_, -static_cast(n)); + } else { + at_ += n; + if (at_ > internalLength_) { + Crash("TR%d control edit descriptor is out of range", n); + } + } +} + +template +int InternalFormattedIoStatementState::EndIoStatement() { + format_.FinishOutput(*this); + auto result{GetIoStat()}; + if (free_) { + FreeMemory(this); + } + return result; +} + +template class InternalFormattedIoStatementState; +} diff --git a/runtime/io-stmt.h b/runtime/io-stmt.h new file mode 100644 index 000000000000..2e70efa591be --- /dev/null +++ b/runtime/io-stmt.h @@ -0,0 +1,64 @@ +//===-- runtime/io-stmt.h ---------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Represents state of an I/O statement in progress + +#ifndef FORTRAN_RUNTIME_IO_STMT_H_ +#define FORTRAN_RUNTIME_IO_STMT_H_ + +#include "descriptor.h" +#include "format.h" +#include "io-error.h" +#include + +namespace Fortran::runtime::io { + +class IoStatementState : public IoErrorHandler { +public: + using IoErrorHandler::IoErrorHandler; + virtual int EndIoStatement(); + +protected: +}; + +class InternalIoStatementState : public IoStatementState { +public: + InternalIoStatementState(const char *sourceFile, int sourceLine); + virtual int EndIoStatement(); + +protected: + bool free_{true}; +}; + +template +class InternalFormattedIoStatementState : public InternalIoStatementState, + private FormatContext { +private: + using Buffer = std::conditional_t; + +public: + InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, + const CHAR *format, std::size_t formatLength, + const char *sourceFile = nullptr, int sourceLine = 0); + void Emit(const CHAR *, std::size_t chars); + // TODO pmk: void HandleSlash(int); + void HandleRelativePosition(int); + void HandleAbsolutePosition(int); + int EndIoStatement(); + +private: + Buffer internal_; + std::size_t internalLength_; + std::size_t at_{0}; + FormatControl format_; // must be last, may be partial +}; + +extern template class InternalFormattedIoStatementState; + +} +#endif // FORTRAN_RUNTIME_IO_STMT_H_ diff --git a/runtime/magic-numbers.h b/runtime/magic-numbers.h index b41666a89130..b60722894009 100644 --- a/runtime/magic-numbers.h +++ b/runtime/magic-numbers.h @@ -17,6 +17,8 @@ These include: to an IOSTAT= or STAT= specifier on a Fortran I/O statement or coindexed data reference (see Fortran 2018 12.11.5, 16.10.2, and 16.10.2.33) +Codes from , e.g. ENOENT, are assumed to be positive +and are used "raw" as IOSTAT values. #endif #ifndef FORTRAN_RUNTIME_MAGIC_NUMBERS_H_ #define FORTRAN_RUNTIME_MAGIC_NUMBERS_H_ @@ -24,7 +26,7 @@ These include: #define FORTRAN_RUNTIME_IOSTAT_END (-1) #define FORTRAN_RUNTIME_IOSTAT_EOR (-2) #define FORTRAN_RUNTIME_IOSTAT_FLUSH (-3) -#define FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT 1 +#define FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT 255 #define FORTRAN_RUNTIME_STAT_FAILED_IMAGE 10 #define FORTRAN_RUNTIME_STAT_LOCKED 11 diff --git a/runtime/main.cc b/runtime/main.cc index 125261409bda..ce36bc2b9bfa 100644 --- a/runtime/main.cc +++ b/runtime/main.cc @@ -7,24 +7,37 @@ //===----------------------------------------------------------------------===// #include "main.h" +#include "io-stmt.h" #include "terminator.h" #include +#include #include +#include namespace Fortran::runtime { -int argc; -const char **argv; -const char **envp; -} +ExecutionEnvironment executionEnvironment; -extern "C" { +void ExecutionEnvironment::Configure( + int ac, const char *av[], const char *env[]) { + argc = ac; + argv = av; + envp = env; + listDirectedOutputLineLengthLimit = 79; // PGI default -void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { - - Fortran::runtime::argc = argc; - Fortran::runtime::argv = argv; - Fortran::runtime::envp = envp; + if (auto *x{std::getenv("FORT_FMT_RECL")}) { + char *end; + auto n{std::strtol(x, &end, 10)}; + if (n > 0 && n < std::numeric_limits::max() && *end == '\0') { + listDirectedOutputLineLengthLimit = n; + } else { + std::fprintf( + stderr, "Fortran runtime: FORT_FMT_RECL=%s is invalid; ignored\n", x); + } + } +} +} +static void ConfigureFloatingPoint() { #ifdef feclearexcept // a macro in some environments; omit std:: feclearexcept(FE_ALL_EXCEPT); #else @@ -35,8 +48,13 @@ void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { #else std::fesetround(FE_TONEAREST); #endif +} +extern "C" { + +void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { std::atexit(Fortran::runtime::NotifyOtherImagesOfNormalEnd); - // TODO: Runtime configuration settings from environment + Fortran::runtime::executionEnvironment.Configure(argc, argv, envp); + ConfigureFloatingPoint(); } } diff --git a/runtime/main.h b/runtime/main.h index 9a076b96cb7d..c966a3674351 100644 --- a/runtime/main.h +++ b/runtime/main.h @@ -1,4 +1,4 @@ -//===-- runtime/main.cc -----------------------------------------*- C++ -*-===// +//===-- runtime/main.h ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,9 +12,15 @@ #include "entry-names.h" namespace Fortran::runtime { -extern int argc; -extern const char **argv; -extern const char **envp; +struct ExecutionEnvironment { + void Configure(int argc, const char *argv[], const char *envp[]); + + int argc; + const char **argv; + const char **envp; + int listDirectedOutputLineLengthLimit; +}; +extern ExecutionEnvironment executionEnvironment; } extern "C" { diff --git a/runtime/memory.cc b/runtime/memory.cc new file mode 100644 index 000000000000..ab7c63c24b74 --- /dev/null +++ b/runtime/memory.cc @@ -0,0 +1,33 @@ +//===-- runtime/memory.cc ---------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "memory.h" +#include "terminator.h" +#include + +namespace Fortran::runtime { + +void *AllocateMemoryOrCrash(Terminator &terminator, std::size_t bytes) { + if (void *p{std::malloc(bytes)}) { + return p; + } + if (bytes > 0) { + terminator.Crash( + "Fortran runtime internal error: out of memory, needed %zd bytes", + bytes); + } + return nullptr; +} + +void FreeMemory(void *p) { std::free(p); } + +void FreeMemoryAndNullify(void *&p) { + std::free(p); + p = nullptr; +} +} diff --git a/runtime/memory.h b/runtime/memory.h new file mode 100644 index 000000000000..f44ceedb3cd1 --- /dev/null +++ b/runtime/memory.h @@ -0,0 +1,43 @@ +//===-- runtime/memory.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Thin wrapper around malloc()/free() to isolate the dependency, +// ease porting, and provide an owning pointer. + +#ifndef FORTRAN_RUNTIME_MEMORY_H_ +#define FORTRAN_RUNTIME_MEMORY_H_ + +#include + +namespace Fortran::runtime { + +class Terminator; + +void *AllocateMemoryOrCrash(Terminator &, std::size_t bytes); +template A &AllocateOrCrash(Terminator &t) { + return *reinterpret_cast(AllocateMemoryOrCrash(t, sizeof(A))); +} +void FreeMemory(void *); +void FreeMemoryAndNullify(void *&); + +template struct New { + template A &operator()(Terminator &terminator, X&&... x) { + return *new (AllocateMemoryOrCrash(terminator, sizeof(A))) A{std::forward(x)...}; + } +}; + +namespace { +template class OwningPtrDeleter { + void operator()(A *p) { FreeMemory(p); } +}; +} + +template using OwningPtr = std::unique_ptr>; +} + +#endif // FORTRAN_RUNTIME_MEMORY_H_ diff --git a/runtime/terminator.cc b/runtime/terminator.cc index 797763854ad6..e2e9b7b327b1 100644 --- a/runtime/terminator.cc +++ b/runtime/terminator.cc @@ -35,6 +35,12 @@ namespace Fortran::runtime { std::abort(); } +[[noreturn]] void Terminator::CheckFailed( + const char *predicate, const char *file, int line) { + Crash("Internal error: RUNTIME_CHECK(%s) failed at %s(%d)", predicate, file, + line); +} + void NotifyOtherImagesOfNormalEnd() { // TODO } diff --git a/runtime/terminator.h b/runtime/terminator.h index 99e0723e7fde..5fe381e5167a 100644 --- a/runtime/terminator.h +++ b/runtime/terminator.h @@ -29,12 +29,20 @@ class Terminator { } [[noreturn]] void Crash(const char *message, ...); [[noreturn]] void CrashArgs(const char *message, va_list &); + [[noreturn]] void CheckFailed( + const char *predicate, const char *file, int line); private: const char *sourceFileName_{nullptr}; int sourceLine_{0}; }; +#define RUNTIME_CHECK(terminator, pred) \ + if (pred) \ + ; \ + else \ + (terminator).CheckFailed(#pred, __FILE__, __LINE__) + void NotifyOtherImagesOfNormalEnd(); void NotifyOtherImagesOfFailImageStatement(); void NotifyOtherImagesOfErrorTermination(); diff --git a/runtime/transformational.cc b/runtime/transformational.cc index 7a02bd7edb18..6c1144717e6c 100644 --- a/runtime/transformational.cc +++ b/runtime/transformational.cc @@ -8,7 +8,6 @@ #include "transformational.h" #include "../lib/common/idioms.h" -#include "../lib/evaluate/integer.h" #include #include #include @@ -16,18 +15,12 @@ namespace Fortran::runtime { -template inline std::int64_t LoadInt64(const char *p) { - using Int = const evaluate::value::Integer; - Int *ip{reinterpret_cast(p)}; - return ip->ToInt64(); -} - static inline std::int64_t GetInt64(const char *p, std::size_t bytes) { switch (bytes) { - case 1: return LoadInt64<8>(p); - case 2: return LoadInt64<16>(p); - case 4: return LoadInt64<32>(p); - case 8: return LoadInt64<64>(p); + case 1: return *reinterpret_cast(p); + case 2: return *reinterpret_cast(p); + case 4: return *reinterpret_cast(p); + case 8: return *reinterpret_cast(p); default: CRASH_NO_CASE; } } diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index a9ef7c4ef342..5cbc230d7eed 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -19,3 +19,13 @@ target_link_libraries(format-test ) add_test(Format format-test) + +add_executable(hello-world + hello.cc +) + +target_link_libraries(hello-world + FortranRuntime +) + +add_test(HelloWorld hello-world) diff --git a/test/runtime/format.cc b/test/runtime/format.cc index 95d44f16fa9b..50381e85ed05 100644 --- a/test/runtime/format.cc +++ b/test/runtime/format.cc @@ -1,5 +1,6 @@ // Test basic FORMAT string traversal #include "../runtime/format.h" +#include "../runtime/terminator.h" #include #include #include @@ -7,24 +8,50 @@ #include using namespace Fortran::runtime; +using namespace Fortran::runtime::io; using namespace std::literals::string_literals; static int failures{0}; using Results = std::list; -static Results results; -static void handleCharacterLiteral(const char *s, std::size_t len) { +// Test harness context for format control +struct TestFormatContext : virtual public Terminator, public FormatContext { + TestFormatContext() : Terminator{"format.cc", 1} {} + void Emit(const char *, std::size_t); + void HandleSlash(int = 1); + void HandleRelativePosition(int); + void HandleAbsolutePosition(int); + void Report(const DataEdit &); + void Check(Results &); + Results results; +}; + +// Override the runtime's Crash() for testing purposes +[[noreturn]] void Fortran::runtime::Terminator::Crash(const char *message, ...) { + std::va_list ap; + va_start(ap, message); + char buffer[1000]; + std::vsnprintf(buffer, sizeof buffer, message, ap); + va_end(ap); + throw std::string{buffer}; +} + +void TestFormatContext::Emit(const char *s, std::size_t len) { std::string str{s, len}; results.push_back("'"s + str + '\''); } -static void handleSlash() { results.emplace_back("/"); } +void TestFormatContext::HandleSlash(int n) { + while (n-- > 0) { + results.emplace_back("/"); + } +} -static void handleAbsolutePosition(int n) { +void TestFormatContext::HandleAbsolutePosition(int n) { results.push_back("T"s + std::to_string(n)); } -static void handleRelativePosition(int n) { +void TestFormatContext::HandleRelativePosition(int n) { if (n < 0) { results.push_back("TL"s + std::to_string(-n)); } else { @@ -32,7 +59,7 @@ static void handleRelativePosition(int n) { } } -static void Report(const DataEdit &edit) { +void TestFormatContext::Report(const DataEdit &edit) { std::string str{edit.descriptor}; if (edit.repeat != 1) { str = std::to_string(edit.repeat) + '*' + str; @@ -51,17 +78,7 @@ static void Report(const DataEdit &edit) { results.push_back(str); } -// Override the Crash() in the runtime library -void Terminator::Crash(const char *message, ...) { - std::va_list ap; - va_start(ap, message); - char buffer[1000]; - std::vsnprintf(buffer, sizeof buffer, message, ap); - va_end(ap); - throw std::string{buffer}; -} - -static void Check(Results &expect) { +void TestFormatContext::Check(Results &expect) { if (expect != results) { std::cerr << "expected:"; for (const std::string &s : expect) { @@ -78,37 +95,33 @@ static void Check(Results &expect) { results.clear(); } -static void Test(FormatContext &context, int n, const char *format, - Results &&expect, int repeat = 1) { - MutableModes modes; - FormatControl control{context, format, std::strlen(format), modes}; +static void Test(int n, const char *format, Results &&expect, int repeat = 1) { + TestFormatContext context; + FormatControl control{context, format, std::strlen(format)}; try { for (int j{0}; j < n; ++j) { DataEdit edit; - control.GetNext(edit, repeat); - Report(edit); + control.GetNext(context, edit, repeat); + context.Report(edit); } - control.FinishOutput(); + control.FinishOutput(context); } catch (const std::string &crash) { - results.push_back("Crash:"s + crash); + context.results.push_back("Crash:"s + crash); } - Check(expect); + context.Check(expect); } int main() { - Terminator terminator{"source", 1}; - FormatContext context{terminator, &handleCharacterLiteral, nullptr, nullptr, - &handleSlash, &handleAbsolutePosition, &handleRelativePosition}; - Test(context, 1, "('PI=',F9.7)", Results{"'PI='", "F9.7"}); - Test(context, 1, "(3HPI=F9.7)", Results{"'PI='", "F9.7"}); - Test(context, 1, "(3HPI=/F9.7)", Results{"'PI='", "/", "F9.7"}); - Test(context, 2, "('PI=',F9.7)", Results{"'PI='", "F9.7", "'PI='", "F9.7"}); - Test(context, 2, "(2('PI=',F9.7),'done')", + Test(1, "('PI=',F9.7)", Results{"'PI='", "F9.7"}); + Test(1, "(3HPI=F9.7)", Results{"'PI='", "F9.7"}); + Test(1, "(3HPI=/F9.7)", Results{"'PI='", "/", "F9.7"}); + Test(2, "('PI=',F9.7)", Results{"'PI='", "F9.7", "/", "'PI='", "F9.7"}); + Test(2, "(2('PI=',F9.7),'done')", Results{"'PI='", "F9.7", "'PI='", "F9.7", "'done'"}); - Test(context, 2, "(3('PI=',F9.7,:),'tooFar')", + Test(2, "(3('PI=',F9.7,:),'tooFar')", Results{"'PI='", "F9.7", "'PI='", "F9.7"}); - Test(context, 2, "(*('PI=',F9.7,:),'tooFar')", + Test(2, "(*('PI=',F9.7,:),'tooFar')", Results{"'PI='", "F9.7", "'PI='", "F9.7"}); - Test(context, 1, "(3F9.7)", Results{"2*F9.7"}, 2); + Test(1, "(3F9.7)", Results{"2*F9.7"}, 2); return failures > 0; } diff --git a/test/runtime/hello.cc b/test/runtime/hello.cc new file mode 100644 index 000000000000..9c52a01b26a9 --- /dev/null +++ b/test/runtime/hello.cc @@ -0,0 +1,33 @@ +// Basic tests of I/O API + +#include "../../runtime/io-api.h" +#include +#include + +using namespace Fortran::runtime::io; + +static int failures{0}; + +int main() { + char buffer[32]; + const char *format1{"(12HHELLO, WORLD)"}; + auto cookie{IONAME(BeginInternalFormattedOutput)(buffer, sizeof buffer, format1, std::strlen(format1))}; + if (auto status{IONAME(EndIoStatement)(cookie)}) { + std::cerr << "format1 failed, status " << static_cast(status) << '\n'; + ++failures; + } + std::string got1{buffer, sizeof buffer}; + std::string expect1{"HELLO, WORLD"}; + expect1.resize(got1.length(), ' '); + if (got1 != expect1) { + std::cerr << "format1 failed, got '" << got1 << "', expected '" << expect1 << "'\n"; + ++failures; + } + + if (failures == 0) { + std::cout << "PASS\n"; + } else { + std::cout << "FAIL " << failures << " tests\n"; + } + return failures > 0; +} From 81a5488ee62b4324d002c348464712c930095a32 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Sat, 25 Jan 2020 08:15:17 -0800 Subject: [PATCH 008/345] Fix bugs detecting impure calls Change Traverse to visit the actual arguments of structure constructors. Change FindImpureCallHelper to visit the actual arguments of a call to a pure procedure in case one of them makes a call to an impure function. --- lib/evaluate/tools.cc | 2 +- lib/evaluate/traverse.h | 17 +++++++++++++++-- test/semantics/call11.f90 | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/evaluate/tools.cc b/lib/evaluate/tools.cc index 2ea4719b9c1b..2b1b9074aba2 100644 --- a/lib/evaluate/tools.cc +++ b/lib/evaluate/tools.cc @@ -828,7 +828,7 @@ class FindImpureCallHelper if (auto chars{characteristics::Procedure::Characterize( call.proc(), intrinsics_)}) { if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) { - return std::nullopt; + return (*this)(call.arguments()); } } return call.proc().GetName(); diff --git a/lib/evaluate/traverse.h b/lib/evaluate/traverse.h index 9300c9d24d49..2fa8995bf70e 100644 --- a/lib/evaluate/traverse.h +++ b/lib/evaluate/traverse.h @@ -85,8 +85,21 @@ template class Traverse { return visitor_.Default(); } Result operator()(const NullPointer &) const { return visitor_.Default(); } - template Result operator()(const Constant &) const { - return visitor_.Default(); + template Result operator()(const Constant &x) const { + if constexpr (T::category == TypeCategory::Derived) { + std::optional result; + for (const StructureConstructorValues &map : x.values()) { + for (const auto &pair : map) { + auto value{visitor_(pair.second.value())}; + result = result + ? visitor_.Combine(std::move(*result), std::move(value)) + : std::move(value); + } + } + return result ? *result : visitor_.Default(); + } else { + return visitor_.Default(); + } } Result operator()(const Symbol &) const { return visitor_.Default(); } Result operator()(const StaticDataObject &) const { diff --git a/test/semantics/call11.f90 b/test/semantics/call11.f90 index 2ff18a0b552a..061b73d2d374 100644 --- a/test/semantics/call11.f90 +++ b/test/semantics/call11.f90 @@ -32,12 +32,17 @@ subroutine test !ERROR: Impure procedure 'impure' may not be referenced in a FORALL a(j) = impure(j) ! C1037 end forall + forall (j=1:1) + !ERROR: Impure procedure 'impure' may not be referenced in a FORALL + a(j) = pure(impure(j)) ! C1037 + end forall !ERROR: Concurrent-header mask expression cannot reference an impure procedure do concurrent (j=1:1, impure(j) /= 0) ! C1121 !ERROR: Call to an impure procedure is not allowed in DO CONCURRENT a(j) = impure(j) ! C1139 end do end subroutine + subroutine test2 type(t) :: x real :: a(x%tbp_pure(1)) ! ok @@ -59,4 +64,19 @@ subroutine test2 a(j) = x%tbp_impure(j) ! C1139 end do end subroutine + + subroutine test3 + type :: t + integer :: i + end type + type(t) :: a(10), b + forall (i=1:10) + a(i) = t(pure(i)) ! OK + end forall + forall (i=1:10) + !ERROR: Impure procedure 'impure' may not be referenced in a FORALL + a(i) = t(impure(i)) ! C1037 + end forall + end subroutine + end module From 3ba77a0c2035644485737a025cc8912485b56225 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 23 Jan 2020 16:10:00 -0800 Subject: [PATCH 009/345] Basic file operation wrapper Asynchronous interfaces and locking --- runtime/CMakeLists.txt | 2 + runtime/file.cc | 326 +++++++++++++++++++++++++++++++++++++++++ runtime/file.h | 77 ++++++++++ runtime/io-api.h | 1 + runtime/io-error.cc | 35 ++--- runtime/io-error.h | 5 +- runtime/lock.h | 47 ++++++ runtime/memory.h | 9 +- runtime/tools.cc | 51 +++++++ runtime/tools.h | 25 ++++ 10 files changed, 550 insertions(+), 28 deletions(-) create mode 100644 runtime/file.cc create mode 100644 runtime/file.h create mode 100644 runtime/lock.h create mode 100644 runtime/tools.cc create mode 100644 runtime/tools.h diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 3253f4b30899..ce42973a7b9c 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(FortranRuntime ISO_Fortran_binding.cc derived-type.cc descriptor.cc + file.cc format.cc io-api.cc io-error.cc @@ -18,6 +19,7 @@ add_library(FortranRuntime memory.cc stop.cc terminator.cc + tools.cc transformational.cc type-code.cc ) diff --git a/runtime/file.cc b/runtime/file.cc new file mode 100644 index 000000000000..c99db0976690 --- /dev/null +++ b/runtime/file.cc @@ -0,0 +1,326 @@ +//===-- runtime/file.cc -----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "file.h" +#include "magic-numbers.h" +#include "memory.h" +#include "tools.h" +#include +#include +#include +#include +#include + +namespace Fortran::runtime::io { + +void OpenFile::Open(const char *path, std::size_t pathLength, + const char *status, std::size_t statusLength, const char *action, + std::size_t actionLength, IoErrorHandler &handler) { + CriticalSection criticalSection{lock_}; + RUNTIME_CHECK(handler, fd_ < 0); + int flags{0}; + static const char *actions[]{"READ", "WRITE", "READWRITE", nullptr}; + switch (IdentifyValue(action, actionLength, actions)) { + case 0: flags = O_RDONLY; break; + case 1: flags = O_WRONLY; break; + case 2: flags = O_RDWR; break; + default: + handler.Crash( + "Invalid ACTION='%.*s'", action, static_cast(actionLength)); + } + if (!status) { + status = "UNKNOWN", statusLength = 7; + } + static const char *statuses[]{ + "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; + switch (IdentifyValue(status, statusLength, statuses)) { + case 0: // STATUS='OLD' + if (!path && fd_ >= 0) { + // TODO: Update OpenFile in situ; can ACTION be changed? + return; + } + break; + case 1: // STATUS='NEW' + flags |= O_CREAT | O_EXCL; + break; + case 2: // STATUS='SCRATCH' + if (path_.get()) { + handler.Crash("FILE= must not appear with STATUS='SCRATCH'"); + path_.reset(); + } + { + char path[]{"/tmp/Fortran-Scratch-XXXXXX"}; + fd_ = ::mkstemp(path); + if (fd_ < 0) { + handler.SignalErrno(); + } + ::unlink(path); + } + return; + case 3: // STATUS='REPLACE' + flags |= O_CREAT | O_TRUNC; + break; + case 4: // STATUS='UNKNOWN' + if (fd_ >= 0) { + return; + } + flags |= O_CREAT; + break; + default: + handler.Crash( + "Invalid STATUS='%.*s'", status, static_cast(statusLength)); + } + // If we reach this point, we're opening a new file + if (fd_ >= 0) { + if (::close(fd_) != 0) { + handler.SignalErrno(); + } + } + path_ = SaveDefaultCharacter(path, pathLength, handler); + if (!path_.get()) { + handler.Crash( + "FILE= is required unless STATUS='OLD' and unit is connected"); + } + fd_ = ::open(path_.get(), flags, 0600); + if (fd_ < 0) { + handler.SignalErrno(); + } + pending_.reset(); + knownSize_.reset(); +} + +void OpenFile::Close( + const char *status, std::size_t statusLength, IoErrorHandler &handler) { + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + pending_.reset(); + knownSize_.reset(); + static const char *statuses[]{"KEEP", "DELETE", nullptr}; + switch (IdentifyValue(status, statusLength, statuses)) { + case 0: break; + case 1: + if (path_.get()) { + ::unlink(path_.get()); + } + break; + default: + if (status) { + handler.Crash( + "Invalid STATUS='%.*s'", status, static_cast(statusLength)); + } + } + path_.reset(); + if (fd_ >= 0) { + if (::close(fd_) != 0) { + handler.SignalErrno(); + } + fd_ = -1; + } +} + +std::size_t OpenFile::Read(Offset at, char *buffer, std::size_t minBytes, + std::size_t maxBytes, IoErrorHandler &handler) { + if (maxBytes == 0) { + return 0; + } + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + if (!Seek(at, handler)) { + return 0; + } + if (maxBytes < minBytes) { + minBytes = maxBytes; + } + std::size_t got{0}; + while (got < minBytes) { + auto chunk{::read(fd_, buffer + got, maxBytes - got)}; + if (chunk == 0) { + handler.SignalEnd(); + break; + } + if (chunk < 0) { + auto err{errno}; + if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { + handler.SignalError(err); + break; + } + } else { + position_ += chunk; + got += chunk; + } + } + return got; +} + +std::size_t OpenFile::Write( + Offset at, const char *buffer, std::size_t bytes, IoErrorHandler &handler) { + if (bytes == 0) { + return 0; + } + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + if (!Seek(at, handler)) { + return 0; + } + std::size_t put{0}; + while (put < bytes) { + auto chunk{::write(fd_, buffer + put, bytes - put)}; + if (chunk >= 0) { + position_ += chunk; + put += chunk; + } else { + auto err{errno}; + if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { + handler.SignalError(err); + break; + } + } + } + if (knownSize_ && position_ > *knownSize_) { + knownSize_ = position_; + } + return put; +} + +void OpenFile::Truncate(Offset at, IoErrorHandler &handler) { + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + if (!knownSize_ || *knownSize_ != at) { + if (::ftruncate(fd_, at) != 0) { + handler.SignalErrno(); + } + knownSize_ = at; + } +} + +// The operation is performed immediately; the results are saved +// to be claimed by a later WAIT statement. +// TODO: True asynchronicity +int OpenFile::ReadAsynchronously( + Offset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) { + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + int iostat{0}; + for (std::size_t got{0}; got < bytes;) { +#if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L + auto chunk{::pread(fd_, buffer + got, bytes - got, at)}; +#else + auto chunk{RawSeek(at) ? ::read(fd_, buffer + got, bytes - got) : -1}; +#endif + if (chunk == 0) { + iostat = FORTRAN_RUNTIME_IOSTAT_END; + break; + } + if (chunk < 0) { + auto err{errno}; + if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { + iostat = err; + break; + } + } else { + at += chunk; + got += chunk; + } + } + return PendingResult(handler, iostat); +} + +// TODO: True asynchronicity +int OpenFile::WriteAsynchronously( + Offset at, const char *buffer, std::size_t bytes, IoErrorHandler &handler) { + CriticalSection criticalSection{lock_}; + CheckOpen(handler); + int iostat{0}; + for (std::size_t put{0}; put < bytes;) { +#if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L + auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)}; +#else + auto chunk{RawSeek(at) ? ::write(fd_, buffer + put, bytes - put) : -1}; +#endif + if (chunk >= 0) { + at += chunk; + put += chunk; + } else { + auto err{errno}; + if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { + iostat = err; + break; + } + } + } + return PendingResult(handler, iostat); +} + +void OpenFile::Wait(int id, IoErrorHandler &handler) { + std::optional ioStat; + { + CriticalSection criticalSection{lock_}; + Pending *prev{nullptr}; + for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) { + if (p->id == id) { + ioStat = p->ioStat; + if (prev) { + prev->next.reset(p->next.release()); + } else { + pending_.reset(p->next.release()); + } + break; + } + } + } + if (ioStat) { + handler.SignalError(*ioStat); + } +} + +void OpenFile::WaitAll(IoErrorHandler &handler) { + while (true) { + int ioStat; + { + CriticalSection criticalSection{lock_}; + if (pending_) { + ioStat = pending_->ioStat; + pending_.reset(pending_->next.release()); + } else { + return; + } + } + handler.SignalError(ioStat); + } +} + +void OpenFile::CheckOpen(Terminator &terminator) { + RUNTIME_CHECK(terminator, fd_ >= 0); +} + +bool OpenFile::Seek(Offset at, IoErrorHandler &handler) { + if (at == position_) { + return true; + } else if (RawSeek(at)) { + position_ = at; + return true; + } else { + handler.SignalErrno(); + return false; + } +} + +bool OpenFile::RawSeek(Offset at) { +#ifdef _LARGEFILE64_SOURCE + return ::lseek64(fd_, at, SEEK_SET) == 0; +#else + return ::lseek(fd_, at, SEEK_SET) == 0; +#endif +} + +int OpenFile::PendingResult(Terminator &terminator, int iostat) { + int id{nextId_++}; + pending_.reset(&New{}(terminator, id, iostat, std::move(pending_))); + return id; +} +} diff --git a/runtime/file.h b/runtime/file.h new file mode 100644 index 000000000000..1c0a10d8f754 --- /dev/null +++ b/runtime/file.h @@ -0,0 +1,77 @@ +//===-- runtime/file.h ------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Raw system I/O wrappers + +#ifndef FORTRAN_RUNTIME_FILE_H_ +#define FORTRAN_RUNTIME_FILE_H_ + +#include "io-error.h" +#include "lock.h" +#include "memory.h" +#include "terminator.h" +#include +#include + +namespace Fortran::runtime::io { + +class OpenFile { +public: + using Offset = std::uint64_t; + + Offset position() const { return position_; } + + void Open(const char *path, std::size_t pathLength, const char *status, + std::size_t statusLength, const char *action, std::size_t actionLength, + IoErrorHandler &); + void Close(const char *action, std::size_t actionLength, IoErrorHandler &); + + // Reads data into memory; returns amount acquired. Synchronous. + // Partial reads (less than minBytes) signify end-of-file. If the + // buffer is larger than minBytes, and extra returned data will be + // preserved for future consumption, set maxBytes larger than minBytes + // to reduce system calls This routine handles EAGAIN/EWOULDBLOCK and EINTR. + std::size_t Read(Offset, char *, std::size_t minBytes, std::size_t maxBytes, + IoErrorHandler &); + + // Writes data. Synchronous. Partial writes indicate program-handled + // error conditions. + std::size_t Write(Offset, const char *, std::size_t, IoErrorHandler &); + + // Truncates the file + void Truncate(Offset, IoErrorHandler &); + + // Asynchronous transfers + int ReadAsynchronously(Offset, char *, std::size_t, IoErrorHandler &); + int WriteAsynchronously(Offset, const char *, std::size_t, IoErrorHandler &); + void Wait(int id, IoErrorHandler &); + void WaitAll(IoErrorHandler &); + +private: + struct Pending { + int id; + int ioStat{0}; + OwningPtr next; + }; + + // lock_ must be held for these + void CheckOpen(Terminator &); + bool Seek(Offset, IoErrorHandler &); + bool RawSeek(Offset); + int PendingResult(Terminator &, int); + + Lock lock_; + int fd_{-1}; + OwningPtr path_; + Offset position_{0}; + std::optional knownSize_; + int nextId_; + OwningPtr pending_; +}; +} +#endif // FORTRAN_RUNTIME_FILE_H_ diff --git a/runtime/io-api.h b/runtime/io-api.h index 20f0f218e041..1c1f81ea4c6f 100644 --- a/runtime/io-api.h +++ b/runtime/io-api.h @@ -127,6 +127,7 @@ AsynchronousId IONAME(BeginAsynchronousOutput)(ExternalUnit, std::int64_t REC, AsynchronousId IONAME(BeginAsynchronousInput)(ExternalUnit, std::int64_t REC, char *, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId); +Cookie IONAME(BeginWaitAll)(ExternalUnit); // Other I/O statements Cookie IONAME(BeginClose)( diff --git a/runtime/io-error.cc b/runtime/io-error.cc index 74dcef8f3c3e..ccf143ae7b1b 100644 --- a/runtime/io-error.cc +++ b/runtime/io-error.cc @@ -17,16 +17,18 @@ namespace Fortran::runtime::io { void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) { flags_ = 0; ioStat_ = 0; - hitEnd_ = false; - hitEor_ = false; SetLocation(sourceFileName, sourceLine); } void IoErrorHandler::SignalError(int iostatOrErrno) { - if (iostatOrErrno != 0) { + if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_END) { + SignalEnd(); + } else if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_EOR) { + SignalEor(); + } else if (iostatOrErrno != 0) { if (flags_ & hasIoStat) { - if (!ioStat_) { - ioStat_ = iostatOrErrno; + if (ioStat_ <= 0) { + ioStat_ = iostatOrErrno; // priority over END=/EOR= } } else if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT) { Crash("INQUIRE on internal unit"); @@ -36,9 +38,13 @@ void IoErrorHandler::SignalError(int iostatOrErrno) { } } +void IoErrorHandler::SignalErrno() { SignalError(errno); } + void IoErrorHandler::SignalEnd() { if (flags_ & hasEnd) { - hitEnd_ = true; + if (!ioStat_ || ioStat_ < FORTRAN_RUNTIME_IOSTAT_END) { + ioStat_ = FORTRAN_RUNTIME_IOSTAT_END; + } } else { Crash("End of file"); } @@ -46,22 +52,11 @@ void IoErrorHandler::SignalEnd() { void IoErrorHandler::SignalEor() { if (flags_ & hasEor) { - hitEor_ = true; + if (!ioStat_ || ioStat_ < FORTRAN_RUNTIME_IOSTAT_EOR) { + ioStat_ = FORTRAN_RUNTIME_IOSTAT_EOR; // least priority + } } else { Crash("End of record"); } } - -int IoErrorHandler::GetIoStat() const { - if (ioStat_) { - return ioStat_; - } else if (hitEnd_) { - return FORTRAN_RUNTIME_IOSTAT_END; - } else if (hitEor_) { - return FORTRAN_RUNTIME_IOSTAT_EOR; - } else { - return 0; - } -} - } diff --git a/runtime/io-error.h b/runtime/io-error.h index 08aea4e9a506..6cab725186e3 100644 --- a/runtime/io-error.h +++ b/runtime/io-error.h @@ -28,10 +28,11 @@ class IoErrorHandler : virtual public Terminator { void HasEorLabel() { flags_ |= hasEor; } void SignalError(int iostatOrErrno); + void SignalErrno(); void SignalEnd(); void SignalEor(); - int GetIoStat() const; + int GetIoStat() const { return ioStat_; } private: enum Flag : std::uint8_t { @@ -41,8 +42,6 @@ class IoErrorHandler : virtual public Terminator { hasEor = 8, // EOR= }; std::uint8_t flags_{0}; - bool hitEnd_{false}; - bool hitEor_{false}; int ioStat_{0}; }; diff --git a/runtime/lock.h b/runtime/lock.h new file mode 100644 index 000000000000..19f0cea79b01 --- /dev/null +++ b/runtime/lock.h @@ -0,0 +1,47 @@ +//===-- runtime/lock.h ------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Wraps pthread_mutex_t (or whatever) + +#ifndef FORTRAN_RUNTIME_LOCK_H_ +#define FORTRAN_RUNTIME_LOCK_H_ + +#include + +namespace Fortran::runtime { + +class Lock { +public: + Lock() { pthread_mutex_init(&mutex_, nullptr); } + ~Lock() { pthread_mutex_destroy(&mutex_); } + void Take() { pthread_mutex_lock(&mutex_); } + bool Try() { return pthread_mutex_trylock(&mutex_) != 0; } + void Drop() { pthread_mutex_unlock(&mutex_); } + + void CheckLocked(Terminator &terminator) { + if (Try()) { + Drop(); + terminator.Crash("Lock::CheckLocked() failed"); + } + } + +private: + pthread_mutex_t mutex_; +}; + +class CriticalSection { +public: + explicit CriticalSection(Lock &lock) : lock_{lock} { lock_.Take(); } + ~CriticalSection() { lock_.Drop(); } + +private: + Lock &lock_; +}; +} + +#endif // FORTRAN_RUNTIME_LOCK_H_ diff --git a/runtime/memory.h b/runtime/memory.h index f44ceedb3cd1..3e65a98fb224 100644 --- a/runtime/memory.h +++ b/runtime/memory.h @@ -26,16 +26,15 @@ void FreeMemory(void *); void FreeMemoryAndNullify(void *&); template struct New { - template A &operator()(Terminator &terminator, X&&... x) { - return *new (AllocateMemoryOrCrash(terminator, sizeof(A))) A{std::forward(x)...}; + template A &operator()(Terminator &terminator, X &&... x) { + return *new (AllocateMemoryOrCrash(terminator, sizeof(A))) + A{std::forward(x)...}; } }; -namespace { -template class OwningPtrDeleter { +template struct OwningPtrDeleter { void operator()(A *p) { FreeMemory(p); } }; -} template using OwningPtr = std::unique_ptr>; } diff --git a/runtime/tools.cc b/runtime/tools.cc new file mode 100644 index 000000000000..8a9980fa50c7 --- /dev/null +++ b/runtime/tools.cc @@ -0,0 +1,51 @@ +//===-- runtime/tools.cc ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "tools.h" +#include + +namespace Fortran::runtime { + +OwningPtr SaveDefaultCharacter( + const char *s, std::size_t length, Terminator &terminator) { + if (s) { + auto *p{static_cast(AllocateMemoryOrCrash(terminator, length + 1))}; + std::memcpy(p, s, length); + p[length] = '\0'; + return OwningPtr{p}; + } else { + return OwningPtr{}; + } +} + +static bool CaseInsensitiveMatch( + const char *value, std::size_t length, const char *possibility) { + for (; length-- > 0; ++value, ++possibility) { + char ch{*value}; + if (ch >= 'a' && ch <= 'z') { + ch += 'A' - 'a'; + } + if (*possibility == '\0' || ch != *possibility) { + return false; + } + } + return *possibility == '\0'; +} + +int IdentifyValue( + const char *value, std::size_t length, const char *possibilities[]) { + if (value) { + for (int j{0}; possibilities[j]; ++j) { + if (CaseInsensitiveMatch(value, length, possibilities[j])) { + return j; + } + } + } + return -1; +} +} diff --git a/runtime/tools.h b/runtime/tools.h new file mode 100644 index 000000000000..184f6af63f8e --- /dev/null +++ b/runtime/tools.h @@ -0,0 +1,25 @@ +//===-- runtime/tools.h -----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_RUNTIME_TOOLS_H_ +#define FORTRAN_RUNTIME_TOOLS_H_ +#include "memory.h" +namespace Fortran::runtime { + +class Terminator; + +OwningPtr SaveDefaultCharacter(const char *, std::size_t, Terminator &); + +// For validating and recognizing default CHARACTER values in a +// case-insensitive manner. Returns the zero-based index into the +// null-terminated array of upper-case possibilities when the value is valid, +// or -1 when it has no match. +int IdentifyValue( + const char *value, std::size_t length, const char *possibilities[]); +} +#endif // FORTRAN_RUNTIME_TOOLS_H_ From 27d76da2a44614b2c4cf4d576410372cabf66577 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Sun, 26 Jan 2020 10:42:34 -0800 Subject: [PATCH 010/345] Fix bug detecting simply contiguous component We were always return false when testing a component for simple contiguity. Change to check that the component is an array that is simply continguous. Also treat a scalar component of scalar as simply contiguous. A pointer with bounds remapping to a complex part is a similar case so add a test for that too. --- lib/evaluate/check-expression.cc | 4 +++- test/semantics/assign03.f90 | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/evaluate/check-expression.cc b/lib/evaluate/check-expression.cc index d9b81bd4977f..92dbb76fe15a 100644 --- a/lib/evaluate/check-expression.cc +++ b/lib/evaluate/check-expression.cc @@ -283,7 +283,9 @@ class IsSimplyContiguousHelper Result operator()(const CoarrayRef &x) const { return CheckSubscripts(x.subscript()); } - Result operator()(const Component &) const { return false; } + Result operator()(const Component &x) const { + return x.base().Rank() == 0 && (*this)(x.GetLastSymbol()); + } Result operator()(const ComplexPart &) const { return false; } Result operator()(const Substring &) const { return false; } diff --git a/test/semantics/assign03.f90 b/test/semantics/assign03.f90 index 54112f4f4097..08070fd3ac55 100644 --- a/test/semantics/assign03.f90 +++ b/test/semantics/assign03.f90 @@ -134,4 +134,29 @@ subroutine s9 p(1:5,1:5) => y(1:100:2) end + subroutine s10 + integer, pointer :: p(:) + type :: t + integer :: a(4, 4) + integer :: b + end type + type(t), target :: x + type(t), target :: y(10,10) + p(1:16) => x%a + p(1:1) => x%b ! We treat scalars as simply contiguous + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + p(1:4) => x%a(::2,::2) + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + p(1:100) => y(:,:)%b + end + + subroutine s11 + complex, target :: x(10,10) + complex, pointer :: p(:) + real, pointer :: q(:) + p(1:100) => x(:,:) + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + q(1:100) => x(:,:)%re + end + end From 21adbc7e05b2454ba3fc725b4697748f98599471 Mon Sep 17 00:00:00 2001 From: David Truby Date: Mon, 27 Jan 2020 20:57:59 +0000 Subject: [PATCH 011/345] Moved public headers to include/flang (#943) --- .clang-format | 2 +- .../flang}/common/Fortran-features.h | 8 ++-- {lib => include/flang}/common/Fortran.h | 2 +- .../flang}/common/bit-population-count.h | 2 +- .../flang}/common/constexpr-bitset.h | 2 +- {lib => include/flang}/common/default-kinds.h | 4 +- {lib => include/flang}/common/enum-set.h | 2 +- {lib => include/flang}/common/format.h | 4 +- {lib => include/flang}/common/idioms.h | 2 +- {lib => include/flang}/common/indirection.h | 2 +- {lib => include/flang}/common/interval.h | 2 +- .../flang}/common/leading-zero-bit-count.h | 2 +- .../flang}/common/reference-counted.h | 2 +- {lib => include/flang}/common/reference.h | 2 +- {lib => include/flang}/common/restorer.h | 2 +- {lib => include/flang}/common/template.h | 3 +- {lib => include/flang}/common/uint128.h | 2 +- .../flang}/common/unsigned-const-division.h | 2 +- {lib => include/flang}/common/unwrap.h | 2 +- .../flang}/decimal/binary-floating-point.h | 4 +- {lib => include/flang}/decimal/decimal.h | 2 +- {lib => include/flang}/evaluate/call.h | 10 ++--- .../flang}/evaluate/characteristics.h | 14 +++---- .../flang}/evaluate/check-expression.h | 2 +- {lib => include/flang}/evaluate/common.h | 18 ++++----- {lib => include/flang}/evaluate/complex.h | 2 +- {lib => include/flang}/evaluate/constant.h | 6 +-- {lib => include/flang}/evaluate/expression.h | 12 +++--- {lib => include/flang}/evaluate/fold.h | 2 +- {lib => include/flang}/evaluate/formatting.h | 4 +- {lib => include/flang}/evaluate/integer.h | 8 ++-- .../flang}/evaluate/intrinsics-library.h | 2 +- {lib => include/flang}/evaluate/intrinsics.h | 8 ++-- {lib => include/flang}/evaluate/logical.h | 2 +- {lib => include/flang}/evaluate/real.h | 4 +- .../flang}/evaluate/rounding-bits.h | 2 +- {lib => include/flang}/evaluate/shape.h | 8 ++-- {lib => include/flang}/evaluate/static-data.h | 4 +- {lib => include/flang}/evaluate/tools.h | 18 ++++----- {lib => include/flang}/evaluate/traverse.h | 6 +-- {lib => include/flang}/evaluate/type.h | 8 ++-- {lib => include/flang}/evaluate/variable.h | 10 ++--- {lib => include/flang}/parser/char-block.h | 4 +- {lib => include/flang}/parser/char-buffer.h | 2 +- {lib => include/flang}/parser/char-set.h | 2 +- {lib => include/flang}/parser/characters.h | 2 +- .../flang}/parser/dump-parse-tree.h | 6 +-- .../flang}/parser/format-specification.h | 2 +- .../flang}/parser/instrumented-parser.h | 6 +-- {lib => include/flang}/parser/message.h | 8 ++-- {lib => include/flang}/parser/parse-state.h | 12 +++--- .../flang}/parser/parse-tree-visitor.h | 2 +- {lib => include/flang}/parser/parse-tree.h | 8 ++-- {lib => include/flang}/parser/parsing.h | 4 +- {lib => include/flang}/parser/provenance.h | 6 +-- {lib => include/flang}/parser/source.h | 2 +- {lib => include/flang}/parser/tools.h | 2 +- {lib => include/flang}/parser/unparse.h | 2 +- {lib => include/flang}/parser/user-state.h | 10 ++--- {lib => include/flang}/semantics/attr.h | 6 +-- {lib => include/flang}/semantics/expression.h | 24 +++++------ {lib => include/flang}/semantics/scope.h | 12 +++--- {lib => include/flang}/semantics/semantics.h | 10 ++--- {lib => include/flang}/semantics/symbol.h | 8 ++-- {lib => include/flang}/semantics/tools.h | 20 +++++----- {lib => include/flang}/semantics/type.h | 10 ++--- .../flang}/semantics/unparse-with-symbols.h | 4 +- lib/common/Fortran-features.cc | 6 +-- lib/common/Fortran.cc | 2 +- lib/common/default-kinds.cc | 4 +- lib/common/idioms.cc | 2 +- lib/decimal/big-radix-floating-point.h | 12 +++--- lib/decimal/binary-to-decimal.cc | 2 +- lib/decimal/decimal-to-binary.cc | 8 ++-- lib/evaluate/call.cc | 12 +++--- lib/evaluate/character.h | 2 +- lib/evaluate/characteristics.cc | 20 +++++----- lib/evaluate/check-expression.cc | 10 ++--- lib/evaluate/common.cc | 4 +- lib/evaluate/complex.cc | 2 +- lib/evaluate/constant.cc | 8 ++-- lib/evaluate/expression.cc | 12 +++--- lib/evaluate/fold-implementation.h | 34 ++++++++-------- lib/evaluate/fold-logical.cc | 2 +- lib/evaluate/fold.cc | 2 +- lib/evaluate/formatting.cc | 16 ++++---- lib/evaluate/host.cc | 2 +- lib/evaluate/host.h | 2 +- lib/evaluate/int-power.h | 2 +- lib/evaluate/integer.cc | 2 +- lib/evaluate/intrinsics-library-templates.h | 6 +-- lib/evaluate/intrinsics.cc | 20 +++++----- lib/evaluate/logical.cc | 2 +- lib/evaluate/real.cc | 8 ++-- lib/evaluate/shape.cc | 20 +++++----- lib/evaluate/static-data.cc | 4 +- lib/evaluate/tools.cc | 10 ++--- lib/evaluate/type.cc | 20 +++++----- lib/evaluate/variable.cc | 16 ++++---- lib/parser/Fortran-parsers.cc | 4 +- lib/parser/basic-parsers.h | 16 ++++---- lib/parser/char-block.cc | 2 +- lib/parser/char-buffer.cc | 4 +- lib/parser/char-set.cc | 2 +- lib/parser/characters.cc | 4 +- lib/parser/debug-parser.cc | 2 +- lib/parser/debug-parser.h | 2 +- lib/parser/executable-parsers.cc | 4 +- lib/parser/expr-parsers.cc | 4 +- lib/parser/expr-parsers.h | 2 +- lib/parser/instrumented-parser.cc | 6 +-- lib/parser/io-parsers.cc | 4 +- lib/parser/message.cc | 6 +-- lib/parser/misc-parsers.h | 4 +- lib/parser/openmp-parsers.cc | 2 +- lib/parser/parse-tree.cc | 8 ++-- lib/parser/parsing.cc | 8 ++-- lib/parser/preprocessor.cc | 6 +-- lib/parser/preprocessor.h | 4 +- lib/parser/prescan.cc | 8 ++-- lib/parser/prescan.h | 8 ++-- lib/parser/program-parsers.cc | 4 +- lib/parser/provenance.cc | 4 +- lib/parser/source.cc | 6 +-- lib/parser/token-parsers.h | 10 ++--- lib/parser/token-sequence.cc | 2 +- lib/parser/token-sequence.h | 4 +- lib/parser/tools.cc | 2 +- lib/parser/type-parsers.h | 4 +- lib/parser/unparse.cc | 14 +++---- lib/parser/user-state.cc | 4 +- lib/semantics/assignment.cc | 23 ++++++----- lib/semantics/assignment.h | 8 ++-- lib/semantics/attr.cc | 4 +- lib/semantics/canonicalize-do.cc | 2 +- lib/semantics/canonicalize-omp.cc | 2 +- lib/semantics/check-allocate.cc | 16 ++++---- lib/semantics/check-allocate.h | 2 +- lib/semantics/check-arithmeticif.cc | 6 +-- lib/semantics/check-arithmeticif.h | 2 +- lib/semantics/check-call.cc | 16 ++++---- lib/semantics/check-call.h | 2 +- lib/semantics/check-coarray.cc | 14 +++---- lib/semantics/check-coarray.h | 2 +- lib/semantics/check-deallocate.cc | 8 ++-- lib/semantics/check-deallocate.h | 2 +- lib/semantics/check-declarations.cc | 16 ++++---- lib/semantics/check-do.cc | 26 ++++++------ lib/semantics/check-do.h | 4 +- lib/semantics/check-if-stmt.cc | 6 +-- lib/semantics/check-if-stmt.h | 2 +- lib/semantics/check-io.cc | 8 ++-- lib/semantics/check-io.h | 8 ++-- lib/semantics/check-nullify.cc | 10 ++--- lib/semantics/check-nullify.h | 2 +- lib/semantics/check-omp-structure.cc | 4 +- lib/semantics/check-omp-structure.h | 6 +-- lib/semantics/check-purity.cc | 4 +- lib/semantics/check-purity.h | 2 +- lib/semantics/check-return.cc | 9 +++-- lib/semantics/check-return.h | 2 +- lib/semantics/check-stop.cc | 10 ++--- lib/semantics/check-stop.h | 2 +- lib/semantics/expression.cc | 26 ++++++------ lib/semantics/mod-file.cc | 14 +++---- lib/semantics/mod-file.h | 2 +- lib/semantics/pointer-assignment.cc | 24 +++++------ lib/semantics/pointer-assignment.h | 6 +-- lib/semantics/program-tree.cc | 6 +-- lib/semantics/program-tree.h | 4 +- lib/semantics/resolve-labels.cc | 8 ++-- lib/semantics/resolve-names-utils.cc | 22 +++++----- lib/semantics/resolve-names-utils.h | 8 ++-- lib/semantics/resolve-names.cc | 40 +++++++++---------- lib/semantics/rewrite-parse-tree.cc | 16 ++++---- lib/semantics/scope.cc | 8 ++-- lib/semantics/semantics.cc | 14 +++---- lib/semantics/symbol.cc | 12 +++--- lib/semantics/tools.cc | 26 ++++++------ lib/semantics/type.cc | 12 +++--- lib/semantics/unparse-with-symbols.cc | 10 ++--- runtime/derived-type.h | 2 +- runtime/descriptor.cc | 2 +- runtime/descriptor.h | 2 +- runtime/format.cc | 4 +- runtime/format.h | 2 +- runtime/transformational.cc | 3 +- runtime/type-code.h | 4 +- test/decimal/quick-sanity-test.cc | 2 +- test/decimal/thorough-test.cc | 2 +- test/evaluate/bit-population-count.cc | 2 +- test/evaluate/expression.cc | 10 ++--- test/evaluate/folding.cc | 10 ++--- test/evaluate/fp-testing.h | 2 +- test/evaluate/integer.cc | 2 +- test/evaluate/intrinsics.cc | 10 ++--- test/evaluate/leading-zero-bit-count.cc | 2 +- test/evaluate/logical.cc | 2 +- test/evaluate/real.cc | 2 +- test/evaluate/uint128.cc | 2 +- tools/f18/f18-parse-demo.cc | 20 +++++----- tools/f18/f18.cc | 28 ++++++------- tools/f18/stub-evaluate.cc | 2 +- 203 files changed, 713 insertions(+), 709 deletions(-) rename {lib => include/flang}/common/Fortran-features.h (94%) rename {lib => include/flang}/common/Fortran.h (97%) rename {lib => include/flang}/common/bit-population-count.h (98%) rename {lib => include/flang}/common/constexpr-bitset.h (98%) rename {lib => include/flang}/common/default-kinds.h (96%) rename {lib => include/flang}/common/enum-set.h (99%) rename {lib => include/flang}/common/format.h (99%) rename {lib => include/flang}/common/idioms.h (98%) rename {lib => include/flang}/common/indirection.h (98%) rename {lib => include/flang}/common/interval.h (98%) rename {lib => include/flang}/common/leading-zero-bit-count.h (98%) rename {lib => include/flang}/common/reference-counted.h (96%) rename {lib => include/flang}/common/reference.h (96%) rename {lib => include/flang}/common/restorer.h (95%) rename {lib => include/flang}/common/template.h (99%) rename {lib => include/flang}/common/uint128.h (99%) rename {lib => include/flang}/common/unsigned-const-division.h (97%) rename {lib => include/flang}/common/unwrap.h (98%) rename {lib => include/flang}/decimal/binary-floating-point.h (97%) rename {lib => include/flang}/decimal/decimal.h (98%) rename {lib => include/flang}/evaluate/call.h (97%) rename {lib => include/flang}/evaluate/characteristics.h (97%) rename {lib => include/flang}/evaluate/check-expression.h (97%) rename {lib => include/flang}/evaluate/common.h (96%) rename {lib => include/flang}/evaluate/complex.h (98%) rename {lib => include/flang}/evaluate/constant.h (98%) rename {lib => include/flang}/evaluate/expression.h (99%) rename {lib => include/flang}/evaluate/fold.h (97%) rename {lib => include/flang}/evaluate/formatting.h (94%) rename {lib => include/flang}/evaluate/integer.h (99%) rename {lib => include/flang}/evaluate/intrinsics-library.h (98%) rename {lib => include/flang}/evaluate/intrinsics.h (93%) rename {lib => include/flang}/evaluate/logical.h (97%) rename {lib => include/flang}/evaluate/real.h (99%) rename {lib => include/flang}/evaluate/rounding-bits.h (97%) rename {lib => include/flang}/evaluate/shape.h (97%) rename {lib => include/flang}/evaluate/static-data.h (95%) rename {lib => include/flang}/evaluate/tools.h (98%) rename {lib => include/flang}/evaluate/traverse.h (98%) rename {lib => include/flang}/evaluate/type.h (99%) rename {lib => include/flang}/evaluate/variable.h (98%) rename {lib => include/flang}/parser/char-block.h (98%) rename {lib => include/flang}/parser/char-buffer.h (97%) rename {lib => include/flang}/parser/char-set.h (97%) rename {lib => include/flang}/parser/characters.h (99%) rename {lib => include/flang}/parser/dump-parse-tree.h (99%) rename {lib => include/flang}/parser/format-specification.h (98%) rename {lib => include/flang}/parser/instrumented-parser.h (94%) rename {lib => include/flang}/parser/message.h (98%) rename {lib => include/flang}/parser/parse-state.h (96%) rename {lib => include/flang}/parser/parse-tree-visitor.h (99%) rename {lib => include/flang}/parser/parse-tree.h (99%) rename {lib => include/flang}/parser/parsing.h (95%) rename {lib => include/flang}/parser/provenance.h (98%) rename {lib => include/flang}/parser/source.h (97%) rename {lib => include/flang}/parser/tools.h (97%) rename {lib => include/flang}/parser/unparse.h (95%) rename {lib => include/flang}/parser/user-state.h (94%) rename {lib => include/flang}/semantics/attr.h (92%) rename {lib => include/flang}/semantics/expression.h (97%) rename {lib => include/flang}/semantics/scope.h (97%) rename {lib => include/flang}/semantics/semantics.h (97%) rename {lib => include/flang}/semantics/symbol.h (99%) rename {lib => include/flang}/semantics/tools.h (98%) rename {lib => include/flang}/semantics/type.h (98%) rename {lib => include/flang}/semantics/unparse-with-symbols.h (87%) diff --git a/.clang-format b/.clang-format index eb459959d4d8..21fb1ae51ac5 100644 --- a/.clang-format +++ b/.clang-format @@ -18,7 +18,7 @@ IncludeCategories: Priority: 4 - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 3 - - Regex: '^"\.\./' + - Regex: '^"(flang|\.\.)/' Priority: 2 - Regex: '.*' Priority: 1 diff --git a/lib/common/Fortran-features.h b/include/flang/common/Fortran-features.h similarity index 94% rename from lib/common/Fortran-features.h rename to include/flang/common/Fortran-features.h index bca723d5a793..0b318e90e3af 100644 --- a/lib/common/Fortran-features.h +++ b/include/flang/common/Fortran-features.h @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran-features.h ---------------------------*- C++ -*-===// +//===-- include/flang/common/Fortran-features.h -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,9 +9,9 @@ #ifndef FORTRAN_COMMON_FORTRAN_FEATURES_H_ #define FORTRAN_COMMON_FORTRAN_FEATURES_H_ -#include "Fortran.h" -#include "enum-set.h" -#include "idioms.h" +#include "flang/common/Fortran.h" +#include "flang/common/enum-set.h" +#include "flang/common/idioms.h" namespace Fortran::common { diff --git a/lib/common/Fortran.h b/include/flang/common/Fortran.h similarity index 97% rename from lib/common/Fortran.h rename to include/flang/common/Fortran.h index c4f367769836..9c73c32dcebb 100644 --- a/lib/common/Fortran.h +++ b/include/flang/common/Fortran.h @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran.h ------------------------------------*- C++ -*-===// +//===-- include/flang/common/Fortran.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/bit-population-count.h b/include/flang/common/bit-population-count.h similarity index 98% rename from lib/common/bit-population-count.h rename to include/flang/common/bit-population-count.h index fb580efae8bf..0a95643eb71d 100644 --- a/lib/common/bit-population-count.h +++ b/include/flang/common/bit-population-count.h @@ -1,4 +1,4 @@ -//===-- lib/common/bit-population-count.h -----------------------*- C++ -*-===// +//===-- include/flang/common/bit-population-count.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/constexpr-bitset.h b/include/flang/common/constexpr-bitset.h similarity index 98% rename from lib/common/constexpr-bitset.h rename to include/flang/common/constexpr-bitset.h index 100b5c346b61..1125655adbab 100644 --- a/lib/common/constexpr-bitset.h +++ b/include/flang/common/constexpr-bitset.h @@ -1,4 +1,4 @@ -//===-- lib/common/constexpr-bitset.h ---------------------------*- C++ -*-===// +//===-- include/flang/common/constexpr-bitset.h -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/default-kinds.h b/include/flang/common/default-kinds.h similarity index 96% rename from lib/common/default-kinds.h rename to include/flang/common/default-kinds.h index a888e58c5e81..e9532ad8ddcb 100644 --- a/lib/common/default-kinds.h +++ b/include/flang/common/default-kinds.h @@ -1,4 +1,4 @@ -//===-- lib/common/default-kinds.h ------------------------------*- C++ -*-===// +//===-- include/flang/common/default-kinds.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_COMMON_DEFAULT_KINDS_H_ #define FORTRAN_COMMON_DEFAULT_KINDS_H_ -#include "Fortran.h" +#include "flang/common/Fortran.h" #include namespace Fortran::common { diff --git a/lib/common/enum-set.h b/include/flang/common/enum-set.h similarity index 99% rename from lib/common/enum-set.h rename to include/flang/common/enum-set.h index d3f55b34d732..04141808a5b8 100644 --- a/lib/common/enum-set.h +++ b/include/flang/common/enum-set.h @@ -1,4 +1,4 @@ -//===-- lib/common/enum-set.h -----------------------------------*- C++ -*-===// +//===-- include/flang/common/enum-set.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/format.h b/include/flang/common/format.h similarity index 99% rename from lib/common/format.h rename to include/flang/common/format.h index df056f8a4451..92cd0e94fad9 100644 --- a/lib/common/format.h +++ b/include/flang/common/format.h @@ -1,4 +1,4 @@ -//===-- lib/common/format.h -------------------------------------*- C++ -*-===// +//===-- include/flang/common/format.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_COMMON_FORMAT_H_ #define FORTRAN_COMMON_FORMAT_H_ -#include "Fortran.h" #include "enum-set.h" +#include "flang/common/Fortran.h" #include // Define a FormatValidator class template to validate a format expression diff --git a/lib/common/idioms.h b/include/flang/common/idioms.h similarity index 98% rename from lib/common/idioms.h rename to include/flang/common/idioms.h index 9e1ea7cf0d61..8debe21acb66 100644 --- a/lib/common/idioms.h +++ b/include/flang/common/idioms.h @@ -1,4 +1,4 @@ -//===-- lib/common/idioms.h -------------------------------------*- C++ -*-===// +//===-- include/flang/common/idioms.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/indirection.h b/include/flang/common/indirection.h similarity index 98% rename from lib/common/indirection.h rename to include/flang/common/indirection.h index 4e8ea324379d..fcf05549ae7e 100644 --- a/lib/common/indirection.h +++ b/include/flang/common/indirection.h @@ -1,4 +1,4 @@ -//===-- lib/common/indirection.h --------------------------------*- C++ -*-===// +//===-- include/flang/common/indirection.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/interval.h b/include/flang/common/interval.h similarity index 98% rename from lib/common/interval.h rename to include/flang/common/interval.h index ca8a346c12fe..144f719de7b3 100644 --- a/lib/common/interval.h +++ b/include/flang/common/interval.h @@ -1,4 +1,4 @@ -//===-- lib/common/interval.h -----------------------------------*- C++ -*-===// +//===-- include/flang/common/interval.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/leading-zero-bit-count.h b/include/flang/common/leading-zero-bit-count.h similarity index 98% rename from lib/common/leading-zero-bit-count.h rename to include/flang/common/leading-zero-bit-count.h index 909ccedbbd3c..fe7bf00378e0 100644 --- a/lib/common/leading-zero-bit-count.h +++ b/include/flang/common/leading-zero-bit-count.h @@ -1,4 +1,4 @@ -//===-- lib/common/leading-zero-bit-count.h ---------------------*- C++ -*-===// +//===-- include/flang/common/leading-zero-bit-count.h -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/reference-counted.h b/include/flang/common/reference-counted.h similarity index 96% rename from lib/common/reference-counted.h rename to include/flang/common/reference-counted.h index 64065ff347ee..d7dc68c76492 100644 --- a/lib/common/reference-counted.h +++ b/include/flang/common/reference-counted.h @@ -1,4 +1,4 @@ -//===-- lib/common/reference-counted.h --------------------------*- C++ -*-===// +//===-- include/flang/common/reference-counted.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/reference.h b/include/flang/common/reference.h similarity index 96% rename from lib/common/reference.h rename to include/flang/common/reference.h index 053f290b06e8..8f01b6587c23 100644 --- a/lib/common/reference.h +++ b/include/flang/common/reference.h @@ -1,4 +1,4 @@ -//===-- lib/common/reference.h ----------------------------------*- C++ -*-===// +//===-- include/flang/common/reference.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/restorer.h b/include/flang/common/restorer.h similarity index 95% rename from lib/common/restorer.h rename to include/flang/common/restorer.h index 46773450ac25..95b730b83513 100644 --- a/lib/common/restorer.h +++ b/include/flang/common/restorer.h @@ -1,4 +1,4 @@ -//===-- lib/common/restorer.h -----------------------------------*- C++ -*-===// +//===-- include/flang/common/restorer.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/template.h b/include/flang/common/template.h similarity index 99% rename from lib/common/template.h rename to include/flang/common/template.h index 0d344bbe9e8a..c8a18e704fb4 100644 --- a/lib/common/template.h +++ b/include/flang/common/template.h @@ -1,4 +1,4 @@ -//===-- lib/common/template.h -----------------------------------*- C++ -*-===// +//===-- include/flang/common/template.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,6 +9,7 @@ #ifndef FORTRAN_COMMON_TEMPLATE_H_ #define FORTRAN_COMMON_TEMPLATE_H_ +#include "flang/common/idioms.h" #include #include #include diff --git a/lib/common/uint128.h b/include/flang/common/uint128.h similarity index 99% rename from lib/common/uint128.h rename to include/flang/common/uint128.h index 51c29a507789..0129101a1ce9 100644 --- a/lib/common/uint128.h +++ b/include/flang/common/uint128.h @@ -1,4 +1,4 @@ -//===-- lib/common/uint128.h ------------------------------------*- C++ -*-===// +//===-- include/flang/common/uint128.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/unsigned-const-division.h b/include/flang/common/unsigned-const-division.h similarity index 97% rename from lib/common/unsigned-const-division.h rename to include/flang/common/unsigned-const-division.h index 4e61f0a7f550..749983e8464c 100644 --- a/lib/common/unsigned-const-division.h +++ b/include/flang/common/unsigned-const-division.h @@ -1,4 +1,4 @@ -//===-- lib/common/unsigned-const-division.h --------------------*- C++ -*-===// +//===-- include/flang/common/unsigned-const-division.h ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/unwrap.h b/include/flang/common/unwrap.h similarity index 98% rename from lib/common/unwrap.h rename to include/flang/common/unwrap.h index ce066b9f8512..1370b1425202 100644 --- a/lib/common/unwrap.h +++ b/include/flang/common/unwrap.h @@ -1,4 +1,4 @@ -//===-- lib/common/unwrap.h -------------------------------------*- C++ -*-===// +//===-- include/flang/common/unwrap.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/decimal/binary-floating-point.h b/include/flang/decimal/binary-floating-point.h similarity index 97% rename from lib/decimal/binary-floating-point.h rename to include/flang/decimal/binary-floating-point.h index ece7518bb9e0..3da4a336c50e 100644 --- a/lib/decimal/binary-floating-point.h +++ b/include/flang/decimal/binary-floating-point.h @@ -1,4 +1,4 @@ -//===-- lib/decimal/binary-floating-point.h ---------------------*- C++ -*-===// +//===-- include/flang/decimal/binary-floating-point.h -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,7 +12,7 @@ // Access and manipulate the fields of an IEEE-754 binary // floating-point value via a generalized template. -#include "../common/uint128.h" +#include "flang/common/uint128.h" #include #include #include diff --git a/lib/decimal/decimal.h b/include/flang/decimal/decimal.h similarity index 98% rename from lib/decimal/decimal.h rename to include/flang/decimal/decimal.h index a8f46f95a1c5..812d08fe8d09 100644 --- a/lib/decimal/decimal.h +++ b/include/flang/decimal/decimal.h @@ -1,4 +1,4 @@ -/*===-- lib/decimal/decimal.h -------------------------------------*- C++ -*-=== +/*===-- include/flang/decimal/decimal.h ---------------------------*- C++ -*-=== * * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. * See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/call.h b/include/flang/evaluate/call.h similarity index 97% rename from lib/evaluate/call.h rename to include/flang/evaluate/call.h index 5a048da773ac..97d1ea1b04f9 100644 --- a/lib/evaluate/call.h +++ b/include/flang/evaluate/call.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/call.h -------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/call.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,10 +13,10 @@ #include "constant.h" #include "formatting.h" #include "type.h" -#include "../common/indirection.h" -#include "../common/reference.h" -#include "../parser/char-block.h" -#include "../semantics/attr.h" +#include "flang/common/indirection.h" +#include "flang/common/reference.h" +#include "flang/parser/char-block.h" +#include "flang/semantics/attr.h" #include #include #include diff --git a/lib/evaluate/characteristics.h b/include/flang/evaluate/characteristics.h similarity index 97% rename from lib/evaluate/characteristics.h rename to include/flang/evaluate/characteristics.h index 3da4d63c5fd3..9e61602579e7 100644 --- a/lib/evaluate/characteristics.h +++ b/include/flang/evaluate/characteristics.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/characteristics.h --------------------------*- C++ -*-===// +//===-- include/flang/evaluate/characteristics.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,12 +17,12 @@ #include "expression.h" #include "shape.h" #include "type.h" -#include "../common/Fortran.h" -#include "../common/enum-set.h" -#include "../common/idioms.h" -#include "../common/indirection.h" -#include "../parser/char-block.h" -#include "../semantics/symbol.h" +#include "flang/common/Fortran.h" +#include "flang/common/enum-set.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/parser/char-block.h" +#include "flang/semantics/symbol.h" #include #include #include diff --git a/lib/evaluate/check-expression.h b/include/flang/evaluate/check-expression.h similarity index 97% rename from lib/evaluate/check-expression.h rename to include/flang/evaluate/check-expression.h index b19d747729e6..31a79fb19585 100644 --- a/lib/evaluate/check-expression.h +++ b/include/flang/evaluate/check-expression.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/check-expression.h -------------------------*- C++ -*-===// +//===-- include/flang/evaluate/check-expression.h ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/common.h b/include/flang/evaluate/common.h similarity index 96% rename from lib/evaluate/common.h rename to include/flang/evaluate/common.h index e716d2198c4e..f24e93d7cd33 100644 --- a/lib/evaluate/common.h +++ b/include/flang/evaluate/common.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/common.h -----------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/common.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,14 +10,14 @@ #define FORTRAN_EVALUATE_COMMON_H_ #include "intrinsics-library.h" -#include "../common/Fortran.h" -#include "../common/default-kinds.h" -#include "../common/enum-set.h" -#include "../common/idioms.h" -#include "../common/indirection.h" -#include "../common/restorer.h" -#include "../parser/char-block.h" -#include "../parser/message.h" +#include "flang/common/Fortran.h" +#include "flang/common/default-kinds.h" +#include "flang/common/enum-set.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/common/restorer.h" +#include "flang/parser/char-block.h" +#include "flang/parser/message.h" #include #include diff --git a/lib/evaluate/complex.h b/include/flang/evaluate/complex.h similarity index 98% rename from lib/evaluate/complex.h rename to include/flang/evaluate/complex.h index 8d3520363b73..201cbcea60ab 100644 --- a/lib/evaluate/complex.h +++ b/include/flang/evaluate/complex.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/complex.h ----------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/complex.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/constant.h b/include/flang/evaluate/constant.h similarity index 98% rename from lib/evaluate/constant.h rename to include/flang/evaluate/constant.h index 290343b98d75..833702a03c91 100644 --- a/lib/evaluate/constant.h +++ b/include/flang/evaluate/constant.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/constant.h ---------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/constant.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,8 +11,8 @@ #include "formatting.h" #include "type.h" -#include "../common/default-kinds.h" -#include "../common/reference.h" +#include "flang/common/default-kinds.h" +#include "flang/common/reference.h" #include #include #include diff --git a/lib/evaluate/expression.h b/include/flang/evaluate/expression.h similarity index 99% rename from lib/evaluate/expression.h rename to include/flang/evaluate/expression.h index 00363b523d5d..b06119107ef8 100644 --- a/lib/evaluate/expression.h +++ b/include/flang/evaluate/expression.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/expression.h -------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/expression.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -21,11 +21,11 @@ #include "formatting.h" #include "type.h" #include "variable.h" -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../common/indirection.h" -#include "../common/template.h" -#include "../parser/char-block.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/common/template.h" +#include "flang/parser/char-block.h" #include #include #include diff --git a/lib/evaluate/fold.h b/include/flang/evaluate/fold.h similarity index 97% rename from lib/evaluate/fold.h rename to include/flang/evaluate/fold.h index 88daa51ee18b..5f33d69a2fe6 100644 --- a/lib/evaluate/fold.h +++ b/include/flang/evaluate/fold.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold.h -------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/fold.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/formatting.h b/include/flang/evaluate/formatting.h similarity index 94% rename from lib/evaluate/formatting.h rename to include/flang/evaluate/formatting.h index 4774b4bbd27a..a2b8458d042f 100644 --- a/lib/evaluate/formatting.h +++ b/include/flang/evaluate/formatting.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/formatting.h -------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/formatting.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -19,7 +19,7 @@ // This header is meant to be included by the headers that define the several // representational class templates that need it, not by external clients. -#include "../common/indirection.h" +#include "flang/common/indirection.h" #include #include #include diff --git a/lib/evaluate/integer.h b/include/flang/evaluate/integer.h similarity index 99% rename from lib/evaluate/integer.h rename to include/flang/evaluate/integer.h index 78f9b84d4a21..1bff2bb7b7ca 100644 --- a/lib/evaluate/integer.h +++ b/include/flang/evaluate/integer.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/integer.h ----------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/integer.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,9 +17,9 @@ // (*"Signed" here means two's-complement, just to be clear. Ones'-complement // and signed-magnitude encodings appear to be extinct in 2018.) -#include "common.h" -#include "../common/bit-population-count.h" -#include "../common/leading-zero-bit-count.h" +#include "flang/common/bit-population-count.h" +#include "flang/common/leading-zero-bit-count.h" +#include "flang/evaluate/common.h" #include #include #include diff --git a/lib/evaluate/intrinsics-library.h b/include/flang/evaluate/intrinsics-library.h similarity index 98% rename from lib/evaluate/intrinsics-library.h rename to include/flang/evaluate/intrinsics-library.h index a558add87069..a7a1959a22bb 100644 --- a/lib/evaluate/intrinsics-library.h +++ b/include/flang/evaluate/intrinsics-library.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics-library.h -----------------------*- C++ -*-===// +//===-- include/flang/evaluate/intrinsics-library.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/intrinsics.h b/include/flang/evaluate/intrinsics.h similarity index 93% rename from lib/evaluate/intrinsics.h rename to include/flang/evaluate/intrinsics.h index 036a401edc47..525e0907a159 100644 --- a/lib/evaluate/intrinsics.h +++ b/include/flang/evaluate/intrinsics.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics.h -------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/intrinsics.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,9 +12,9 @@ #include "call.h" #include "characteristics.h" #include "type.h" -#include "../common/default-kinds.h" -#include "../parser/char-block.h" -#include "../parser/message.h" +#include "flang/common/default-kinds.h" +#include "flang/parser/char-block.h" +#include "flang/parser/message.h" #include #include #include diff --git a/lib/evaluate/logical.h b/include/flang/evaluate/logical.h similarity index 97% rename from lib/evaluate/logical.h rename to include/flang/evaluate/logical.h index e98637acf553..d76abf854d6a 100644 --- a/lib/evaluate/logical.h +++ b/include/flang/evaluate/logical.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/logical.h ----------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/logical.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/real.h b/include/flang/evaluate/real.h similarity index 99% rename from lib/evaluate/real.h rename to include/flang/evaluate/real.h index 92bb7d19d33a..84e2d556baf3 100644 --- a/lib/evaluate/real.h +++ b/include/flang/evaluate/real.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/real.h -------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/real.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_EVALUATE_REAL_H_ #define FORTRAN_EVALUATE_REAL_H_ -#include "common.h" #include "formatting.h" #include "integer.h" #include "rounding-bits.h" +#include "flang/evaluate/common.h" #include #include #include diff --git a/lib/evaluate/rounding-bits.h b/include/flang/evaluate/rounding-bits.h similarity index 97% rename from lib/evaluate/rounding-bits.h rename to include/flang/evaluate/rounding-bits.h index 07db7ce40d13..bc5771659595 100644 --- a/lib/evaluate/rounding-bits.h +++ b/include/flang/evaluate/rounding-bits.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/rounding-bits.h ----------------------------*- C++ -*-===// +//===-- include/flang/evaluate/rounding-bits.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/shape.h b/include/flang/evaluate/shape.h similarity index 97% rename from lib/evaluate/shape.h rename to include/flang/evaluate/shape.h index 15e16eb4665e..ca9700b02583 100644 --- a/lib/evaluate/shape.h +++ b/include/flang/evaluate/shape.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/shape.h ------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/shape.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,11 +13,11 @@ #define FORTRAN_EVALUATE_SHAPE_H_ #include "expression.h" -#include "tools.h" #include "traverse.h" -#include "type.h" #include "variable.h" -#include "../common/indirection.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" #include #include diff --git a/lib/evaluate/static-data.h b/include/flang/evaluate/static-data.h similarity index 95% rename from lib/evaluate/static-data.h rename to include/flang/evaluate/static-data.h index 902dcff20eed..ce66351ba140 100644 --- a/lib/evaluate/static-data.h +++ b/include/flang/evaluate/static-data.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/static-data.h ------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/static-data.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,7 +13,7 @@ #include "formatting.h" #include "type.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" #include #include #include diff --git a/lib/evaluate/tools.h b/include/flang/evaluate/tools.h similarity index 98% rename from lib/evaluate/tools.h rename to include/flang/evaluate/tools.h index 5b2824a41bfb..9a473619d5b0 100644 --- a/lib/evaluate/tools.h +++ b/include/flang/evaluate/tools.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/tools.h ------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/tools.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,15 +9,15 @@ #ifndef FORTRAN_EVALUATE_TOOLS_H_ #define FORTRAN_EVALUATE_TOOLS_H_ -#include "constant.h" -#include "expression.h" #include "traverse.h" -#include "../common/idioms.h" -#include "../common/template.h" -#include "../common/unwrap.h" -#include "../parser/message.h" -#include "../semantics/attr.h" -#include "../semantics/symbol.h" +#include "flang/common/idioms.h" +#include "flang/common/template.h" +#include "flang/common/unwrap.h" +#include "flang/evaluate/constant.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/message.h" +#include "flang/semantics/attr.h" +#include "flang/semantics/symbol.h" #include #include #include diff --git a/lib/evaluate/traverse.h b/include/flang/evaluate/traverse.h similarity index 98% rename from lib/evaluate/traverse.h rename to include/flang/evaluate/traverse.h index 2fa8995bf70e..326104b1e1ce 100644 --- a/lib/evaluate/traverse.h +++ b/include/flang/evaluate/traverse.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/traverse.h ---------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/traverse.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -35,8 +35,8 @@ // - Overloads of operator() in each visitor handle the cases of interest. #include "expression.h" -#include "../semantics/symbol.h" -#include "../semantics/type.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/type.h" #include #include diff --git a/lib/evaluate/type.h b/include/flang/evaluate/type.h similarity index 99% rename from lib/evaluate/type.h rename to include/flang/evaluate/type.h index 91a7c6011065..29dde4eeb1a4 100644 --- a/lib/evaluate/type.h +++ b/include/flang/evaluate/type.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/type.h -------------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/type.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -22,9 +22,9 @@ #include "integer.h" #include "logical.h" #include "real.h" -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../common/template.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/common/template.h" #include #include #include diff --git a/lib/evaluate/variable.h b/include/flang/evaluate/variable.h similarity index 98% rename from lib/evaluate/variable.h rename to include/flang/evaluate/variable.h index 15cd63874968..bbbdd7c74903 100644 --- a/lib/evaluate/variable.h +++ b/include/flang/evaluate/variable.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/variable.h ---------------------------------*- C++ -*-===// +//===-- include/flang/evaluate/variable.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,10 +20,10 @@ #include "formatting.h" #include "static-data.h" #include "type.h" -#include "../common/idioms.h" -#include "../common/reference.h" -#include "../common/template.h" -#include "../parser/char-block.h" +#include "flang/common/idioms.h" +#include "flang/common/reference.h" +#include "flang/common/template.h" +#include "flang/parser/char-block.h" #include #include #include diff --git a/lib/parser/char-block.h b/include/flang/parser/char-block.h similarity index 98% rename from lib/parser/char-block.h rename to include/flang/parser/char-block.h index 3f5585e868bb..f05211fa8845 100644 --- a/lib/parser/char-block.h +++ b/include/flang/parser/char-block.h @@ -1,4 +1,4 @@ -//===-- lib/parser/char-block.h ---------------------------------*- C++ -*-===// +//===-- include/flang/parser/char-block.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,7 +11,7 @@ // Describes a contiguous block of characters; does not own their storage. -#include "../common/interval.h" +#include "flang/common/interval.h" #include #include #include diff --git a/lib/parser/char-buffer.h b/include/flang/parser/char-buffer.h similarity index 97% rename from lib/parser/char-buffer.h rename to include/flang/parser/char-buffer.h index c5c0c8a51078..b9f66c6f3dde 100644 --- a/lib/parser/char-buffer.h +++ b/include/flang/parser/char-buffer.h @@ -1,4 +1,4 @@ -//===-- lib/parser/char-buffer.h --------------------------------*- C++ -*-===// +//===-- include/flang/parser/char-buffer.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/char-set.h b/include/flang/parser/char-set.h similarity index 97% rename from lib/parser/char-set.h rename to include/flang/parser/char-set.h index 6f041b241c0a..9bac7ea01180 100644 --- a/lib/parser/char-set.h +++ b/include/flang/parser/char-set.h @@ -1,4 +1,4 @@ -//===-- lib/parser/char-set.h -----------------------------------*- C++ -*-===// +//===-- include/flang/parser/char-set.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/characters.h b/include/flang/parser/characters.h similarity index 99% rename from lib/parser/characters.h rename to include/flang/parser/characters.h index ceb7aa18d4bc..102d886d3a0b 100644 --- a/lib/parser/characters.h +++ b/include/flang/parser/characters.h @@ -1,4 +1,4 @@ -//===-- lib/parser/characters.h ---------------------------------*- C++ -*-===// +//===-- include/flang/parser/characters.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/dump-parse-tree.h b/include/flang/parser/dump-parse-tree.h similarity index 99% rename from lib/parser/dump-parse-tree.h rename to include/flang/parser/dump-parse-tree.h index 1ebbbbd6c7ea..aca18137f955 100644 --- a/lib/parser/dump-parse-tree.h +++ b/include/flang/parser/dump-parse-tree.h @@ -1,4 +1,4 @@ -//===-- lib/parser/dump-parse-tree.h ----------------------------*- C++ -*-===// +//===-- include/flang/parser/dump-parse-tree.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,8 +13,8 @@ #include "parse-tree-visitor.h" #include "parse-tree.h" #include "unparse.h" -#include "../common/idioms.h" -#include "../common/indirection.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" #include #include #include diff --git a/lib/parser/format-specification.h b/include/flang/parser/format-specification.h similarity index 98% rename from lib/parser/format-specification.h rename to include/flang/parser/format-specification.h index 64bce964a8ec..6f1de183a30e 100644 --- a/lib/parser/format-specification.h +++ b/include/flang/parser/format-specification.h @@ -1,4 +1,4 @@ -//===-- lib/parser/format-specification.h -----------------------*- C++ -*-===// +//===-- include/flang/parser/format-specification.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/instrumented-parser.h b/include/flang/parser/instrumented-parser.h similarity index 94% rename from lib/parser/instrumented-parser.h rename to include/flang/parser/instrumented-parser.h index 66bb73e5fbf1..ec760a610a08 100644 --- a/lib/parser/instrumented-parser.h +++ b/include/flang/parser/instrumented-parser.h @@ -1,4 +1,4 @@ -//===-- lib/parser/instrumented-parser.h ------------------------*- C++ -*-===// +//===-- include/flang/parser/instrumented-parser.h --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_PARSER_INSTRUMENTED_PARSER_H_ #define FORTRAN_PARSER_INSTRUMENTED_PARSER_H_ -#include "message.h" #include "parse-state.h" -#include "provenance.h" #include "user-state.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/message.h b/include/flang/parser/message.h similarity index 98% rename from lib/parser/message.h rename to include/flang/parser/message.h index eb946c2bb2fd..fd62f4671f55 100644 --- a/lib/parser/message.h +++ b/include/flang/parser/message.h @@ -1,4 +1,4 @@ -//===-- lib/parser/message.h ------------------------------------*- C++ -*-===// +//===-- include/flang/parser/message.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,9 +15,9 @@ #include "char-block.h" #include "char-set.h" #include "provenance.h" -#include "../common/idioms.h" -#include "../common/reference-counted.h" -#include "../common/restorer.h" +#include "flang/common/idioms.h" +#include "flang/common/reference-counted.h" +#include "flang/common/restorer.h" #include #include #include diff --git a/lib/parser/parse-state.h b/include/flang/parser/parse-state.h similarity index 96% rename from lib/parser/parse-state.h rename to include/flang/parser/parse-state.h index 03bfaba96d42..afd4516403a3 100644 --- a/lib/parser/parse-state.h +++ b/include/flang/parser/parse-state.h @@ -1,4 +1,4 @@ -//===-- lib/parser/parse-state.h --------------------------------*- C++ -*-===// +//===-- include/flang/parser/parse-state.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,12 +15,12 @@ // attempts. Must be efficient to duplicate and assign for backtracking // and recovery during parsing! -#include "characters.h" -#include "message.h" -#include "provenance.h" #include "user-state.h" -#include "../common/Fortran-features.h" -#include "../common/idioms.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/idioms.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/parse-tree-visitor.h b/include/flang/parser/parse-tree-visitor.h similarity index 99% rename from lib/parser/parse-tree-visitor.h rename to include/flang/parser/parse-tree-visitor.h index 2fac69278593..b2c49fe48034 100644 --- a/lib/parser/parse-tree-visitor.h +++ b/include/flang/parser/parse-tree-visitor.h @@ -1,4 +1,4 @@ -//===-- lib/parser/parse-tree-visitor.h -------------------------*- C++ -*-===// +//===-- include/flang/parser/parse-tree-visitor.h ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/parse-tree.h b/include/flang/parser/parse-tree.h similarity index 99% rename from lib/parser/parse-tree.h rename to include/flang/parser/parse-tree.h index e94432e0bad5..29638957f35b 100644 --- a/lib/parser/parse-tree.h +++ b/include/flang/parser/parse-tree.h @@ -1,4 +1,4 @@ -//===-- lib/parser/parse-tree.h ---------------------------------*- C++ -*-===// +//===-- include/flang/parser/parse-tree.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -22,9 +22,9 @@ #include "format-specification.h" #include "message.h" #include "provenance.h" -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../common/indirection.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" #include #include #include diff --git a/lib/parser/parsing.h b/include/flang/parser/parsing.h similarity index 95% rename from lib/parser/parsing.h rename to include/flang/parser/parsing.h index d251b62daa00..a163a77e5856 100644 --- a/lib/parser/parsing.h +++ b/include/flang/parser/parsing.h @@ -1,4 +1,4 @@ -//===-- lib/parser/parsing.h ------------------------------------*- C++ -*-===// +//===-- include/flang/parser/parsing.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,7 +14,7 @@ #include "message.h" #include "parse-tree.h" #include "provenance.h" -#include "../common/Fortran-features.h" +#include "flang/common/Fortran-features.h" #include #include #include diff --git a/lib/parser/provenance.h b/include/flang/parser/provenance.h similarity index 98% rename from lib/parser/provenance.h rename to include/flang/parser/provenance.h index 52e6d74b2015..08cdf51345d3 100644 --- a/lib/parser/provenance.h +++ b/include/flang/parser/provenance.h @@ -1,4 +1,4 @@ -//===-- lib/parser/provenance.h ---------------------------------*- C++ -*-===// +//===-- include/flang/parser/provenance.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,8 +13,8 @@ #include "char-buffer.h" #include "characters.h" #include "source.h" -#include "../common/idioms.h" -#include "../common/interval.h" +#include "flang/common/idioms.h" +#include "flang/common/interval.h" #include #include #include diff --git a/lib/parser/source.h b/include/flang/parser/source.h similarity index 97% rename from lib/parser/source.h rename to include/flang/parser/source.h index 1b809e7911d1..5eb000da2163 100644 --- a/lib/parser/source.h +++ b/include/flang/parser/source.h @@ -1,4 +1,4 @@ -//===-- lib/parser/source.h -------------------------------------*- C++ -*-===// +//===-- include/flang/parser/source.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/tools.h b/include/flang/parser/tools.h similarity index 97% rename from lib/parser/tools.h rename to include/flang/parser/tools.h index 461b0f2584e8..447d08c1f496 100644 --- a/lib/parser/tools.h +++ b/include/flang/parser/tools.h @@ -1,4 +1,4 @@ -//===-- lib/parser/tools.h --------------------------------------*- C++ -*-===// +//===-- include/flang/parser/tools.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/unparse.h b/include/flang/parser/unparse.h similarity index 95% rename from lib/parser/unparse.h rename to include/flang/parser/unparse.h index 08d8b8405d23..0055ce30a701 100644 --- a/lib/parser/unparse.h +++ b/include/flang/parser/unparse.h @@ -1,4 +1,4 @@ -//===-- lib/parser/unparse.h ------------------------------------*- C++ -*-===// +//===-- include/flang/parser/unparse.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/user-state.h b/include/flang/parser/user-state.h similarity index 94% rename from lib/parser/user-state.h rename to include/flang/parser/user-state.h index cb14ae35690b..aaf86720d749 100644 --- a/lib/parser/user-state.h +++ b/include/flang/parser/user-state.h @@ -1,4 +1,4 @@ -//===-- lib/parser/user-state.h ---------------------------------*- C++ -*-===// +//===-- include/flang/parser/user-state.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,10 +14,10 @@ // parse tree construction so as to avoid any need for representing // state in static data. -#include "char-block.h" -#include "parse-tree.h" -#include "../common/Fortran-features.h" -#include "../common/idioms.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/idioms.h" +#include "flang/parser/char-block.h" +#include "flang/parser/parse-tree.h" #include #include #include diff --git a/lib/semantics/attr.h b/include/flang/semantics/attr.h similarity index 92% rename from lib/semantics/attr.h rename to include/flang/semantics/attr.h index 99db4c82329f..475d6891d528 100644 --- a/lib/semantics/attr.h +++ b/include/flang/semantics/attr.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/attr.h ------------------------------------*- C++ -*-===// +//===-- include/flang/semantics/attr.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_ATTR_H_ #define FORTRAN_SEMANTICS_ATTR_H_ -#include "../common/enum-set.h" -#include "../common/idioms.h" +#include "flang/common/enum-set.h" +#include "flang/common/idioms.h" #include #include diff --git a/lib/semantics/expression.h b/include/flang/semantics/expression.h similarity index 97% rename from lib/semantics/expression.h rename to include/flang/semantics/expression.h index bac545b08f26..110a0bdb0853 100644 --- a/lib/semantics/expression.h +++ b/include/flang/semantics/expression.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/expression.h ------------------------------*- C++ -*-===// +//===-- include/flang/semantics/expression.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,17 +10,17 @@ #define FORTRAN_SEMANTICS_EXPRESSION_H_ #include "semantics.h" -#include "../common/Fortran.h" -#include "../common/indirection.h" -#include "../evaluate/characteristics.h" -#include "../evaluate/check-expression.h" -#include "../evaluate/expression.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" -#include "../evaluate/type.h" -#include "../parser/char-block.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" +#include "flang/common/Fortran.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/check-expression.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" +#include "flang/parser/char-block.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" #include #include #include diff --git a/lib/semantics/scope.h b/include/flang/semantics/scope.h similarity index 97% rename from lib/semantics/scope.h rename to include/flang/semantics/scope.h index c44a68726e51..1de11bb53171 100644 --- a/lib/semantics/scope.h +++ b/include/flang/semantics/scope.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/scope.h -----------------------------------*- C++ -*-===// +//===-- include/flang/semantics/scope.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,11 +11,11 @@ #include "attr.h" #include "symbol.h" -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../common/reference.h" -#include "../parser/message.h" -#include "../parser/provenance.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/common/reference.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/semantics/semantics.h b/include/flang/semantics/semantics.h similarity index 97% rename from lib/semantics/semantics.h rename to include/flang/semantics/semantics.h index d7ebb5a9e553..e823f48dc397 100644 --- a/lib/semantics/semantics.h +++ b/include/flang/semantics/semantics.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/semantics.h -------------------------------*- C++ -*-===// +//===-- include/flang/semantics/semantics.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,10 +11,10 @@ #include "scope.h" #include "symbol.h" -#include "../common/Fortran-features.h" -#include "../evaluate/common.h" -#include "../evaluate/intrinsics.h" -#include "../parser/message.h" +#include "flang/common/Fortran-features.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/parser/message.h" #include #include #include diff --git a/lib/semantics/symbol.h b/include/flang/semantics/symbol.h similarity index 99% rename from lib/semantics/symbol.h rename to include/flang/semantics/symbol.h index b37e560f9f75..335e6ed936a5 100644 --- a/lib/semantics/symbol.h +++ b/include/flang/semantics/symbol.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/symbol.h ----------------------------------*- C++ -*-===// +//===-- include/flang/semantics/symbol.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,9 +10,9 @@ #define FORTRAN_SEMANTICS_SYMBOL_H_ #include "type.h" -#include "../common/Fortran.h" -#include "../common/enum-set.h" -#include "../common/reference.h" +#include "flang/common/Fortran.h" +#include "flang/common/enum-set.h" +#include "flang/common/reference.h" #include #include #include diff --git a/lib/semantics/tools.h b/include/flang/semantics/tools.h similarity index 98% rename from lib/semantics/tools.h rename to include/flang/semantics/tools.h index 83741bdddafe..5f56325cc913 100644 --- a/lib/semantics/tools.h +++ b/include/flang/semantics/tools.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/tools.h -----------------------------------*- C++ -*-===// +//===-- include/flang/semantics/tools.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,15 +12,15 @@ // Simple predicates and look-up functions that are best defined // canonically for use in semantic checking. -#include "attr.h" -#include "expression.h" -#include "semantics.h" -#include "../common/Fortran.h" -#include "../evaluate/expression.h" -#include "../evaluate/type.h" -#include "../evaluate/variable.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/common/Fortran.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/type.h" +#include "flang/evaluate/variable.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/attr.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/semantics.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/type.h b/include/flang/semantics/type.h similarity index 98% rename from lib/semantics/type.h rename to include/flang/semantics/type.h index 9a3142389c5a..19d02000c45c 100644 --- a/lib/semantics/type.h +++ b/include/flang/semantics/type.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/type.h ------------------------------------*- C++ -*-===// +//===-- include/flang/semantics/type.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_TYPE_H_ #define FORTRAN_SEMANTICS_TYPE_H_ -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../evaluate/expression.h" -#include "../parser/char-block.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/char-block.h" #include #include #include diff --git a/lib/semantics/unparse-with-symbols.h b/include/flang/semantics/unparse-with-symbols.h similarity index 87% rename from lib/semantics/unparse-with-symbols.h rename to include/flang/semantics/unparse-with-symbols.h index 727dc89b9955..8a6760f0094b 100644 --- a/lib/semantics/unparse-with-symbols.h +++ b/include/flang/semantics/unparse-with-symbols.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/unparse-with-symbols.h --------------------*- C++ -*-===// +//===-- include/flang/semantics/unparse-with-symbols.h ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_ #define FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_ -#include "../parser/characters.h" +#include "flang/parser/characters.h" #include namespace Fortran::parser { diff --git a/lib/common/Fortran-features.cc b/lib/common/Fortran-features.cc index 8e5e08e68fbd..f92162673e46 100644 --- a/lib/common/Fortran-features.cc +++ b/lib/common/Fortran-features.cc @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "Fortran-features.h" -#include "Fortran.h" -#include "idioms.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" namespace Fortran::common { diff --git a/lib/common/Fortran.cc b/lib/common/Fortran.cc index 35d61f0d2360..8b915bbee6dd 100644 --- a/lib/common/Fortran.cc +++ b/lib/common/Fortran.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "Fortran.h" +#include "flang/common/Fortran.h" namespace Fortran::common { diff --git a/lib/common/default-kinds.cc b/lib/common/default-kinds.cc index 4b40c4976ffd..1f459935961e 100644 --- a/lib/common/default-kinds.cc +++ b/lib/common/default-kinds.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "default-kinds.h" -#include "idioms.h" +#include "flang/common/default-kinds.h" +#include "flang/common/idioms.h" namespace Fortran::common { diff --git a/lib/common/idioms.cc b/lib/common/idioms.cc index d942269f3670..f27f7b1a0030 100644 --- a/lib/common/idioms.cc +++ b/lib/common/idioms.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "idioms.h" +#include "flang/common/idioms.h" #include #include #include diff --git a/lib/decimal/big-radix-floating-point.h b/lib/decimal/big-radix-floating-point.h index 7b84015bd257..51eb9ec8c5b6 100644 --- a/lib/decimal/big-radix-floating-point.h +++ b/lib/decimal/big-radix-floating-point.h @@ -21,12 +21,12 @@ // for conversions between binary and decimal representations; it is not // a general-purpose facility. -#include "binary-floating-point.h" -#include "decimal.h" -#include "../common/bit-population-count.h" -#include "../common/leading-zero-bit-count.h" -#include "../common/uint128.h" -#include "../common/unsigned-const-division.h" +#include "flang/common/bit-population-count.h" +#include "flang/common/leading-zero-bit-count.h" +#include "flang/common/uint128.h" +#include "flang/common/unsigned-const-division.h" +#include "flang/decimal/binary-floating-point.h" +#include "flang/decimal/decimal.h" #include #include #include diff --git a/lib/decimal/binary-to-decimal.cc b/lib/decimal/binary-to-decimal.cc index 50bca7327640..53b00c39e4ed 100644 --- a/lib/decimal/binary-to-decimal.cc +++ b/lib/decimal/binary-to-decimal.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "big-radix-floating-point.h" -#include "../decimal/decimal.h" +#include "flang/decimal/decimal.h" namespace Fortran::decimal { diff --git a/lib/decimal/decimal-to-binary.cc b/lib/decimal/decimal-to-binary.cc index 34ecb7cc664e..3f57a3bb41f1 100644 --- a/lib/decimal/decimal-to-binary.cc +++ b/lib/decimal/decimal-to-binary.cc @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "big-radix-floating-point.h" -#include "binary-floating-point.h" -#include "decimal.h" -#include "../common/bit-population-count.h" -#include "../common/leading-zero-bit-count.h" +#include "flang/common/bit-population-count.h" +#include "flang/common/leading-zero-bit-count.h" +#include "flang/decimal/binary-floating-point.h" +#include "flang/decimal/decimal.h" #include #include #include diff --git a/lib/evaluate/call.cc b/lib/evaluate/call.cc index ceb595360fad..a61f679935f0 100644 --- a/lib/evaluate/call.cc +++ b/lib/evaluate/call.cc @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "call.h" -#include "characteristics.h" -#include "expression.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../semantics/symbol.h" +#include "flang/evaluate/call.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/tools.h" +#include "flang/semantics/symbol.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/character.h b/lib/evaluate/character.h index 032f729b1514..be1303eacca8 100644 --- a/lib/evaluate/character.h +++ b/lib/evaluate/character.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_EVALUATE_CHARACTER_H_ #define FORTRAN_EVALUATE_CHARACTER_H_ -#include "type.h" +#include "flang/evaluate/type.h" #include // Provides implementations of intrinsic functions operating on character diff --git a/lib/evaluate/characteristics.cc b/lib/evaluate/characteristics.cc index 83c79461b792..1e4f2825ba08 100644 --- a/lib/evaluate/characteristics.cc +++ b/lib/evaluate/characteristics.cc @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "characteristics.h" -#include "check-expression.h" -#include "fold.h" -#include "intrinsics.h" -#include "tools.h" -#include "type.h" -#include "../common/indirection.h" -#include "../parser/message.h" -#include "../semantics/scope.h" -#include "../semantics/symbol.h" +#include "flang/evaluate/characteristics.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/check-expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" +#include "flang/parser/message.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" #include #include diff --git a/lib/evaluate/check-expression.cc b/lib/evaluate/check-expression.cc index 92dbb76fe15a..31d1454ff176 100644 --- a/lib/evaluate/check-expression.cc +++ b/lib/evaluate/check-expression.cc @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "check-expression.h" -#include "traverse.h" -#include "type.h" -#include "../semantics/symbol.h" -#include "../semantics/tools.h" +#include "flang/evaluate/check-expression.h" +#include "flang/evaluate/traverse.h" +#include "flang/evaluate/type.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/common.cc b/lib/evaluate/common.cc index 5bf76ecbccf9..a1f7cbbf6005 100644 --- a/lib/evaluate/common.cc +++ b/lib/evaluate/common.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "common.h" -#include "../common/idioms.h" +#include "flang/evaluate/common.h" +#include "flang/common/idioms.h" using namespace Fortran::parser::literals; diff --git a/lib/evaluate/complex.cc b/lib/evaluate/complex.cc index c68228f8f698..e93245997cd2 100644 --- a/lib/evaluate/complex.cc +++ b/lib/evaluate/complex.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "complex.h" +#include "flang/evaluate/complex.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/constant.cc b/lib/evaluate/constant.cc index d49d93f00712..4c83e34c8c68 100644 --- a/lib/evaluate/constant.cc +++ b/lib/evaluate/constant.cc @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "constant.h" -#include "expression.h" -#include "shape.h" -#include "type.h" +#include "flang/evaluate/constant.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/shape.h" +#include "flang/evaluate/type.h" #include namespace Fortran::evaluate { diff --git a/lib/evaluate/expression.cc b/lib/evaluate/expression.cc index 39aa4dc0f087..21a0176e32ac 100644 --- a/lib/evaluate/expression.cc +++ b/lib/evaluate/expression.cc @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "expression.h" -#include "common.h" +#include "flang/evaluate/expression.h" #include "int-power.h" -#include "tools.h" -#include "variable.h" -#include "../common/idioms.h" -#include "../parser/message.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/variable.h" +#include "flang/parser/message.h" #include #include diff --git a/lib/evaluate/fold-implementation.h b/lib/evaluate/fold-implementation.h index d78c52785276..fdb85dfb0a7f 100644 --- a/lib/evaluate/fold-implementation.h +++ b/lib/evaluate/fold-implementation.h @@ -10,26 +10,26 @@ #define FORTRAN_EVALUATE_FOLD_IMPLEMENTATION_H_ #include "character.h" -#include "characteristics.h" -#include "common.h" -#include "constant.h" -#include "expression.h" -#include "fold.h" -#include "formatting.h" #include "host.h" #include "int-power.h" #include "intrinsics-library-templates.h" -#include "shape.h" -#include "tools.h" -#include "traverse.h" -#include "type.h" -#include "../common/indirection.h" -#include "../common/template.h" -#include "../common/unwrap.h" -#include "../parser/message.h" -#include "../semantics/scope.h" -#include "../semantics/symbol.h" -#include "../semantics/tools.h" +#include "flang/common/indirection.h" +#include "flang/common/template.h" +#include "flang/common/unwrap.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/constant.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/formatting.h" +#include "flang/evaluate/shape.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/traverse.h" +#include "flang/evaluate/type.h" +#include "flang/parser/message.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/evaluate/fold-logical.cc b/lib/evaluate/fold-logical.cc index fd67fb10f77c..649f745897b8 100644 --- a/lib/evaluate/fold-logical.cc +++ b/lib/evaluate/fold-logical.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "check-expression.h" #include "fold-implementation.h" +#include "flang/evaluate/check-expression.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/fold.cc b/lib/evaluate/fold.cc index 0a36c91a5139..af1de4094daf 100644 --- a/lib/evaluate/fold.cc +++ b/lib/evaluate/fold.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "fold.h" +#include "flang/evaluate/fold.h" #include "fold-implementation.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/formatting.cc b/lib/evaluate/formatting.cc index 67320ac7a826..6eb43a4e4967 100644 --- a/lib/evaluate/formatting.cc +++ b/lib/evaluate/formatting.cc @@ -6,14 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "formatting.h" -#include "call.h" -#include "constant.h" -#include "expression.h" -#include "fold.h" -#include "tools.h" -#include "../parser/characters.h" -#include "../semantics/symbol.h" +#include "flang/evaluate/formatting.h" +#include "flang/evaluate/call.h" +#include "flang/evaluate/constant.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/characters.h" +#include "flang/semantics/symbol.h" #include namespace Fortran::evaluate { diff --git a/lib/evaluate/host.cc b/lib/evaluate/host.cc index 718bc7a5474f..62dd44863989 100644 --- a/lib/evaluate/host.cc +++ b/lib/evaluate/host.cc @@ -8,7 +8,7 @@ #include "host.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" #include #include diff --git a/lib/evaluate/host.h b/lib/evaluate/host.h index fb3059083500..f2d8c1dc37b4 100644 --- a/lib/evaluate/host.h +++ b/lib/evaluate/host.h @@ -17,7 +17,7 @@ // hardware type maps to Fortran intrinsic type T. Then HostType can be used // to safely refer to this hardware type. -#include "type.h" +#include "flang/evaluate/type.h" #include #include #include diff --git a/lib/evaluate/int-power.h b/lib/evaluate/int-power.h index 01b090856543..6a6fe831a7c4 100644 --- a/lib/evaluate/int-power.h +++ b/lib/evaluate/int-power.h @@ -11,7 +11,7 @@ // Computes an integer power of a real or complex value. -#include "common.h" +#include "flang/evaluate/common.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/integer.cc b/lib/evaluate/integer.cc index abc94739bfd7..30484d90664a 100644 --- a/lib/evaluate/integer.cc +++ b/lib/evaluate/integer.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "integer.h" +#include "flang/evaluate/integer.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/intrinsics-library-templates.h b/lib/evaluate/intrinsics-library-templates.h index 78b0c6746770..4ab657da01e0 100644 --- a/lib/evaluate/intrinsics-library-templates.h +++ b/lib/evaluate/intrinsics-library-templates.h @@ -17,9 +17,9 @@ // which version should be instantiated in a generic way. #include "host.h" -#include "intrinsics-library.h" -#include "type.h" -#include "../common/template.h" +#include "flang/common/template.h" +#include "flang/evaluate/intrinsics-library.h" +#include "flang/evaluate/type.h" #include #include diff --git a/lib/evaluate/intrinsics.cc b/lib/evaluate/intrinsics.cc index e57bfcd58481..1a8b5ba3248b 100644 --- a/lib/evaluate/intrinsics.cc +++ b/lib/evaluate/intrinsics.cc @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "intrinsics.h" -#include "common.h" -#include "expression.h" -#include "fold.h" -#include "shape.h" -#include "tools.h" -#include "type.h" -#include "../common/Fortran.h" -#include "../common/enum-set.h" -#include "../common/idioms.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/common/Fortran.h" +#include "flang/common/enum-set.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/shape.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" #include #include #include diff --git a/lib/evaluate/logical.cc b/lib/evaluate/logical.cc index e3fcc0e128b1..bdbfedb05d7f 100644 --- a/lib/evaluate/logical.cc +++ b/lib/evaluate/logical.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "logical.h" +#include "flang/evaluate/logical.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/real.cc b/lib/evaluate/real.cc index 44b7cbe121b0..b803a8ea1de3 100644 --- a/lib/evaluate/real.cc +++ b/lib/evaluate/real.cc @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "real.h" +#include "flang/evaluate/real.h" #include "int-power.h" -#include "../common/idioms.h" -#include "../decimal/decimal.h" -#include "../parser/characters.h" +#include "flang/common/idioms.h" +#include "flang/decimal/decimal.h" +#include "flang/parser/characters.h" #include namespace Fortran::evaluate::value { diff --git a/lib/evaluate/shape.cc b/lib/evaluate/shape.cc index 1ccbfea9e891..62bc879895de 100644 --- a/lib/evaluate/shape.cc +++ b/lib/evaluate/shape.cc @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "shape.h" -#include "characteristics.h" -#include "fold.h" -#include "intrinsics.h" -#include "tools.h" -#include "type.h" -#include "../common/idioms.h" -#include "../common/template.h" -#include "../parser/message.h" -#include "../semantics/symbol.h" +#include "flang/evaluate/shape.h" +#include "flang/common/idioms.h" +#include "flang/common/template.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" +#include "flang/parser/message.h" +#include "flang/semantics/symbol.h" #include using namespace std::placeholders; // _1, _2, &c. for std::bind() diff --git a/lib/evaluate/static-data.cc b/lib/evaluate/static-data.cc index 3b08ddb426e9..f5311cee1a95 100644 --- a/lib/evaluate/static-data.cc +++ b/lib/evaluate/static-data.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "static-data.h" -#include "../parser/characters.h" +#include "flang/evaluate/static-data.h" +#include "flang/parser/characters.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/tools.cc b/lib/evaluate/tools.cc index 2b1b9074aba2..4710343f4ad2 100644 --- a/lib/evaluate/tools.cc +++ b/lib/evaluate/tools.cc @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "tools.h" -#include "characteristics.h" -#include "traverse.h" -#include "../common/idioms.h" -#include "../parser/message.h" +#include "flang/evaluate/tools.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/traverse.h" +#include "flang/parser/message.h" #include #include diff --git a/lib/evaluate/type.cc b/lib/evaluate/type.cc index c78c8eebdcf8..bf7e8012e0ff 100644 --- a/lib/evaluate/type.cc +++ b/lib/evaluate/type.cc @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "type.h" -#include "expression.h" -#include "fold.h" -#include "../common/idioms.h" -#include "../common/template.h" -#include "../parser/characters.h" -#include "../semantics/scope.h" -#include "../semantics/symbol.h" -#include "../semantics/tools.h" -#include "../semantics/type.h" +#include "flang/evaluate/type.h" +#include "flang/common/idioms.h" +#include "flang/common/template.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/parser/characters.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" #include #include #include diff --git a/lib/evaluate/variable.cc b/lib/evaluate/variable.cc index a0f16853ba71..0922ba659cd5 100644 --- a/lib/evaluate/variable.cc +++ b/lib/evaluate/variable.cc @@ -6,14 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "variable.h" -#include "fold.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../parser/char-block.h" -#include "../parser/characters.h" -#include "../parser/message.h" -#include "../semantics/symbol.h" +#include "flang/evaluate/variable.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/char-block.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" +#include "flang/semantics/symbol.h" #include #include diff --git a/lib/parser/Fortran-parsers.cc b/lib/parser/Fortran-parsers.cc index 9efa895e24b8..bfc7122a4dd5 100644 --- a/lib/parser/Fortran-parsers.cc +++ b/lib/parser/Fortran-parsers.cc @@ -33,11 +33,11 @@ #include "basic-parsers.h" #include "expr-parsers.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "user-state.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/user-state.h" namespace Fortran::parser { diff --git a/lib/parser/basic-parsers.h b/lib/parser/basic-parsers.h index 3cad01164395..31491a238363 100644 --- a/lib/parser/basic-parsers.h +++ b/lib/parser/basic-parsers.h @@ -22,14 +22,14 @@ // This header defines the fundamental parser class templates and helper // template functions. See parser-combinators.txt for documentation. -#include "char-block.h" -#include "message.h" -#include "parse-state.h" -#include "provenance.h" -#include "user-state.h" -#include "../common/Fortran-features.h" -#include "../common/idioms.h" -#include "../common/indirection.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/parser/char-block.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-state.h" +#include "flang/parser/provenance.h" +#include "flang/parser/user-state.h" #include #include #include diff --git a/lib/parser/char-block.cc b/lib/parser/char-block.cc index e8254d09f66a..8c9784e55ad1 100644 --- a/lib/parser/char-block.cc +++ b/lib/parser/char-block.cc @@ -6,7 +6,7 @@ // //----------------------------------------------------------------------------// -#include "char-block.h" +#include "flang/parser/char-block.h" #include namespace Fortran::parser { diff --git a/lib/parser/char-buffer.cc b/lib/parser/char-buffer.cc index 5cfddb875443..6c426243ce3f 100644 --- a/lib/parser/char-buffer.cc +++ b/lib/parser/char-buffer.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "char-buffer.h" -#include "../common/idioms.h" +#include "flang/parser/char-buffer.h" +#include "flang/common/idioms.h" #include #include #include diff --git a/lib/parser/char-set.cc b/lib/parser/char-set.cc index ae56e9a955e6..c8a324f5ae9e 100644 --- a/lib/parser/char-set.cc +++ b/lib/parser/char-set.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "char-set.h" +#include "flang/parser/char-set.h" namespace Fortran::parser { diff --git a/lib/parser/characters.cc b/lib/parser/characters.cc index 89f16ecea214..d6fc0d6464f7 100644 --- a/lib/parser/characters.cc +++ b/lib/parser/characters.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "characters.h" -#include "../common/idioms.h" +#include "flang/parser/characters.h" +#include "flang/common/idioms.h" #include #include #include diff --git a/lib/parser/debug-parser.cc b/lib/parser/debug-parser.cc index 1f2a9101d0c2..97fdf2820141 100644 --- a/lib/parser/debug-parser.cc +++ b/lib/parser/debug-parser.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "debug-parser.h" -#include "user-state.h" +#include "flang/parser/user-state.h" #include #include diff --git a/lib/parser/debug-parser.h b/lib/parser/debug-parser.h index 82871646f95a..dbb812c9cec7 100644 --- a/lib/parser/debug-parser.h +++ b/lib/parser/debug-parser.h @@ -14,7 +14,7 @@ // flow of the parsers. Not to be used in production. #include "basic-parsers.h" -#include "parse-state.h" +#include "flang/parser/parse-state.h" #include #include diff --git a/lib/parser/executable-parsers.cc b/lib/parser/executable-parsers.cc index 1cbc4936dcf1..a1557a0e0e46 100644 --- a/lib/parser/executable-parsers.cc +++ b/lib/parser/executable-parsers.cc @@ -9,14 +9,14 @@ // Per-type parsers for executable statements #include "basic-parsers.h" -#include "characters.h" #include "debug-parser.h" #include "expr-parsers.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" +#include "flang/parser/characters.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/expr-parsers.cc b/lib/parser/expr-parsers.cc index 4989a6b8e30a..14bc391691ee 100644 --- a/lib/parser/expr-parsers.cc +++ b/lib/parser/expr-parsers.cc @@ -10,13 +10,13 @@ #include "expr-parsers.h" #include "basic-parsers.h" -#include "characters.h" #include "debug-parser.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" +#include "flang/parser/characters.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/expr-parsers.h b/lib/parser/expr-parsers.h index 3ed9a2c7096d..fcaeedad3994 100644 --- a/lib/parser/expr-parsers.h +++ b/lib/parser/expr-parsers.h @@ -10,9 +10,9 @@ #define FORTRAN_PARSER_EXPR_PARSERS_H_ #include "basic-parsers.h" -#include "parse-tree.h" #include "token-parsers.h" #include "type-parsers.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/instrumented-parser.cc b/lib/parser/instrumented-parser.cc index 24a59cc735dd..b845b22dffdd 100644 --- a/lib/parser/instrumented-parser.cc +++ b/lib/parser/instrumented-parser.cc @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "instrumented-parser.h" -#include "message.h" -#include "provenance.h" +#include "flang/parser/instrumented-parser.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" #include #include diff --git a/lib/parser/io-parsers.cc b/lib/parser/io-parsers.cc index 95770081c02c..5488a32c4769 100644 --- a/lib/parser/io-parsers.cc +++ b/lib/parser/io-parsers.cc @@ -9,14 +9,14 @@ // Per-type parsers for I/O statements and FORMAT #include "basic-parsers.h" -#include "characters.h" #include "debug-parser.h" #include "expr-parsers.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" +#include "flang/parser/characters.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { // R1201 io-unit -> file-unit-number | * | internal-file-variable diff --git a/lib/parser/message.cc b/lib/parser/message.cc index a8a321a50dcc..5589707effc2 100644 --- a/lib/parser/message.cc +++ b/lib/parser/message.cc @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "message.h" -#include "char-set.h" -#include "../common/idioms.h" +#include "flang/parser/message.h" +#include "flang/common/idioms.h" +#include "flang/parser/char-set.h" #include #include #include diff --git a/lib/parser/misc-parsers.h b/lib/parser/misc-parsers.h index 62589bb4d5fb..1a7c641557a5 100644 --- a/lib/parser/misc-parsers.h +++ b/lib/parser/misc-parsers.h @@ -13,10 +13,10 @@ #define FORTRAN_PARSER_MISC_PARSERS_H_ #include "basic-parsers.h" -#include "message.h" -#include "parse-tree.h" #include "token-parsers.h" #include "type-parsers.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/openmp-parsers.cc b/lib/parser/openmp-parsers.cc index d30a93a33569..4a5a083747bd 100644 --- a/lib/parser/openmp-parsers.cc +++ b/lib/parser/openmp-parsers.cc @@ -12,10 +12,10 @@ #include "basic-parsers.h" #include "expr-parsers.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" +#include "flang/parser/parse-tree.h" // OpenMP Directives and Clauses namespace Fortran::parser { diff --git a/lib/parser/parse-tree.cc b/lib/parser/parse-tree.cc index afd2c68323ce..6e0e017570b5 100644 --- a/lib/parser/parse-tree.cc +++ b/lib/parser/parse-tree.cc @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "parse-tree.h" -#include "user-state.h" -#include "../common/idioms.h" -#include "../common/indirection.h" +#include "flang/parser/parse-tree.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/parser/user-state.h" #include // So "delete Expr;" calls an external destructor for its typedExpr. diff --git a/lib/parser/parsing.cc b/lib/parser/parsing.cc index e42752926810..c2b96ab8b383 100644 --- a/lib/parser/parsing.cc +++ b/lib/parser/parsing.cc @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "parsing.h" -#include "message.h" +#include "flang/parser/parsing.h" #include "preprocessor.h" #include "prescan.h" -#include "provenance.h" -#include "source.h" #include "type-parsers.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" +#include "flang/parser/source.h" #include namespace Fortran::parser { diff --git a/lib/parser/preprocessor.cc b/lib/parser/preprocessor.cc index 27ce7f17e647..270e72ca3735 100644 --- a/lib/parser/preprocessor.cc +++ b/lib/parser/preprocessor.cc @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "preprocessor.h" -#include "characters.h" -#include "message.h" #include "prescan.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" #include #include #include diff --git a/lib/parser/preprocessor.h b/lib/parser/preprocessor.h index 1a0189629ea8..523c880316c2 100644 --- a/lib/parser/preprocessor.h +++ b/lib/parser/preprocessor.h @@ -15,9 +15,9 @@ // performed, so that special compiler command options &/or source file name // extensions for preprocessing will not be necessary. -#include "char-block.h" -#include "provenance.h" #include "token-sequence.h" +#include "flang/parser/char-block.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/prescan.cc b/lib/parser/prescan.cc index 2af12df9aa9b..07f294a9eb77 100644 --- a/lib/parser/prescan.cc +++ b/lib/parser/prescan.cc @@ -7,12 +7,12 @@ //===----------------------------------------------------------------------===// #include "prescan.h" -#include "characters.h" -#include "message.h" #include "preprocessor.h" -#include "source.h" #include "token-sequence.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" +#include "flang/parser/source.h" #include #include #include diff --git a/lib/parser/prescan.h b/lib/parser/prescan.h index a399cabf1273..1b13cb7feac5 100644 --- a/lib/parser/prescan.h +++ b/lib/parser/prescan.h @@ -16,11 +16,11 @@ // fixed form character literals on truncated card images, file // inclusion, and driving the Fortran source preprocessor. -#include "characters.h" -#include "message.h" -#include "provenance.h" #include "token-sequence.h" -#include "../common/Fortran-features.h" +#include "flang/common/Fortran-features.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/program-parsers.cc b/lib/parser/program-parsers.cc index bded0cba5658..f19824248181 100644 --- a/lib/parser/program-parsers.cc +++ b/lib/parser/program-parsers.cc @@ -9,14 +9,14 @@ // Per-type parsers for program units #include "basic-parsers.h" -#include "characters.h" #include "debug-parser.h" #include "expr-parsers.h" #include "misc-parsers.h" -#include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" +#include "flang/parser/characters.h" +#include "flang/parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/provenance.cc b/lib/parser/provenance.cc index bfa5133b2e81..9ad864e0da95 100644 --- a/lib/parser/provenance.cc +++ b/lib/parser/provenance.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "provenance.h" -#include "../common/idioms.h" +#include "flang/parser/provenance.h" +#include "flang/common/idioms.h" #include #include diff --git a/lib/parser/source.cc b/lib/parser/source.cc index 6dc6c7108b28..4e4c2736781f 100644 --- a/lib/parser/source.cc +++ b/lib/parser/source.cc @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "source.h" -#include "char-buffer.h" -#include "../common/idioms.h" +#include "flang/parser/source.h" +#include "flang/common/idioms.h" +#include "flang/parser/char-buffer.h" #include #include #include diff --git a/lib/parser/token-parsers.h b/lib/parser/token-parsers.h index 6b4ce9ec55d9..d36d78906954 100644 --- a/lib/parser/token-parsers.h +++ b/lib/parser/token-parsers.h @@ -13,12 +13,12 @@ // the prescanned character stream and recognize context-sensitive tokens. #include "basic-parsers.h" -#include "char-set.h" -#include "characters.h" -#include "instrumented-parser.h" -#include "provenance.h" #include "type-parsers.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" +#include "flang/parser/char-set.h" +#include "flang/parser/characters.h" +#include "flang/parser/instrumented-parser.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/token-sequence.cc b/lib/parser/token-sequence.cc index 6270d3fbf212..d6337f475cec 100644 --- a/lib/parser/token-sequence.cc +++ b/lib/parser/token-sequence.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "token-sequence.h" -#include "characters.h" +#include "flang/parser/characters.h" namespace Fortran::parser { diff --git a/lib/parser/token-sequence.h b/lib/parser/token-sequence.h index d1896928f89e..e4b7dce8b6ad 100644 --- a/lib/parser/token-sequence.h +++ b/lib/parser/token-sequence.h @@ -13,8 +13,8 @@ // and a partitioning thereof into preprocessing tokens, along with their // associated provenances. -#include "char-block.h" -#include "provenance.h" +#include "flang/parser/char-block.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/lib/parser/tools.cc b/lib/parser/tools.cc index ab0ab153b8ba..1ef05b427a25 100644 --- a/lib/parser/tools.cc +++ b/lib/parser/tools.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "tools.h" +#include "flang/parser/tools.h" namespace Fortran::parser { diff --git a/lib/parser/type-parsers.h b/lib/parser/type-parsers.h index 7a18b3c671af..82273a374274 100644 --- a/lib/parser/type-parsers.h +++ b/lib/parser/type-parsers.h @@ -9,8 +9,8 @@ #ifndef FORTRAN_PARSER_TYPE_PARSERS_H_ #define FORTRAN_PARSER_TYPE_PARSERS_H_ -#include "instrumented-parser.h" -#include "parse-tree.h" +#include "flang/parser/instrumented-parser.h" +#include "flang/parser/parse-tree.h" #include namespace Fortran::parser { diff --git a/lib/parser/unparse.cc b/lib/parser/unparse.cc index 6e63962a3fbe..6550efa65880 100644 --- a/lib/parser/unparse.cc +++ b/lib/parser/unparse.cc @@ -9,13 +9,13 @@ // Generates Fortran from the content of a parse tree, using the // traversal templates in parse-tree-visitor.h. -#include "unparse.h" -#include "characters.h" -#include "parse-tree-visitor.h" -#include "parse-tree.h" -#include "../common/Fortran.h" -#include "../common/idioms.h" -#include "../common/indirection.h" +#include "flang/parser/unparse.h" +#include "flang/common/Fortran.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/parser/characters.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" #include #include #include diff --git a/lib/parser/user-state.cc b/lib/parser/user-state.cc index a0f2ea6cd4bc..b23b8f579c84 100644 --- a/lib/parser/user-state.cc +++ b/lib/parser/user-state.cc @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "user-state.h" -#include "parse-state.h" +#include "flang/parser/user-state.h" #include "stmt-parser.h" #include "type-parsers.h" +#include "flang/parser/parse-state.h" #include namespace Fortran::parser { diff --git a/lib/semantics/assignment.cc b/lib/semantics/assignment.cc index d7b9d91ed739..70f2f655a41b 100644 --- a/lib/semantics/assignment.cc +++ b/lib/semantics/assignment.cc @@ -7,18 +7,19 @@ //===----------------------------------------------------------------------===// #include "assignment.h" -#include "expression.h" #include "pointer-assignment.h" -#include "symbol.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../common/restorer.h" -#include "../evaluate/expression.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" +#include "flang/common/idioms.h" +#include "flang/common/restorer.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/semantics/assignment.h b/lib/semantics/assignment.h index d963b6a4e2d9..4bce8cb16dd5 100644 --- a/lib/semantics/assignment.h +++ b/lib/semantics/assignment.h @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_ASSIGNMENT_H_ #define FORTRAN_SEMANTICS_ASSIGNMENT_H_ -#include "semantics.h" -#include "tools.h" -#include "../common/indirection.h" -#include "../evaluate/expression.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/expression.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" #include namespace Fortran::parser { diff --git a/lib/semantics/attr.cc b/lib/semantics/attr.cc index 765569a9736d..25d9201f3fb7 100644 --- a/lib/semantics/attr.cc +++ b/lib/semantics/attr.cc @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "attr.h" -#include "../common/idioms.h" +#include "flang/semantics/attr.h" +#include "flang/common/idioms.h" #include #include diff --git a/lib/semantics/canonicalize-do.cc b/lib/semantics/canonicalize-do.cc index 0f813a6f05a0..b4ac3771e8e8 100644 --- a/lib/semantics/canonicalize-do.cc +++ b/lib/semantics/canonicalize-do.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "canonicalize-do.h" -#include "../parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree-visitor.h" namespace Fortran::parser { diff --git a/lib/semantics/canonicalize-omp.cc b/lib/semantics/canonicalize-omp.cc index ae57d3f46770..cbd26d19b763 100644 --- a/lib/semantics/canonicalize-omp.cc +++ b/lib/semantics/canonicalize-omp.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "canonicalize-omp.h" -#include "../parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree-visitor.h" // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP // Constructs more structured which provide explicit scopes for later diff --git a/lib/semantics/check-allocate.cc b/lib/semantics/check-allocate.cc index e72d3bdb9bad..4db1434f5cee 100644 --- a/lib/semantics/check-allocate.cc +++ b/lib/semantics/check-allocate.cc @@ -8,14 +8,14 @@ #include "check-allocate.h" #include "assignment.h" -#include "attr.h" -#include "expression.h" -#include "tools.h" -#include "type.h" -#include "../evaluate/fold.h" -#include "../evaluate/type.h" -#include "../parser/parse-tree.h" -#include "../parser/tools.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/type.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/tools.h" +#include "flang/semantics/attr.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-allocate.h b/lib/semantics/check-allocate.h index 79e0af5a6797..2d495b13746c 100644 --- a/lib/semantics/check-allocate.h +++ b/lib/semantics/check-allocate.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_ALLOCATE_H_ #define FORTRAN_SEMANTICS_CHECK_ALLOCATE_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct AllocateStmt; diff --git a/lib/semantics/check-arithmeticif.cc b/lib/semantics/check-arithmeticif.cc index e6e05f82806b..fd293ce5e3c8 100644 --- a/lib/semantics/check-arithmeticif.cc +++ b/lib/semantics/check-arithmeticif.cc @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "check-arithmeticif.h" -#include "tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-arithmeticif.h b/lib/semantics/check-arithmeticif.h index 26a04400d37d..32e2b354cf22 100644 --- a/lib/semantics/check-arithmeticif.h +++ b/lib/semantics/check-arithmeticif.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_ARITHMETICIF_STMT_H_ #define FORTRAN_SEMANTICS_CHECK_ARITHMETICIF_STMT_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct ArithmeticIfStmt; diff --git a/lib/semantics/check-call.cc b/lib/semantics/check-call.cc index a643c384d589..164dbc86f898 100644 --- a/lib/semantics/check-call.cc +++ b/lib/semantics/check-call.cc @@ -8,14 +8,14 @@ #include "check-call.h" #include "pointer-assignment.h" -#include "scope.h" -#include "tools.h" -#include "../evaluate/characteristics.h" -#include "../evaluate/check-expression.h" -#include "../evaluate/shape.h" -#include "../evaluate/tools.h" -#include "../parser/characters.h" -#include "../parser/message.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/check-expression.h" +#include "flang/evaluate/shape.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/characters.h" +#include "flang/parser/message.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/tools.h" #include #include diff --git a/lib/semantics/check-call.h b/lib/semantics/check-call.h index 89953ae6d726..68c5b53b078a 100644 --- a/lib/semantics/check-call.h +++ b/lib/semantics/check-call.h @@ -11,7 +11,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_CALL_H_ #define FORTRAN_SEMANTICS_CHECK_CALL_H_ -#include "../evaluate/call.h" +#include "flang/evaluate/call.h" namespace Fortran::parser { class Messages; diff --git a/lib/semantics/check-coarray.cc b/lib/semantics/check-coarray.cc index 1df756a5bfea..44f8cf4e89c0 100644 --- a/lib/semantics/check-coarray.cc +++ b/lib/semantics/check-coarray.cc @@ -7,13 +7,13 @@ //===----------------------------------------------------------------------===// #include "check-coarray.h" -#include "expression.h" -#include "tools.h" -#include "../common/indirection.h" -#include "../evaluate/expression.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" -#include "../parser/tools.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/tools.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-coarray.h b/lib/semantics/check-coarray.h index a843105b6817..fe4176c1d943 100644 --- a/lib/semantics/check-coarray.h +++ b/lib/semantics/check-coarray.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_COARRAY_H_ #define FORTRAN_SEMANTICS_CHECK_COARRAY_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" #include namespace Fortran::parser { diff --git a/lib/semantics/check-deallocate.cc b/lib/semantics/check-deallocate.cc index 0d8238262019..7e66fcdfbdbf 100644 --- a/lib/semantics/check-deallocate.cc +++ b/lib/semantics/check-deallocate.cc @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "check-deallocate.h" -#include "expression.h" -#include "tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-deallocate.h b/lib/semantics/check-deallocate.h index 1ad9f3d7d759..6855055bd65d 100644 --- a/lib/semantics/check-deallocate.h +++ b/lib/semantics/check-deallocate.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_DEALLOCATE_H_ #define FORTRAN_SEMANTICS_CHECK_DEALLOCATE_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct DeallocateStmt; diff --git a/lib/semantics/check-declarations.cc b/lib/semantics/check-declarations.cc index 752a73e25453..a3352d94f907 100644 --- a/lib/semantics/check-declarations.cc +++ b/lib/semantics/check-declarations.cc @@ -9,14 +9,14 @@ // Static declaration checking #include "check-declarations.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "type.h" -#include "../evaluate/check-expression.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" +#include "flang/evaluate/check-expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-do.cc b/lib/semantics/check-do.cc index d614751612ad..8112c631a12c 100644 --- a/lib/semantics/check-do.cc +++ b/lib/semantics/check-do.cc @@ -7,19 +7,19 @@ //===----------------------------------------------------------------------===// #include "check-do.h" -#include "attr.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "type.h" -#include "../common/template.h" -#include "../evaluate/call.h" -#include "../evaluate/expression.h" -#include "../evaluate/tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/tools.h" +#include "flang/common/template.h" +#include "flang/evaluate/call.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/tools.h" +#include "flang/semantics/attr.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" namespace Fortran::evaluate { using ActualArgumentRef = common::Reference; diff --git a/lib/semantics/check-do.h b/lib/semantics/check-do.h index 8d6cfeebf1e7..03d8c75212b1 100644 --- a/lib/semantics/check-do.h +++ b/lib/semantics/check-do.h @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_CHECK_DO_H_ #define FORTRAN_SEMANTICS_CHECK_DO_H_ -#include "semantics.h" -#include "../common/idioms.h" +#include "flang/common/idioms.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct AssignmentStmt; diff --git a/lib/semantics/check-if-stmt.cc b/lib/semantics/check-if-stmt.cc index b77f11ec5214..ec423e35301c 100644 --- a/lib/semantics/check-if-stmt.cc +++ b/lib/semantics/check-if-stmt.cc @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "check-if-stmt.h" -#include "tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-if-stmt.h b/lib/semantics/check-if-stmt.h index 7e6326e7d2da..01aac0ec5cf1 100644 --- a/lib/semantics/check-if-stmt.h +++ b/lib/semantics/check-if-stmt.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_IF_STMT_H_ #define FORTRAN_SEMANTICS_CHECK_IF_STMT_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct IfStmt; diff --git a/lib/semantics/check-io.cc b/lib/semantics/check-io.cc index 929907f5db0a..dc6ef9ef240f 100644 --- a/lib/semantics/check-io.cc +++ b/lib/semantics/check-io.cc @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "check-io.h" -#include "expression.h" -#include "tools.h" -#include "../common/format.h" -#include "../parser/tools.h" +#include "flang/common/format.h" +#include "flang/parser/tools.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-io.h b/lib/semantics/check-io.h index e8d61c659dbd..96315d5d865e 100644 --- a/lib/semantics/check-io.h +++ b/lib/semantics/check-io.h @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_CHECK_IO_H_ #define FORTRAN_SEMANTICS_CHECK_IO_H_ -#include "semantics.h" -#include "tools.h" -#include "../common/enum-set.h" -#include "../parser/parse-tree.h" +#include "flang/common/enum-set.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-nullify.cc b/lib/semantics/check-nullify.cc index 7c570dad9abd..9951a1627686 100644 --- a/lib/semantics/check-nullify.cc +++ b/lib/semantics/check-nullify.cc @@ -8,11 +8,11 @@ #include "check-nullify.h" #include "assignment.h" -#include "expression.h" -#include "tools.h" -#include "../evaluate/expression.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-nullify.h b/lib/semantics/check-nullify.h index c034db7ea7ab..f06fc662c8d0 100644 --- a/lib/semantics/check-nullify.h +++ b/lib/semantics/check-nullify.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_NULLIFY_H_ #define FORTRAN_SEMANTICS_CHECK_NULLIFY_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct NullifyStmt; diff --git a/lib/semantics/check-omp-structure.cc b/lib/semantics/check-omp-structure.cc index 1c1ed3320e86..15b51d3bcdb2 100644 --- a/lib/semantics/check-omp-structure.cc +++ b/lib/semantics/check-omp-structure.cc @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "check-omp-structure.h" -#include "tools.h" -#include "../parser/parse-tree.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-omp-structure.h b/lib/semantics/check-omp-structure.h index a08884c450b9..cfe4a2f0cda5 100644 --- a/lib/semantics/check-omp-structure.h +++ b/lib/semantics/check-omp-structure.h @@ -14,9 +14,9 @@ #ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ #define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ -#include "semantics.h" -#include "../common/enum-set.h" -#include "../parser/parse-tree.h" +#include "flang/common/enum-set.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/semantics.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-purity.cc b/lib/semantics/check-purity.cc index 6d33aa1e8946..541696f4d807 100644 --- a/lib/semantics/check-purity.cc +++ b/lib/semantics/check-purity.cc @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "check-purity.h" -#include "tools.h" -#include "../parser/parse-tree.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { void PurityChecker::Enter(const parser::ExecutableConstruct &exec) { diff --git a/lib/semantics/check-purity.h b/lib/semantics/check-purity.h index 80e6d7796725..189f72ca0396 100644 --- a/lib/semantics/check-purity.h +++ b/lib/semantics/check-purity.h @@ -8,7 +8,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_PURITY_H_ #define FORTRAN_SEMANTICS_CHECK_PURITY_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" #include namespace Fortran::parser { struct ExecutableConstruct; diff --git a/lib/semantics/check-return.cc b/lib/semantics/check-return.cc index 939bb8b37828..fc2f2cfccb1f 100644 --- a/lib/semantics/check-return.cc +++ b/lib/semantics/check-return.cc @@ -7,10 +7,11 @@ //===----------------------------------------------------------------------===// #include "check-return.h" -#include "semantics.h" -#include "tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" +#include "flang/common/Fortran-features.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-return.h b/lib/semantics/check-return.h index 176d9458485c..b2f4f0655e82 100644 --- a/lib/semantics/check-return.h +++ b/lib/semantics/check-return.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_RETURN_H_ #define FORTRAN_SEMANTICS_CHECK_RETURN_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct ReturnStmt; diff --git a/lib/semantics/check-stop.cc b/lib/semantics/check-stop.cc index 7655ab7365c4..0cf56e9a6085 100644 --- a/lib/semantics/check-stop.cc +++ b/lib/semantics/check-stop.cc @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "check-stop.h" -#include "semantics.h" -#include "tools.h" -#include "../common/Fortran.h" -#include "../evaluate/expression.h" -#include "../parser/parse-tree.h" +#include "flang/common/Fortran.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-stop.h b/lib/semantics/check-stop.h index 957555de0dea..3daf7da12110 100644 --- a/lib/semantics/check-stop.h +++ b/lib/semantics/check-stop.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_STOP_H_ #define FORTRAN_SEMANTICS_CHECK_STOP_H_ -#include "semantics.h" +#include "flang/semantics/semantics.h" namespace Fortran::parser { struct StopStmt; diff --git a/lib/semantics/expression.cc b/lib/semantics/expression.cc index e24b31045184..3334bf27dce2 100644 --- a/lib/semantics/expression.cc +++ b/lib/semantics/expression.cc @@ -6,21 +6,21 @@ // //===----------------------------------------------------------------------===// -#include "expression.h" +#include "flang/semantics/expression.h" #include "check-call.h" #include "pointer-assignment.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../evaluate/common.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" -#include "../parser/characters.h" -#include "../parser/dump-parse-tree.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/characters.h" +#include "flang/parser/dump-parse-tree.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/semantics/mod-file.cc b/lib/semantics/mod-file.cc index 6846e2827f41..e0a18b28ede0 100644 --- a/lib/semantics/mod-file.cc +++ b/lib/semantics/mod-file.cc @@ -8,13 +8,13 @@ #include "mod-file.h" #include "resolve-names.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "../evaluate/tools.h" -#include "../parser/message.h" -#include "../parser/parsing.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/message.h" +#include "flang/parser/parsing.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/semantics/mod-file.h b/lib/semantics/mod-file.h index 4d5afae0d5be..ba6bae0d014d 100644 --- a/lib/semantics/mod-file.h +++ b/lib/semantics/mod-file.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_MOD_FILE_H_ #define FORTRAN_SEMANTICS_MOD_FILE_H_ -#include "attr.h" +#include "flang/semantics/attr.h" #include #include diff --git a/lib/semantics/pointer-assignment.cc b/lib/semantics/pointer-assignment.cc index 017d0e80e033..bf93bdba3998 100644 --- a/lib/semantics/pointer-assignment.cc +++ b/lib/semantics/pointer-assignment.cc @@ -7,18 +7,18 @@ //===----------------------------------------------------------------------===// #include "pointer-assignment.h" -#include "expression.h" -#include "symbol.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../common/restorer.h" -#include "../evaluate/characteristics.h" -#include "../evaluate/expression.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" -#include "../parser/message.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" +#include "flang/common/idioms.h" +#include "flang/common/restorer.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/semantics/pointer-assignment.h b/lib/semantics/pointer-assignment.h index 83652aef550c..70c805683016 100644 --- a/lib/semantics/pointer-assignment.h +++ b/lib/semantics/pointer-assignment.h @@ -9,9 +9,9 @@ #ifndef FORTRAN_SEMANTICS_POINTER_ASSIGNMENT_H_ #define FORTRAN_SEMANTICS_POINTER_ASSIGNMENT_H_ -#include "type.h" -#include "../evaluate/expression.h" -#include "../parser/char-block.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/char-block.h" +#include "flang/semantics/type.h" #include namespace Fortran::evaluate::characteristics { diff --git a/lib/semantics/program-tree.cc b/lib/semantics/program-tree.cc index a026f749368d..f20819132ef4 100644 --- a/lib/semantics/program-tree.cc +++ b/lib/semantics/program-tree.cc @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "program-tree.h" -#include "scope.h" -#include "../common/idioms.h" -#include "../parser/char-block.h" +#include "flang/common/idioms.h" +#include "flang/parser/char-block.h" +#include "flang/semantics/scope.h" namespace Fortran::semantics { diff --git a/lib/semantics/program-tree.h b/lib/semantics/program-tree.h index 8df2f9b960ac..88a274999361 100644 --- a/lib/semantics/program-tree.h +++ b/lib/semantics/program-tree.h @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_PROGRAM_TREE_H_ #define FORTRAN_SEMANTICS_PROGRAM_TREE_H_ -#include "symbol.h" -#include "../parser/parse-tree.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/symbol.h" #include // A ProgramTree represents a tree of program units and their contained diff --git a/lib/semantics/resolve-labels.cc b/lib/semantics/resolve-labels.cc index c8c2c4293366..0c09fb41c335 100644 --- a/lib/semantics/resolve-labels.cc +++ b/lib/semantics/resolve-labels.cc @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "resolve-labels.h" -#include "semantics.h" -#include "../common/enum-set.h" -#include "../common/template.h" -#include "../parser/parse-tree-visitor.h" +#include "flang/common/enum-set.h" +#include "flang/common/template.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/semantics/semantics.h" #include #include #include diff --git a/lib/semantics/resolve-names-utils.cc b/lib/semantics/resolve-names-utils.cc index 4fc70a6bb5ae..dd8c85d9ffa4 100644 --- a/lib/semantics/resolve-names-utils.cc +++ b/lib/semantics/resolve-names-utils.cc @@ -7,17 +7,17 @@ //===----------------------------------------------------------------------===// #include "resolve-names-utils.h" -#include "expression.h" -#include "semantics.h" -#include "tools.h" -#include "../common/Fortran-features.h" -#include "../common/idioms.h" -#include "../common/indirection.h" -#include "../evaluate/fold.h" -#include "../evaluate/tools.h" -#include "../evaluate/type.h" -#include "../parser/char-block.h" -#include "../parser/parse-tree.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/idioms.h" +#include "flang/common/indirection.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" +#include "flang/parser/char-block.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" #include #include #include diff --git a/lib/semantics/resolve-names-utils.h b/lib/semantics/resolve-names-utils.h index 05ca093efc71..0036cb7eb0b7 100644 --- a/lib/semantics/resolve-names-utils.h +++ b/lib/semantics/resolve-names-utils.h @@ -11,10 +11,10 @@ // Utility functions and class for use in resolve-names.cc. -#include "scope.h" -#include "symbol.h" -#include "type.h" -#include "../parser/message.h" +#include "flang/parser/message.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/type.h" #include namespace Fortran::parser { diff --git a/lib/semantics/resolve-names.cc b/lib/semantics/resolve-names.cc index 002024a2143d..ae20be1e942b 100644 --- a/lib/semantics/resolve-names.cc +++ b/lib/semantics/resolve-names.cc @@ -8,30 +8,30 @@ #include "resolve-names.h" #include "assignment.h" -#include "attr.h" -#include "expression.h" #include "mod-file.h" #include "program-tree.h" #include "resolve-names-utils.h" #include "rewrite-parse-tree.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "type.h" -#include "../common/Fortran.h" -#include "../common/default-kinds.h" -#include "../common/indirection.h" -#include "../common/restorer.h" -#include "../evaluate/characteristics.h" -#include "../evaluate/common.h" -#include "../evaluate/fold.h" -#include "../evaluate/intrinsics.h" -#include "../evaluate/tools.h" -#include "../evaluate/type.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" -#include "../parser/tools.h" +#include "flang/common/Fortran.h" +#include "flang/common/default-kinds.h" +#include "flang/common/indirection.h" +#include "flang/common/restorer.h" +#include "flang/evaluate/characteristics.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/evaluate/tools.h" +#include "flang/evaluate/type.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/tools.h" +#include "flang/semantics/attr.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" #include #include #include diff --git a/lib/semantics/rewrite-parse-tree.cc b/lib/semantics/rewrite-parse-tree.cc index c23aad7e6fe3..932a6ecd1e3e 100644 --- a/lib/semantics/rewrite-parse-tree.cc +++ b/lib/semantics/rewrite-parse-tree.cc @@ -7,14 +7,14 @@ //===----------------------------------------------------------------------===// #include "rewrite-parse-tree.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "tools.h" -#include "../common/indirection.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" -#include "../parser/tools.h" +#include "flang/common/indirection.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/tools.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/scope.cc b/lib/semantics/scope.cc index 9173d9499000..23166b15cfeb 100644 --- a/lib/semantics/scope.cc +++ b/lib/semantics/scope.cc @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "scope.h" -#include "symbol.h" -#include "type.h" -#include "../parser/characters.h" +#include "flang/semantics/scope.h" +#include "flang/parser/characters.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/type.h" #include #include #include diff --git a/lib/semantics/semantics.cc b/lib/semantics/semantics.cc index cac83ec28b73..7642366ccd17 100644 --- a/lib/semantics/semantics.cc +++ b/lib/semantics/semantics.cc @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "semantics.h" +#include "flang/semantics/semantics.h" #include "assignment.h" #include "canonicalize-do.h" #include "canonicalize-omp.h" @@ -23,16 +23,16 @@ #include "check-purity.h" #include "check-return.h" #include "check-stop.h" -#include "expression.h" #include "mod-file.h" #include "resolve-labels.h" #include "resolve-names.h" #include "rewrite-parse-tree.h" -#include "scope.h" -#include "symbol.h" -#include "../common/default-kinds.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/tools.h" +#include "flang/common/default-kinds.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/tools.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" namespace Fortran::semantics { diff --git a/lib/semantics/symbol.cc b/lib/semantics/symbol.cc index 4015cf754103..6393fa115709 100644 --- a/lib/semantics/symbol.cc +++ b/lib/semantics/symbol.cc @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "symbol.h" -#include "scope.h" -#include "semantics.h" -#include "tools.h" -#include "../common/idioms.h" -#include "../evaluate/expression.h" +#include "flang/semantics/symbol.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/expression.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" #include #include diff --git a/lib/semantics/tools.cc b/lib/semantics/tools.cc index 8342e4126259..24b87b496e2d 100644 --- a/lib/semantics/tools.cc +++ b/lib/semantics/tools.cc @@ -6,17 +6,17 @@ // //===----------------------------------------------------------------------===// -#include "tools.h" -#include "scope.h" -#include "semantics.h" -#include "symbol.h" -#include "type.h" -#include "../common/Fortran.h" -#include "../common/indirection.h" -#include "../parser/dump-parse-tree.h" -#include "../parser/message.h" -#include "../parser/parse-tree.h" -#include "../parser/tools.h" +#include "flang/parser/tools.h" +#include "flang/common/Fortran.h" +#include "flang/common/indirection.h" +#include "flang/parser/dump-parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" +#include "flang/semantics/type.h" #include #include #include @@ -83,7 +83,8 @@ Tristate IsDefinedAssignment( const auto *lhsDerived{evaluate::GetDerivedTypeSpec(lhsType)}; const auto *rhsDerived{evaluate::GetDerivedTypeSpec(rhsType)}; if (lhsDerived && rhsDerived && *lhsDerived == *rhsDerived) { - return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or intrinsic + return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or + // intrinsic } else { return Tristate::Yes; } @@ -1275,5 +1276,4 @@ void LabelEnforce::SayWithConstruct(SemanticsContext &context, context.Say(stmtLocation, message) .Attach(constructLocation, GetEnclosingConstructMsg()); } - } diff --git a/lib/semantics/type.cc b/lib/semantics/type.cc index 3e96186f6563..0f196dbeb369 100644 --- a/lib/semantics/type.cc +++ b/lib/semantics/type.cc @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "type.h" -#include "scope.h" -#include "symbol.h" -#include "tools.h" -#include "../evaluate/fold.h" -#include "../parser/characters.h" +#include "flang/semantics/type.h" +#include "flang/evaluate/fold.h" +#include "flang/parser/characters.h" +#include "flang/semantics/scope.h" +#include "flang/semantics/symbol.h" +#include "flang/semantics/tools.h" #include #include diff --git a/lib/semantics/unparse-with-symbols.cc b/lib/semantics/unparse-with-symbols.cc index 67d27f9c7057..70ed49d4fa22 100644 --- a/lib/semantics/unparse-with-symbols.cc +++ b/lib/semantics/unparse-with-symbols.cc @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "unparse-with-symbols.h" -#include "symbol.h" -#include "../parser/parse-tree-visitor.h" -#include "../parser/parse-tree.h" -#include "../parser/unparse.h" +#include "flang/semantics/unparse-with-symbols.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/unparse.h" +#include "flang/semantics/symbol.h" #include #include #include diff --git a/runtime/derived-type.h b/runtime/derived-type.h index e55c770b5d29..1d0e8502745e 100644 --- a/runtime/derived-type.h +++ b/runtime/derived-type.h @@ -10,7 +10,7 @@ #define FORTRAN_RUNTIME_DERIVED_TYPE_H_ #include "type-code.h" -#include "../include/flang/ISO_Fortran_binding.h" +#include "flang/ISO_Fortran_binding.h" #include #include diff --git a/runtime/descriptor.cc b/runtime/descriptor.cc index 8cd66bf48c95..e412df410dd7 100644 --- a/runtime/descriptor.cc +++ b/runtime/descriptor.cc @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "descriptor.h" -#include "../lib/common/idioms.h" +#include "flang/common/idioms.h" #include #include diff --git a/runtime/descriptor.h b/runtime/descriptor.h index 5d5dd094ea8e..3a4f2ce3a29c 100644 --- a/runtime/descriptor.h +++ b/runtime/descriptor.h @@ -20,7 +20,7 @@ #include "derived-type.h" #include "type-code.h" -#include "../include/flang/ISO_Fortran_binding.h" +#include "flang/ISO_Fortran_binding.h" #include #include #include diff --git a/runtime/format.cc b/runtime/format.cc index 9324d1528a65..2dd76e389bb3 100644 --- a/runtime/format.cc +++ b/runtime/format.cc @@ -8,8 +8,8 @@ #include "format.h" #include "io-stmt.h" -#include "../lib/common/format.h" -#include "../lib/decimal/decimal.h" +#include "flang/common/format.h" +#include "flang/decimal/decimal.h" #include namespace Fortran::runtime::io { diff --git a/runtime/format.h b/runtime/format.h index 1f576d24bb46..ed164e8902d1 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -12,7 +12,7 @@ #define FORTRAN_RUNTIME_FORMAT_H_ #include "terminator.h" -#include "../lib/common/Fortran.h" +#include "flang/common/Fortran.h" #include #include diff --git a/runtime/transformational.cc b/runtime/transformational.cc index 6c1144717e6c..bd408dec02ee 100644 --- a/runtime/transformational.cc +++ b/runtime/transformational.cc @@ -7,7 +7,8 @@ //===----------------------------------------------------------------------===// #include "transformational.h" -#include "../lib/common/idioms.h" +#include "flang/common/idioms.h" +#include "flang/evaluate/integer.h" #include #include #include diff --git a/runtime/type-code.h b/runtime/type-code.h index 497549997ac1..b04d45388371 100644 --- a/runtime/type-code.h +++ b/runtime/type-code.h @@ -9,8 +9,8 @@ #ifndef FORTRAN_RUNTIME_TYPE_CODE_H_ #define FORTRAN_RUNTIME_TYPE_CODE_H_ -#include "../include/flang/ISO_Fortran_binding.h" -#include "../lib/common/Fortran.h" +#include "flang/ISO_Fortran_binding.h" +#include "flang/common/Fortran.h" namespace Fortran::runtime { diff --git a/test/decimal/quick-sanity-test.cc b/test/decimal/quick-sanity-test.cc index 49c8ada29452..d9ebf8d3a0dd 100644 --- a/test/decimal/quick-sanity-test.cc +++ b/test/decimal/quick-sanity-test.cc @@ -1,4 +1,4 @@ -#include "../../lib/decimal/decimal.h" +#include "flang/decimal/decimal.h" #include #include #include diff --git a/test/decimal/thorough-test.cc b/test/decimal/thorough-test.cc index 40e37adf22dc..f5e3274d3208 100644 --- a/test/decimal/thorough-test.cc +++ b/test/decimal/thorough-test.cc @@ -1,4 +1,4 @@ -#include "../../lib/decimal/decimal.h" +#include "flang/decimal/decimal.h" #include #include #include diff --git a/test/evaluate/bit-population-count.cc b/test/evaluate/bit-population-count.cc index 162ea7835ab8..0b98f644c215 100644 --- a/test/evaluate/bit-population-count.cc +++ b/test/evaluate/bit-population-count.cc @@ -1,4 +1,4 @@ -#include "../../lib/common/bit-population-count.h" +#include "flang/common/bit-population-count.h" #include "testing.h" using Fortran::common::BitPopulationCount; diff --git a/test/evaluate/expression.cc b/test/evaluate/expression.cc index 24a6e0f5f5d7..ced868d5a5e2 100644 --- a/test/evaluate/expression.cc +++ b/test/evaluate/expression.cc @@ -1,9 +1,9 @@ -#include "../../lib/evaluate/expression.h" +#include "flang/evaluate/expression.h" #include "testing.h" -#include "../../lib/evaluate/fold.h" -#include "../../lib/evaluate/intrinsics.h" -#include "../../lib/evaluate/tools.h" -#include "../../lib/parser/message.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/message.h" #include #include #include diff --git a/test/evaluate/folding.cc b/test/evaluate/folding.cc index 83ec52aa92fe..f72670ed60eb 100644 --- a/test/evaluate/folding.cc +++ b/test/evaluate/folding.cc @@ -1,11 +1,11 @@ #include "testing.h" -#include "../../lib/evaluate/call.h" -#include "../../lib/evaluate/expression.h" -#include "../../lib/evaluate/fold.h" #include "../../lib/evaluate/host.h" #include "../../lib/evaluate/intrinsics-library-templates.h" -#include "../../lib/evaluate/intrinsics.h" -#include "../../lib/evaluate/tools.h" +#include "flang/evaluate/call.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/fold.h" +#include "flang/evaluate/intrinsics.h" +#include "flang/evaluate/tools.h" #include using namespace Fortran::evaluate; diff --git a/test/evaluate/fp-testing.h b/test/evaluate/fp-testing.h index acb2d68d3eb4..c86fbbaa2700 100644 --- a/test/evaluate/fp-testing.h +++ b/test/evaluate/fp-testing.h @@ -1,7 +1,7 @@ #ifndef FORTRAN_TEST_EVALUATE_FP_TESTING_H_ #define FORTRAN_TEST_EVALUATE_FP_TESTING_H_ -#include "../../lib/evaluate/common.h" +#include "flang/evaluate/common.h" #include using Fortran::common::RoundingMode; diff --git a/test/evaluate/integer.cc b/test/evaluate/integer.cc index db024184f723..e31f230cb88a 100644 --- a/test/evaluate/integer.cc +++ b/test/evaluate/integer.cc @@ -1,4 +1,4 @@ -#include "../../lib/evaluate/integer.h" +#include "flang/evaluate/integer.h" #include "testing.h" #include #include diff --git a/test/evaluate/intrinsics.cc b/test/evaluate/intrinsics.cc index cb9260b4c2d1..1826a4691b9d 100644 --- a/test/evaluate/intrinsics.cc +++ b/test/evaluate/intrinsics.cc @@ -1,9 +1,9 @@ -#include "../../lib/evaluate/intrinsics.h" +#include "flang/evaluate/intrinsics.h" #include "testing.h" -#include "../../lib/evaluate/common.h" -#include "../../lib/evaluate/expression.h" -#include "../../lib/evaluate/tools.h" -#include "../../lib/parser/provenance.h" +#include "flang/evaluate/common.h" +#include "flang/evaluate/expression.h" +#include "flang/evaluate/tools.h" +#include "flang/parser/provenance.h" #include #include #include diff --git a/test/evaluate/leading-zero-bit-count.cc b/test/evaluate/leading-zero-bit-count.cc index 2ab53aa605a2..1abd0f7b6c11 100644 --- a/test/evaluate/leading-zero-bit-count.cc +++ b/test/evaluate/leading-zero-bit-count.cc @@ -1,4 +1,4 @@ -#include "../../lib/common/leading-zero-bit-count.h" +#include "flang/common/leading-zero-bit-count.h" #include "testing.h" using Fortran::common::LeadingZeroBitCount; diff --git a/test/evaluate/logical.cc b/test/evaluate/logical.cc index 8b37a79c78db..3edbd6afd142 100644 --- a/test/evaluate/logical.cc +++ b/test/evaluate/logical.cc @@ -1,5 +1,5 @@ #include "testing.h" -#include "../../lib/evaluate/type.h" +#include "flang/evaluate/type.h" #include template void testKind() { diff --git a/test/evaluate/real.cc b/test/evaluate/real.cc index 7ac487866c78..919bc3ca87c7 100644 --- a/test/evaluate/real.cc +++ b/test/evaluate/real.cc @@ -1,6 +1,6 @@ #include "fp-testing.h" #include "testing.h" -#include "../../lib/evaluate/type.h" +#include "flang/evaluate/type.h" #include #include #include diff --git a/test/evaluate/uint128.cc b/test/evaluate/uint128.cc index 3b693bc91485..6a20d32de92a 100644 --- a/test/evaluate/uint128.cc +++ b/test/evaluate/uint128.cc @@ -1,5 +1,5 @@ #define AVOID_NATIVE_UINT128_T 1 -#include "../../lib/common/uint128.h" +#include "flang/common/uint128.h" #include "testing.h" #include #include diff --git a/tools/f18/f18-parse-demo.cc b/tools/f18/f18-parse-demo.cc index 4bb30f9767e3..f65b47b78341 100644 --- a/tools/f18/f18-parse-demo.cc +++ b/tools/f18/f18-parse-demo.cc @@ -21,16 +21,16 @@ // scaffolding compiler driver that can test some semantic passes of the // F18 compiler under development. -#include "../../lib/common/Fortran-features.h" -#include "../../lib/common/default-kinds.h" -#include "../../lib/parser/characters.h" -#include "../../lib/parser/dump-parse-tree.h" -#include "../../lib/parser/message.h" -#include "../../lib/parser/parse-tree-visitor.h" -#include "../../lib/parser/parse-tree.h" -#include "../../lib/parser/parsing.h" -#include "../../lib/parser/provenance.h" -#include "../../lib/parser/unparse.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/default-kinds.h" +#include "flang/parser/characters.h" +#include "flang/parser/dump-parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/parsing.h" +#include "flang/parser/provenance.h" +#include "flang/parser/unparse.h" #include #include #include diff --git a/tools/f18/f18.cc b/tools/f18/f18.cc index 6e6854051b4c..32a5cd4dce81 100644 --- a/tools/f18/f18.cc +++ b/tools/f18/f18.cc @@ -8,20 +8,20 @@ // Temporary Fortran front end driver main program for development scaffolding. -#include "../../lib/common/Fortran-features.h" -#include "../../lib/common/default-kinds.h" -#include "../../lib/evaluate/expression.h" -#include "../../lib/parser/characters.h" -#include "../../lib/parser/dump-parse-tree.h" -#include "../../lib/parser/message.h" -#include "../../lib/parser/parse-tree-visitor.h" -#include "../../lib/parser/parse-tree.h" -#include "../../lib/parser/parsing.h" -#include "../../lib/parser/provenance.h" -#include "../../lib/parser/unparse.h" -#include "../../lib/semantics/expression.h" -#include "../../lib/semantics/semantics.h" -#include "../../lib/semantics/unparse-with-symbols.h" +#include "flang/common/Fortran-features.h" +#include "flang/common/default-kinds.h" +#include "flang/evaluate/expression.h" +#include "flang/parser/characters.h" +#include "flang/parser/dump-parse-tree.h" +#include "flang/parser/message.h" +#include "flang/parser/parse-tree-visitor.h" +#include "flang/parser/parse-tree.h" +#include "flang/parser/parsing.h" +#include "flang/parser/provenance.h" +#include "flang/parser/unparse.h" +#include "flang/semantics/expression.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/unparse-with-symbols.h" #include #include #include diff --git a/tools/f18/stub-evaluate.cc b/tools/f18/stub-evaluate.cc index 14f16c7bda62..0b2020383b71 100644 --- a/tools/f18/stub-evaluate.cc +++ b/tools/f18/stub-evaluate.cc @@ -11,7 +11,7 @@ // libraries, as here, we need to stub out the dependences on the external // destructors, which will never actually be called. -#include "../../lib/common/indirection.h" +#include "flang/common/indirection.h" namespace Fortran::evaluate { struct GenericExprWrapper { From ae7721e611918631d1e3821dbb60f5ffcd9a69b1 Mon Sep 17 00:00:00 2001 From: Alexis Perry Date: Mon, 27 Jan 2020 19:18:45 -0700 Subject: [PATCH 012/345] Changed *.cc file extension to *.cpp (updated scripts) (#958) Updated CMake files accordingly, using better regex Updated license headers to match new extension and fit within 80 columns Updated other comments within files that referred to the old extension --- documentation/C++style.md | 8 +-- documentation/PullRequestChecklist.md | 2 +- include/flang/evaluate/traverse.h | 2 +- lib/common/CMakeLists.txt | 8 +-- ...rtran-features.cc => Fortran-features.cpp} | 2 +- lib/common/{Fortran.cc => Fortran.cpp} | 2 +- .../{default-kinds.cc => default-kinds.cpp} | 2 +- lib/common/{idioms.cc => idioms.cpp} | 2 +- lib/decimal/CMakeLists.txt | 4 +- ...ry-to-decimal.cc => binary-to-decimal.cpp} | 2 +- ...mal-to-binary.cc => decimal-to-binary.cpp} | 2 +- lib/evaluate/CMakeLists.txt | 50 +++++++-------- lib/evaluate/{call.cc => call.cpp} | 2 +- ...characteristics.cc => characteristics.cpp} | 2 +- ...eck-expression.cc => check-expression.cpp} | 2 +- lib/evaluate/{common.cc => common.cpp} | 2 +- lib/evaluate/{complex.cc => complex.cpp} | 2 +- lib/evaluate/{constant.cc => constant.cpp} | 2 +- .../{expression.cc => expression.cpp} | 2 +- .../{fold-character.cc => fold-character.cpp} | 2 +- .../{fold-complex.cc => fold-complex.cpp} | 2 +- .../{fold-integer.cc => fold-integer.cpp} | 2 +- .../{fold-logical.cc => fold-logical.cpp} | 2 +- lib/evaluate/{fold-real.cc => fold-real.cpp} | 2 +- lib/evaluate/{fold.cc => fold.cpp} | 2 +- .../{formatting.cc => formatting.cpp} | 2 +- lib/evaluate/{host.cc => host.cpp} | 2 +- lib/evaluate/{integer.cc => integer.cpp} | 2 +- lib/evaluate/intrinsics-library-templates.h | 2 +- ...sics-library.cc => intrinsics-library.cpp} | 2 +- .../{intrinsics.cc => intrinsics.cpp} | 2 +- lib/evaluate/{logical.cc => logical.cpp} | 2 +- lib/evaluate/{real.cc => real.cpp} | 2 +- lib/evaluate/{shape.cc => shape.cpp} | 2 +- .../{static-data.cc => static-data.cpp} | 2 +- lib/evaluate/{tools.cc => tools.cpp} | 2 +- lib/evaluate/{type.cc => type.cpp} | 2 +- lib/evaluate/{variable.cc => variable.cpp} | 2 +- lib/parser/CMakeLists.txt | 46 ++++++------- ...Fortran-parsers.cc => Fortran-parsers.cpp} | 12 ++-- lib/parser/{char-block.cc => char-block.cpp} | 2 +- .../{char-buffer.cc => char-buffer.cpp} | 2 +- lib/parser/{char-set.cc => char-set.cpp} | 2 +- lib/parser/{characters.cc => characters.cpp} | 2 +- .../{debug-parser.cc => debug-parser.cpp} | 2 +- ...able-parsers.cc => executable-parsers.cpp} | 2 +- .../{expr-parsers.cc => expr-parsers.cpp} | 2 +- ...nted-parser.cc => instrumented-parser.cpp} | 2 +- lib/parser/{io-parsers.cc => io-parsers.cpp} | 2 +- lib/parser/{message.cc => message.cpp} | 2 +- .../{openmp-parsers.cc => openmp-parsers.cpp} | 2 +- lib/parser/{parse-tree.cc => parse-tree.cpp} | 2 +- lib/parser/{parsing.cc => parsing.cpp} | 2 +- .../{preprocessor.cc => preprocessor.cpp} | 2 +- lib/parser/{prescan.cc => prescan.cpp} | 2 +- ...program-parsers.cc => program-parsers.cpp} | 2 +- lib/parser/{provenance.cc => provenance.cpp} | 2 +- lib/parser/{source.cc => source.cpp} | 2 +- .../{token-sequence.cc => token-sequence.cpp} | 2 +- lib/parser/{tools.cc => tools.cpp} | 2 +- lib/parser/{unparse.cc => unparse.cpp} | 2 +- lib/parser/{user-state.cc => user-state.cpp} | 2 +- lib/semantics/CMakeLists.txt | 64 +++++++++---------- .../{assignment.cc => assignment.cpp} | 4 +- lib/semantics/{attr.cc => attr.cpp} | 2 +- ...canonicalize-do.cc => canonicalize-do.cpp} | 2 +- ...nonicalize-omp.cc => canonicalize-omp.cpp} | 2 +- .../{check-allocate.cc => check-allocate.cpp} | 2 +- ...arithmeticif.cc => check-arithmeticif.cpp} | 2 +- .../{check-call.cc => check-call.cpp} | 2 +- .../{check-coarray.cc => check-coarray.cpp} | 2 +- ...eck-deallocate.cc => check-deallocate.cpp} | 2 +- ...declarations.cc => check-declarations.cpp} | 4 +- lib/semantics/{check-do.cc => check-do.cpp} | 2 +- .../{check-if-stmt.cc => check-if-stmt.cpp} | 2 +- lib/semantics/{check-io.cc => check-io.cpp} | 2 +- .../{check-nullify.cc => check-nullify.cpp} | 2 +- ...p-structure.cc => check-omp-structure.cpp} | 2 +- .../{check-purity.cc => check-purity.cpp} | 2 +- .../{check-return.cc => check-return.cpp} | 2 +- .../{check-stop.cc => check-stop.cpp} | 2 +- .../{expression.cc => expression.cpp} | 2 +- lib/semantics/{mod-file.cc => mod-file.cpp} | 2 +- ...r-assignment.cc => pointer-assignment.cpp} | 2 +- .../{program-tree.cc => program-tree.cpp} | 2 +- .../{resolve-labels.cc => resolve-labels.cpp} | 2 +- ...names-utils.cc => resolve-names-utils.cpp} | 2 +- lib/semantics/resolve-names-utils.h | 2 +- .../{resolve-names.cc => resolve-names.cpp} | 4 +- ...e-parse-tree.cc => rewrite-parse-tree.cpp} | 2 +- lib/semantics/{scope.cc => scope.cpp} | 2 +- lib/semantics/{semantics.cc => semantics.cpp} | 2 +- lib/semantics/{symbol.cc => symbol.cpp} | 2 +- lib/semantics/{tools.cc => tools.cpp} | 2 +- lib/semantics/{type.cc => type.cpp} | 2 +- ...th-symbols.cc => unparse-with-symbols.cpp} | 2 +- runtime/CMakeLists.txt | 30 ++++----- ...ran_binding.cc => ISO_Fortran_binding.cpp} | 2 +- runtime/{derived-type.cc => derived-type.cpp} | 2 +- runtime/{descriptor.cc => descriptor.cpp} | 2 +- runtime/{file.cc => file.cpp} | 2 +- runtime/{format.cc => format.cpp} | 2 +- runtime/{io-api.cc => io-api.cpp} | 2 +- runtime/{io-error.cc => io-error.cpp} | 2 +- runtime/{io-stmt.cc => io-stmt.cpp} | 2 +- runtime/{main.cc => main.cpp} | 2 +- runtime/{memory.cc => memory.cpp} | 2 +- runtime/{stop.cc => stop.cpp} | 2 +- runtime/{terminator.cc => terminator.cpp} | 2 +- runtime/{tools.cc => tools.cpp} | 2 +- ...ansformational.cc => transformational.cpp} | 2 +- runtime/{type-code.cc => type-code.cpp} | 2 +- test/decimal/CMakeLists.txt | 4 +- ...k-sanity-test.cc => quick-sanity-test.cpp} | 0 .../{thorough-test.cc => thorough-test.cpp} | 0 test/evaluate/CMakeLists.txt | 28 ++++---- ...ran-binding.cc => ISO-Fortran-binding.cpp} | 0 ...tion-count.cc => bit-population-count.cpp} | 0 .../{expression.cc => expression.cpp} | 0 test/evaluate/{folding.cc => folding.cpp} | 2 +- .../{fp-testing.cc => fp-testing.cpp} | 0 test/evaluate/{integer.cc => integer.cpp} | 0 .../{intrinsics.cc => intrinsics.cpp} | 0 ...it-count.cc => leading-zero-bit-count.cpp} | 0 test/evaluate/{logical.cc => logical.cpp} | 0 test/evaluate/{real.cc => real.cpp} | 0 test/evaluate/{reshape.cc => reshape.cpp} | 0 test/evaluate/{testing.cc => testing.cpp} | 0 test/evaluate/{uint128.cc => uint128.cpp} | 0 test/runtime/CMakeLists.txt | 4 +- test/runtime/{format.cc => format.cpp} | 2 +- test/runtime/{hello.cc => hello.cpp} | 0 tools/f18/CMakeLists.txt | 8 +-- tools/f18/{dump.cc => dump.cpp} | 2 +- .../{f18-parse-demo.cc => f18-parse-demo.cpp} | 4 +- tools/f18/{f18.cc => f18.cpp} | 2 +- .../{stub-evaluate.cc => stub-evaluate.cpp} | 2 +- 137 files changed, 247 insertions(+), 247 deletions(-) rename lib/common/{Fortran-features.cc => Fortran-features.cpp} (96%) rename lib/common/{Fortran.cc => Fortran.cpp} (95%) rename lib/common/{default-kinds.cc => default-kinds.cpp} (96%) rename lib/common/{idioms.cc => idioms.cpp} (94%) rename lib/decimal/{binary-to-decimal.cc => binary-to-decimal.cpp} (99%) rename lib/decimal/{decimal-to-binary.cc => decimal-to-binary.cpp} (99%) rename lib/evaluate/{call.cc => call.cpp} (99%) rename lib/evaluate/{characteristics.cc => characteristics.cpp} (99%) rename lib/evaluate/{check-expression.cc => check-expression.cpp} (99%) rename lib/evaluate/{common.cc => common.cpp} (96%) rename lib/evaluate/{complex.cc => complex.cpp} (98%) rename lib/evaluate/{constant.cc => constant.cpp} (99%) rename lib/evaluate/{expression.cc => expression.cpp} (99%) rename lib/evaluate/{fold-character.cc => fold-character.cpp} (98%) rename lib/evaluate/{fold-complex.cc => fold-complex.cpp} (98%) rename lib/evaluate/{fold-integer.cc => fold-integer.cpp} (99%) rename lib/evaluate/{fold-logical.cc => fold-logical.cpp} (99%) rename lib/evaluate/{fold-real.cc => fold-real.cpp} (99%) rename lib/evaluate/{fold.cc => fold.cpp} (99%) rename lib/evaluate/{formatting.cc => formatting.cpp} (99%) rename lib/evaluate/{host.cc => host.cpp} (98%) rename lib/evaluate/{integer.cc => integer.cpp} (94%) rename lib/evaluate/{intrinsics-library.cc => intrinsics-library.cpp} (99%) rename lib/evaluate/{intrinsics.cc => intrinsics.cpp} (99%) rename lib/evaluate/{logical.cc => logical.cpp} (88%) rename lib/evaluate/{real.cc => real.cpp} (99%) rename lib/evaluate/{shape.cc => shape.cpp} (99%) rename lib/evaluate/{static-data.cc => static-data.cpp} (97%) rename lib/evaluate/{tools.cc => tools.cpp} (99%) rename lib/evaluate/{type.cc => type.cpp} (99%) rename lib/evaluate/{variable.cc => variable.cpp} (99%) rename lib/parser/{Fortran-parsers.cc => Fortran-parsers.cpp} (99%) rename lib/parser/{char-block.cc => char-block.cpp} (88%) rename lib/parser/{char-buffer.cc => char-buffer.cpp} (97%) rename lib/parser/{char-set.cc => char-set.cpp} (90%) rename lib/parser/{characters.cc => characters.cpp} (99%) rename lib/parser/{debug-parser.cc => debug-parser.cpp} (92%) rename lib/parser/{executable-parsers.cc => executable-parsers.cpp} (99%) rename lib/parser/{expr-parsers.cc => expr-parsers.cpp} (99%) rename lib/parser/{instrumented-parser.cc => instrumented-parser.cpp} (97%) rename lib/parser/{io-parsers.cc => io-parsers.cpp} (99%) rename lib/parser/{message.cc => message.cpp} (99%) rename lib/parser/{openmp-parsers.cc => openmp-parsers.cpp} (99%) rename lib/parser/{parse-tree.cc => parse-tree.cpp} (99%) rename lib/parser/{parsing.cc => parsing.cpp} (98%) rename lib/parser/{preprocessor.cc => preprocessor.cpp} (99%) rename lib/parser/{prescan.cc => prescan.cpp} (99%) rename lib/parser/{program-parsers.cc => program-parsers.cpp} (99%) rename lib/parser/{provenance.cc => provenance.cpp} (99%) rename lib/parser/{source.cc => source.cpp} (99%) rename lib/parser/{token-sequence.cc => token-sequence.cpp} (99%) rename lib/parser/{tools.cc => tools.cpp} (97%) rename lib/parser/{unparse.cc => unparse.cpp} (99%) rename lib/parser/{user-state.cc => user-state.cpp} (97%) rename lib/semantics/{assignment.cc => assignment.cpp} (99%) rename lib/semantics/{attr.cc => attr.cpp} (95%) rename lib/semantics/{canonicalize-do.cc => canonicalize-do.cpp} (98%) rename lib/semantics/{canonicalize-omp.cc => canonicalize-omp.cpp} (98%) rename lib/semantics/{check-allocate.cc => check-allocate.cpp} (99%) rename lib/semantics/{check-arithmeticif.cc => check-arithmeticif.cpp} (96%) rename lib/semantics/{check-call.cc => check-call.cpp} (99%) rename lib/semantics/{check-coarray.cc => check-coarray.cpp} (98%) rename lib/semantics/{check-deallocate.cc => check-deallocate.cpp} (97%) rename lib/semantics/{check-declarations.cc => check-declarations.cpp} (99%) rename lib/semantics/{check-do.cc => check-do.cpp} (99%) rename lib/semantics/{check-if-stmt.cc => check-if-stmt.cpp} (93%) rename lib/semantics/{check-io.cc => check-io.cpp} (99%) rename lib/semantics/{check-nullify.cc => check-nullify.cpp} (97%) rename lib/semantics/{check-omp-structure.cc => check-omp-structure.cpp} (99%) rename lib/semantics/{check-purity.cc => check-purity.cpp} (97%) rename lib/semantics/{check-return.cc => check-return.cpp} (96%) rename lib/semantics/{check-stop.cc => check-stop.cpp} (95%) rename lib/semantics/{expression.cc => expression.cpp} (99%) rename lib/semantics/{mod-file.cc => mod-file.cpp} (99%) rename lib/semantics/{pointer-assignment.cc => pointer-assignment.cpp} (99%) rename lib/semantics/{program-tree.cc => program-tree.cpp} (99%) rename lib/semantics/{resolve-labels.cc => resolve-labels.cpp} (99%) rename lib/semantics/{resolve-names-utils.cc => resolve-names-utils.cpp} (99%) rename lib/semantics/{resolve-names.cc => resolve-names.cpp} (99%) rename lib/semantics/{rewrite-parse-tree.cc => rewrite-parse-tree.cpp} (98%) rename lib/semantics/{scope.cc => scope.cpp} (99%) rename lib/semantics/{semantics.cc => semantics.cpp} (99%) rename lib/semantics/{symbol.cc => symbol.cpp} (99%) rename lib/semantics/{tools.cc => tools.cpp} (99%) rename lib/semantics/{type.cc => type.cpp} (99%) rename lib/semantics/{unparse-with-symbols.cc => unparse-with-symbols.cpp} (98%) rename runtime/{ISO_Fortran_binding.cc => ISO_Fortran_binding.cpp} (99%) rename runtime/{derived-type.cc => derived-type.cpp} (97%) rename runtime/{descriptor.cc => descriptor.cpp} (98%) rename runtime/{file.cc => file.cpp} (99%) rename runtime/{format.cc => format.cpp} (99%) rename runtime/{io-api.cc => io-api.cpp} (94%) rename runtime/{io-error.cc => io-error.cpp} (96%) rename runtime/{io-stmt.cc => io-stmt.cpp} (97%) rename runtime/{main.cc => main.cpp} (96%) rename runtime/{memory.cc => memory.cpp} (92%) rename runtime/{stop.cc => stop.cpp} (96%) rename runtime/{terminator.cc => terminator.cpp} (95%) rename runtime/{tools.cc => tools.cpp} (95%) rename runtime/{transformational.cc => transformational.cpp} (98%) rename runtime/{type-code.cc => type-code.cpp} (96%) rename test/decimal/{quick-sanity-test.cc => quick-sanity-test.cpp} (100%) rename test/decimal/{thorough-test.cc => thorough-test.cpp} (100%) rename test/evaluate/{ISO-Fortran-binding.cc => ISO-Fortran-binding.cpp} (100%) rename test/evaluate/{bit-population-count.cc => bit-population-count.cpp} (100%) rename test/evaluate/{expression.cc => expression.cpp} (100%) rename test/evaluate/{folding.cc => folding.cpp} (98%) rename test/evaluate/{fp-testing.cc => fp-testing.cpp} (100%) rename test/evaluate/{integer.cc => integer.cpp} (100%) rename test/evaluate/{intrinsics.cc => intrinsics.cpp} (100%) rename test/evaluate/{leading-zero-bit-count.cc => leading-zero-bit-count.cpp} (100%) rename test/evaluate/{logical.cc => logical.cpp} (100%) rename test/evaluate/{real.cc => real.cpp} (100%) rename test/evaluate/{reshape.cc => reshape.cpp} (100%) rename test/evaluate/{testing.cc => testing.cpp} (100%) rename test/evaluate/{uint128.cc => uint128.cpp} (100%) rename test/runtime/{format.cc => format.cpp} (98%) rename test/runtime/{hello.cc => hello.cpp} (100%) rename tools/f18/{dump.cc => dump.cpp} (95%) rename tools/f18/{f18-parse-demo.cc => f18-parse-demo.cpp} (99%) rename tools/f18/{f18.cc => f18.cpp} (99%) rename tools/f18/{stub-evaluate.cc => stub-evaluate.cpp} (94%) diff --git a/documentation/C++style.md b/documentation/C++style.md index fb835856800d..9136a3f21e6a 100644 --- a/documentation/C++style.md +++ b/documentation/C++style.md @@ -36,7 +36,7 @@ unless they introduce ambiguity. ### Files 1. File names should use dashes, not underscores. C++ sources have the -extension ".cc", not ".C" or ".cpp" or ".cxx". Don't create needless +extension ".cpp", not ".C" or ".cc" or ".cxx". Don't create needless source directory hierarchies. 1. Header files should be idempotent. Use the usual technique: ``` @@ -46,11 +46,11 @@ source directory hierarchies. #endif // FORTRAN_header_H_ ``` 1. `#include` every header defining an entity that your project header or source -file actually uses directly. (Exception: when foo.cc starts, as it should, +file actually uses directly. (Exception: when foo.cpp starts, as it should, with `#include "foo.h"`, and foo.h includes bar.h in order to define the interface to the module foo, you don't have to redundantly `#include "bar.h"` -in foo.cc.) -1. In the source file "foo.cc", put its corresponding `#include "foo.h"` +in foo.cpp.) +1. In the source file "foo.cpp", put its corresponding `#include "foo.h"` first in the sequence of inclusions. Then `#include` other project headers in alphabetic order; then C++ standard headers, also alphabetically; then C and system headers. diff --git a/documentation/PullRequestChecklist.md b/documentation/PullRequestChecklist.md index 782c3b61ca0e..041ec500280c 100644 --- a/documentation/PullRequestChecklist.md +++ b/documentation/PullRequestChecklist.md @@ -29,7 +29,7 @@ can also be used when reviewing pull requests. ## Follow the style guide The following items are taken from the [C++ style guide](C++style.md). But even though I've read the style guide, they regularly trip me up. -* Run clang-format version 7 on all .cc and .h files. +* Run clang-format version 7 on all .cpp and .h files. * Make sure that all source lines have 80 or fewer characters. Note that clang-format will do this for most code. But you may need to break up long strings. diff --git a/include/flang/evaluate/traverse.h b/include/flang/evaluate/traverse.h index 326104b1e1ce..d85afac239e0 100644 --- a/include/flang/evaluate/traverse.h +++ b/include/flang/evaluate/traverse.h @@ -25,7 +25,7 @@ // - overrides for "Result operator()" // // Boilerplate classes also appear below to ease construction of visitors. -// See CheckSpecificationExpr() in check-expression.cc for an example client. +// See CheckSpecificationExpr() in check-expression.cpp for an example client. // // How this works: // - The operator() overloads in Traverse<> invoke the visitor's Default() for diff --git a/lib/common/CMakeLists.txt b/lib/common/CMakeLists.txt index c72efd4dc414..bfa8bf7953e0 100644 --- a/lib/common/CMakeLists.txt +++ b/lib/common/CMakeLists.txt @@ -7,10 +7,10 @@ #===------------------------------------------------------------------------===# add_library(FortranCommon - Fortran.cc - Fortran-features.cc - default-kinds.cc - idioms.cc + Fortran.cpp + Fortran-features.cpp + default-kinds.cpp + idioms.cpp ) install (TARGETS FortranCommon diff --git a/lib/common/Fortran-features.cc b/lib/common/Fortran-features.cpp similarity index 96% rename from lib/common/Fortran-features.cc rename to lib/common/Fortran-features.cpp index f92162673e46..62c3e505ea17 100644 --- a/lib/common/Fortran-features.cc +++ b/lib/common/Fortran-features.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran-features.cc ------------------------------------===// +//===-- lib/common/Fortran-features.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/Fortran.cc b/lib/common/Fortran.cpp similarity index 95% rename from lib/common/Fortran.cc rename to lib/common/Fortran.cpp index 8b915bbee6dd..61ff0ee2f6e9 100644 --- a/lib/common/Fortran.cc +++ b/lib/common/Fortran.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran.cc ---------------------------------------------===// +//===-- lib/common/Fortran.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/default-kinds.cc b/lib/common/default-kinds.cpp similarity index 96% rename from lib/common/default-kinds.cc rename to lib/common/default-kinds.cpp index 1f459935961e..490f4dc1f156 100644 --- a/lib/common/default-kinds.cc +++ b/lib/common/default-kinds.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/default-kinds.cc ---------------------------------------===// +//===-- lib/common/default-kinds.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/idioms.cc b/lib/common/idioms.cpp similarity index 94% rename from lib/common/idioms.cc rename to lib/common/idioms.cpp index f27f7b1a0030..d28d76fe83b8 100644 --- a/lib/common/idioms.cc +++ b/lib/common/idioms.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/idioms.cc ----------------------------------------------===// +//===-- lib/common/idioms.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/decimal/CMakeLists.txt b/lib/decimal/CMakeLists.txt index d28a1f9ae168..28c9c828ac3a 100644 --- a/lib/decimal/CMakeLists.txt +++ b/lib/decimal/CMakeLists.txt @@ -7,8 +7,8 @@ #===------------------------------------------------------------------------===# add_library(FortranDecimal - binary-to-decimal.cc - decimal-to-binary.cc + binary-to-decimal.cpp + decimal-to-binary.cpp ) install (TARGETS FortranDecimal diff --git a/lib/decimal/binary-to-decimal.cc b/lib/decimal/binary-to-decimal.cpp similarity index 99% rename from lib/decimal/binary-to-decimal.cc rename to lib/decimal/binary-to-decimal.cpp index 53b00c39e4ed..fbc043eed374 100644 --- a/lib/decimal/binary-to-decimal.cc +++ b/lib/decimal/binary-to-decimal.cpp @@ -1,4 +1,4 @@ -//===-- lib/decimal/binary-to-decimal.cc ----------------------------------===// +//===-- lib/decimal/binary-to-decimal.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/decimal/decimal-to-binary.cc b/lib/decimal/decimal-to-binary.cpp similarity index 99% rename from lib/decimal/decimal-to-binary.cc rename to lib/decimal/decimal-to-binary.cpp index 3f57a3bb41f1..de15833098f5 100644 --- a/lib/decimal/decimal-to-binary.cc +++ b/lib/decimal/decimal-to-binary.cpp @@ -1,4 +1,4 @@ -//===-- lib/decimal/decimal-to-binary.cc ----------------------------------===// +//===-- lib/decimal/decimal-to-binary.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/CMakeLists.txt b/lib/evaluate/CMakeLists.txt index 54d5d834252e..8714819c49a8 100644 --- a/lib/evaluate/CMakeLists.txt +++ b/lib/evaluate/CMakeLists.txt @@ -7,31 +7,31 @@ #===------------------------------------------------------------------------===# add_library(FortranEvaluate - call.cc - characteristics.cc - check-expression.cc - common.cc - complex.cc - constant.cc - expression.cc - fold.cc - fold-character.cc - fold-complex.cc - fold-integer.cc - fold-logical.cc - fold-real.cc - formatting.cc - host.cc - integer.cc - intrinsics.cc - intrinsics-library.cc - logical.cc - real.cc - shape.cc - static-data.cc - tools.cc - type.cc - variable.cc + call.cpp + characteristics.cpp + check-expression.cpp + common.cpp + complex.cpp + constant.cpp + expression.cpp + fold.cpp + fold-character.cpp + fold-complex.cpp + fold-integer.cpp + fold-logical.cpp + fold-real.cpp + formatting.cpp + host.cpp + integer.cpp + intrinsics.cpp + intrinsics-library.cpp + logical.cpp + real.cpp + shape.cpp + static-data.cpp + tools.cpp + type.cpp + variable.cpp ) target_link_libraries(FortranEvaluate diff --git a/lib/evaluate/call.cc b/lib/evaluate/call.cpp similarity index 99% rename from lib/evaluate/call.cc rename to lib/evaluate/call.cpp index a61f679935f0..d1272af63484 100644 --- a/lib/evaluate/call.cc +++ b/lib/evaluate/call.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/call.cc ----------------------------------------------===// +//===-- lib/evaluate/call.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/characteristics.cc b/lib/evaluate/characteristics.cpp similarity index 99% rename from lib/evaluate/characteristics.cc rename to lib/evaluate/characteristics.cpp index 1e4f2825ba08..ac18b5a1730b 100644 --- a/lib/evaluate/characteristics.cc +++ b/lib/evaluate/characteristics.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/characteristics.cc -----------------------------------===// +//===-- lib/evaluate/characteristics.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/check-expression.cc b/lib/evaluate/check-expression.cpp similarity index 99% rename from lib/evaluate/check-expression.cc rename to lib/evaluate/check-expression.cpp index 31d1454ff176..34b9025abf25 100644 --- a/lib/evaluate/check-expression.cc +++ b/lib/evaluate/check-expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/check-expression.cc ----------------------------------===// +//===-- lib/evaluate/check-expression.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/common.cc b/lib/evaluate/common.cpp similarity index 96% rename from lib/evaluate/common.cc rename to lib/evaluate/common.cpp index a1f7cbbf6005..9c45e668767d 100644 --- a/lib/evaluate/common.cc +++ b/lib/evaluate/common.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/common.cc --------------------------------------------===// +//===-- lib/evaluate/common.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/complex.cc b/lib/evaluate/complex.cpp similarity index 98% rename from lib/evaluate/complex.cc rename to lib/evaluate/complex.cpp index e93245997cd2..210fd1fb54c5 100644 --- a/lib/evaluate/complex.cc +++ b/lib/evaluate/complex.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/complex.cc -------------------------------------------===// +//===-- lib/evaluate/complex.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/constant.cc b/lib/evaluate/constant.cpp similarity index 99% rename from lib/evaluate/constant.cc rename to lib/evaluate/constant.cpp index 4c83e34c8c68..65da1378f10f 100644 --- a/lib/evaluate/constant.cc +++ b/lib/evaluate/constant.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/constant.cc ------------------------------------------===// +//===-- lib/evaluate/constant.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/expression.cc b/lib/evaluate/expression.cpp similarity index 99% rename from lib/evaluate/expression.cc rename to lib/evaluate/expression.cpp index 21a0176e32ac..fbf20daba7af 100644 --- a/lib/evaluate/expression.cc +++ b/lib/evaluate/expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/expression.cc ----------------------------------------===// +//===-- lib/evaluate/expression.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-character.cc b/lib/evaluate/fold-character.cpp similarity index 98% rename from lib/evaluate/fold-character.cc rename to lib/evaluate/fold-character.cpp index 73711ec61a9f..0c99b96df71b 100644 --- a/lib/evaluate/fold-character.cc +++ b/lib/evaluate/fold-character.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-character.cc ------------------------------------===// +//===-- lib/evaluate/fold-character.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-complex.cc b/lib/evaluate/fold-complex.cpp similarity index 98% rename from lib/evaluate/fold-complex.cc rename to lib/evaluate/fold-complex.cpp index e96bc9dbc359..d4c5f00873a4 100644 --- a/lib/evaluate/fold-complex.cc +++ b/lib/evaluate/fold-complex.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-complex.cc --------------------------------------===// +//===-- lib/evaluate/fold-complex.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-integer.cc b/lib/evaluate/fold-integer.cpp similarity index 99% rename from lib/evaluate/fold-integer.cc rename to lib/evaluate/fold-integer.cpp index aa696dd3582c..cda0a39e616f 100644 --- a/lib/evaluate/fold-integer.cc +++ b/lib/evaluate/fold-integer.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-integer.cc --------------------------------------===// +//===-- lib/evaluate/fold-integer.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-logical.cc b/lib/evaluate/fold-logical.cpp similarity index 99% rename from lib/evaluate/fold-logical.cc rename to lib/evaluate/fold-logical.cpp index 649f745897b8..cf1bb9a1374c 100644 --- a/lib/evaluate/fold-logical.cc +++ b/lib/evaluate/fold-logical.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-logical.cc --------------------------------------===// +//===-- lib/evaluate/fold-logical.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-real.cc b/lib/evaluate/fold-real.cpp similarity index 99% rename from lib/evaluate/fold-real.cc rename to lib/evaluate/fold-real.cpp index 05b719e6cb42..b1d3ed33ca44 100644 --- a/lib/evaluate/fold-real.cc +++ b/lib/evaluate/fold-real.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-real.cc -----------------------------------------===// +//===-- lib/evaluate/fold-real.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold.cc b/lib/evaluate/fold.cpp similarity index 99% rename from lib/evaluate/fold.cc rename to lib/evaluate/fold.cpp index af1de4094daf..4a7263f1ffce 100644 --- a/lib/evaluate/fold.cc +++ b/lib/evaluate/fold.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold.cc ----------------------------------------------===// +//===-- lib/evaluate/fold.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/formatting.cc b/lib/evaluate/formatting.cpp similarity index 99% rename from lib/evaluate/formatting.cc rename to lib/evaluate/formatting.cpp index 6eb43a4e4967..5bc627748675 100644 --- a/lib/evaluate/formatting.cc +++ b/lib/evaluate/formatting.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/formatting.cc ----------------------------------------===// +//===-- lib/evaluate/formatting.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/host.cc b/lib/evaluate/host.cpp similarity index 98% rename from lib/evaluate/host.cc rename to lib/evaluate/host.cpp index 62dd44863989..47685e08b762 100644 --- a/lib/evaluate/host.cc +++ b/lib/evaluate/host.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/host.cc ----------------------------------------------===// +//===-- lib/evaluate/host.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/integer.cc b/lib/evaluate/integer.cpp similarity index 94% rename from lib/evaluate/integer.cc rename to lib/evaluate/integer.cpp index 30484d90664a..06503e6f42b8 100644 --- a/lib/evaluate/integer.cc +++ b/lib/evaluate/integer.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/integer.cc -------------------------------------------===// +//===-- lib/evaluate/integer.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/intrinsics-library-templates.h b/lib/evaluate/intrinsics-library-templates.h index 4ab657da01e0..65d3de7e05f9 100644 --- a/lib/evaluate/intrinsics-library-templates.h +++ b/lib/evaluate/intrinsics-library-templates.h @@ -13,7 +13,7 @@ // function of the structures defined in intrinsics-library.h. It should only be // included if these member functions are used, else intrinsics-library.h is // sufficient. This is to avoid circular dependencies. The below implementation -// cannot be defined in .cc file because it would be too cumbersome to decide +// cannot be defined in .cpp file because it would be too cumbersome to decide // which version should be instantiated in a generic way. #include "host.h" diff --git a/lib/evaluate/intrinsics-library.cc b/lib/evaluate/intrinsics-library.cpp similarity index 99% rename from lib/evaluate/intrinsics-library.cc rename to lib/evaluate/intrinsics-library.cpp index 4a8e4c6f61b1..bfddcb366b8f 100644 --- a/lib/evaluate/intrinsics-library.cc +++ b/lib/evaluate/intrinsics-library.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics-library.cc --------------------------------===// +//===-- lib/evaluate/intrinsics-library.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/intrinsics.cc b/lib/evaluate/intrinsics.cpp similarity index 99% rename from lib/evaluate/intrinsics.cc rename to lib/evaluate/intrinsics.cpp index 1a8b5ba3248b..93b2f864ae41 100644 --- a/lib/evaluate/intrinsics.cc +++ b/lib/evaluate/intrinsics.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics.cc ----------------------------------------===// +//===-- lib/evaluate/intrinsics.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/logical.cc b/lib/evaluate/logical.cpp similarity index 88% rename from lib/evaluate/logical.cc rename to lib/evaluate/logical.cpp index bdbfedb05d7f..8bd3a3b7524a 100644 --- a/lib/evaluate/logical.cc +++ b/lib/evaluate/logical.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/logical.cc -------------------------------------------===// +//===-- lib/evaluate/logical.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/real.cc b/lib/evaluate/real.cpp similarity index 99% rename from lib/evaluate/real.cc rename to lib/evaluate/real.cpp index b803a8ea1de3..ec9ab1dd4373 100644 --- a/lib/evaluate/real.cc +++ b/lib/evaluate/real.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/real.cc ----------------------------------------------===// +//===-- lib/evaluate/real.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/shape.cc b/lib/evaluate/shape.cpp similarity index 99% rename from lib/evaluate/shape.cc rename to lib/evaluate/shape.cpp index 62bc879895de..ea14c4b244ad 100644 --- a/lib/evaluate/shape.cc +++ b/lib/evaluate/shape.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/shape.cc ---------------------------------------------===// +//===-- lib/evaluate/shape.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/static-data.cc b/lib/evaluate/static-data.cpp similarity index 97% rename from lib/evaluate/static-data.cc rename to lib/evaluate/static-data.cpp index f5311cee1a95..668fcb47191f 100644 --- a/lib/evaluate/static-data.cc +++ b/lib/evaluate/static-data.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/static-data.cc ---------------------------------------===// +//===-- lib/evaluate/static-data.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/tools.cc b/lib/evaluate/tools.cpp similarity index 99% rename from lib/evaluate/tools.cc rename to lib/evaluate/tools.cpp index 4710343f4ad2..8f9af2e1fa34 100644 --- a/lib/evaluate/tools.cc +++ b/lib/evaluate/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/tools.cc ---------------------------------------------===// +//===-- lib/evaluate/tools.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/type.cc b/lib/evaluate/type.cpp similarity index 99% rename from lib/evaluate/type.cc rename to lib/evaluate/type.cpp index bf7e8012e0ff..11f800349ca6 100644 --- a/lib/evaluate/type.cc +++ b/lib/evaluate/type.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/type.cc ----------------------------------------------===// +//===-- lib/evaluate/type.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/variable.cc b/lib/evaluate/variable.cpp similarity index 99% rename from lib/evaluate/variable.cc rename to lib/evaluate/variable.cpp index 0922ba659cd5..f10b0a6ee2f2 100644 --- a/lib/evaluate/variable.cc +++ b/lib/evaluate/variable.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/variable.cc ------------------------------------------===// +//===-- lib/evaluate/variable.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/CMakeLists.txt b/lib/parser/CMakeLists.txt index 96306019633d..72ced8cc9e45 100644 --- a/lib/parser/CMakeLists.txt +++ b/lib/parser/CMakeLists.txt @@ -7,29 +7,29 @@ #===------------------------------------------------------------------------===# add_library(FortranParser - Fortran-parsers.cc - char-buffer.cc - char-block.cc - char-set.cc - characters.cc - debug-parser.cc - executable-parsers.cc - expr-parsers.cc - instrumented-parser.cc - io-parsers.cc - message.cc - openmp-parsers.cc - parse-tree.cc - parsing.cc - preprocessor.cc - prescan.cc - program-parsers.cc - provenance.cc - source.cc - token-sequence.cc - tools.cc - unparse.cc - user-state.cc + Fortran-parsers.cpp + char-buffer.cpp + char-block.cpp + char-set.cpp + characters.cpp + debug-parser.cpp + executable-parsers.cpp + expr-parsers.cpp + instrumented-parser.cpp + io-parsers.cpp + message.cpp + openmp-parsers.cpp + parse-tree.cpp + parsing.cpp + preprocessor.cpp + prescan.cpp + program-parsers.cpp + provenance.cpp + source.cpp + token-sequence.cpp + tools.cpp + unparse.cpp + user-state.cpp ) target_link_libraries(FortranParser diff --git a/lib/parser/Fortran-parsers.cc b/lib/parser/Fortran-parsers.cpp similarity index 99% rename from lib/parser/Fortran-parsers.cc rename to lib/parser/Fortran-parsers.cpp index bfc7122a4dd5..f895189aa99a 100644 --- a/lib/parser/Fortran-parsers.cc +++ b/lib/parser/Fortran-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/Fortran-parsers.cc -------------------------------------===// +//===-- lib/parser/Fortran-parsers.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -24,11 +24,11 @@ // various per-type parsers are partitioned into several C++ source // files. This file contains parsers for constants, types, declarations, // and misfits (mostly clauses 7, 8, & 9 of Fortran 2018). The others: -// executable-parsers.cc Executable statements -// expr-parsers.cc Expressions -// io-parsers.cc I/O statements and FORMAT -// openmp-parsers.cc OpenMP directives -// program-parsers.cc Program units +// executable-parsers.cpp Executable statements +// expr-parsers.cpp Expressions +// io-parsers.cpp I/O statements and FORMAT +// openmp-parsers.cpp OpenMP directives +// program-parsers.cpp Program units #include "basic-parsers.h" #include "expr-parsers.h" diff --git a/lib/parser/char-block.cc b/lib/parser/char-block.cpp similarity index 88% rename from lib/parser/char-block.cc rename to lib/parser/char-block.cpp index 8c9784e55ad1..a3cb60a9b13f 100644 --- a/lib/parser/char-block.cc +++ b/lib/parser/char-block.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-block.cc --------------------------------*- C++ -*-===// +//===-- lib/parser/char-block.cpp -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/char-buffer.cc b/lib/parser/char-buffer.cpp similarity index 97% rename from lib/parser/char-buffer.cc rename to lib/parser/char-buffer.cpp index 6c426243ce3f..3de83ec87b5a 100644 --- a/lib/parser/char-buffer.cc +++ b/lib/parser/char-buffer.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-buffer.cc -----------------------------------------===// +//===-- lib/parser/char-buffer.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/char-set.cc b/lib/parser/char-set.cpp similarity index 90% rename from lib/parser/char-set.cc rename to lib/parser/char-set.cpp index c8a324f5ae9e..1390f5ca35f6 100644 --- a/lib/parser/char-set.cc +++ b/lib/parser/char-set.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-set.cc --------------------------------------------===// +//===-- lib/parser/char-set.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/characters.cc b/lib/parser/characters.cpp similarity index 99% rename from lib/parser/characters.cc rename to lib/parser/characters.cpp index d6fc0d6464f7..f4703565a7d4 100644 --- a/lib/parser/characters.cc +++ b/lib/parser/characters.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/characters.cc ------------------------------------------===// +//===-- lib/parser/characters.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/debug-parser.cc b/lib/parser/debug-parser.cpp similarity index 92% rename from lib/parser/debug-parser.cc rename to lib/parser/debug-parser.cpp index 97fdf2820141..4957ce1dcbd5 100644 --- a/lib/parser/debug-parser.cc +++ b/lib/parser/debug-parser.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/debug-parser.cc ----------------------------------------===// +//===-- lib/parser/debug-parser.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/executable-parsers.cc b/lib/parser/executable-parsers.cpp similarity index 99% rename from lib/parser/executable-parsers.cc rename to lib/parser/executable-parsers.cpp index a1557a0e0e46..72408f1da2f5 100644 --- a/lib/parser/executable-parsers.cc +++ b/lib/parser/executable-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/executable-parsers.cc ----------------------------------===// +//===-- lib/parser/executable-parsers.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/expr-parsers.cc b/lib/parser/expr-parsers.cpp similarity index 99% rename from lib/parser/expr-parsers.cc rename to lib/parser/expr-parsers.cpp index 14bc391691ee..11e94fc0da2d 100644 --- a/lib/parser/expr-parsers.cc +++ b/lib/parser/expr-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/expr-parsers.cc ----------------------------------------===// +//===-- lib/parser/expr-parsers.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/instrumented-parser.cc b/lib/parser/instrumented-parser.cpp similarity index 97% rename from lib/parser/instrumented-parser.cc rename to lib/parser/instrumented-parser.cpp index b845b22dffdd..fc5e14867032 100644 --- a/lib/parser/instrumented-parser.cc +++ b/lib/parser/instrumented-parser.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/instrumented-parser.cc ---------------------------------===// +//===-- lib/parser/instrumented-parser.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/io-parsers.cc b/lib/parser/io-parsers.cpp similarity index 99% rename from lib/parser/io-parsers.cc rename to lib/parser/io-parsers.cpp index 5488a32c4769..a9ccbb75566d 100644 --- a/lib/parser/io-parsers.cc +++ b/lib/parser/io-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/io-parsers.cc ------------------------------------------===// +//===-- lib/parser/io-parsers.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/message.cc b/lib/parser/message.cpp similarity index 99% rename from lib/parser/message.cc rename to lib/parser/message.cpp index 5589707effc2..2f5655eb1e48 100644 --- a/lib/parser/message.cc +++ b/lib/parser/message.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/message.cc ---------------------------------------------===// +//===-- lib/parser/message.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/openmp-parsers.cc b/lib/parser/openmp-parsers.cpp similarity index 99% rename from lib/parser/openmp-parsers.cc rename to lib/parser/openmp-parsers.cpp index 4a5a083747bd..076c3e8218a3 100644 --- a/lib/parser/openmp-parsers.cc +++ b/lib/parser/openmp-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/openmp-parsers.cc --------------------------------------===// +//===-- lib/parser/openmp-parsers.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/parse-tree.cc b/lib/parser/parse-tree.cpp similarity index 99% rename from lib/parser/parse-tree.cc rename to lib/parser/parse-tree.cpp index 6e0e017570b5..c412214b7432 100644 --- a/lib/parser/parse-tree.cc +++ b/lib/parser/parse-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/parse-tree.cc ------------------------------------------===// +//===-- lib/parser/parse-tree.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/parsing.cc b/lib/parser/parsing.cpp similarity index 98% rename from lib/parser/parsing.cc rename to lib/parser/parsing.cpp index c2b96ab8b383..ab39c2b1f25c 100644 --- a/lib/parser/parsing.cc +++ b/lib/parser/parsing.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/parsing.cc ---------------------------------------------===// +//===-- lib/parser/parsing.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/preprocessor.cc b/lib/parser/preprocessor.cpp similarity index 99% rename from lib/parser/preprocessor.cc rename to lib/parser/preprocessor.cpp index 270e72ca3735..cd5cee714af8 100644 --- a/lib/parser/preprocessor.cc +++ b/lib/parser/preprocessor.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/preprocessor.cc ----------------------------------------===// +//===-- lib/parser/preprocessor.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/prescan.cc b/lib/parser/prescan.cpp similarity index 99% rename from lib/parser/prescan.cc rename to lib/parser/prescan.cpp index 07f294a9eb77..679f136305cb 100644 --- a/lib/parser/prescan.cc +++ b/lib/parser/prescan.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/prescan.cc ---------------------------------------------===// +//===-- lib/parser/prescan.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/program-parsers.cc b/lib/parser/program-parsers.cpp similarity index 99% rename from lib/parser/program-parsers.cc rename to lib/parser/program-parsers.cpp index f19824248181..f0e4e69498b1 100644 --- a/lib/parser/program-parsers.cc +++ b/lib/parser/program-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/program-parsers.cc -------------------------------------===// +//===-- lib/parser/program-parsers.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/provenance.cc b/lib/parser/provenance.cpp similarity index 99% rename from lib/parser/provenance.cc rename to lib/parser/provenance.cpp index 9ad864e0da95..391e650e1b30 100644 --- a/lib/parser/provenance.cc +++ b/lib/parser/provenance.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/provenance.cc ------------------------------------------===// +//===-- lib/parser/provenance.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/source.cc b/lib/parser/source.cpp similarity index 99% rename from lib/parser/source.cc rename to lib/parser/source.cpp index 4e4c2736781f..e6635e9f10d5 100644 --- a/lib/parser/source.cc +++ b/lib/parser/source.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/source.cc ----------------------------------------------===// +//===-- lib/parser/source.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/token-sequence.cc b/lib/parser/token-sequence.cpp similarity index 99% rename from lib/parser/token-sequence.cc rename to lib/parser/token-sequence.cpp index d6337f475cec..3f984e1f3838 100644 --- a/lib/parser/token-sequence.cc +++ b/lib/parser/token-sequence.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/token-sequence.cc --------------------------------------===// +//===-- lib/parser/token-sequence.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/tools.cc b/lib/parser/tools.cpp similarity index 97% rename from lib/parser/tools.cc rename to lib/parser/tools.cpp index 1ef05b427a25..522bd3afc642 100644 --- a/lib/parser/tools.cc +++ b/lib/parser/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/tools.cc -----------------------------------------------===// +//===-- lib/parser/tools.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/unparse.cc b/lib/parser/unparse.cpp similarity index 99% rename from lib/parser/unparse.cc rename to lib/parser/unparse.cpp index 6550efa65880..82d52960f5f2 100644 --- a/lib/parser/unparse.cc +++ b/lib/parser/unparse.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/unparse.cc ---------------------------------------------===// +//===-- lib/parser/unparse.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/user-state.cc b/lib/parser/user-state.cpp similarity index 97% rename from lib/parser/user-state.cc rename to lib/parser/user-state.cpp index b23b8f579c84..bd84463d9ab0 100644 --- a/lib/parser/user-state.cc +++ b/lib/parser/user-state.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/user-state.cc ------------------------------------------===// +//===-- lib/parser/user-state.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/CMakeLists.txt b/lib/semantics/CMakeLists.txt index 1f0fa359e11e..14e636e177d5 100644 --- a/lib/semantics/CMakeLists.txt +++ b/lib/semantics/CMakeLists.txt @@ -7,38 +7,38 @@ #===------------------------------------------------------------------------===# add_library(FortranSemantics - assignment.cc - attr.cc - canonicalize-do.cc - canonicalize-omp.cc - check-allocate.cc - check-arithmeticif.cc - check-call.cc - check-coarray.cc - check-deallocate.cc - check-declarations.cc - check-do.cc - check-if-stmt.cc - check-io.cc - check-nullify.cc - check-omp-structure.cc - check-purity.cc - check-return.cc - check-stop.cc - expression.cc - mod-file.cc - pointer-assignment.cc - program-tree.cc - resolve-labels.cc - resolve-names.cc - resolve-names-utils.cc - rewrite-parse-tree.cc - scope.cc - semantics.cc - symbol.cc - tools.cc - type.cc - unparse-with-symbols.cc + assignment.cpp + attr.cpp + canonicalize-do.cpp + canonicalize-omp.cpp + check-allocate.cpp + check-arithmeticif.cpp + check-call.cpp + check-coarray.cpp + check-deallocate.cpp + check-declarations.cpp + check-do.cpp + check-if-stmt.cpp + check-io.cpp + check-nullify.cpp + check-omp-structure.cpp + check-purity.cpp + check-return.cpp + check-stop.cpp + expression.cpp + mod-file.cpp + pointer-assignment.cpp + program-tree.cpp + resolve-labels.cpp + resolve-names.cpp + resolve-names-utils.cpp + rewrite-parse-tree.cpp + scope.cpp + semantics.cpp + symbol.cpp + tools.cpp + type.cpp + unparse-with-symbols.cpp ) target_link_libraries(FortranSemantics diff --git a/lib/semantics/assignment.cc b/lib/semantics/assignment.cpp similarity index 99% rename from lib/semantics/assignment.cc rename to lib/semantics/assignment.cpp index 70f2f655a41b..362df0465480 100644 --- a/lib/semantics/assignment.cc +++ b/lib/semantics/assignment.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/assignment.cc ---------------------------------------===// +//===-- lib/semantics/assignment.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -139,7 +139,7 @@ class AssignmentContext { }; void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { - // Assignment statement analysis is in expression.cc where user-defined + // Assignment statement analysis is in expression.cpp where user-defined // assignments can be recognized and replaced. if (const evaluate::Assignment * asst{GetAssignment(stmt)}) { if (const auto *intrinsicAsst{ diff --git a/lib/semantics/attr.cc b/lib/semantics/attr.cpp similarity index 95% rename from lib/semantics/attr.cc rename to lib/semantics/attr.cpp index 25d9201f3fb7..65623442a2d6 100644 --- a/lib/semantics/attr.cc +++ b/lib/semantics/attr.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/attr.cc ---------------------------------------------===// +//===-- lib/semantics/attr.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/canonicalize-do.cc b/lib/semantics/canonicalize-do.cpp similarity index 98% rename from lib/semantics/canonicalize-do.cc rename to lib/semantics/canonicalize-do.cpp index b4ac3771e8e8..45353c97c1f8 100644 --- a/lib/semantics/canonicalize-do.cc +++ b/lib/semantics/canonicalize-do.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-do.cc ----------------------------------===// +//===-- lib/semantics/canonicalize-do.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/canonicalize-omp.cc b/lib/semantics/canonicalize-omp.cpp similarity index 98% rename from lib/semantics/canonicalize-omp.cc rename to lib/semantics/canonicalize-omp.cpp index cbd26d19b763..1af2f3be6277 100644 --- a/lib/semantics/canonicalize-omp.cc +++ b/lib/semantics/canonicalize-omp.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-omp.cc ---------------------------------===// +//===-- lib/semantics/canonicalize-omp.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-allocate.cc b/lib/semantics/check-allocate.cpp similarity index 99% rename from lib/semantics/check-allocate.cc rename to lib/semantics/check-allocate.cpp index 4db1434f5cee..83f3ae9ebfc6 100644 --- a/lib/semantics/check-allocate.cc +++ b/lib/semantics/check-allocate.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-allocate.cc -----------------------------------===// +//===-- lib/semantics/check-allocate.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-arithmeticif.cc b/lib/semantics/check-arithmeticif.cpp similarity index 96% rename from lib/semantics/check-arithmeticif.cc rename to lib/semantics/check-arithmeticif.cpp index fd293ce5e3c8..d0b08a3e062a 100644 --- a/lib/semantics/check-arithmeticif.cc +++ b/lib/semantics/check-arithmeticif.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-arithmeticif.cc -------------------------------===// +//===-- lib/semantics/check-arithmeticif.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-call.cc b/lib/semantics/check-call.cpp similarity index 99% rename from lib/semantics/check-call.cc rename to lib/semantics/check-call.cpp index 164dbc86f898..2afd00f4627e 100644 --- a/lib/semantics/check-call.cc +++ b/lib/semantics/check-call.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-call.cc ---------------------------------------===// +//===-- lib/semantics/check-call.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-coarray.cc b/lib/semantics/check-coarray.cpp similarity index 98% rename from lib/semantics/check-coarray.cc rename to lib/semantics/check-coarray.cpp index 44f8cf4e89c0..0e43314de40d 100644 --- a/lib/semantics/check-coarray.cc +++ b/lib/semantics/check-coarray.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-coarray.cc ------------------------------------===// +//===-- lib/semantics/check-coarray.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-deallocate.cc b/lib/semantics/check-deallocate.cpp similarity index 97% rename from lib/semantics/check-deallocate.cc rename to lib/semantics/check-deallocate.cpp index 7e66fcdfbdbf..3f48fb4cd03f 100644 --- a/lib/semantics/check-deallocate.cc +++ b/lib/semantics/check-deallocate.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-deallocate.cc ---------------------------------===// +//===-- lib/semantics/check-deallocate.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-declarations.cc b/lib/semantics/check-declarations.cpp similarity index 99% rename from lib/semantics/check-declarations.cc rename to lib/semantics/check-declarations.cpp index a3352d94f907..a96bc6fb056b 100644 --- a/lib/semantics/check-declarations.cc +++ b/lib/semantics/check-declarations.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-declarations.cc -------------------------------===// +//===-- lib/semantics/check-declarations.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -1074,7 +1074,7 @@ void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) { } } } - // TODO: Move C8106 (&al.) checks here from resolve-names-utils.cc + // TODO: Move C8106 (&al.) checks here from resolve-names-utils.cpp } void CheckHelper::CheckBlockData(const Scope &scope) { diff --git a/lib/semantics/check-do.cc b/lib/semantics/check-do.cpp similarity index 99% rename from lib/semantics/check-do.cc rename to lib/semantics/check-do.cpp index 8112c631a12c..96c8ba73f35a 100644 --- a/lib/semantics/check-do.cc +++ b/lib/semantics/check-do.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-do.cc -----------------------------------------===// +//===-- lib/semantics/check-do.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-if-stmt.cc b/lib/semantics/check-if-stmt.cpp similarity index 93% rename from lib/semantics/check-if-stmt.cc rename to lib/semantics/check-if-stmt.cpp index ec423e35301c..589caf2abb7e 100644 --- a/lib/semantics/check-if-stmt.cc +++ b/lib/semantics/check-if-stmt.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-if-stmt.cc ------------------------------------===// +//===-- lib/semantics/check-if-stmt.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-io.cc b/lib/semantics/check-io.cpp similarity index 99% rename from lib/semantics/check-io.cc rename to lib/semantics/check-io.cpp index dc6ef9ef240f..6f824a3164a1 100644 --- a/lib/semantics/check-io.cc +++ b/lib/semantics/check-io.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-io.cc -----------------------------------------===// +//===-- lib/semantics/check-io.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-nullify.cc b/lib/semantics/check-nullify.cpp similarity index 97% rename from lib/semantics/check-nullify.cc rename to lib/semantics/check-nullify.cpp index 9951a1627686..06a551ffe656 100644 --- a/lib/semantics/check-nullify.cc +++ b/lib/semantics/check-nullify.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-nullify.cc ------------------------------------===// +//===-- lib/semantics/check-nullify.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-omp-structure.cc b/lib/semantics/check-omp-structure.cpp similarity index 99% rename from lib/semantics/check-omp-structure.cc rename to lib/semantics/check-omp-structure.cpp index 15b51d3bcdb2..8d22d422e909 100644 --- a/lib/semantics/check-omp-structure.cc +++ b/lib/semantics/check-omp-structure.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-omp-structure.cc ------------------------------===// +//===-- lib/semantics/check-omp-structure.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-purity.cc b/lib/semantics/check-purity.cpp similarity index 97% rename from lib/semantics/check-purity.cc rename to lib/semantics/check-purity.cpp index 541696f4d807..986a22ad0080 100644 --- a/lib/semantics/check-purity.cc +++ b/lib/semantics/check-purity.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-purity.cc -------------------------------------===// +//===-- lib/semantics/check-purity.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-return.cc b/lib/semantics/check-return.cpp similarity index 96% rename from lib/semantics/check-return.cc rename to lib/semantics/check-return.cpp index fc2f2cfccb1f..0fb3b6159aa8 100644 --- a/lib/semantics/check-return.cc +++ b/lib/semantics/check-return.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-return.cc -------------------------------------===// +//===-- lib/semantics/check-return.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-stop.cc b/lib/semantics/check-stop.cpp similarity index 95% rename from lib/semantics/check-stop.cc rename to lib/semantics/check-stop.cpp index 0cf56e9a6085..105f0df27b4d 100644 --- a/lib/semantics/check-stop.cc +++ b/lib/semantics/check-stop.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-stop.cc ---------------------------------------===// +//===-- lib/semantics/check-stop.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/expression.cc b/lib/semantics/expression.cpp similarity index 99% rename from lib/semantics/expression.cc rename to lib/semantics/expression.cpp index 3334bf27dce2..59593ae90e8f 100644 --- a/lib/semantics/expression.cc +++ b/lib/semantics/expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/expression.cc ---------------------------------------===// +//===-- lib/semantics/expression.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/mod-file.cc b/lib/semantics/mod-file.cpp similarity index 99% rename from lib/semantics/mod-file.cc rename to lib/semantics/mod-file.cpp index e0a18b28ede0..857c52b2b461 100644 --- a/lib/semantics/mod-file.cc +++ b/lib/semantics/mod-file.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/mod-file.cc -----------------------------------------===// +//===-- lib/semantics/mod-file.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/pointer-assignment.cc b/lib/semantics/pointer-assignment.cpp similarity index 99% rename from lib/semantics/pointer-assignment.cc rename to lib/semantics/pointer-assignment.cpp index bf93bdba3998..8e111eff4061 100644 --- a/lib/semantics/pointer-assignment.cc +++ b/lib/semantics/pointer-assignment.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/pointer-assignment.cc -------------------------------===// +//===-- lib/semantics/pointer-assignment.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/program-tree.cc b/lib/semantics/program-tree.cpp similarity index 99% rename from lib/semantics/program-tree.cc rename to lib/semantics/program-tree.cpp index f20819132ef4..74381e18cf42 100644 --- a/lib/semantics/program-tree.cc +++ b/lib/semantics/program-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/program-tree.cc -------------------------------------===// +//===-- lib/semantics/program-tree.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/resolve-labels.cc b/lib/semantics/resolve-labels.cpp similarity index 99% rename from lib/semantics/resolve-labels.cc rename to lib/semantics/resolve-labels.cpp index 0c09fb41c335..87cacb8376c3 100644 --- a/lib/semantics/resolve-labels.cc +++ b/lib/semantics/resolve-labels.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-labels.cc -----------------------------------===// +//===-- lib/semantics/resolve-labels.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/resolve-names-utils.cc b/lib/semantics/resolve-names-utils.cpp similarity index 99% rename from lib/semantics/resolve-names-utils.cc rename to lib/semantics/resolve-names-utils.cpp index dd8c85d9ffa4..3b1a0b6cd824 100644 --- a/lib/semantics/resolve-names-utils.cc +++ b/lib/semantics/resolve-names-utils.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names-utils.cc ------------------------------===// +//===-- lib/semantics/resolve-names-utils.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/resolve-names-utils.h b/lib/semantics/resolve-names-utils.h index 0036cb7eb0b7..b0748c263016 100644 --- a/lib/semantics/resolve-names-utils.h +++ b/lib/semantics/resolve-names-utils.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_RESOLVE_NAMES_UTILS_H_ #define FORTRAN_SEMANTICS_RESOLVE_NAMES_UTILS_H_ -// Utility functions and class for use in resolve-names.cc. +// Utility functions and class for use in resolve-names.cpp. #include "flang/parser/message.h" #include "flang/semantics/scope.h" diff --git a/lib/semantics/resolve-names.cc b/lib/semantics/resolve-names.cpp similarity index 99% rename from lib/semantics/resolve-names.cc rename to lib/semantics/resolve-names.cpp index ae20be1e942b..e6a9b7f9bfeb 100644 --- a/lib/semantics/resolve-names.cc +++ b/lib/semantics/resolve-names.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names.cc ------------------------------------===// +//===-- lib/semantics/resolve-names.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -3762,7 +3762,7 @@ bool DeclarationVisitor::Pre(const parser::StructureConstructor &x) { // N.B C7102 is implicitly enforced by having inaccessible types not // being found in resolution. - // More constraints are enforced in expression.cc so that they + // More constraints are enforced in expression.cpp so that they // can apply to structure constructors that have been converted // from misparsed function references. for (const auto &component : diff --git a/lib/semantics/rewrite-parse-tree.cc b/lib/semantics/rewrite-parse-tree.cpp similarity index 98% rename from lib/semantics/rewrite-parse-tree.cc rename to lib/semantics/rewrite-parse-tree.cpp index 932a6ecd1e3e..37c55376d1a7 100644 --- a/lib/semantics/rewrite-parse-tree.cc +++ b/lib/semantics/rewrite-parse-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/rewrite-parse-tree.cc -------------------------------===// +//===-- lib/semantics/rewrite-parse-tree.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/scope.cc b/lib/semantics/scope.cpp similarity index 99% rename from lib/semantics/scope.cc rename to lib/semantics/scope.cpp index 23166b15cfeb..96400b626f03 100644 --- a/lib/semantics/scope.cc +++ b/lib/semantics/scope.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/scope.cc --------------------------------------------===// +//===-- lib/semantics/scope.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/semantics.cc b/lib/semantics/semantics.cpp similarity index 99% rename from lib/semantics/semantics.cc rename to lib/semantics/semantics.cpp index 7642366ccd17..1f9958e08469 100644 --- a/lib/semantics/semantics.cc +++ b/lib/semantics/semantics.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/semantics.cc ----------------------------------------===// +//===-- lib/semantics/semantics.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/symbol.cc b/lib/semantics/symbol.cpp similarity index 99% rename from lib/semantics/symbol.cc rename to lib/semantics/symbol.cpp index 6393fa115709..d513962b021c 100644 --- a/lib/semantics/symbol.cc +++ b/lib/semantics/symbol.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/symbol.cc -------------------------------------------===// +//===-- lib/semantics/symbol.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/tools.cc b/lib/semantics/tools.cpp similarity index 99% rename from lib/semantics/tools.cc rename to lib/semantics/tools.cpp index 24b87b496e2d..a39ff409db04 100644 --- a/lib/semantics/tools.cc +++ b/lib/semantics/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/tools.cc --------------------------------------------===// +//===-- lib/semantics/tools.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/type.cc b/lib/semantics/type.cpp similarity index 99% rename from lib/semantics/type.cc rename to lib/semantics/type.cpp index 0f196dbeb369..b216261f8e4a 100644 --- a/lib/semantics/type.cc +++ b/lib/semantics/type.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/type.cc ---------------------------------------------===// +//===-- lib/semantics/type.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/unparse-with-symbols.cc b/lib/semantics/unparse-with-symbols.cpp similarity index 98% rename from lib/semantics/unparse-with-symbols.cc rename to lib/semantics/unparse-with-symbols.cpp index 70ed49d4fa22..1af5c2a5638f 100644 --- a/lib/semantics/unparse-with-symbols.cc +++ b/lib/semantics/unparse-with-symbols.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/unparse-with-symbols.cc -----------------------------===// +//===-- lib/semantics/unparse-with-symbols.cpp ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ce42973a7b9c..a9a71b9f17ce 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -7,21 +7,21 @@ #===------------------------------------------------------------------------===# add_library(FortranRuntime - ISO_Fortran_binding.cc - derived-type.cc - descriptor.cc - file.cc - format.cc - io-api.cc - io-error.cc - io-stmt.cc - main.cc - memory.cc - stop.cc - terminator.cc - tools.cc - transformational.cc - type-code.cc + ISO_Fortran_binding.cpp + derived-type.cpp + descriptor.cpp + file.cpp + format.cpp + io-api.cpp + io-error.cpp + io-stmt.cpp + main.cpp + memory.cpp + stop.cpp + terminator.cpp + tools.cpp + transformational.cpp + type-code.cpp ) target_link_libraries(FortranRuntime diff --git a/runtime/ISO_Fortran_binding.cc b/runtime/ISO_Fortran_binding.cpp similarity index 99% rename from runtime/ISO_Fortran_binding.cc rename to runtime/ISO_Fortran_binding.cpp index 2a72aeac1db8..bcb0d055a740 100644 --- a/runtime/ISO_Fortran_binding.cc +++ b/runtime/ISO_Fortran_binding.cpp @@ -1,4 +1,4 @@ -//===-- runtime/ISO_Fortran_binding.cc ------------------------------------===// +//===-- runtime/ISO_Fortran_binding.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/derived-type.cc b/runtime/derived-type.cpp similarity index 97% rename from runtime/derived-type.cc rename to runtime/derived-type.cpp index 69b3292ea0c3..fb0e5a88277d 100644 --- a/runtime/derived-type.cc +++ b/runtime/derived-type.cpp @@ -1,4 +1,4 @@ -//===-- runtime/derived-type.cc -------------------------------------------===// +//===-- runtime/derived-type.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/descriptor.cc b/runtime/descriptor.cpp similarity index 98% rename from runtime/descriptor.cc rename to runtime/descriptor.cpp index e412df410dd7..c8895dd6d246 100644 --- a/runtime/descriptor.cc +++ b/runtime/descriptor.cpp @@ -1,4 +1,4 @@ -//===-- runtime/descriptor.cc ---------------------------------------------===// +//===-- runtime/descriptor.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/file.cc b/runtime/file.cpp similarity index 99% rename from runtime/file.cc rename to runtime/file.cpp index c99db0976690..3936bcdfcfa6 100644 --- a/runtime/file.cc +++ b/runtime/file.cpp @@ -1,4 +1,4 @@ -//===-- runtime/file.cc -----------------------------------------*- C++ -*-===// +//===-- runtime/file.cpp ----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/format.cc b/runtime/format.cpp similarity index 99% rename from runtime/format.cc rename to runtime/format.cpp index 2dd76e389bb3..46ad2ea2b5d0 100644 --- a/runtime/format.cc +++ b/runtime/format.cpp @@ -1,4 +1,4 @@ -//===-- runtime/format.cc ---------------------------------------*- C++ -*-===// +//===-- runtime/format.cpp --------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/io-api.cc b/runtime/io-api.cpp similarity index 94% rename from runtime/io-api.cc rename to runtime/io-api.cpp index a140e0e8cf99..56e6dff932c5 100644 --- a/runtime/io-api.cc +++ b/runtime/io-api.cpp @@ -1,4 +1,4 @@ -//===-- runtime/io.cc -------------------------------------------*- C++ -*-===// +//===-- runtime/io.cpp ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/io-error.cc b/runtime/io-error.cpp similarity index 96% rename from runtime/io-error.cc rename to runtime/io-error.cpp index ccf143ae7b1b..52fff2d10cfa 100644 --- a/runtime/io-error.cc +++ b/runtime/io-error.cpp @@ -1,4 +1,4 @@ -//===-- runtime/io-error.cc -------------------------------------*- C++ -*-===// +//===-- runtime/io-error.cpp ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/io-stmt.cc b/runtime/io-stmt.cpp similarity index 97% rename from runtime/io-stmt.cc rename to runtime/io-stmt.cpp index 221cd2de9c97..617e3e697a14 100644 --- a/runtime/io-stmt.cc +++ b/runtime/io-stmt.cpp @@ -1,4 +1,4 @@ -//===-- runtime/io-stmt.cc --------------------------------------*- C++ -*-===// +//===-- runtime/io-stmt.cpp -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/main.cc b/runtime/main.cpp similarity index 96% rename from runtime/main.cc rename to runtime/main.cpp index ce36bc2b9bfa..25b3b02001bf 100644 --- a/runtime/main.cc +++ b/runtime/main.cpp @@ -1,4 +1,4 @@ -//===-- runtime/main.cc -----------------------------------------*- C++ -*-===// +//===-- runtime/main.cpp ----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/memory.cc b/runtime/memory.cpp similarity index 92% rename from runtime/memory.cc rename to runtime/memory.cpp index ab7c63c24b74..e2d997caea78 100644 --- a/runtime/memory.cc +++ b/runtime/memory.cpp @@ -1,4 +1,4 @@ -//===-- runtime/memory.cc ---------------------------------------*- C++ -*-===// +//===-- runtime/memory.cpp --------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/stop.cc b/runtime/stop.cpp similarity index 96% rename from runtime/stop.cc rename to runtime/stop.cpp index a1f421a0c3a6..8bf665f82512 100644 --- a/runtime/stop.cc +++ b/runtime/stop.cpp @@ -1,4 +1,4 @@ -//===-- runtime/stop.cc -----------------------------------------*- C++ -*-===// +//===-- runtime/stop.cpp ----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/terminator.cc b/runtime/terminator.cpp similarity index 95% rename from runtime/terminator.cc rename to runtime/terminator.cpp index e2e9b7b327b1..c516af3c854a 100644 --- a/runtime/terminator.cc +++ b/runtime/terminator.cpp @@ -1,4 +1,4 @@ -//===-- runtime/terminate.cc ------------------------------------*- C++ -*-===// +//===-- runtime/terminate.cpp -----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/tools.cc b/runtime/tools.cpp similarity index 95% rename from runtime/tools.cc rename to runtime/tools.cpp index 8a9980fa50c7..43a0f68b0fe8 100644 --- a/runtime/tools.cc +++ b/runtime/tools.cpp @@ -1,4 +1,4 @@ -//===-- runtime/tools.cc ----------------------------------------*- C++ -*-===// +//===-- runtime/tools.cpp ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/transformational.cc b/runtime/transformational.cpp similarity index 98% rename from runtime/transformational.cc rename to runtime/transformational.cpp index bd408dec02ee..42a05e627962 100644 --- a/runtime/transformational.cc +++ b/runtime/transformational.cpp @@ -1,4 +1,4 @@ -//===-- runtime/transformational.cc ---------------------------------------===// +//===-- runtime/transformational.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/runtime/type-code.cc b/runtime/type-code.cpp similarity index 96% rename from runtime/type-code.cc rename to runtime/type-code.cpp index d4fbced71a37..8b57cdb4e5a7 100644 --- a/runtime/type-code.cc +++ b/runtime/type-code.cpp @@ -1,4 +1,4 @@ -//===-- runtime/type-code.cc ----------------------------------------------===// +//===-- runtime/type-code.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/test/decimal/CMakeLists.txt b/test/decimal/CMakeLists.txt index 19050bafba45..d1210ba1f4ac 100644 --- a/test/decimal/CMakeLists.txt +++ b/test/decimal/CMakeLists.txt @@ -7,7 +7,7 @@ #===------------------------------------------------------------------------===# add_executable(quick-sanity-test - quick-sanity-test.cc + quick-sanity-test.cpp ) target_link_libraries(quick-sanity-test @@ -15,7 +15,7 @@ target_link_libraries(quick-sanity-test ) add_executable(thorough-test - thorough-test.cc + thorough-test.cpp ) target_link_libraries(thorough-test diff --git a/test/decimal/quick-sanity-test.cc b/test/decimal/quick-sanity-test.cpp similarity index 100% rename from test/decimal/quick-sanity-test.cc rename to test/decimal/quick-sanity-test.cpp diff --git a/test/decimal/thorough-test.cc b/test/decimal/thorough-test.cpp similarity index 100% rename from test/decimal/thorough-test.cc rename to test/decimal/thorough-test.cpp diff --git a/test/evaluate/CMakeLists.txt b/test/evaluate/CMakeLists.txt index 47b1f780543f..088280f9277b 100644 --- a/test/evaluate/CMakeLists.txt +++ b/test/evaluate/CMakeLists.txt @@ -7,12 +7,12 @@ #===------------------------------------------------------------------------===# add_library(FortranEvaluateTesting - testing.cc - fp-testing.cc + testing.cpp + fp-testing.cpp ) add_executable(leading-zero-bit-count-test - leading-zero-bit-count.cc + leading-zero-bit-count.cpp ) target_link_libraries(leading-zero-bit-count-test @@ -20,7 +20,7 @@ target_link_libraries(leading-zero-bit-count-test ) add_executable(bit-population-count-test - bit-population-count.cc + bit-population-count.cpp ) target_link_libraries(bit-population-count-test @@ -28,7 +28,7 @@ target_link_libraries(bit-population-count-test ) add_executable(uint128-test - uint128.cc + uint128.cpp ) target_link_libraries(uint128-test @@ -41,7 +41,7 @@ add_test(Leadz leading-zero-bit-count-test) add_test(PopPar bit-population-count-test) add_executable(expression-test - expression.cc + expression.cpp ) target_link_libraries(expression-test @@ -52,7 +52,7 @@ target_link_libraries(expression-test ) add_executable(integer-test - integer.cc + integer.cpp ) target_link_libraries(integer-test @@ -62,7 +62,7 @@ target_link_libraries(integer-test ) add_executable(intrinsics-test - intrinsics.cc + intrinsics.cpp ) target_link_libraries(intrinsics-test @@ -75,7 +75,7 @@ target_link_libraries(intrinsics-test ) add_executable(logical-test - logical.cc + logical.cpp ) target_link_libraries(logical-test @@ -88,9 +88,9 @@ target_link_libraries(logical-test # IEEE exception flags (different use of the word "exception") # in the actual hardware floating-point status register, so ensure that # C++ exceptions are enabled for this test. -set_source_files_properties(real.cc PROPERTIES COMPILE_FLAGS -fexceptions) +set_source_files_properties(real.cpp PROPERTIES COMPILE_FLAGS -fexceptions) add_executable(real-test - real.cc + real.cpp ) target_link_libraries(real-test @@ -102,7 +102,7 @@ target_link_libraries(real-test ) add_executable(reshape-test - reshape.cc + reshape.cpp ) target_link_libraries(reshape-test @@ -113,7 +113,7 @@ target_link_libraries(reshape-test ) add_executable(ISO-Fortran-binding-test - ISO-Fortran-binding.cc + ISO-Fortran-binding.cpp ) target_link_libraries(ISO-Fortran-binding-test @@ -124,7 +124,7 @@ target_link_libraries(ISO-Fortran-binding-test ) add_executable(folding-test - folding.cc + folding.cpp ) target_link_libraries(folding-test diff --git a/test/evaluate/ISO-Fortran-binding.cc b/test/evaluate/ISO-Fortran-binding.cpp similarity index 100% rename from test/evaluate/ISO-Fortran-binding.cc rename to test/evaluate/ISO-Fortran-binding.cpp diff --git a/test/evaluate/bit-population-count.cc b/test/evaluate/bit-population-count.cpp similarity index 100% rename from test/evaluate/bit-population-count.cc rename to test/evaluate/bit-population-count.cpp diff --git a/test/evaluate/expression.cc b/test/evaluate/expression.cpp similarity index 100% rename from test/evaluate/expression.cc rename to test/evaluate/expression.cpp diff --git a/test/evaluate/folding.cc b/test/evaluate/folding.cpp similarity index 98% rename from test/evaluate/folding.cc rename to test/evaluate/folding.cpp index f72670ed60eb..bf68a74e0976 100644 --- a/test/evaluate/folding.cc +++ b/test/evaluate/folding.cpp @@ -41,7 +41,7 @@ static FunctionRef CreateIntrinsicElementalCall( } // Test flushSubnormalsToZero when folding with host runtime. -// Subnormal value flushing on host is handle in host.cc +// Subnormal value flushing on host is handle in host.cpp // HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment // Dummy host runtime functions where subnormal flushing matters diff --git a/test/evaluate/fp-testing.cc b/test/evaluate/fp-testing.cpp similarity index 100% rename from test/evaluate/fp-testing.cc rename to test/evaluate/fp-testing.cpp diff --git a/test/evaluate/integer.cc b/test/evaluate/integer.cpp similarity index 100% rename from test/evaluate/integer.cc rename to test/evaluate/integer.cpp diff --git a/test/evaluate/intrinsics.cc b/test/evaluate/intrinsics.cpp similarity index 100% rename from test/evaluate/intrinsics.cc rename to test/evaluate/intrinsics.cpp diff --git a/test/evaluate/leading-zero-bit-count.cc b/test/evaluate/leading-zero-bit-count.cpp similarity index 100% rename from test/evaluate/leading-zero-bit-count.cc rename to test/evaluate/leading-zero-bit-count.cpp diff --git a/test/evaluate/logical.cc b/test/evaluate/logical.cpp similarity index 100% rename from test/evaluate/logical.cc rename to test/evaluate/logical.cpp diff --git a/test/evaluate/real.cc b/test/evaluate/real.cpp similarity index 100% rename from test/evaluate/real.cc rename to test/evaluate/real.cpp diff --git a/test/evaluate/reshape.cc b/test/evaluate/reshape.cpp similarity index 100% rename from test/evaluate/reshape.cc rename to test/evaluate/reshape.cpp diff --git a/test/evaluate/testing.cc b/test/evaluate/testing.cpp similarity index 100% rename from test/evaluate/testing.cc rename to test/evaluate/testing.cpp diff --git a/test/evaluate/uint128.cc b/test/evaluate/uint128.cpp similarity index 100% rename from test/evaluate/uint128.cc rename to test/evaluate/uint128.cpp diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index 5cbc230d7eed..fda3776cc906 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -11,7 +11,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) endif() add_executable(format-test - format.cc + format.cpp ) target_link_libraries(format-test @@ -21,7 +21,7 @@ target_link_libraries(format-test add_test(Format format-test) add_executable(hello-world - hello.cc + hello.cpp ) target_link_libraries(hello-world diff --git a/test/runtime/format.cc b/test/runtime/format.cpp similarity index 98% rename from test/runtime/format.cc rename to test/runtime/format.cpp index 50381e85ed05..937a4434b8d7 100644 --- a/test/runtime/format.cc +++ b/test/runtime/format.cpp @@ -16,7 +16,7 @@ using Results = std::list; // Test harness context for format control struct TestFormatContext : virtual public Terminator, public FormatContext { - TestFormatContext() : Terminator{"format.cc", 1} {} + TestFormatContext() : Terminator{"format.cpp", 1} {} void Emit(const char *, std::size_t); void HandleSlash(int = 1); void HandleRelativePosition(int); diff --git a/test/runtime/hello.cc b/test/runtime/hello.cpp similarity index 100% rename from test/runtime/hello.cc rename to test/runtime/hello.cpp diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 1a4c97ec03a5..676549c95cf4 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -10,8 +10,8 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") add_executable(f18 - f18.cc - dump.cc + f18.cpp + dump.cpp ) target_link_libraries(f18 @@ -21,8 +21,8 @@ target_link_libraries(f18 ) add_executable(f18-parse-demo - f18-parse-demo.cc - stub-evaluate.cc + f18-parse-demo.cpp + stub-evaluate.cpp ) target_link_libraries(f18-parse-demo diff --git a/tools/f18/dump.cc b/tools/f18/dump.cpp similarity index 95% rename from tools/f18/dump.cc rename to tools/f18/dump.cpp index fd2c75e8f4b5..26f4d730f959 100644 --- a/tools/f18/dump.cc +++ b/tools/f18/dump.cpp @@ -1,4 +1,4 @@ -//===-- tools/f18/dump.cc -------------------------------------------------===// +//===-- tools/f18/dump.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/tools/f18/f18-parse-demo.cc b/tools/f18/f18-parse-demo.cpp similarity index 99% rename from tools/f18/f18-parse-demo.cc rename to tools/f18/f18-parse-demo.cpp index f65b47b78341..d52499e16416 100644 --- a/tools/f18/f18-parse-demo.cc +++ b/tools/f18/f18-parse-demo.cpp @@ -1,4 +1,4 @@ -//===-- tools/f18/f18-parse-demo.cc ---------------------------------------===// +//===-- tools/f18/f18-parse-demo.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,7 +17,7 @@ // always run, whatever the case of the source file extension. Unrecognized // options are passed through to the underlying Fortran compiler. // -// This program is actually a stripped-down variant of f18.cc, a temporary +// This program is actually a stripped-down variant of f18.cpp, a temporary // scaffolding compiler driver that can test some semantic passes of the // F18 compiler under development. diff --git a/tools/f18/f18.cc b/tools/f18/f18.cpp similarity index 99% rename from tools/f18/f18.cc rename to tools/f18/f18.cpp index 32a5cd4dce81..56f008ba6fe1 100644 --- a/tools/f18/f18.cc +++ b/tools/f18/f18.cpp @@ -1,4 +1,4 @@ -//===-- tools/f18/f18.cc --------------------------------------------------===// +//===-- tools/f18/f18.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/tools/f18/stub-evaluate.cc b/tools/f18/stub-evaluate.cpp similarity index 94% rename from tools/f18/stub-evaluate.cc rename to tools/f18/stub-evaluate.cpp index 0b2020383b71..99e2635430c0 100644 --- a/tools/f18/stub-evaluate.cc +++ b/tools/f18/stub-evaluate.cpp @@ -1,4 +1,4 @@ -//===-- tools/f18/stub-evaluate.cc ----------------------------------------===// +//===-- tools/f18/stub-evaluate.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. From 0c3c39d30e3f166a6a1303337c5fd7eead720fd0 Mon Sep 17 00:00:00 2001 From: "Jinxin (Brian) Yang" Date: Tue, 28 Jan 2020 12:51:35 -0800 Subject: [PATCH 013/345] [OpenMP] Name Resolution for OpenMP constructs (#940) This is an extended framework based on the previous work that addresses the NR on OpenMP directives/clauses (b2ea520). In this change: * New `OmpVisitor` is created (ResolveNamesVisitor derives from it) to create necessary scopes for certain OpenMP constructs. This is along with the regular Fortran NR process. * Old `OmpVisitor` is adjusted and converted to a standalone visitor-- `OmpAttributeVisitor`. This is used to walk through the OpenMP constructs and do the NR for variables on the OpenMP directives or data references within the OpenMP constructs. "Do the NR" here means that based on the NR results of the regular Fortran NR, fix the symbols of `Names` related to the OpenMP constructs. Note that there is an `OmpContext` in this visitor (similar to the one in `OmpStructureChecker`), this is necessary when dealing with the nested OpenMP constructs in the future. Given an OpenMP code: ``` real*8 a, b a = 1. b = 2. !$omp parallel private(a) a = 3. b = 4. !$omp end parallel print *, a, b end ``` w/o -fopenmp: ``` real*8 a, b !REF: /MainProgram1/a a = 1. !REF: /MainProgram1/b b = 2. !!!! OMP parallel !REF: /MainProgram1/a a = 3. !REF: /MainProgram1/b b = 4. !!!! OMP end parallel !REF: /MainProgram1/a !REF: /MainProgram1/b print *, a, b end ``` w/ -fopenmp: ``` real*8 a, b !REF: /MainProgram1/a a = 1. !REF: /MainProgram1/b b = 2. !$omp parallel private(a) <-- new Symbol for 'a' created !DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 3. <-- fix the old symbol with new Symbol in parallel scope !REF: /MainProgram1/b b = 4. <-- do nothing because by default it is shared in this scope !$omp end parallel !REF: /MainProgram1/a !REF: /MainProgram1/b print *, a, b end ``` Please note that this is a framework update, there are still many things on the TODO list for finishing the NR for OpenMP (based on the `OpenMP-semantics.md` design doc), which will be on top of this framework. Some TODO items: - Create a generic function to go through all the rules for deciding `predetermined`, `explicitly determined`, and `implicitly determined` data-sharing attributes. (This is the next biggest part) - Handle `Array Sections` and `Array or Structure Element`. - Take association into consideration for example Pointer association, `ASSOCIATE` construct, and etc. - Handle all the name resolution for directives/clauses that have `parser::Name`. * N.B. Extend `AddSourceRange` to apply to current and parent scopes - motivated by a few cases that need to call `AddSourceRange` for current & parent scopes; the extension should be safe - global scope is not included --- include/flang/parser/parse-tree.h | 6 + include/flang/semantics/symbol.h | 2 +- lib/parser/openmp-parsers.cpp | 24 +- lib/semantics/resolve-names.cpp | 679 ++++++++++++++++------- lib/semantics/scope.cpp | 5 +- test/semantics/CMakeLists.txt | 2 + test/semantics/omp-device-constructs.f90 | 2 +- test/semantics/omp-resolve05.f90 | 23 + test/semantics/omp-symbol07.f90 | 37 ++ 9 files changed, 569 insertions(+), 211 deletions(-) create mode 100644 test/semantics/omp-resolve05.f90 create mode 100644 test/semantics/omp-symbol07.f90 diff --git a/include/flang/parser/parse-tree.h b/include/flang/parser/parse-tree.h index 29638957f35b..57093e3903cb 100644 --- a/include/flang/parser/parse-tree.h +++ b/include/flang/parser/parse-tree.h @@ -3498,10 +3498,12 @@ struct OmpSectionsDirective { struct OmpBeginSectionsDirective { TUPLE_CLASS_BOILERPLATE(OmpBeginSectionsDirective); std::tuple t; + CharBlock source; }; struct OmpEndSectionsDirective { TUPLE_CLASS_BOILERPLATE(OmpEndSectionsDirective); std::tuple t; + CharBlock source; }; // [!$omp section] @@ -3742,21 +3744,25 @@ struct OpenMPStandaloneConstruct { struct OmpBeginLoopDirective { TUPLE_CLASS_BOILERPLATE(OmpBeginLoopDirective); std::tuple t; + CharBlock source; }; struct OmpEndLoopDirective { TUPLE_CLASS_BOILERPLATE(OmpEndLoopDirective); std::tuple t; + CharBlock source; }; struct OmpBeginBlockDirective { TUPLE_CLASS_BOILERPLATE(OmpBeginBlockDirective); std::tuple t; + CharBlock source; }; struct OmpEndBlockDirective { TUPLE_CLASS_BOILERPLATE(OmpEndBlockDirective); std::tuple t; + CharBlock source; }; struct OpenMPBlockConstruct { diff --git a/include/flang/semantics/symbol.h b/include/flang/semantics/symbol.h index 335e6ed936a5..faef1ca7af2b 100644 --- a/include/flang/semantics/symbol.h +++ b/include/flang/semantics/symbol.h @@ -467,7 +467,7 @@ class Symbol { // OpenMP miscellaneous flags OmpCommonBlock, OmpReduction, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, OmpFlushed, OmpCriticalLock, - OmpIfSpecified); + OmpIfSpecified, OmpNone); using Flags = common::EnumSet; const Scope &owner() const { return *owner_; } diff --git a/lib/parser/openmp-parsers.cpp b/lib/parser/openmp-parsers.cpp index 076c3e8218a3..fd1b96191b24 100644 --- a/lib/parser/openmp-parsers.cpp +++ b/lib/parser/openmp-parsers.cpp @@ -280,8 +280,8 @@ TYPE_PARSER(sourced(construct(first( pure(OmpLoopDirective::Directive::TeamsDistributeSimd), "TEAMS DISTRIBUTE" >> pure(OmpLoopDirective::Directive::TeamsDistribute))))) -TYPE_PARSER(construct( - sourced(Parser{}), Parser{})) +TYPE_PARSER(sourced(construct( + sourced(Parser{}), Parser{}))) // 2.14.1 construct-type-clause -> PARALLEL | SECTIONS | DO | TASKGROUP TYPE_PARSER(sourced(construct( @@ -345,8 +345,8 @@ TYPE_PARSER(construct( "TEAMS" >> pure(OmpBlockDirective::Directive::Teams), "WORKSHARE" >> pure(OmpBlockDirective::Directive::Workshare)))) -TYPE_PARSER(construct( - sourced(Parser{}), Parser{})) +TYPE_PARSER(sourced(construct( + sourced(Parser{}), Parser{}))) TYPE_PARSER(construct( "INITIALIZER" >> parenthesized("OMP_PRIV =" >> expr))) @@ -482,12 +482,12 @@ TYPE_PARSER(construct( pure(OmpSectionsDirective::Directive::ParallelSections)))) // OMP BEGIN and END SECTIONS Directive -TYPE_PARSER(construct( - sourced(Parser{}), Parser{})) +TYPE_PARSER(sourced(construct( + sourced(Parser{}), Parser{}))) TYPE_PARSER( - startOmpLine >> construct( + startOmpLine >> sourced(construct( sourced("END"_tok >> Parser{}), - Parser{})) + Parser{}))) // OMP SECTION-BLOCK TYPE_PARSER(maybe(startOmpLine >> "SECTION"_tok / endOmpLine) >> @@ -512,15 +512,15 @@ TYPE_CONTEXT_PARSER("OpenMP construct"_en_US, // END OMP Block directives TYPE_PARSER( - startOmpLine >> construct( + startOmpLine >> sourced(construct( sourced("END"_tok >> Parser{}), - Parser{})) + Parser{}))) // END OMP Loop directives TYPE_PARSER( - startOmpLine >> construct( + startOmpLine >> sourced(construct( sourced("END"_tok >> Parser{}), - Parser{})) + Parser{}))) TYPE_PARSER(construct( Parser{} / endOmpLine)) diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index e6a9b7f9bfeb..ca8e26700128 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -8,6 +8,7 @@ #include "resolve-names.h" #include "assignment.h" +#include "check-omp-structure.h" #include "mod-file.h" #include "program-tree.h" #include "resolve-names-utils.h" @@ -444,12 +445,7 @@ class ScopeHandler : public ImplicitRulesVisitor { template bool Pre(const parser::Statement &x) { messageHandler().set_currStmtSource(x.source); - for (auto *scope = currScope_; scope; scope = &scope->parent()) { - scope->AddSourceRange(x.source); - if (scope->IsGlobal()) { - break; - } - } + currScope_->AddSourceRange(x.source); return true; } template void Post(const parser::Statement &) { @@ -786,6 +782,8 @@ class DeclarationVisitor : public ArraySpecVisitor, void CheckExplicitInterface(const parser::Name &); void CheckBindings(const parser::TypeBoundProcedureStmt::WithoutInterface &); + const parser::Name *ResolveDesignator(const parser::Designator &); + protected: bool BeginDecl(); void EndDecl(); @@ -814,7 +812,6 @@ class DeclarationVisitor : public ArraySpecVisitor, // or nullptr in case of error. const parser::Name *ResolveStructureComponent( const parser::StructureComponent &); - const parser::Name *ResolveDesignator(const parser::Designator &); const parser::Name *ResolveDataRef(const parser::DataRef &); const parser::Name *ResolveVariable(const parser::Variable &); const parser::Name *ResolveName(const parser::Name &); @@ -1045,43 +1042,132 @@ class ConstructVisitor : public virtual DeclarationVisitor { void PopAssociation(); }; -// Resolve OpenMP construct entities and statement (TODO) entities +// Create scopes for OpenMP constructs class OmpVisitor : public virtual DeclarationVisitor { public: - static const parser::Name *GetDesignatorNameIfDataRef( - const parser::Designator &designator) { - const auto *dataRef{std::get_if(&designator.u)}; - return dataRef ? std::get_if(&dataRef->u) : nullptr; - } + void AddOmpSourceRange(const parser::CharBlock &); - bool Pre(const parser::OpenMPBlockConstruct &) { - PushScope(Scope::Kind::Block, nullptr); + static bool NeedsScope(const parser::OpenMPBlockConstruct &); + + bool Pre(const parser::OpenMPBlockConstruct &); + void Post(const parser::OpenMPBlockConstruct &); + bool Pre(const parser::OmpBeginBlockDirective &x) { + AddOmpSourceRange(x.source); return true; } - void Post(const parser::OpenMPBlockConstruct &) { PopScope(); } - bool Pre(const parser::OmpBeginBlockDirective &) { - ClearDataSharingAttributeObjects(); + void Post(const parser::OmpBeginBlockDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } + bool Pre(const parser::OmpEndBlockDirective &x) { + AddOmpSourceRange(x.source); return true; } + void Post(const parser::OmpEndBlockDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } bool Pre(const parser::OpenMPLoopConstruct &) { PushScope(Scope::Kind::Block, nullptr); return true; } void Post(const parser::OpenMPLoopConstruct &) { PopScope(); } - bool Pre(const parser::OmpBeginLoopDirective &) { - ClearDataSharingAttributeObjects(); + bool Pre(const parser::OmpBeginLoopDirective &x) { + AddOmpSourceRange(x.source); + return true; + } + void Post(const parser::OmpBeginLoopDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } + bool Pre(const parser::OmpEndLoopDirective &x) { + AddOmpSourceRange(x.source); + return true; + } + void Post(const parser::OmpEndLoopDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } + + bool Pre(const parser::OpenMPSectionsConstruct &) { + PushScope(Scope::Kind::Block, nullptr); + return true; + } + void Post(const parser::OpenMPSectionsConstruct &) { PopScope(); } + bool Pre(const parser::OmpBeginSectionsDirective &x) { + AddOmpSourceRange(x.source); + return true; + } + void Post(const parser::OmpBeginSectionsDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } + bool Pre(const parser::OmpEndSectionsDirective &x) { + AddOmpSourceRange(x.source); return true; } + void Post(const parser::OmpEndSectionsDirective &) { + messageHandler().set_currStmtSource(std::nullopt); + } +}; + +bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) { + const auto &beginBlockDir{std::get(x.t)}; + const auto &beginDir{std::get(beginBlockDir.t)}; + switch (beginDir.v) { + case parser::OmpBlockDirective::Directive::TargetData: + case parser::OmpBlockDirective::Directive::Master: + case parser::OmpBlockDirective::Directive::Ordered: return false; + default: return true; + } +} - bool Pre(const parser::OpenMPThreadprivate &x) { +void OmpVisitor::AddOmpSourceRange(const parser::CharBlock &source) { + messageHandler().set_currStmtSource(source); + currScope().AddSourceRange(source); +} + +bool OmpVisitor::Pre(const parser::OpenMPBlockConstruct &x) { + if (NeedsScope(x)) { PushScope(Scope::Kind::Block, nullptr); - const auto &list{std::get(x.t)}; - ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); + } + return true; +} + +void OmpVisitor::Post(const parser::OpenMPBlockConstruct &x) { + if (NeedsScope(x)) { PopScope(); - return false; } +} + +// Data-sharing and Data-mapping attributes for data-refs in OpenMP construct +class OmpAttributeVisitor { +public: + explicit OmpAttributeVisitor( + SemanticsContext &context, ResolveNamesVisitor &resolver) + : context_{context}, resolver_{resolver} {} + + template void Walk(const A &x) { parser::Walk(x, *this); } + + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + + bool Pre(const parser::OpenMPBlockConstruct &); + void Post(const parser::OpenMPBlockConstruct &) { PopContext(); } + void Post(const parser::OmpBeginBlockDirective &) { + GetContext().withinConstruct = true; + } + + bool Pre(const parser::OpenMPLoopConstruct &); + void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } + void Post(const parser::OmpBeginLoopDirective &) { + GetContext().withinConstruct = true; + } + + bool Pre(const parser::OpenMPSectionsConstruct &); + void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } + + bool Pre(const parser::OpenMPThreadprivate &); + void Post(const parser::OpenMPThreadprivate &) { PopContext(); } + // 2.15.3 Data-Sharing Attribute Clauses + void Post(const parser::OmpDefaultClause &); bool Pre(const parser::OmpClause::Shared &x) { ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); return false; @@ -1099,7 +1185,60 @@ class OmpVisitor : public virtual DeclarationVisitor { return false; } + void Post(const parser::Name &); + private: + struct OmpContext { + OmpContext(const parser::CharBlock &source, OmpDirective d, Scope &s) + : directiveSource{source}, directive{d}, scope{s} {} + parser::CharBlock directiveSource; + OmpDirective directive; + Scope &scope; + // TODO: default DSA is implicitly determined in different ways + Symbol::Flag defaultDSA{Symbol::Flag::OmpShared}; + // variables on Data-sharing attribute clauses + std::map objectWithDSA; + bool withinConstruct{false}; + }; + // back() is the top of the stack + OmpContext &GetContext() { + CHECK(!ompContext_.empty()); + return ompContext_.back(); + } + void PushContext(const parser::CharBlock &source, OmpDirective dir) { + ompContext_.emplace_back(source, dir, context_.FindScope(source)); + } + void PopContext() { ompContext_.pop_back(); } + void SetContextDirectiveSource(parser::CharBlock &dir) { + GetContext().directiveSource = dir; + } + void SetContextDirectiveEnum(OmpDirective dir) { + GetContext().directive = dir; + } + const Scope &currScope() { return GetContext().scope; } + void SetContextDefaultDSA(Symbol::Flag flag) { + GetContext().defaultDSA = flag; + } + void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { + GetContext().objectWithDSA.emplace(&symbol, flag); + } + bool IsObjectWithDSA(const Symbol &symbol) { + auto it{GetContext().objectWithDSA.find(&symbol)}; + return it != GetContext().objectWithDSA.end(); + } + + Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { + const auto pair{ + GetContext().scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; + return *pair.first->second; + } + + static const parser::Name *GetDesignatorNameIfDataRef( + const parser::Designator &designator) { + const auto *dataRef{std::get_if(&designator.u)}; + return dataRef ? std::get_if(&dataRef->u) : nullptr; + } + static constexpr Symbol::Flags dataSharingAttributeFlags{ Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, @@ -1121,185 +1260,23 @@ class OmpVisitor : public virtual DeclarationVisitor { } bool HasDataSharingAttributeObject(const Symbol &); - // TODO: resolve variables referenced in the OpenMP region void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); - Symbol &ResolveOmp(const parser::Name &, Symbol::Flag); - Symbol &ResolveOmp(Symbol &, Symbol::Flag); + Symbol *ResolveOmp(const parser::Name &, Symbol::Flag); + Symbol *ResolveOmp(Symbol &, Symbol::Flag); Symbol *ResolveOmpCommonBlockName(const parser::Name *); - Symbol &DeclarePrivateAccessEntity(const parser::Name &, Symbol::Flag); - Symbol &DeclarePrivateAccessEntity(Symbol &, Symbol::Flag); - Symbol &DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); - Symbol &DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); + Symbol *DeclarePrivateAccessEntity(const parser::Name &, Symbol::Flag); + Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag); + Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); + Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); void CheckMultipleAppearances( const parser::Name &, const Symbol &, Symbol::Flag); - SymbolSet dataSharingAttributeObjects_; // on one directive -}; - -bool OmpVisitor::HasDataSharingAttributeObject(const Symbol &object) { - auto it{dataSharingAttributeObjects_.find(object)}; - return it != dataSharingAttributeObjects_.end(); -} - -Symbol *OmpVisitor::ResolveOmpCommonBlockName(const parser::Name *name) { - if (auto *prev{name ? currScope().parent().FindCommonBlock(name->source) - : nullptr}) { - name->symbol = prev; - return prev; - } else { - return nullptr; - } -} -void OmpVisitor::ResolveOmpObjectList( - const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { - for (const auto &ompObject : ompObjectList.v) { - ResolveOmpObject(ompObject, ompFlag); - } -} - -void OmpVisitor::ResolveOmpObject( - const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { - std::visit( - common::visitors{ - [&](const parser::Designator &designator) { - if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { - auto &symbol{ResolveOmp(*name, ompFlag)}; - if (dataSharingAttributeFlags.test(ompFlag)) { - CheckMultipleAppearances(*name, symbol, ompFlag); - } - } else if (const auto *name{ResolveDesignator(designator)}; - name && name->symbol) { - // Array sections to be changed to substrings as needed - if (AnalyzeExpr(context(), designator)) { - if (std::holds_alternative(designator.u)) { - Say(designator.source, - "Substrings are not allowed on OpenMP " - "directives or clauses"_err_en_US); - return; - } - } - // other checks, more TBD - if (const auto *details{ - name->symbol->detailsIf()}) { - if (details->IsArray()) { - // TODO: check Array Sections - } else if (name->symbol->owner().IsDerivedType()) { - // TODO: check Structure Component - } - } - } - }, - [&](const parser::Name &name) { // common block - if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { - CheckMultipleAppearances( - name, *symbol, Symbol::Flag::OmpCommonBlock); - // 2.15.3 When a named common block appears in a list, it has the - // same meaning as if every explicit member of the common block - // appeared in the list - for (const Symbol &object : - symbol->get().objects()) { - Symbol &mutableObject{const_cast(object)}; - ResolveOmp(mutableObject, ompFlag); - } - } else { - Say(name.source, // 2.15.3 - "COMMON block must be declared in the same scoping unit " - "in which the OpenMP directive or clause appears"_err_en_US); - } - }, - }, - ompObject.u); -} - -Symbol &OmpVisitor::ResolveOmp(const parser::Name &name, Symbol::Flag ompFlag) { - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(name, ompFlag); - } else { - return DeclareOrMarkOtherAccessEntity(name, ompFlag); - } -} - -Symbol &OmpVisitor::ResolveOmp(Symbol &symbol, Symbol::Flag ompFlag) { - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(symbol, ompFlag); - } else { - return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); - } -} - -Symbol &OmpVisitor::DeclarePrivateAccessEntity( - const parser::Name &name, Symbol::Flag ompFlag) { - Symbol &prev{FindOrDeclareEnclosingEntity(name)}; - if (prev.owner() != currScope()) { - auto &symbol{MakeSymbol(name, HostAssocDetails{prev})}; - symbol.set(ompFlag); - name.symbol = &symbol; // override resolution to parent - return symbol; - } else { - prev.set(ompFlag); - return prev; - } -} - -Symbol &OmpVisitor::DeclarePrivateAccessEntity( - Symbol &object, Symbol::Flag ompFlag) { - if (object.owner() != currScope() && - !FindInScope(currScope(), object.name())) { - auto &symbol{MakeSymbol(object.name(), Attrs{}, HostAssocDetails{object})}; - symbol.set(ompFlag); - return symbol; - } else { - object.set(ompFlag); - return object; - } -} - -Symbol &OmpVisitor::DeclareOrMarkOtherAccessEntity( - const parser::Name &name, Symbol::Flag ompFlag) { - Symbol &prev{FindOrDeclareEnclosingEntity(name)}; - name.symbol = &prev; - if (ompFlagsRequireMark.test(ompFlag)) { - prev.set(ompFlag); - } - return prev; -} - -Symbol &OmpVisitor::DeclareOrMarkOtherAccessEntity( - Symbol &object, Symbol::Flag ompFlag) { - if (ompFlagsRequireMark.test(ompFlag)) { - object.set(ompFlag); - } - return object; -} - -static bool WithMultipleAppearancesException( - const Symbol &symbol, Symbol::Flag ompFlag) { - return (ompFlag == Symbol::Flag::OmpFirstPrivate && - symbol.test(Symbol::Flag::OmpLastPrivate)) || - (ompFlag == Symbol::Flag::OmpLastPrivate && - symbol.test(Symbol::Flag::OmpFirstPrivate)); -} - -void OmpVisitor::CheckMultipleAppearances( - const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { - const auto *target{&symbol}; - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - if (const auto *details{symbol.detailsIf()}) { - target = &details->symbol(); - } - } - if (HasDataSharingAttributeObject(*target) && - !WithMultipleAppearancesException(symbol, ompFlag)) { - Say(name.source, - "'%s' appears in more than one data-sharing clause " - "on the same OpenMP directive"_err_en_US, - name.ToString()); - } else { - AddDataSharingAttributeObject(*target); - } -} + SemanticsContext &context_; + ResolveNamesVisitor &resolver_; + std::vector ompContext_; // used as a stack +}; // Walk the parse tree and resolve names to symbols. class ResolveNamesVisitor : public virtual ScopeHandler, @@ -5730,6 +5707,7 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { ResolveSpecificationParts(root); FinishSpecificationParts(root); ResolveExecutionParts(root); + OmpAttributeVisitor{context(), *this}.Walk(x); return false; } @@ -5915,6 +5893,315 @@ class DeferredCheckVisitor { bool pushedScope_{false}; }; +bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { + const auto &beginBlockDir{std::get(x.t)}; + const auto &beginDir{std::get(beginBlockDir.t)}; + switch (beginDir.v) { + case parser::OmpBlockDirective::Directive::Master: + PushContext(beginDir.source, OmpDirective::MASTER); + break; + case parser::OmpBlockDirective::Directive::Ordered: + PushContext(beginDir.source, OmpDirective::ORDERED); + break; + case parser::OmpBlockDirective::Directive::Parallel: + PushContext(beginDir.source, OmpDirective::PARALLEL); + break; + case parser::OmpBlockDirective::Directive::Single: + PushContext(beginDir.source, OmpDirective::SINGLE); + break; + case parser::OmpBlockDirective::Directive::Target: + PushContext(beginDir.source, OmpDirective::TARGET); + break; + case parser::OmpBlockDirective::Directive::TargetData: + PushContext(beginDir.source, OmpDirective::TARGET_DATA); + break; + case parser::OmpBlockDirective::Directive::Task: + PushContext(beginDir.source, OmpDirective::TASK); + break; + case parser::OmpBlockDirective::Directive::Teams: + PushContext(beginDir.source, OmpDirective::TEAMS); + break; + case parser::OmpBlockDirective::Directive::Workshare: + PushContext(beginDir.source, OmpDirective::WORKSHARE); + break; + default: + // TODO others + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { + const auto &beginLoopDir{std::get(x.t)}; + const auto &beginDir{std::get(beginLoopDir.t)}; + switch (beginDir.v) { + case parser::OmpLoopDirective::Directive::Distribute: + PushContext(beginDir.source, OmpDirective::DISTRIBUTE); + break; + case parser::OmpLoopDirective::Directive::Do: + PushContext(beginDir.source, OmpDirective::DO); + break; + case parser::OmpLoopDirective::Directive::DoSimd: + PushContext(beginDir.source, OmpDirective::DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::ParallelDo: + PushContext(beginDir.source, OmpDirective::PARALLEL_DO); + break; + case parser::OmpLoopDirective::Directive::ParallelDoSimd: + PushContext(beginDir.source, OmpDirective::PARALLEL_DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::Simd: + PushContext(beginDir.source, OmpDirective::SIMD); + break; + case parser::OmpLoopDirective::Directive::Taskloop: + PushContext(beginDir.source, OmpDirective::TASKLOOP); + break; + case parser::OmpLoopDirective::Directive::TaskloopSimd: + PushContext(beginDir.source, OmpDirective::TASKLOOP_SIMD); + break; + default: + // TODO others + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { + const auto &beginSectionsDir{ + std::get(x.t)}; + const auto &beginDir{ + std::get(beginSectionsDir.t)}; + switch (beginDir.v) { + case parser::OmpSectionsDirective::Directive::ParallelSections: + PushContext(beginDir.source, OmpDirective::PARALLEL_SECTIONS); + break; + case parser::OmpSectionsDirective::Directive::Sections: + PushContext(beginDir.source, OmpDirective::SECTIONS); + break; + default: break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { + PushContext(x.source, OmpDirective::THREADPRIVATE); + const auto &list{std::get(x.t)}; + ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); + return false; +} + +void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { + if (!ompContext_.empty()) { + switch (x.v) { + case parser::OmpDefaultClause::Type::Private: + SetContextDefaultDSA(Symbol::Flag::OmpPrivate); + break; + case parser::OmpDefaultClause::Type::Firstprivate: + SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); + break; + case parser::OmpDefaultClause::Type::Shared: + SetContextDefaultDSA(Symbol::Flag::OmpShared); + break; + case parser::OmpDefaultClause::Type::None: + SetContextDefaultDSA(Symbol::Flag::OmpNone); + break; + } + } +} + +// For OpenMP constructs, check all the data-refs within the constructs +// and adjust the symbol for each Name if necessary +void OmpAttributeVisitor::Post(const parser::Name &name) { + auto *symbol{name.symbol}; + if (symbol && !ompContext_.empty() && GetContext().withinConstruct) { + if (!symbol->owner().IsDerivedType() && !symbol->has() && + !IsObjectWithDSA(*symbol)) { + // TODO: create a separate function to go through the rules for + // predetermined, explicitly determined, and implicitly + // determined data-sharing attributes (2.15.1.1). + if (Symbol * found{currScope().FindSymbol(name.source)}) { + if (IsObjectWithDSA(*found)) { + name.symbol = found; // adjust the symbol within region + } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { + context_.Say(name.source, + "The DEFAULT(NONE) clause requires that '%s' must be listed in " + "a data-sharing attribute clause"_err_en_US, + symbol->name()); + } + } + } + } // within OpenMP construct +} + +bool OmpAttributeVisitor::HasDataSharingAttributeObject(const Symbol &object) { + auto it{dataSharingAttributeObjects_.find(object)}; + return it != dataSharingAttributeObjects_.end(); +} + +Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( + const parser::Name *name) { + if (auto *prev{name + ? GetContext().scope.parent().FindCommonBlock(name->source) + : nullptr}) { + name->symbol = prev; + return prev; + } else { + return nullptr; + } +} + +void OmpAttributeVisitor::ResolveOmpObjectList( + const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { + for (const auto &ompObject : ompObjectList.v) { + ResolveOmpObject(ompObject, ompFlag); + } +} + +void OmpAttributeVisitor::ResolveOmpObject( + const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { + std::visit( + common::visitors{ + [&](const parser::Designator &designator) { + if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { + if (auto *symbol{ResolveOmp(*name, ompFlag)}) { + AddToContextObjectWithDSA(*symbol, ompFlag); + if (dataSharingAttributeFlags.test(ompFlag)) { + CheckMultipleAppearances(*name, *symbol, ompFlag); + } + } + } else if (const auto *designatorName{ + resolver_.ResolveDesignator(designator)}; + designatorName->symbol) { + // Array sections to be changed to substrings as needed + if (AnalyzeExpr(context_, designator)) { + if (std::holds_alternative(designator.u)) { + context_.Say(designator.source, + "Substrings are not allowed on OpenMP " + "directives or clauses"_err_en_US); + } + } + // other checks, more TBD + if (const auto *details{designatorName->symbol + ->detailsIf()}) { + if (details->IsArray()) { + // TODO: check Array Sections + } else if (designatorName->symbol->owner().IsDerivedType()) { + // TODO: check Structure Component + } + } + } + }, + [&](const parser::Name &name) { // common block + if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { + CheckMultipleAppearances( + name, *symbol, Symbol::Flag::OmpCommonBlock); + // 2.15.3 When a named common block appears in a list, it has the + // same meaning as if every explicit member of the common block + // appeared in the list + for (const Symbol &object : + symbol->get().objects()) { + Symbol &mutableObject{const_cast(object)}; + if (auto *resolvedObject{ResolveOmp(mutableObject, ompFlag)}) { + AddToContextObjectWithDSA(*resolvedObject, ompFlag); + } + } + } else { + context_.Say(name.source, // 2.15.3 + "COMMON block must be declared in the same scoping unit " + "in which the OpenMP directive or clause appears"_err_en_US); + } + }, + }, + ompObject.u); +} + +Symbol *OmpAttributeVisitor::ResolveOmp( + const parser::Name &name, Symbol::Flag ompFlag) { + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + return DeclarePrivateAccessEntity(name, ompFlag); + } else { + return DeclareOrMarkOtherAccessEntity(name, ompFlag); + } +} + +Symbol *OmpAttributeVisitor::ResolveOmp(Symbol &symbol, Symbol::Flag ompFlag) { + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + return DeclarePrivateAccessEntity(symbol, ompFlag); + } else { + return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); + } +} + +Symbol *OmpAttributeVisitor::DeclarePrivateAccessEntity( + const parser::Name &name, Symbol::Flag ompFlag) { + if (!name.symbol) { + return nullptr; // not resolved by Name Resolution step, do nothing + } + name.symbol = DeclarePrivateAccessEntity(*name.symbol, ompFlag); + return name.symbol; +} + +Symbol *OmpAttributeVisitor::DeclarePrivateAccessEntity( + Symbol &object, Symbol::Flag ompFlag) { + if (object.owner() != currScope()) { + auto &symbol{MakeAssocSymbol(object.name(), object)}; + symbol.set(ompFlag); + return &symbol; + } else { + object.set(ompFlag); + return &object; + } +} + +Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( + const parser::Name &name, Symbol::Flag ompFlag) { + Symbol *prev{currScope().FindSymbol(name.source)}; + if (!name.symbol || !prev) { + return nullptr; + } else if (prev != name.symbol) { + name.symbol = prev; + } + return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); +} + +Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( + Symbol &object, Symbol::Flag ompFlag) { + if (ompFlagsRequireMark.test(ompFlag)) { + object.set(ompFlag); + } + return &object; +} + +static bool WithMultipleAppearancesException( + const Symbol &symbol, Symbol::Flag ompFlag) { + return (ompFlag == Symbol::Flag::OmpFirstPrivate && + symbol.test(Symbol::Flag::OmpLastPrivate)) || + (ompFlag == Symbol::Flag::OmpLastPrivate && + symbol.test(Symbol::Flag::OmpFirstPrivate)); +} + +void OmpAttributeVisitor::CheckMultipleAppearances( + const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { + const auto *target{&symbol}; + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + if (const auto *details{symbol.detailsIf()}) { + target = &details->symbol(); + } + } + if (HasDataSharingAttributeObject(*target) && + !WithMultipleAppearancesException(symbol, ompFlag)) { + context_.Say(name.source, + "'%s' appears in more than one data-sharing clause " + "on the same OpenMP directive"_err_en_US, + name.ToString()); + } else { + AddDataSharingAttributeObject(*target); + } +} + // Perform checks and completions that need to happen after all of // the specification parts but before any of the execution parts. void ResolveNamesVisitor::FinishSpecificationParts(const ProgramTree &node) { diff --git a/lib/semantics/scope.cpp b/lib/semantics/scope.cpp index 96400b626f03..9db599560650 100644 --- a/lib/semantics/scope.cpp +++ b/lib/semantics/scope.cpp @@ -244,7 +244,10 @@ Scope *Scope::FindScope(parser::CharBlock source) { } void Scope::AddSourceRange(const parser::CharBlock &source) { - sourceRange_.ExtendToCover(source); + for (auto *scope = this; !scope->IsGlobal(); + scope = &scope->parent()) { + scope->sourceRange_.ExtendToCover(source); + } } std::ostream &operator<<(std::ostream &os, const Scope &scope) { diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index 1765b4367ef0..5dfbcb2bf928 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -163,6 +163,7 @@ set(ERROR_TESTS omp-resolve02.f90 omp-resolve03.f90 omp-resolve04.f90 + omp-resolve05.f90 omp-clause-validity01.f90 omp-loop-association.f90 # omp-nested01.f90 @@ -227,6 +228,7 @@ set(SYMBOL_TESTS omp-symbol04.f90 omp-symbol05.f90 omp-symbol06.f90 + omp-symbol07.f90 kinds01.f90 kinds03.f90 procinterface01.f90 diff --git a/test/semantics/omp-device-constructs.f90 b/test/semantics/omp-device-constructs.f90 index ca9468707253..118e49c9afc9 100644 --- a/test/semantics/omp-device-constructs.f90 +++ b/test/semantics/omp-device-constructs.f90 @@ -91,7 +91,7 @@ program main !$omp end teams !ERROR: At most one DEFAULT clause can appear on the TEAMS directive - !$omp teams default(shared) default(none) + !$omp teams default(shared) default(private) do i = 1, N a = 3.14 enddo diff --git a/test/semantics/omp-resolve05.f90 b/test/semantics/omp-resolve05.f90 new file mode 100644 index 000000000000..0ba4fd816d92 --- /dev/null +++ b/test/semantics/omp-resolve05.f90 @@ -0,0 +1,23 @@ +!OPTIONS: -fopenmp + +! 2.15.3 Data-Sharing Attribute Clauses +! 2.15.3.1 default Clause + +subroutine default_none() + integer a(3) + + A = 1 + B = 2 + !$omp parallel default(none) private(c) + !ERROR: The DEFAULT(NONE) clause requires that 'a' must be listed in a data-sharing attribute clause + A(1:2) = 3 + !ERROR: The DEFAULT(NONE) clause requires that 'b' must be listed in a data-sharing attribute clause + B = 4 + C = 5 + !$omp end parallel +end subroutine default_none + +program mm + call default_none() + !TODO: private, firstprivate, shared +end diff --git a/test/semantics/omp-symbol07.f90 b/test/semantics/omp-symbol07.f90 new file mode 100644 index 000000000000..170452959e01 --- /dev/null +++ b/test/semantics/omp-symbol07.f90 @@ -0,0 +1,37 @@ +!OPTIONS: -fopenmp + +! Generic tests +! 1. subroutine or function calls should not be fixed for DSA or DMA + +!DEF: /foo (Function) Subprogram REAL(4) +!DEF: /foo/rnum ObjectEntity REAL(4) +function foo(rnum) + !REF: /foo/rnum + real rnum + !REF: /foo/rnum + rnum = rnum+1. +end function foo +!DEF: /function_call_in_region EXTERNAL (Subroutine) Subprogram +subroutine function_call_in_region + implicit none + !DEF: /function_call_in_region/foo (Function) ProcEntity REAL(4) + real foo + !DEF: /function_call_in_region/a ObjectEntity REAL(4) + real :: a = 0. + !DEF: /function_call_in_region/b ObjectEntity REAL(4) + real :: b = 5. + !$omp parallel default(none) private(a) shared(b) + !DEF: /function_call_in_region/Block1/a (OmpPrivate) HostAssoc REAL(4) + !REF: /function_call_in_region/foo + !REF: /function_call_in_region/b + a = foo(b) + !$omp end parallel + !REF: /function_call_in_region/a + !REF: /function_call_in_region/b + print *, a, b +end subroutine function_call_in_region +!DEF: /mm MainProgram +program mm + !REF: /function_call_in_region + call function_call_in_region +end program mm From 3f37e0dbaf32f1127553be2149915cab1b93cdcf Mon Sep 17 00:00:00 2001 From: Jinxin Yang Date: Tue, 28 Jan 2020 13:58:14 -0800 Subject: [PATCH 014/345] Remove `default` case for OmpSectionsDirective (only two enum values) --- lib/semantics/resolve-names.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index ca8e26700128..5ab1424b246f 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -5980,7 +5980,6 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { case parser::OmpSectionsDirective::Directive::Sections: PushContext(beginDir.source, OmpDirective::SECTIONS); break; - default: break; } ClearDataSharingAttributeObjects(); return true; From d2294ff511aebd64889df57d02325bd6fcdf914a Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Mon, 27 Jan 2020 14:12:35 -0800 Subject: [PATCH 015/345] Semantic checks for deallocating entities with IMPURE FINAL procedures You cannot call an IMPURE procedure in a DO CONCURRENT construct. One way that can happen is if an entity with an IMPURE FINAL procedure gets deallocated. Similar to the checks for deallocating coarrays, there are three ways that an entity can get deallocated that are applicable to DO CONCURRENT constructs -- an actual DEALLOCATE statement, block exit, and assignment. This change depends on the utility function `HasImpureFinal()` in tools.h to determine if an entity has a derived type with an IMPURE FINAL procedure. In the course of testing this change, I realized that this check is incorrect, but the code specific to DO CONCURRENT is independent of the check, so I might as well implement it. --- include/flang/semantics/tools.h | 2 +- lib/semantics/check-do.cpp | 68 ++++++++++++++++++++------- lib/semantics/tools.cpp | 4 +- test/semantics/doconcurrent08.f90 | 76 +++++++++++++++++++++++++++++-- 4 files changed, 128 insertions(+), 22 deletions(-) diff --git a/include/flang/semantics/tools.h b/include/flang/semantics/tools.h index 5f56325cc913..59e41700c211 100644 --- a/include/flang/semantics/tools.h +++ b/include/flang/semantics/tools.h @@ -48,7 +48,7 @@ const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &); const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &); const DeclTypeSpec *FindParentTypeSpec(const Scope &); const DeclTypeSpec *FindParentTypeSpec(const Symbol &); - + // Return the Symbol of the variable of a construct association, if it exists const Symbol *GetAssociationRoot(const Symbol &); diff --git a/lib/semantics/check-do.cpp b/lib/semantics/check-do.cpp index 96c8ba73f35a..75acd1b887f6 100644 --- a/lib/semantics/check-do.cpp +++ b/lib/semantics/check-do.cpp @@ -95,15 +95,33 @@ class DoConcurrentBodyEnforce { return true; } + template bool Pre(const parser::UnlabeledStatement &stmt) { + currentStatementSourcePosition_ = stmt.source; + return true; + } + // C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT. // Deallocation can be caused by exiting a block that declares an allocatable // entity, assignment to an allocatable variable, or an actual DEALLOCATE // statement // // Note also that the deallocation of a derived type entity might cause the - // invocation of an IMPURE final subroutine. + // invocation of an IMPURE final subroutine. (C1139) // + // Only to be called for symbols with ObjectEntityDetails + static bool HasImpureFinal(const Symbol &symbol) { + if (const Symbol * root{GetAssociationRoot(symbol)}) { + CHECK(root->has()); + if (const DeclTypeSpec * symType{root->GetType()}) { + if (const DerivedTypeSpec * derived{symType->AsDerived()}) { + return semantics::HasImpureFinal(*derived); + } + } + } + return false; + } + // Predicate for deallocations caused by block exit and direct deallocation static bool DeallocateAll(const Symbol &) { return true; } @@ -143,6 +161,21 @@ class DoConcurrentBodyEnforce { return false; } + void SayDeallocateWithImpureFinal(const Symbol &entity, const char *reason) { + context_.SayWithDecl(entity, currentStatementSourcePosition_, + "Deallocation of an entity with an IMPURE FINAL procedure" + " caused by %s not allowed in DO CONCURRENT"_err_en_US, + reason); + } + + void SayDeallocateOfPolymorph( + parser::CharBlock location, const Symbol &entity, const char *reason) { + context_.SayWithDecl(entity, location, + "Deallocation of a polymorphic entity caused by %s" + " not allowed in DO CONCURRENT"_err_en_US, + reason); + } + // Deallocation caused by block exit // Allocatable entities and all of their allocatable subcomponents will be // deallocated. This test is different from the other two because it does @@ -154,16 +187,16 @@ class DoConcurrentBodyEnforce { const Scope &blockScope{context_.FindScope(endBlockStmt.source)}; const Scope &doScope{context_.FindScope(doConcurrentSourcePosition_)}; if (DoesScopeContain(&doScope, blockScope)) { + const char *reason{"block exit"}; for (auto &pair : blockScope) { - Symbol &entity{*pair.second}; + const Symbol &entity{*pair.second}; if (IsAllocatable(entity) && !entity.attrs().test(Attr::SAVE) && MightDeallocatePolymorphic(entity, DeallocateAll)) { - context_.SayWithDecl(entity, endBlockStmt.source, - "Deallocation of a polymorphic entity caused by block" - " exit not allowed in DO CONCURRENT"_err_en_US); + SayDeallocateOfPolymorph(endBlockStmt.source, entity, reason); + } + if (HasImpureFinal(entity)) { + SayDeallocateWithImpureFinal(entity, reason); } - // TODO: Check for deallocation of a variable with an IMPURE FINAL - // subroutine } } } @@ -173,12 +206,12 @@ class DoConcurrentBodyEnforce { void Post(const parser::AssignmentStmt &stmt) { const auto &variable{std::get(stmt.t)}; if (const Symbol * entity{GetLastName(variable).symbol}) { + const char *reason{"assignment"}; if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) { - context_.SayWithDecl(*entity, variable.GetSource(), - "Deallocation of a polymorphic entity caused by " - "assignment not allowed in DO CONCURRENT"_err_en_US); - // TODO: Check for deallocation of a variable with an IMPURE FINAL - // subroutine + SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason); + } + if (HasImpureFinal(*entity)) { + SayDeallocateWithImpureFinal(*entity, reason); } } } @@ -191,17 +224,18 @@ class DoConcurrentBodyEnforce { std::get>(stmt.t)}; for (const auto &allocateObject : allocateObjectList) { const parser::Name &name{GetLastName(allocateObject)}; + const char *reason{"a DEALLOCATE statement"}; if (name.symbol) { const Symbol &entity{*name.symbol}; const DeclTypeSpec *entityType{entity.GetType()}; if ((entityType && entityType->IsPolymorphic()) || // POINTER case MightDeallocatePolymorphic(entity, DeallocateAll)) { - context_.SayWithDecl(entity, currentStatementSourcePosition_, - "Deallocation of a polymorphic entity not allowed in DO" - " CONCURRENT"_err_en_US); + SayDeallocateOfPolymorph( + currentStatementSourcePosition_, entity, reason); + } + if (HasImpureFinal(entity)) { + SayDeallocateWithImpureFinal(entity, reason); } - // TODO: Check for deallocation of a variable with an IMPURE FINAL - // subroutine } } } diff --git a/lib/semantics/tools.cpp b/lib/semantics/tools.cpp index a39ff409db04..8e31a81a0573 100644 --- a/lib/semantics/tools.cpp +++ b/lib/semantics/tools.cpp @@ -508,7 +508,7 @@ const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) { return nullptr; } -// When an construct association maps to a variable, and that variable +// When a construct association maps to a variable, and that variable // is not an array with a vector-valued subscript, return the base // Symbol of that variable, else nullptr. Descends into other construct // associations when one associations maps to another. @@ -665,6 +665,8 @@ bool IsFinalizable(const DerivedTypeSpec &derived) { components.end(); } +// TODO The following function returns true for all types with FINAL procedures +// This is because we don't yet fill in the data for FinalProcDetails bool HasImpureFinal(const DerivedTypeSpec &derived) { ScopeComponentIterator components{derived}; return std::find_if( diff --git a/test/semantics/doconcurrent08.f90 b/test/semantics/doconcurrent08.f90 index b4b5d413e9a0..b42ab6160a9f 100644 --- a/test/semantics/doconcurrent08.f90 +++ b/test/semantics/doconcurrent08.f90 @@ -188,18 +188,88 @@ subroutine s3() do concurrent (i = 1:10) ! Bad because deallocation of a polymorphic entity -!ERROR: Deallocation of a polymorphic entity not allowed in DO CONCURRENT +!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT deallocate(polyVar) ! Bad, deallocation of an entity with a polymorphic component -!ERROR: Deallocation of a polymorphic entity not allowed in DO CONCURRENT +!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT deallocate(polyComponentVar) ! Bad, deallocation of a pointer to an entity with a polymorphic component -!ERROR: Deallocation of a polymorphic entity not allowed in DO CONCURRENT +!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT deallocate(pointerPolyComponentVar) ! Deallocation of a nonpolymorphic entity deallocate(nonPolyVar) end do end subroutine s3 + +module m2 + type :: impureFinal + contains + final :: impureSub + end type + + type :: pureFinal + contains + final :: pureSub + end type + + contains + + impure subroutine impureSub(x) + type(impureFinal), intent(in) :: x + end subroutine + + pure subroutine pureSub(x) + type(pureFinal), intent(in) :: x + end subroutine + + subroutine s4() + type(impureFinal), allocatable :: ifVar, ifvar1 + type(pureFinal), allocatable :: pfVar + allocate(ifVar) + allocate(ifVar1) + allocate(pfVar) + + ! OK for an ordinary DO loop + do i = 1,10 + if (i .eq. 1) deallocate(ifVar) + end do + + ! OK to invoke a PURE FINAL procedure in a DO CONCURRENT + ! This case does not work currently because the compiler's test for + ! HasImpureFinal() in .../lib/semantics/tools.cc doesn't work correctly +! do concurrent (i = 1:10) +! if (i .eq. 1) deallocate(pfVar) +! end do + + ! Error to invoke an IMPURE FINAL procedure in a DO CONCURRENT + do concurrent (i = 1:10) + !ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by a DEALLOCATE statement not allowed in DO CONCURRENT + if (i .eq. 1) deallocate(ifVar) + end do + + do concurrent (i = 1:10) + if (i .eq. 1) then + block + type(impureFinal), allocatable :: ifVar + allocate(ifVar) + ! Error here because exiting this scope causes the finalization of + !ifvar which causes the invocation of an IMPURE FINAL procedure + !ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by block exit not allowed in DO CONCURRENT + end block + end if + end do + + do concurrent (i = 1:10) + if (i .eq. 1) then + ! Error here because the assignment statement causes the finalization + ! of ifvar which causes the invocation of an IMPURE FINAL procedure +!ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by assignment not allowed in DO CONCURRENT + ifvar = ifvar1 + end if + end do + end subroutine s4 + +end module m2 From aa0a0887325bd1fc6c3a1ad40fc6711d2e458a1c Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 28 Jan 2020 15:06:03 -0800 Subject: [PATCH 016/345] Fix another bug checking simple contiguity The test still wasn't correct for structure components. If the last part-ref is a non-array or a single array element, but the whole ArrayRef has non-zero rank, it is not contiguous. Otherwise, if there are subscripts on the last part-ref they can be checked normally. Add some tests for cases that were previously failing, and also for cases with vector subscripts. --- lib/evaluate/check-expression.cpp | 29 +++++++++++++++++++-------- test/semantics/assign03.f90 | 33 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/lib/evaluate/check-expression.cpp b/lib/evaluate/check-expression.cpp index 34b9025abf25..b809211dbdb4 100644 --- a/lib/evaluate/check-expression.cpp +++ b/lib/evaluate/check-expression.cpp @@ -277,11 +277,18 @@ class IsSimplyContiguousHelper } Result operator()(const ArrayRef &x) const { - return (x.base().IsSymbol() || x.base().Rank() == 0) && - CheckSubscripts(x.subscript()) && (*this)(x.base()); + const auto &symbol{x.GetLastSymbol()}; + if (!(*this)(symbol)) { + return false; + } else if (auto rank{CheckSubscripts(x.subscript())}) { + // a(:)%b(1,1) is not contiguous; a(1)%b(:,:) is + return *rank > 0 || x.Rank() == 0; + } else { + return false; + } } Result operator()(const CoarrayRef &x) const { - return CheckSubscripts(x.subscript()); + return CheckSubscripts(x.subscript()).has_value(); } Result operator()(const Component &x) const { return x.base().Rank() == 0 && (*this)(x.GetLastSymbol()); @@ -304,24 +311,30 @@ class IsSimplyContiguousHelper } private: - static bool CheckSubscripts(const std::vector &subscript) { + // If the subscripts can possibly be on a simply-contiguous array reference, + // return the rank. + static std::optional CheckSubscripts( + const std::vector &subscript) { bool anyTriplet{false}; + int rank{0}; for (auto j{subscript.size()}; j-- > 0;) { if (const auto *triplet{std::get_if(&subscript[j].u)}) { if (!triplet->IsStrideOne()) { - return false; + return std::nullopt; } else if (anyTriplet) { if (triplet->lower() || triplet->upper()) { - return false; // all triplets before the last one must be just ":" + // all triplets before the last one must be just ":" + return std::nullopt; } } else { anyTriplet = true; } + ++rank; } else if (anyTriplet || subscript[j].Rank() > 0) { - return false; + return std::nullopt; } } - return true; + return rank; } const IntrinsicProcTable &table_; diff --git a/test/semantics/assign03.f90 b/test/semantics/assign03.f90 index 08070fd3ac55..7c4895368c52 100644 --- a/test/semantics/assign03.f90 +++ b/test/semantics/assign03.f90 @@ -142,12 +142,22 @@ subroutine s10 end type type(t), target :: x type(t), target :: y(10,10) + integer :: v(10) p(1:16) => x%a + p(1:8) => x%a(:,3:4) p(1:1) => x%b ! We treat scalars as simply contiguous + p(1:1) => x%a(1,1) + p(1:1) => y(1,1)%a(1,1) + p(1:1) => y(:,1)%a(1,1) ! Rank 1 RHS !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous p(1:4) => x%a(::2,::2) !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous p(1:100) => y(:,:)%b + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + p(1:100) => y(:,:)%a(1,1) + !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous + !ERROR: An array section with a vector subscript may not be a pointer target + p(1:4) => x%a(:,v) end subroutine s11 @@ -155,8 +165,31 @@ subroutine s11 complex, pointer :: p(:) real, pointer :: q(:) p(1:100) => x(:,:) + q(1:10) => x(1,:)%im !ERROR: Pointer bounds remapping target must have rank 1 or be simply contiguous q(1:100) => x(:,:)%re end + ! Check is_contiguous, which is usually the same as when pointer bounds + ! remapping is used. If it's not simply contiguous it's not constant so + ! an error is reported. + subroutine s12 + integer, pointer :: p(:) + type :: t + integer :: a(4, 4) + integer :: b + end type + type(t), target :: x + type(t), target :: y(10,10) + integer :: v(10) + logical, parameter :: l1 = is_contiguous(x%a(:,:)) + logical, parameter :: l2 = is_contiguous(y(1,1)%a(1,1)) + !ERROR: Must be a constant value + logical, parameter :: l3 = is_contiguous(y(:,1)%a(1,1)) + !ERROR: Must be a constant value + logical, parameter :: l4 = is_contiguous(x%a(:,v)) + !ERROR: Must be a constant value + logical, parameter :: l5 = is_contiguous(y(v,1)%a(1,1)) + end + end From 889f90913f280f285eb9960a0e0baadfd43a555f Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Wed, 15 Jan 2020 16:25:26 -0800 Subject: [PATCH 017/345] Explanation of how to implement a semantic check This is the story of implementing semantic checks for passing DO variables to functions with dummy arguments with INTENT(OUT) or INTENT(INOUT). --- documentation/ImplementingASemanticCheck.md | 833 ++++++++++++++++++++ 1 file changed, 833 insertions(+) create mode 100644 documentation/ImplementingASemanticCheck.md diff --git a/documentation/ImplementingASemanticCheck.md b/documentation/ImplementingASemanticCheck.md new file mode 100644 index 000000000000..cc5ff7f62667 --- /dev/null +++ b/documentation/ImplementingASemanticCheck.md @@ -0,0 +1,833 @@ + +# Introduction +I recently added a semantic check to the f18 compiler front end. This document +describes my thought process and the resulting implementation. + +For more information about the compiler, start with the +[compiler overview](Overview.md). + +# Problem definition + +In the 2018 Fortran standard, section 11.1.7.4.3, paragraph 2, states that: + +``` +Except for the incrementation of the DO variable that occurs in step (3), the DO variable +shall neither be redefined nor become undefined while the DO construct is active. +``` +One of the ways that DO variables might be redefined is if they are passed to +functions with dummy arguments whose `INTENT` is `INTENT(OUT)` or +`INTENT(INOUT)`. I implemented this semantic check. Specifically, I changed +the compiler to emit an error message if an active DO variable was passed to a +dummy argument of a FUNCTION with INTENT(OUT). Similarly, I had the compiler +emit a warning if an active DO variable was passed to a dummy argument with +INTENT(INOUT). Previously, I had implemented similar checks for SUBROUTINE +calls. + +# Creating a test + +My first step was to create a test case to cause the problem. I called it testfun.f90 and used it to check the behavior of other Fortran compilers. Here's the initial version: + +```fortran + subroutine s() + Integer :: ivar, jvar + + do ivar = 1, 10 + jvar = intentOutFunc(ivar) ! Error since ivar is a DO variable + end do + + contains + function intentOutFunc(dummyArg) + integer, intent(out) :: dummyArg + integer :: intentOutFunc + + dummyArg = 216 + end function intentOutFunc + end subroutine s +``` + +I verified that other Fortran compilers produced an error message at the point +of the call to `intentOutFunc()`: + +```fortran + jvar = intentOutFunc(ivar) ! Error since ivar is a DO variable +``` + + +I also used this program to produce a parse tree for the program using the command: +```bash + f18 -fdebug-dump-parse-tree -fparse-only testfun.f90 +``` + +Here's the relevant fragment of the parse tree produced by the compiler: + +``` +| | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct +| | | NonLabelDoStmt +| | | | LoopControl -> LoopBounds +| | | | | Scalar -> Name = 'ivar' +| | | | | Scalar -> Expr = '1_4' +| | | | | | LiteralConstant -> IntLiteralConstant = '1' +| | | | | Scalar -> Expr = '10_4' +| | | | | | LiteralConstant -> IntLiteralConstant = '10' +| | | Block +| | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'jvar=intentoutfunc(ivar)' +| | | | | Variable -> Designator -> DataRef -> Name = 'jvar' +| | | | | Expr = 'intentoutfunc(ivar)' +| | | | | | FunctionReference -> Call +| | | | | | | ProcedureDesignator -> Name = 'intentoutfunc' +| | | | | | | ActualArgSpec +| | | | | | | | ActualArg -> Expr = 'ivar' +| | | | | | | | | Designator -> DataRef -> Name = 'ivar' +| | | EndDoStmt -> +``` + +Note that this fragment of the tree only shows four `parser::Expr` nodes, +but the full parse tree also contained a fifth `parser::Expr` node for the +constant 216 in the statement: + +```fortran + dummyArg = 216 +``` +# Analysis and implementation planning + +I then considered what I needed to do. I needed to detect situations where an +active DO variable was passed to a dummy argument with `INTENT(OUT)` or +`INTENT(INOUT)`. Once I detected such a situation, I needed to produce a +message that highlighted the erroneous source code. + +## Deciding where to add the code to the compiler +This new semantic check would depend on several types of information -- the +parse tree, source code location information, symbols, and expressions. Thus I +needed to put my new code in a place in the compiler after the parse tree had +been created, name resolution had already happened, and expression semantic +checking had already taken place. + +Most semantic checks for statements are implemented by walking the parse tree +and performing analysis on the nodes they visit. My plan was to use this +method. The infrastructure for walking the parse tree for statement semantic +checking is implemented in the files `lib/semantics/semantics.cpp`. +Here's a fragment of the declaration of the framework's parse tree visitor from +`lib/semantics/semantics.cpp`: + +```C++ + // A parse tree visitor that calls Enter/Leave functions from each checker + // class C supplied as template parameters. Enter is called before the node's + // children are visited, Leave is called after. No two checkers may have the + // same Enter or Leave function. Each checker must be constructible from + // SemanticsContext and have BaseChecker as a virtual base class. + template class SemanticsVisitor : public virtual C... { + public: + using C::Enter...; + using C::Leave...; + using BaseChecker::Enter; + using BaseChecker::Leave; + SemanticsVisitor(SemanticsContext &context) + : C{context}..., context_{context} {} + ... + +``` + +Since FUNCTION calls are a kind of expression, I was planning to base my +implementation on the contents of `parser::Expr` nodes. I would need to define +either an `Enter()` or `Leave()` function whose parameter was a `parser::Expr` +node. Here's the declaration I put into `lib/semantics/check-do.h`: + +```C++ + void Leave(const parser::Expr &); +``` +The `Enter()` functions get called at the time the node is first visited -- +that is, before its children. The `Leave()` function gets called after the +children are visited. For my check the visitation order didn't matter, so I +arbitrarily chose to implement the `Leave()` function to visit the parse tree +node. + +Since my semantic check was focused on DO CONCURRENT statements, I added it to +the file `lib/semantics/check-do.cpp` where most of the semantic checking for +DO statements already lived. + +## Taking advantage of prior work +When implementing a similar check for SUBROUTINE calls, I created a utility +functions in `lib/semantics/semantics.cpp` to emit messages if +a symbol corresponding to an active DO variable was being potentially modified: + +```C++ + void WarnDoVarRedefine(const parser::CharBlock &location, const Symbol &var); + void CheckDoVarRedefine(const parser::CharBlock &location, const Symbol &var); +``` + +The first function is intended for dummy arguments of `INTENT(INOUT)` and +the second for `INTENT(OUT)`. + +Thus I needed three pieces of +information -- +1. the source location of the erroneous text, +2. the `INTENT` of the associated dummy argument, and +3. the relevant symbol passed as the actual argument. + +The first and third are needed since they're required to call the utility +functions. The second is needed to determine whether to call them. + +## Finding the source location +The source code location information that I'd need for the error message must +come from the parse tree. I looked in the file +`include/flang/parser/parse-tree.h` and determined that a `struct Expr` +contained source location information since it had the field `CharBlock +source`. Thus, if I visited a `parser::Expr` node, I could get the source +location information for the associated expression. + +## Determining the `INTENT` +I knew that I could find the `INTENT` of the dummy argument associated with the +actual argument from the function called `dummyIntent()` in the class +`evaluate::ActualArgument` in the file `include/flang/evaluate/call.h`. So +if I could find an `evaluate::ActualArgument` in an expression, I could + determine the `INTENT` of the associated dummy argument. I knew that it was + valid to call `dummyIntent()` because the data on which `dummyIntent()` + depends is established during semantic processing for expressions, and the + semantic processing for expressions happens before semantic checking for DO + constructs. + +In my prior work on checking the INTENT of arguments for SUBROUTINE calls, +the parse tree held a node for the call (a `parser::CallStmt`) that contained +an `evaluate::ProcedureRef` node. +```C++ + struct CallStmt { + WRAPPER_CLASS_BOILERPLATE(CallStmt, Call); + mutable std::unique_ptr> + typedCall; // filled by semantics + }; +``` +The `evaluate::ProcedureRef` contains a list of `evaluate::ActualArgument` +nodes. I could then find the INTENT of a dummy argument from the +`evaluate::ActualArgument` node. + +For a FUNCTION call, though, there is no similar way to get from a parse tree +node to an `evaluate::ProcedureRef` node. But I knew that there was an +existing framework used in DO construct semantic checking that traversed an +`evaluate::Expr` node collecting `semantics::Symbol` nodes. I guessed that I'd +be able to use a similar framework to traverse an `evaluate::Expr` node to +find all of the `evaluate::ActualArgument` nodes. + +Note that there are two distinct data types in the compiler called `Expr`. One +is in the `parser` namespace. `parser::Expr` is defined in the file +`include/flang/parser/parse-tree.h`. It represents a parsed expression that +maps directly to the source code and has fields that specify any operators in +the expression, the operands, and the source position of the expression. + +The second `Expr` type is in the `evaluate` namespace. The `evaluate` +namespace contains many types associated with semantic checking of expressions. +`evaluate::Expr` is defined in the file `include/flang/evaluate/expression.h`. +It represents an expression after it has undergone semantic checking and +contains information that is only available after semantic analysis. This +information includes the Fortran type of the expression, whether it's a +reference to a function, whether it's an actual argument, etc. After an +expression has undergone semantic analysis, the field `typedExpr` in the +`parser::Expr` node is filled in with a pointer to the analyzed expression in +`evaluate::Expr`. + +All of the declarations associated with both FUNCTION and SUBROUTINE calls are +in `include/flang/evaluate/call.h`. An `evaluate::FunctionRef` inherits from +an `evaluate::ProcedureRef` which contains the list of +`evaluate::ActualArgument` nodes. But the relationship between an +`evaluate::FunctionRef` node and its associated arguments is not relevant. I +only needed to find the `evaluate::ActualArgument` nodes in an expression. +They hold all of the information I needed. + +So my plan was to start with the `parser::Expr` node and extract its +associated `evaluate::Expr` field. I would then traverse the +`evaluate::Expr` tree collecting all of the `evaluate::ActualArgument` +nodes. I would look at each of these nodes to determine the `INTENT` of +the associated dummy argument. + +This combination of the traversal framework and `dummyIntent()` would give +me the `INTENT` of all of the dummy arguments in a FUNCTION call. Thus, I +would have the second piece of information I needed. + +## Determining if the actual argument is a variable +I also guessed that I could determine if the `evaluate::ActualArgument` +consisted of a variable. + +Once I had a symbol for the variable, I could call one of the functions: +```C++ + void WarnDoVarRedefine(const parser::CharBlock &, const Symbol &); + void CheckDoVarRedefine(const parser::CharBlock &, const Symbol &); +``` +to emit the messages. + +If my plans worked out, this would give me the three pieces of information I +needed -- the source location of the erroneous text, the `INTENT` of the dummy +argument, and a symbol that I could use to determine whether the actual +argument was an active DO variable. + +# Implementation + +## Adding a parse tree visitor +I started my implementation by adding a visitor for `parser::Expr` nodes. +Since this analysis is part of DO construct checking, I did this in +`lib/semantics/check-do.cpp`. I added a print statement to the visitor to +verify that my new code was actually getting executed. + +In `lib/semantics/check-do.h`, I added the declaration for the visitor: + +```C++ + void Leave(const parser::Expr &); +``` + +In `lib/semantics/check-do.cpp`, I added an (almost empty) implementation: + +```C++ + void DoChecker::Leave(const parser::Expr &) { + std::cout << "In Leave for parser::Expr\n"; + } +``` + +I then built the compiler with these changes and ran it on my test program. +This time, I made sure to invoke semantic checking. Here's the command I used: +```bash + f18 -fdebug-resolve-names -fdebug-dump-parse-tree -funparse-with-symbols testfun.f90 +``` + +This produced the output: + +``` + In Leave for parser::Expr + In Leave for parser::Expr + In Leave for parser::Expr + In Leave for parser::Expr + In Leave for parser::Expr +``` + +This made sense since the parse tree contained five `parser::Expr` nodes. +So far, so good. Note that a `parse::Expr` node has a field with the +source position of the associated expression (`CharBlock source`). So I +now had one of the three pieces of information needed to detect and report +errors. + +## Collecting the actual arguments +To get the `INTENT` of the dummy arguments and the `semantics::Symbol` associated with the +actual argument, I needed to find all of the actual arguments embedded in an +expression that contained a FUNCTION call. So my next step was to write the +framework to walk the `evaluate::Expr` to gather all of the +`evaluate::ActualArgument` nodes. The code that I planned to model it on +was the existing infrastructure that collected all of the `semantics::Symbol` nodes from an +`evaluate::Expr`. I found this implementation in +`lib/evaluate/tools.cpp`: + +```C++ + struct CollectSymbolsHelper + : public SetTraverse { + using Base = SetTraverse; + CollectSymbolsHelper() : Base{*this} {} + using Base::operator(); + semantics::SymbolSet operator()(const Symbol &symbol) const { + return {symbol}; + } + }; + template semantics::SymbolSet CollectSymbols(const A &x) { + return CollectSymbolsHelper{}(x); + } +``` + +Note that the `CollectSymbols()` function returns a `semantics::Symbolset`, +which is declared in `include/flang/semantics/symbol.h`: + +```C++ + using SymbolSet = std::set; +``` + +This infrastructure yields a collection based on `std::set<>`. Using an +`std::set<>` means that if the same object is inserted twice, the +collection only gets one copy. This was the behavior that I wanted. + +Here's a sample invocation of `CollectSymbols()` that I found: +```C++ + if (const auto *expr{GetExpr(parsedExpr)}) { + for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { +``` + +I noted that a `SymbolSet` did not actually contain an +`std::set`. This wasn't surprising since we don't want to put the +full `semantics::Symbol` objects into the set. Ideally, we would be able to create an +`std::set` (a set of C++ references to symbols). But C++ doesn't +support sets that contain references. This limitation is part of the rationale +for the f18 implementation of type `common::Reference`, which is defined in + `include/flang/common/reference.h`. + +`SymbolRef`, the specialization of the template `common::Reference` for +`semantics::Symbol`, is declared in the file +`include/flang/semantics/symbol.h`: + +```C++ + using SymbolRef = common::Reference; +``` + +So to implement something that would collect `evaluate::ActualArgument` +nodes from an `evaluate::Expr`, I first defined the required types +`ActualArgumentRef` and `ActualArgumentSet`. Since these are being +used exclusively for DO construct semantic checking (currently), I put their +definitions into `lib/semantics/check-do.cpp`: + + +```C++ + namespace Fortran::evaluate { + using ActualArgumentRef = common::Reference; + } + + + using ActualArgumentSet = std::set; +``` + +Since `ActualArgument` is in the namespace `evaluate`, I put the +definition for `ActualArgumentRef` in that namespace, too. + +I then modeled the code to create an `ActualArgumentSet` after the code to +collect a `SymbolSet` and put it into `lib/semantics/check-do.cpp`: + + +```C++ + struct CollectActualArgumentsHelper + : public evaluate::SetTraverse { + using Base = SetTraverse; + CollectActualArgumentsHelper() : Base{*this} {} + using Base::operator(); + ActualArgumentSet operator()(const evaluate::ActualArgument &arg) const { + return ActualArgumentSet{arg}; + } + }; + + template ActualArgumentSet CollectActualArguments(const A &x) { + return CollectActualArgumentsHelper{}(x); + } + + template ActualArgumentSet CollectActualArguments(const SomeExpr &); +``` + +Unfortunately, when I tried to build this code, I got an error message saying +`std::set` requires the `<` operator to be defined for its contents. +To fix this, I added a definition for `<`. I didn't care how `<` was +defined, so I just used the address of the object: + +```C++ + inline bool operator<(ActualArgumentRef x, ActualArgumentRef y) { + return &*x < &*y; + } +``` + +I was surprised when this did not make the error message saying that I needed +the `<` operator go away. Eventually, I figured out that the definition of +the `<` operator needed to be in the `evaluate` namespace. Once I put +it there, everything compiled successfully. Here's the code that worked: + +```C++ + namespace Fortran::evaluate { + using ActualArgumentRef = common::Reference; + + inline bool operator<(ActualArgumentRef x, ActualArgumentRef y) { + return &*x < &*y; + } + } +``` + +I then modified my visitor for the parser::Expr to invoke my new collection +framework. To verify that it was actually doing something, I printed out the +number of `evaluate::ActualArgument` nodes that it collected. Note the +call to `GetExpr()` in the invocation of `CollectActualArguments()`. I +modeled this on similar code that collected a `SymbolSet` described above: + +```C++ + void DoChecker::Leave(const parser::Expr &parsedExpr) { + std::cout << "In Leave for parser::Expr\n"; + ActualArgumentSet argSet{CollectActualArguments(GetExpr(parsedExpr))}; + std::cout << "Number of arguments: " << argSet.size() << "\n"; + } +``` + +I compiled and tested this code on my little test program. Here's the output that I got: +``` + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 1 + In Leave for parser::Expr + Number of arguments: 0 +``` + +So most of the `parser::Expr`nodes contained no actual arguments, but the +fourth expression in the parse tree walk contained a single argument. This may +seem wrong since the third `parser::Expr` node in the file contains the +`FunctionReference` node along with the arguments that we're gathering. +But since the tree walk function is being called upon leaving a +`parser::Expr` node, the function visits the `parser::Expr` node +associated with the `parser::ActualArg` node before it visits the +`parser::Expr` node associated with the `parser::FunctionReference` +node. + +So far, so good. + +## Finding the `INTENT` of the dummy argument +I now wanted to find the `INTENT` of the dummy argument associated with the +arguments in the set. As mentioned earlier, the type +`evaluate::ActualArgument` has a member function called `dummyIntent()` +that gives this value. So I augmented my code to print out the `INTENT`: + +```C++ + void DoChecker::Leave(const parser::Expr &parsedExpr) { + std::cout << "In Leave for parser::Expr\n"; + ActualArgumentSet argSet{CollectActualArguments(GetExpr(parsedExpr))}; + std::cout << "Number of arguments: " << argSet.size() << "\n"; + for (const evaluate::ActualArgumentRef &argRef : argSet) { + common::Intent intent{argRef->dummyIntent()}; + switch (intent) { + case common::Intent::In: std::cout << "INTENT(IN)\n"; break; + case common::Intent::Out: std::cout << "INTENT(OUT)\n"; break; + case common::Intent::InOut: std::cout << "INTENT(INOUT)\n"; break; + default: std::cout << "default INTENT\n"; + } + } + } +``` + +I then rebuilt my compiler and ran it on my test case. This produced the following output: + +``` + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 1 + INTENT(OUT) + In Leave for parser::Expr + Number of arguments: 0 +``` + +I then modified my test case to convince myself that I was getting the correct +`INTENT` for `IN`, `INOUT`, and default cases. + +So far, so good. + +## Finding the symbols for arguments that are variables +The third and last piece of information I needed was to determine if a variable +was being passed as an actual argument. In such cases, I wanted to get the +symbol table node (`semantics::Symbol`) for the variable. My starting point was the +`evaluate::ActualArgument` node. + +I was unsure of how to do this, so I browsed through existing code to look for +how it treated `evaluate::ActualArgument` objects. Since most of the code that deals with the `evaluate` namespace is in the lib/evaluate directory, I looked there. I ran `grep` on all of the `.cpp` files looking for +uses of `ActualArgument`. One of the first hits I got was in `lib/evaluate/call.cpp` in the definition of `ActualArgument::GetType()`: + +```C++ +std::optional ActualArgument::GetType() const { + if (const Expr *expr{UnwrapExpr()}) { + return expr->GetType(); + } else if (std::holds_alternative(u_)) { + return DynamicType::AssumedType(); + } else { + return std::nullopt; + } +} +``` + +I noted the call to `UnwrapExpr()` that yielded a value of +`Expr`. So I guessed that I could use this member function to +get an `evaluate::Expr` on which I could perform further analysis. + +I also knew that the header file `include/flang/evaluate/tools.h` held many +utility functions for dealing with `evaluate::Expr` objects. I was hoping to +find something that would determine if an `evaluate::Expr` was a variable. So +I searched for `IsVariable` and got a hit immediately. +```C++ + template bool IsVariable(const A &x) { + if (auto known{IsVariableHelper{}(x)}) { + return *known; + } else { + return false; + } + } +``` + +But I actually needed more than just the knowledge that an `evaluate::Expr` was +a variable. I needed the `semantics::Symbol` associated with the variable. So +I searched in `include/flang/evaluate/tools.h` for functions that returned a +`semantics::Symbol`. I found the following: + +```C++ +// If an expression is simply a whole symbol data designator, +// extract and return that symbol, else null. +template const Symbol *UnwrapWholeSymbolDataRef(const A &x) { + if (auto dataRef{ExtractDataRef(x)}) { + if (const SymbolRef * p{std::get_if(&dataRef->u)}) { + return &p->get(); + } + } + return nullptr; +} +``` + +This was exactly what I wanted. DO variables must be whole symbols. So I +could try to extract a whole `semantics::Symbol` from the `evaluate::Expr` in my +`evaluate::ActualArgument`. If this extraction resulted in a `semantics::Symbol` +that wasn't a `nullptr`, I could then conclude if it was a variable that I +could pass to existing functions that would determine if it was an active DO +variable. + +I then modified the compiler to perform the analysis that I'd guessed would +work: + +```C++ + void DoChecker::Leave(const parser::Expr &parsedExpr) { + std::cout << "In Leave for parser::Expr\n"; + ActualArgumentSet argSet{CollectActualArguments(GetExpr(parsedExpr))}; + std::cout << "Number of arguments: " << argSet.size() << "\n"; + for (const evaluate::ActualArgumentRef &argRef : argSet) { + if (const SomeExpr * argExpr{argRef->UnwrapExpr()}) { + std::cout << "Got an unwrapped Expr\n"; + if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { + std::cout << "Found a whole variable: " << *var << "\n"; + } + } + common::Intent intent{argRef->dummyIntent()}; + switch (intent) { + case common::Intent::In: std::cout << "INTENT(IN)\n"; break; + case common::Intent::Out: std::cout << "INTENT(OUT)\n"; break; + case common::Intent::InOut: std::cout << "INTENT(INOUT)\n"; break; + default: std::cout << "default INTENT\n"; + } + } + } +``` + +Note the line that prints out the symbol table entry for the variable: + +```C++ + std::cout << "Found a whole variable: " << *var << "\n"; +``` + +The compiler defines the "<<" operator for `semantics::Symbol`, which is handy +for analyzing the compiler's behavior. + +Here's the result of running the modified compiler on my Fortran test case: + +``` + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 1 + Got an unwrapped Expr + Found a whole variable: ivar: ObjectEntity type: INTEGER(4) + INTENT(OUT) + In Leave for parser::Expr + Number of arguments: 0 +``` + +Sweet. + +## Emitting the messages +At this point, using the source location information from the original +`parser::Expr`, I had enough information to plug into the exiting +interfaces for emitting messages for active DO variables. I modified the +compiler code accordingly: + + +```C++ + void DoChecker::Leave(const parser::Expr &parsedExpr) { + std::cout << "In Leave for parser::Expr\n"; + ActualArgumentSet argSet{CollectActualArguments(GetExpr(parsedExpr))}; + std::cout << "Number of arguments: " << argSet.size() << "\n"; + for (const evaluate::ActualArgumentRef &argRef : argSet) { + if (const SomeExpr * argExpr{argRef->UnwrapExpr()}) { + std::cout << "Got an unwrapped Expr\n"; + if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { + std::cout << "Found a whole variable: " << *var << "\n"; + common::Intent intent{argRef->dummyIntent()}; + switch (intent) { + case common::Intent::In: std::cout << "INTENT(IN)\n"; break; + case common::Intent::Out: + std::cout << "INTENT(OUT)\n"; + context_.CheckDoVarRedefine(parsedExpr.source, *var); + break; + case common::Intent::InOut: + std::cout << "INTENT(INOUT)\n"; + context_.WarnDoVarRedefine(parsedExpr.source, *var); + break; + default: std::cout << "default INTENT\n"; + } + } + } + } + } +``` + +I then ran this code on my test case, and miraculously, got the following +output: + +``` + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 0 + In Leave for parser::Expr + Number of arguments: 1 + Got an unwrapped Expr + Found a whole variable: ivar: ObjectEntity type: INTEGER(4) + INTENT(OUT) + In Leave for parser::Expr + Number of arguments: 0 + testfun.f90:6:12: error: Cannot redefine DO variable 'ivar' + jvar = intentOutFunc(ivar) + ^^^^^^^^^^^^^^^^^^^ + testfun.f90:5:6: Enclosing DO construct + do ivar = 1, 10 + ^^^^ +``` + +Even sweeter. + +# Improving the test case +At this point, my implementation seemed to be working. But I was concerned +about the limitations of my test case. So I augmented it to include arguments +other than `INTENT(OUT)` and more complex expressions. Luckily, my +augmented test did not reveal any new problems. + +Here's the test I ended up with: + +```Fortran + subroutine s() + + Integer :: ivar, jvar + + ! This one is OK + do ivar = 1, 10 + jvar = intentInFunc(ivar) + end do + + ! Error for passing a DO variable to an INTENT(OUT) dummy + do ivar = 1, 10 + jvar = intentOutFunc(ivar) + end do + + ! Error for passing a DO variable to an INTENT(OUT) dummy, more complex + ! expression + do ivar = 1, 10 + jvar = 83 + intentInFunc(intentOutFunc(ivar)) + end do + + ! Warning for passing a DO variable to an INTENT(INOUT) dummy + do ivar = 1, 10 + jvar = intentInOutFunc(ivar) + end do + + contains + function intentInFunc(dummyArg) + integer, intent(in) :: dummyArg + integer :: intentInFunc + + intentInFunc = 343 + end function intentInFunc + + function intentOutFunc(dummyArg) + integer, intent(out) :: dummyArg + integer :: intentOutFunc + + dummyArg = 216 + intentOutFunc = 343 + end function intentOutFunc + + function intentInOutFunc(dummyArg) + integer, intent(inout) :: dummyArg + integer :: intentInOutFunc + + dummyArg = 216 + intentInOutFunc = 343 + end function intentInOutFunc + + end subroutine s +``` + +# Submitting the pull request +At this point, my implementation seemed functionally complete, so I stripped out all of the debug statements, ran `clang-format` on it and reviewed it +to make sure that the names were clear. Here's what I ended up with: + +```C++ + void DoChecker::Leave(const parser::Expr &parsedExpr) { + ActualArgumentSet argSet{CollectActualArguments(GetExpr(parsedExpr))}; + for (const evaluate::ActualArgumentRef &argRef : argSet) { + if (const SomeExpr * argExpr{argRef->UnwrapExpr()}) { + if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { + common::Intent intent{argRef->dummyIntent()}; + switch (intent) { + case common::Intent::Out: + context_.CheckDoVarRedefine(parsedExpr.source, *var); + break; + case common::Intent::InOut: + context_.WarnDoVarRedefine(parsedExpr.source, *var); + break; + default:; // INTENT(IN) or default intent + } + } + } + } + } +``` + +I then created a pull request to get review comments. + +# Responding to pull request comments +I got feedback suggesting that I use an `if` statement rather than a +`case` statement. Another comment reminded me that I should look at the +code I'd previously writted to do a similar check for SUBROUTINE calls to see +if there was an opportunity to share code. This examination resulted in + converting my existing code to the following pair of functions: + + +```C++ + static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, + const parser::CharBlock location, SemanticsContext &context) { + common::Intent intent{arg.dummyIntent()}; + if (intent == common::Intent::Out || intent == common::Intent::InOut) { + if (const SomeExpr * argExpr{arg.UnwrapExpr()}) { + if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { + if (intent == common::Intent::Out) { + context.CheckDoVarRedefine(location, *var); + } else { + context.WarnDoVarRedefine(location, *var); // INTENT(INOUT) + } + } + } + } + } + + void DoChecker::Leave(const parser::Expr &parsedExpr) { + if (const SomeExpr * expr{GetExpr(parsedExpr)}) { + ActualArgumentSet argSet{CollectActualArguments(*expr)}; + for (const evaluate::ActualArgumentRef &argRef : argSet) { + CheckIfArgIsDoVar(*argRef, parsedExpr.source, context_); + } + } + } +``` + +The function `CheckIfArgIsDoVar()` was shared with the checks for DO +variables being passed to SUBROUTINE calls. + +At this point, my pull request was approved, and I merged it and deleted the +associated branch. From 4542cb57082eaf578799c76482d4b706ae5da077 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Tue, 4 Feb 2020 10:30:16 -0800 Subject: [PATCH 018/345] Fix template step limit issue with clang While working on PR 959, I instanciated a `common::TupleToVariant` with ~50+ types inside the tuple. Clang would then crash after 1hr compilation with message: "constexpr evaluation hit maximum step limit; possible infinite loop" After investigating, it turned out clang handles very badly the way `common::AreTypesDistinctHelper` was implemented. Its "number of steps" was exponential with the number of types. This fix makes this number quadratic which solves the issue. --- include/flang/common/template.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/include/flang/common/template.h b/include/flang/common/template.h index c8a18e704fb4..460f1a8bdaed 100644 --- a/include/flang/common/template.h +++ b/include/flang/common/template.h @@ -153,15 +153,12 @@ template struct VariantToTupleHelper> { template using VariantToTuple = typename VariantToTupleHelper::type; -template -struct AreTypesDistinctHelper { +template struct AreTypesDistinctHelper { static constexpr bool value() { - if constexpr (std::is_same_v) { - return false; - } if constexpr (sizeof...(REST) > 0) { - return AreTypesDistinctHelper::value() && - AreTypesDistinctHelper::value(); + // extra () for clang-format + return ((... && !std::is_same_v)) && + AreTypesDistinctHelper::value(); } return true; } From c3f1774f8eee903928b7e46636edfb03425eabc0 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 23 Jan 2020 16:59:27 -0800 Subject: [PATCH 019/345] Initial buffer framing code Address review comments Integer output data editing (I,B,O,Z) Full integer output formatting Stub out some work in progress Progress on E output data editing E, D, EN, and ES output editing done Fw.d output editing Real G output editing G output editing for reals Make environment a distinct module CHARACTER and LOGICAL output editing Minimal decimal representations for E0, F0, G0 editing Move real output editing code into its own file Fix/dodge some GCC build problems Prep work for external I/O statement state External HELLO, WORLD Fix build problem with GCC Add virtual destructors where needed Add new test --- lib/decimal/binary-to-decimal.cpp | 14 +- runtime/CMakeLists.txt | 3 + runtime/buffer.cpp | 23 ++ runtime/buffer.h | 178 ++++++++++++ runtime/environment.cpp | 37 +++ runtime/environment.h | 26 ++ runtime/file.cpp | 55 +++- runtime/file.h | 39 ++- runtime/format.cpp | 59 ++-- runtime/format.h | 28 +- runtime/io-api.cpp | 63 ++++- runtime/io-stmt.cpp | 192 ++++++++++--- runtime/io-stmt.h | 87 ++++-- runtime/main.cpp | 29 +- runtime/main.h | 17 +- runtime/memory.cpp | 5 - runtime/memory.h | 31 ++- runtime/numeric-output.h | 449 ++++++++++++++++++++++++++++++ runtime/stop.cpp | 8 + runtime/stop.h | 1 + runtime/tools.h | 11 + runtime/unit.cpp | 137 +++++++++ runtime/unit.h | 114 ++++++++ test/runtime/CMakeLists.txt | 8 + test/runtime/external-hello.cpp | 15 + test/runtime/format.cpp | 40 ++- test/runtime/hello.cpp | 332 +++++++++++++++++++++- 27 files changed, 1797 insertions(+), 204 deletions(-) create mode 100644 runtime/buffer.cpp create mode 100644 runtime/buffer.h create mode 100644 runtime/environment.cpp create mode 100644 runtime/environment.h create mode 100644 runtime/numeric-output.h create mode 100644 runtime/unit.cpp create mode 100644 runtime/unit.h create mode 100644 test/runtime/external-hello.cpp diff --git a/lib/decimal/binary-to-decimal.cpp b/lib/decimal/binary-to-decimal.cpp index fbc043eed374..ba061856b089 100644 --- a/lib/decimal/binary-to-decimal.cpp +++ b/lib/decimal/binary-to-decimal.cpp @@ -108,7 +108,7 @@ template ConversionToDecimalResult BigRadixFloatingPointNumber::ConvertToDecimal(char *buffer, std::size_t n, enum DecimalConversionFlags flags, int maxDigits) const { - if (n < static_cast(3 + digits_ * LOG10RADIX) || maxDigits < 1) { + if (n < static_cast(3 + digits_ * LOG10RADIX)) { return {nullptr, 0, 0, Overflow}; } char *start{buffer}; @@ -160,18 +160,21 @@ BigRadixFloatingPointNumber::ConvertToDecimal(char *buffer, while (p[-1] == '0') { --p; } - if (p <= start + maxDigits) { + char *end{start + maxDigits}; + if (maxDigits == 0) { + p = end; + } + if (p <= end) { *p = '\0'; return {buffer, static_cast(p - buffer), expo, Exact}; } else { // Apply a digit limit, possibly with rounding. - char *end{start + maxDigits}; bool incr{false}; switch (rounding_) { case RoundNearest: case RoundDefault: - incr = - *end > '5' || (*end == '5' && (p > end || ((p[-1] - '0') & 1) != 0)); + incr = *end > '5' || + (*end == '5' && (p > end + 1 || ((end[-1] - '0') & 1) != 0)); break; case RoundUp: incr = !isNegative_; break; case RoundDown: incr = isNegative_; break; @@ -190,6 +193,7 @@ BigRadixFloatingPointNumber::ConvertToDecimal(char *buffer, ++p[-1]; } } + *p = '\0'; return {buffer, static_cast(p - buffer), expo, Inexact}; } diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index a9a71b9f17ce..4c1ecf0be736 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -8,8 +8,10 @@ add_library(FortranRuntime ISO_Fortran_binding.cpp + buffer.cpp derived-type.cpp descriptor.cpp + environment.cpp file.cpp format.cpp io-api.cpp @@ -22,6 +24,7 @@ add_library(FortranRuntime tools.cpp transformational.cpp type-code.cpp + unit.cpp ) target_link_libraries(FortranRuntime diff --git a/runtime/buffer.cpp b/runtime/buffer.cpp new file mode 100644 index 000000000000..607bbfcc9ec2 --- /dev/null +++ b/runtime/buffer.cpp @@ -0,0 +1,23 @@ +//===-- runtime/buffer.cpp --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "buffer.h" +#include + +namespace Fortran::runtime::io { + +// Here's a very old trick for shifting circular buffer data cheaply +// without a need for a temporary array. +void LeftShiftBufferCircularly( + char *buffer, std::size_t bytes, std::size_t shift) { + // Assume that we start with "efgabcd" and the left shift is 3. + std::reverse(buffer, buffer + shift); // "gfeabcd" + std::reverse(buffer, buffer + bytes); // "dcbaefg" + std::reverse(buffer, buffer + bytes - shift); // "abcdefg" +} +} diff --git a/runtime/buffer.h b/runtime/buffer.h new file mode 100644 index 000000000000..a7b31848df8d --- /dev/null +++ b/runtime/buffer.h @@ -0,0 +1,178 @@ +//===-- runtime/buffer.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// External file buffering + +#ifndef FORTRAN_RUNTIME_BUFFER_H_ +#define FORTRAN_RUNTIME_BUFFER_H_ + +#include "io-error.h" +#include "memory.h" +#include +#include +#include + +namespace Fortran::runtime::io { + +void LeftShiftBufferCircularly(char *, std::size_t bytes, std::size_t shift); + +// Maintains a view of a contiguous region of a file in a memory buffer. +// The valid data in the buffer may be circular, but any active frame +// will also be contiguous in memory. The requirement stems from the need to +// preserve read data that may be reused by means of Tn/TLn edit descriptors +// without needing to position the file (which may not always be possible, +// e.g. a socket) and a general desire to reduce system call counts. +template class FileFrame { +public: + using FileOffset = std::int64_t; + + ~FileFrame() { FreeMemoryAndNullify(buffer_); } + + // The valid data in the buffer begins at buffer_[start_] and proceeds + // with possible wrap-around for length_ bytes. The current frame + // is offset by frame_ bytes into that region and is guaranteed to + // be contiguous for at least as many bytes as were requested. + + FileOffset FrameAt() const { return fileOffset_ + frame_; } + char *Frame() const { return buffer_ + start_ + frame_; } + std::size_t FrameLength() const { + return std::min( + static_cast(length_ - frame_), size_ - (start_ + frame_)); + } + + // Returns a short frame at a non-fatal EOF. Can return a long frame as well. + std::size_t ReadFrame( + FileOffset at, std::size_t bytes, IoErrorHandler &handler) { + Flush(handler); + Reallocate(bytes, handler); + if (at < fileOffset_ || at > fileOffset_ + length_) { + Reset(at); + } + frame_ = static_cast(at - fileOffset_); + if (start_ + frame_ + bytes > size_) { + DiscardLeadingBytes(frame_, handler); + if (start_ + bytes > size_) { + // Frame would wrap around; shift current data (if any) to force + // contiguity. + RUNTIME_CHECK(handler, length_ < size_); + if (start_ + length_ <= size_) { + // [......abcde..] -> [abcde........] + std::memmove(buffer_, buffer_ + start_, length_); + } else { + // [cde........ab] -> [abcde........] + auto n{start_ + length_ - size_}; // 3 for cde + auto gap{size_ - length_}; // 13 - 5 = 8 + RUNTIME_CHECK(handler, length_ >= n); + std::memmove(buffer_ + n, buffer_ + start_, length_ - n); // cdeab + LeftShiftBufferCircularly(buffer_, length_, n); // abcde + } + start_ = 0; + } + } + while (FrameLength() < bytes) { + auto next{start_ + length_}; + RUNTIME_CHECK(handler, next < size_); + auto minBytes{bytes - FrameLength()}; + auto maxBytes{size_ - next}; + auto got{Store().Read( + fileOffset_ + length_, buffer_ + next, minBytes, maxBytes, handler)}; + length_ += got; + RUNTIME_CHECK(handler, length_ < size_); + if (got < minBytes) { + break; // error or EOF & program can handle it + } + } + return FrameLength(); + } + + void WriteFrame(FileOffset at, std::size_t bytes, IoErrorHandler &handler) { + if (!dirty_ || at < fileOffset_ || at > fileOffset_ + length_ || + start_ + (at - fileOffset_) + bytes > size_) { + Flush(handler); + fileOffset_ = at; + Reallocate(bytes, handler); + } + dirty_ = true; + frame_ = at - fileOffset_; + length_ = std::max(length_, static_cast(frame_ + bytes)); + } + + void Flush(IoErrorHandler &handler) { + if (dirty_) { + while (length_ > 0) { + std::size_t chunk{std::min(static_cast(length_), + static_cast(size_ - start_))}; + std::size_t put{ + Store().Write(fileOffset_, buffer_ + start_, chunk, handler)}; + length_ -= put; + start_ += put; + fileOffset_ += put; + if (put < chunk) { + break; + } + } + Reset(fileOffset_); + } + } + +private: + STORE &Store() { return static_cast(*this); } + + void Reallocate(std::size_t bytes, Terminator &terminator) { + if (bytes > size_) { + char *old{buffer_}; + auto oldSize{size_}; + size_ = std::max(bytes, minBuffer); + buffer_ = + reinterpret_cast(AllocateMemoryOrCrash(terminator, size_)); + auto chunk{ + std::min(length_, static_cast(oldSize - start_))}; + std::memcpy(buffer_, old + start_, chunk); + start_ = 0; + std::memcpy(buffer_ + chunk, old, length_ - chunk); + FreeMemory(old); + } + } + + void Reset(FileOffset at) { + start_ = length_ = frame_ = 0; + fileOffset_ = at; + dirty_ = false; + } + + void DiscardLeadingBytes(std::size_t n, Terminator &terminator) { + RUNTIME_CHECK(terminator, length_ >= n); + length_ -= n; + if (length_ == 0) { + start_ = 0; + } else { + start_ += n; + if (start_ >= size_) { + start_ -= size_; + } + } + if (frame_ >= n) { + frame_ -= n; + } else { + frame_ = 0; + } + fileOffset_ += n; + } + + static constexpr std::size_t minBuffer{64 << 10}; + + char *buffer_{nullptr}; + std::size_t size_{0}; // current allocated buffer size + FileOffset fileOffset_{0}; // file offset corresponding to buffer valid data + std::int64_t start_{0}; // buffer_[] offset of valid data + std::int64_t length_{0}; // valid data length (can wrap) + std::int64_t frame_{0}; // offset of current frame in valid data + bool dirty_{false}; +}; +} +#endif // FORTRAN_RUNTIME_BUFFER_H_ diff --git a/runtime/environment.cpp b/runtime/environment.cpp new file mode 100644 index 000000000000..5ce55ab47459 --- /dev/null +++ b/runtime/environment.cpp @@ -0,0 +1,37 @@ +//===-- runtime/environment.cpp ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "environment.h" +#include +#include + +namespace Fortran::runtime { +ExecutionEnvironment executionEnvironment; + +void ExecutionEnvironment::Configure( + int ac, const char *av[], const char *env[]) { + argc = ac; + argv = av; + envp = env; + listDirectedOutputLineLengthLimit = 79; // PGI default + defaultOutputRoundingMode = common::RoundingMode::TiesToEven; // RP=RN + + if (auto *x{std::getenv("FORT_FMT_RECL")}) { + char *end; + auto n{std::strtol(x, &end, 10)}; + if (n > 0 && n < std::numeric_limits::max() && *end == '\0') { + listDirectedOutputLineLengthLimit = n; + } else { + std::fprintf( + stderr, "Fortran runtime: FORT_FMT_RECL=%s is invalid; ignored\n", x); + } + } + + // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment +} +} diff --git a/runtime/environment.h b/runtime/environment.h new file mode 100644 index 000000000000..25a98959b741 --- /dev/null +++ b/runtime/environment.h @@ -0,0 +1,26 @@ +//===-- runtime/environment.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_RUNTIME_ENVIRONMENT_H_ +#define FORTRAN_RUNTIME_ENVIRONMENT_H_ + +#include "flang/common/Fortran.h" + +namespace Fortran::runtime { +struct ExecutionEnvironment { + void Configure(int argc, const char *argv[], const char *envp[]); + + int argc; + const char **argv; + const char **envp; + int listDirectedOutputLineLengthLimit; + common::RoundingMode defaultOutputRoundingMode; +}; +extern ExecutionEnvironment executionEnvironment; +} +#endif // FORTRAN_RUNTIME_ENVIRONMENT_H_ diff --git a/runtime/file.cpp b/runtime/file.cpp index 3936bcdfcfa6..f9c18c741734 100644 --- a/runtime/file.cpp +++ b/runtime/file.cpp @@ -22,13 +22,25 @@ void OpenFile::Open(const char *path, std::size_t pathLength, const char *status, std::size_t statusLength, const char *action, std::size_t actionLength, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; - RUNTIME_CHECK(handler, fd_ < 0); + RUNTIME_CHECK(handler, fd_ < 0); // TODO handle re-openings int flags{0}; static const char *actions[]{"READ", "WRITE", "READWRITE", nullptr}; switch (IdentifyValue(action, actionLength, actions)) { - case 0: flags = O_RDONLY; break; - case 1: flags = O_WRONLY; break; - case 2: flags = O_RDWR; break; + case 0: + flags = O_RDONLY; + mayRead_ = true; + mayWrite_ = false; + break; + case 1: + flags = O_WRONLY; + mayRead_ = false; + mayWrite_ = true; + break; + case 2: + mayRead_ = true; + mayWrite_ = true; + flags = O_RDWR; + break; default: handler.Crash( "Invalid ACTION='%.*s'", action, static_cast(actionLength)); @@ -82,6 +94,7 @@ void OpenFile::Open(const char *path, std::size_t pathLength, } } path_ = SaveDefaultCharacter(path, pathLength, handler); + pathLength_ = pathLength; if (!path_.get()) { handler.Crash( "FILE= is required unless STATUS='OLD' and unit is connected"); @@ -94,6 +107,17 @@ void OpenFile::Open(const char *path, std::size_t pathLength, knownSize_.reset(); } +void OpenFile::Predefine(int fd) { + CriticalSection criticalSection{lock_}; + fd_ = fd; + path_.reset(); + pathLength_ = 0; + position_ = 0; + knownSize_.reset(); + nextId_ = 0; + pending_.reset(); +} + void OpenFile::Close( const char *status, std::size_t statusLength, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; @@ -123,7 +147,7 @@ void OpenFile::Close( } } -std::size_t OpenFile::Read(Offset at, char *buffer, std::size_t minBytes, +std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes, std::size_t maxBytes, IoErrorHandler &handler) { if (maxBytes == 0) { return 0; @@ -157,8 +181,8 @@ std::size_t OpenFile::Read(Offset at, char *buffer, std::size_t minBytes, return got; } -std::size_t OpenFile::Write( - Offset at, const char *buffer, std::size_t bytes, IoErrorHandler &handler) { +std::size_t OpenFile::Write(FileOffset at, const char *buffer, + std::size_t bytes, IoErrorHandler &handler) { if (bytes == 0) { return 0; } @@ -187,7 +211,7 @@ std::size_t OpenFile::Write( return put; } -void OpenFile::Truncate(Offset at, IoErrorHandler &handler) { +void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; CheckOpen(handler); if (!knownSize_ || *knownSize_ != at) { @@ -202,7 +226,7 @@ void OpenFile::Truncate(Offset at, IoErrorHandler &handler) { // to be claimed by a later WAIT statement. // TODO: True asynchronicity int OpenFile::ReadAsynchronously( - Offset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) { + FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; CheckOpen(handler); int iostat{0}; @@ -210,7 +234,7 @@ int OpenFile::ReadAsynchronously( #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L auto chunk{::pread(fd_, buffer + got, bytes - got, at)}; #else - auto chunk{RawSeek(at) ? ::read(fd_, buffer + got, bytes - got) : -1}; + auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1}; #endif if (chunk == 0) { iostat = FORTRAN_RUNTIME_IOSTAT_END; @@ -231,8 +255,8 @@ int OpenFile::ReadAsynchronously( } // TODO: True asynchronicity -int OpenFile::WriteAsynchronously( - Offset at, const char *buffer, std::size_t bytes, IoErrorHandler &handler) { +int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer, + std::size_t bytes, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; CheckOpen(handler); int iostat{0}; @@ -240,7 +264,8 @@ int OpenFile::WriteAsynchronously( #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)}; #else - auto chunk{RawSeek(at) ? ::write(fd_, buffer + put, bytes - put) : -1}; + auto chunk{ + Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1}; #endif if (chunk >= 0) { at += chunk; @@ -298,7 +323,7 @@ void OpenFile::CheckOpen(Terminator &terminator) { RUNTIME_CHECK(terminator, fd_ >= 0); } -bool OpenFile::Seek(Offset at, IoErrorHandler &handler) { +bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) { if (at == position_) { return true; } else if (RawSeek(at)) { @@ -310,7 +335,7 @@ bool OpenFile::Seek(Offset at, IoErrorHandler &handler) { } } -bool OpenFile::RawSeek(Offset at) { +bool OpenFile::RawSeek(FileOffset at) { #ifdef _LARGEFILE64_SOURCE return ::lseek64(fd_, at, SEEK_SET) == 0; #else diff --git a/runtime/file.h b/runtime/file.h index 1c0a10d8f754..d5e521756653 100644 --- a/runtime/file.h +++ b/runtime/file.h @@ -14,7 +14,6 @@ #include "io-error.h" #include "lock.h" #include "memory.h" -#include "terminator.h" #include #include @@ -22,33 +21,43 @@ namespace Fortran::runtime::io { class OpenFile { public: - using Offset = std::uint64_t; + using FileOffset = std::int64_t; - Offset position() const { return position_; } + FileOffset position() const { return position_; } void Open(const char *path, std::size_t pathLength, const char *status, std::size_t statusLength, const char *action, std::size_t actionLength, IoErrorHandler &); + void Predefine(int fd); void Close(const char *action, std::size_t actionLength, IoErrorHandler &); + int fd() const { return fd_; } + bool mayRead() const { return mayRead_; } + bool mayWrite() const { return mayWrite_; } + bool mayPosition() const { return mayPosition_; } + void set_mayRead(bool yes) { mayRead_ = yes; } + void set_mayWrite(bool yes) { mayWrite_ = yes; } + void set_mayPosition(bool yes) { mayPosition_ = yes; } + // Reads data into memory; returns amount acquired. Synchronous. // Partial reads (less than minBytes) signify end-of-file. If the // buffer is larger than minBytes, and extra returned data will be // preserved for future consumption, set maxBytes larger than minBytes // to reduce system calls This routine handles EAGAIN/EWOULDBLOCK and EINTR. - std::size_t Read(Offset, char *, std::size_t minBytes, std::size_t maxBytes, - IoErrorHandler &); + std::size_t Read(FileOffset, char *, std::size_t minBytes, + std::size_t maxBytes, IoErrorHandler &); // Writes data. Synchronous. Partial writes indicate program-handled // error conditions. - std::size_t Write(Offset, const char *, std::size_t, IoErrorHandler &); + std::size_t Write(FileOffset, const char *, std::size_t, IoErrorHandler &); // Truncates the file - void Truncate(Offset, IoErrorHandler &); + void Truncate(FileOffset, IoErrorHandler &); // Asynchronous transfers - int ReadAsynchronously(Offset, char *, std::size_t, IoErrorHandler &); - int WriteAsynchronously(Offset, const char *, std::size_t, IoErrorHandler &); + int ReadAsynchronously(FileOffset, char *, std::size_t, IoErrorHandler &); + int WriteAsynchronously( + FileOffset, const char *, std::size_t, IoErrorHandler &); void Wait(int id, IoErrorHandler &); void WaitAll(IoErrorHandler &); @@ -61,15 +70,19 @@ class OpenFile { // lock_ must be held for these void CheckOpen(Terminator &); - bool Seek(Offset, IoErrorHandler &); - bool RawSeek(Offset); + bool Seek(FileOffset, IoErrorHandler &); + bool RawSeek(FileOffset); int PendingResult(Terminator &, int); Lock lock_; int fd_{-1}; OwningPtr path_; - Offset position_{0}; - std::optional knownSize_; + std::size_t pathLength_; + bool mayRead_{false}; + bool mayWrite_{false}; + bool mayPosition_{false}; + FileOffset position_{0}; + std::optional knownSize_; int nextId_; OwningPtr pending_; }; diff --git a/runtime/format.cpp b/runtime/format.cpp index 46ad2ea2b5d0..f31139ebb5ac 100644 --- a/runtime/format.cpp +++ b/runtime/format.cpp @@ -8,43 +8,25 @@ #include "format.h" #include "io-stmt.h" +#include "main.h" #include "flang/common/format.h" #include "flang/decimal/decimal.h" #include namespace Fortran::runtime::io { -// Default FormatContext virtual member functions -void FormatContext::Emit(const char *, std::size_t) { - Crash("Cannot emit data from this FORMAT string"); -} -void FormatContext::Emit(const char16_t *, std::size_t) { - Crash("Cannot emit data from this FORMAT string"); -} -void FormatContext::Emit(const char32_t *, std::size_t) { - Crash("Cannot emit data from this FORMAT string"); -} -void FormatContext::HandleSlash(int) { - Crash("A / control edit descriptor may not appear in this FORMAT string"); -} -void FormatContext::HandleAbsolutePosition(int) { - Crash("A Tn control edit descriptor may not appear in this FORMAT string"); -} -void FormatContext::HandleRelativePosition(int) { - Crash("An nX, TLn, or TRn control edit descriptor may not appear in this " - "FORMAT string"); -} - template FormatControl::FormatControl(Terminator &terminator, const CHAR *format, std::size_t formatLength, int maxHeight) : maxHeight_{static_cast(maxHeight)}, format_{format}, formatLength_{static_cast(formatLength)} { - // The additional two items are for the whole string and a - // repeated non-parenthesized edit descriptor. - if (maxHeight > std::numeric_limits::max()) { + if (maxHeight != maxHeight_) { terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); } + if (formatLength != static_cast(formatLength_)) { + terminator.Crash( + "internal Fortran runtime error: formatLength %zd", formatLength); + } stack_[0].start = offset_; stack_[0].remaining = Iteration::unlimited; // 13.4(8) } @@ -95,8 +77,7 @@ int FormatControl::GetIntField(Terminator &terminator, CHAR firstCh) { return result; } -static void HandleControl( - FormatContext &context, std::uint16_t &scale, char ch, char next, int n) { +static void HandleControl(FormatContext &context, char ch, char next, int n) { MutableModes &modes{context.mutableModes()}; switch (ch) { case 'B': @@ -121,7 +102,7 @@ static void HandleControl( break; case 'P': if (!next) { - scale = n; // kP - decimal scaling by 10**k (TODO) + modes.scale = n; // kP - decimal scaling by 10**k return; } break; @@ -134,6 +115,9 @@ static void HandleControl( case 'C': modes.roundingMode = common::RoundingMode::TiesAwayFromZero; return; + case 'P': + modes.roundingMode = executionEnvironment.defaultOutputRoundingMode; + return; default: break; } break; @@ -269,13 +253,15 @@ int FormatControl::CueUpNextDataEdit(FormatContext &context, bool stop) { } else if (ch >= 'A' && ch <= 'Z') { int start{offset_ - 1}; CHAR next{Capitalize(PeekNext())}; - if (next < 'A' || next > 'Z') { + if (next >= 'A' && next <= 'Z') { + ++offset_; + } else { next = '\0'; } if (ch == 'E' || (!next && (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' || - ch == 'F' || ch == 'D' || ch == 'G'))) { + ch == 'F' || ch == 'D' || ch == 'G' || ch == 'L'))) { // Data edit descriptor found offset_ = start; return repeat && *repeat > 0 ? *repeat : 1; @@ -284,8 +270,8 @@ int FormatControl::CueUpNextDataEdit(FormatContext &context, bool stop) { if (ch == 'T') { // Tn, TLn, TRn repeat = GetIntField(context); } - HandleControl(context, scale_, static_cast(ch), - static_cast(next), repeat && *repeat > 0 ? *repeat : 1); + HandleControl(context, static_cast(ch), static_cast(next), + repeat ? *repeat : 1); } } else if (ch == '/') { context.HandleSlash(repeat && *repeat > 0 ? *repeat : 1); @@ -316,7 +302,16 @@ void FormatControl::GetNext( edit.variation = '\0'; } - edit.width = GetIntField(context); + if (edit.descriptor == 'A') { // width is optional for A[w] + auto ch{PeekNext()}; + if (ch >= '0' && ch <= '9') { + edit.width = GetIntField(context); + } else { + edit.width.reset(); + } + } else { + edit.width = GetIntField(context); + } edit.modes = context.mutableModes(); if (PeekNext() == '.') { ++offset_; diff --git a/runtime/format.h b/runtime/format.h index ed164e8902d1..c954c7f3872f 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -11,6 +11,7 @@ #ifndef FORTRAN_RUNTIME_FORMAT_H_ #define FORTRAN_RUNTIME_FORMAT_H_ +#include "environment.h" #include "terminator.h" #include "flang/common/Fortran.h" #include @@ -26,16 +27,19 @@ enum EditingFlags { struct MutableModes { std::uint8_t editingFlags{0}; // BN, DP, SS - common::RoundingMode roundingMode{common::RoundingMode::TiesToEven}; // RN + common::RoundingMode roundingMode{ + executionEnvironment + .defaultOutputRoundingMode}; // RP/ROUND='PROCESSOR_DEFAULT' bool pad{false}; // PAD= mode on READ char delim{'\0'}; // DELIM= + short scale{0}; // kP }; // A single edit descriptor extracted from a FORMAT struct DataEdit { char descriptor; // capitalized: one of A, I, B, O, Z, F, E(N/S/X), D, G char variation{'\0'}; // N, S, or X for EN, ES, EX - int width; // the 'w' field + std::optional width; // the 'w' field; optional for A std::optional digits; // the 'm' or 'd' field std::optional expoDigits; // 'Ee' field MutableModes modes; @@ -45,13 +49,14 @@ struct DataEdit { class FormatContext : virtual public Terminator { public: FormatContext() {} + virtual ~FormatContext() {} explicit FormatContext(const MutableModes &modes) : mutableModes_{modes} {} - virtual void Emit(const char *, std::size_t); - virtual void Emit(const char16_t *, std::size_t); - virtual void Emit(const char32_t *, std::size_t); - virtual void HandleSlash(int = 1); - virtual void HandleRelativePosition(int); - virtual void HandleAbsolutePosition(int); + virtual bool Emit(const char *, std::size_t) = 0; + virtual bool Emit(const char16_t *, std::size_t) = 0; + virtual bool Emit(const char32_t *, std::size_t) = 0; + virtual bool HandleSlash(int = 1) = 0; + virtual bool HandleRelativePosition(std::int64_t) = 0; + virtual bool HandleAbsolutePosition(std::int64_t) = 0; MutableModes &mutableModes() { return mutableModes_; } private: @@ -63,6 +68,8 @@ class FormatContext : virtual public Terminator { // Errors are fatal. See clause 13.4 in Fortran 2018 for background. template class FormatControl { public: + FormatControl() {} + // TODO: make 'format' a reference here and below FormatControl(Terminator &, const CHAR *format, std::size_t formatLength, int maxHeight = maxMaxHeight); @@ -125,11 +132,10 @@ template class FormatControl { // Data members are arranged and typed so as to reduce size. // This structure may be allocated in stack space loaned by the // user program for internal I/O. - std::uint16_t scale_{0}; // kP const std::uint8_t maxHeight_{maxMaxHeight}; std::uint8_t height_{0}; - const CHAR *format_; - int formatLength_; + const CHAR *format_{nullptr}; + int formatLength_{0}; int offset_{0}; // next item is at format_[offset_] // must be last, may be incomplete diff --git a/runtime/io-api.cpp b/runtime/io-api.cpp index 56e6dff932c5..d5840a03b75d 100644 --- a/runtime/io-api.cpp +++ b/runtime/io-api.cpp @@ -6,11 +6,15 @@ // //===----------------------------------------------------------------------===// +// Implements the I/O statement API + #include "io-api.h" #include "format.h" #include "io-stmt.h" #include "memory.h" +#include "numeric-output.h" #include "terminator.h" +#include "unit.h" #include #include @@ -25,7 +29,62 @@ Cookie IONAME(BeginInternalFormattedOutput)(char *internal, internalLength, format, formatLength, sourceFile, sourceLine); } -enum Iostat IONAME(EndIoStatement)(Cookie io) { - return static_cast(io->EndIoStatement()); +Cookie IONAME(BeginExternalFormattedOutput)(const char *format, + std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, + int sourceLine) { + Terminator terminator{sourceFile, sourceLine}; + int unit{unitNumber == DefaultUnit ? 6 : unitNumber}; + ExternalFile &file{ExternalFile::LookUpOrCrash(unit, terminator)}; + return &file.BeginIoStatement>( + file, format, formatLength, sourceFile, sourceLine); +} + +bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { + IoStatementState &io{*cookie}; + DataEdit edit; + io.GetNext(edit); + return EditIntegerOutput(io, edit, n); +} + +bool IONAME(OutputReal64)(Cookie cookie, double x) { + IoStatementState &io{*cookie}; + DataEdit edit; + io.GetNext(edit); + return RealOutputEditing{io, x}.Edit(edit); +} + +bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { + IoStatementState &io{*cookie}; + DataEdit edit; + io.GetNext(edit); + if (edit.descriptor != 'A' && edit.descriptor != 'G') { + io.Crash( + "Data edit descriptor '%c' may not be used with a CHARACTER data item", + edit.descriptor); + return false; + } + int len{static_cast(length)}; + int width{edit.width.value_or(len)}; + return EmitRepeated(io, ' ', std::max(0, width - len)) && + io.Emit(x, std::min(width, len)); +} + +bool IONAME(OutputLogical)(Cookie cookie, bool truth) { + IoStatementState &io{*cookie}; + DataEdit edit; + io.GetNext(edit); + if (edit.descriptor != 'L' && edit.descriptor != 'G') { + io.Crash( + "Data edit descriptor '%c' may not be used with a LOGICAL data item", + edit.descriptor); + return false; + } + return EmitRepeated(io, ' ', std::max(0, edit.width.value_or(1) - 1)) && + io.Emit(truth ? "T" : "F", 1); +} + +enum Iostat IONAME(EndIoStatement)(Cookie cookie) { + IoStatementState &io{*cookie}; + return static_cast(io.EndIoStatement()); } } diff --git a/runtime/io-stmt.cpp b/runtime/io-stmt.cpp index 617e3e697a14..e54a67a328e2 100644 --- a/runtime/io-stmt.cpp +++ b/runtime/io-stmt.cpp @@ -8,81 +8,193 @@ #include "io-stmt.h" #include "memory.h" +#include "unit.h" #include #include namespace Fortran::runtime::io { +IoStatementState::IoStatementState(const char *sourceFile, int sourceLine) + : IoErrorHandler{sourceFile, sourceLine} {} + int IoStatementState::EndIoStatement() { return GetIoStat(); } -int InternalIoStatementState::EndIoStatement() { - auto result{GetIoStat()}; - if (free_) { - FreeMemory(this); - } - return result; +// Defaults +void IoStatementState::GetNext(DataEdit &, int) { + Crash("GetNext() called for I/O statement that is not a formatted data " + "transfer statement"); +} +bool IoStatementState::Emit(const char *, std::size_t) { + Crash("Emit() called for I/O statement that is not an output statement"); + return false; +} +bool IoStatementState::Emit(const char16_t *, std::size_t) { + Crash("Emit() called for I/O statement that is not an output statement"); + return false; +} +bool IoStatementState::Emit(const char32_t *, std::size_t) { + Crash("Emit() called for I/O statement that is not an output statement"); + return false; +} +bool IoStatementState::HandleSlash(int) { + Crash("HandleSlash() called for I/O statement that is not a formatted data " + "transfer statement"); + return false; +} +bool IoStatementState::HandleRelativePosition(std::int64_t) { + Crash("HandleRelativePosition() called for I/O statement that is not a " + "formatted data transfer statement"); + return false; +} +bool IoStatementState::HandleAbsolutePosition(std::int64_t) { + Crash("HandleAbsolutePosition() called for I/O statement that is not a " + "formatted data transfer statement"); + return false; } - -InternalIoStatementState::InternalIoStatementState( - const char *sourceFile, int sourceLine) - : IoStatementState(sourceFile, sourceLine) {} template -InternalFormattedIoStatementState::InternalFormattedIoStatementState(Buffer internal, - std::size_t internalLength, const CHAR *format, std::size_t formatLength, - const char *sourceFile, int sourceLine) - : InternalIoStatementState{sourceFile, sourceLine}, FormatContext{}, - internal_{internal}, internalLength_{internalLength}, format_{*this, format, - formatLength} { - std::fill_n(internal_, internalLength_, static_cast(' ')); +FixedRecordIoStatementState::FixedRecordIoStatementState( + Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) + : IoStatementState{sourceFile, sourceLine}, buffer_{buffer}, length_{length} { } template -void InternalFormattedIoStatementState::Emit( +bool FixedRecordIoStatementState::Emit( const CHAR *data, std::size_t chars) { if constexpr (isInput) { - FormatContext::Emit(data, chars); // default Crash() - } else if (at_ + chars > internalLength_) { + IoStatementState::Emit(data, chars); // default Crash() + return false; + } else if (at_ + chars > length_) { SignalEor(); + if (at_ < length_) { + std::memcpy(buffer_ + at_, data, (length_ - at_) * sizeof(CHAR)); + at_ = furthest_ = length_; + } + return false; } else { - std::memcpy(internal_ + at_, data, chars * sizeof(CHAR)); + std::memcpy(buffer_ + at_, data, chars * sizeof(CHAR)); at_ += chars; + furthest_ = std::max(furthest_, at_); + return true; } } template -void InternalFormattedIoStatementState::HandleAbsolutePosition( - int n) { - if (n < 0 || static_cast(n) >= internalLength_) { - Crash("T%d control edit descriptor is out of range", n); - } else { - at_ = n; +bool FixedRecordIoStatementState::HandleAbsolutePosition( + std::int64_t n) { + if (n < 0) { + n = 0; } + n += leftTabLimit_; + bool ok{true}; + if (static_cast(n) > length_) { + SignalEor(); + n = length_; + ok = false; + } + if constexpr (!isInput) { + if (static_cast(n) > furthest_) { + std::fill_n(buffer_ + furthest_, n - furthest_, static_cast(' ')); + } + } + at_ = n; + furthest_ = std::max(furthest_, at_); + return ok; } template -void InternalFormattedIoStatementState::HandleRelativePosition( - int n) { - if (n < 0) { - at_ -= std::min(at_, -static_cast(n)); - } else { - at_ += n; - if (at_ > internalLength_) { - Crash("TR%d control edit descriptor is out of range", n); - } +bool FixedRecordIoStatementState::HandleRelativePosition( + std::int64_t n) { + return HandleAbsolutePosition(n + at_ - leftTabLimit_); +} + +template +int FixedRecordIoStatementState::EndIoStatement() { + if constexpr (!isInput) { + HandleAbsolutePosition(length_ - leftTabLimit_); // fill } + return GetIoStat(); } template -int InternalFormattedIoStatementState::EndIoStatement() { - format_.FinishOutput(*this); - auto result{GetIoStat()}; +int InternalIoStatementState::EndIoStatement() { + auto result{FixedRecordIoStatementState::EndIoStatement()}; if (free_) { FreeMemory(this); } return result; } +template +InternalIoStatementState::InternalIoStatementState( + Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) + : FixedRecordIoStatementState( + buffer, length, sourceFile, sourceLine) {} + +template +InternalFormattedIoStatementState::InternalFormattedIoStatementState(Buffer buffer, std::size_t length, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : InternalIoStatementState{buffer, length, sourceFile, + sourceLine}, + format_{*this, format, formatLength} {} + +template +int InternalFormattedIoStatementState::EndIoStatement() { + format_.FinishOutput(*this); + return InternalIoStatementState::EndIoStatement(); +} + +template +ExternalFormattedIoStatementState::ExternalFormattedIoStatementState(ExternalFile &file, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : IoStatementState{sourceFile, sourceLine}, file_{file}, format_{*this, + format, + formatLength} {} + +template +bool ExternalFormattedIoStatementState::Emit( + const CHAR *data, std::size_t chars) { + // TODO: UTF-8 encoding of 2- and 4-byte characters + return file_.Emit(data, chars * sizeof(CHAR), *this); +} + +template +bool ExternalFormattedIoStatementState::HandleSlash(int n) { + while (n-- > 0) { + if (!file_.NextOutputRecord(*this)) { + return false; + } + } + return true; +} + +template +bool ExternalFormattedIoStatementState::HandleAbsolutePosition( + std::int64_t n) { + return file_.HandleAbsolutePosition(n, *this); +} + +template +bool ExternalFormattedIoStatementState::HandleRelativePosition( + std::int64_t n) { + return file_.HandleRelativePosition(n, *this); +} + +template +int ExternalFormattedIoStatementState::EndIoStatement() { + format_.FinishOutput(*this); + if constexpr (!isInput) { + file_.NextOutputRecord(*this); // TODO: non-advancing I/O + } + int result{GetIoStat()}; + file_.EndIoStatement(); // annihilates *this in file_.u_ + return result; +} + template class InternalFormattedIoStatementState; +template class ExternalFormattedIoStatementState; } diff --git a/runtime/io-stmt.h b/runtime/io-stmt.h index 2e70efa591be..002f38e82596 100644 --- a/runtime/io-stmt.h +++ b/runtime/io-stmt.h @@ -18,47 +18,100 @@ namespace Fortran::runtime::io { -class IoStatementState : public IoErrorHandler { +class ExternalFile; + +class IoStatementState : public IoErrorHandler, public FormatContext { public: - using IoErrorHandler::IoErrorHandler; + IoStatementState(const char *sourceFile, int sourceLine); + virtual ~IoStatementState() {} + virtual int EndIoStatement(); + // Default (crashing) callback overrides for FormatContext + virtual void GetNext(DataEdit &, int maxRepeat = 1); + virtual bool Emit(const char *, std::size_t); + virtual bool Emit(const char16_t *, std::size_t); + virtual bool Emit(const char32_t *, std::size_t); + virtual bool HandleSlash(int); + virtual bool HandleRelativePosition(std::int64_t); + virtual bool HandleAbsolutePosition(std::int64_t); +}; + +template +class FixedRecordIoStatementState : public IoStatementState { protected: + using Buffer = std::conditional_t; + +public: + FixedRecordIoStatementState( + Buffer, std::size_t, const char *sourceFile, int sourceLine); + + virtual bool Emit(const CHAR *, std::size_t chars /* not bytes */); + // TODO virtual void HandleSlash(int); + virtual bool HandleRelativePosition(std::int64_t); + virtual bool HandleAbsolutePosition(std::int64_t); + virtual int EndIoStatement(); + +private: + Buffer buffer_{nullptr}; + std::size_t length_; // RECL= or internal I/O character variable length + std::size_t leftTabLimit_{0}; // nonzero only when non-advancing + std::size_t at_{0}; + std::size_t furthest_{0}; }; -class InternalIoStatementState : public IoStatementState { +template +class InternalIoStatementState + : public FixedRecordIoStatementState { public: - InternalIoStatementState(const char *sourceFile, int sourceLine); + using typename FixedRecordIoStatementState::Buffer; + InternalIoStatementState(Buffer, std::size_t, + const char *sourceFile = nullptr, int sourceLine = 0); virtual int EndIoStatement(); protected: bool free_{true}; }; -template -class InternalFormattedIoStatementState : public InternalIoStatementState, - private FormatContext { -private: - using Buffer = std::conditional_t; - +template +class InternalFormattedIoStatementState + : public InternalIoStatementState { public: + using typename InternalIoStatementState::Buffer; InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, const CHAR *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); - void Emit(const CHAR *, std::size_t chars); - // TODO pmk: void HandleSlash(int); - void HandleRelativePosition(int); - void HandleAbsolutePosition(int); + void GetNext(DataEdit &edit, int maxRepeat = 1) { + format_.GetNext(*this, edit, maxRepeat); + } int EndIoStatement(); private: - Buffer internal_; - std::size_t internalLength_; - std::size_t at_{0}; FormatControl format_; // must be last, may be partial }; +template +class ExternalFormattedIoStatementState : public IoStatementState { +public: + ExternalFormattedIoStatementState(ExternalFile &, const CHAR *format, + std::size_t formatLength, const char *sourceFile = nullptr, + int sourceLine = 0); + void GetNext(DataEdit &edit, int maxRepeat = 1) { + format_.GetNext(*this, edit, maxRepeat); + } + bool Emit(const CHAR *, std::size_t chars /* not bytes */); + bool HandleSlash(int); + bool HandleRelativePosition(std::int64_t); + bool HandleAbsolutePosition(std::int64_t); + int EndIoStatement(); + +private: + ExternalFile &file_; + FormatControl format_; +}; + extern template class InternalFormattedIoStatementState; +extern template class ExternalFormattedIoStatementState; } #endif // FORTRAN_RUNTIME_IO_STMT_H_ diff --git a/runtime/main.cpp b/runtime/main.cpp index 25b3b02001bf..8c2caa570df5 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -7,35 +7,12 @@ //===----------------------------------------------------------------------===// #include "main.h" -#include "io-stmt.h" +#include "environment.h" #include "terminator.h" +#include "unit.h" #include #include #include -#include - -namespace Fortran::runtime { -ExecutionEnvironment executionEnvironment; - -void ExecutionEnvironment::Configure( - int ac, const char *av[], const char *env[]) { - argc = ac; - argv = av; - envp = env; - listDirectedOutputLineLengthLimit = 79; // PGI default - - if (auto *x{std::getenv("FORT_FMT_RECL")}) { - char *end; - auto n{std::strtol(x, &end, 10)}; - if (n > 0 && n < std::numeric_limits::max() && *end == '\0') { - listDirectedOutputLineLengthLimit = n; - } else { - std::fprintf( - stderr, "Fortran runtime: FORT_FMT_RECL=%s is invalid; ignored\n", x); - } - } -} -} static void ConfigureFloatingPoint() { #ifdef feclearexcept // a macro in some environments; omit std:: @@ -56,5 +33,7 @@ void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { std::atexit(Fortran::runtime::NotifyOtherImagesOfNormalEnd); Fortran::runtime::executionEnvironment.Configure(argc, argv, envp); ConfigureFloatingPoint(); + Fortran::runtime::Terminator terminator{"ProgramStart()"}; + Fortran::runtime::io::ExternalFile::InitializePredefinedUnits(terminator); } } diff --git a/runtime/main.h b/runtime/main.h index c966a3674351..2f2504826465 100644 --- a/runtime/main.h +++ b/runtime/main.h @@ -9,22 +9,11 @@ #ifndef FORTRAN_RUNTIME_MAIN_H_ #define FORTRAN_RUNTIME_MAIN_H_ +#include "c-or-cpp.h" #include "entry-names.h" -namespace Fortran::runtime { -struct ExecutionEnvironment { - void Configure(int argc, const char *argv[], const char *envp[]); - - int argc; - const char **argv; - const char **envp; - int listDirectedOutputLineLengthLimit; -}; -extern ExecutionEnvironment executionEnvironment; -} - -extern "C" { +EXTERN_C_BEGIN void RTNAME(ProgramStart)(int, const char *[], const char *[]); -} +EXTERN_C_END #endif // FORTRAN_RUNTIME_MAIN_H_ diff --git a/runtime/memory.cpp b/runtime/memory.cpp index e2d997caea78..ac456a5dd9d1 100644 --- a/runtime/memory.cpp +++ b/runtime/memory.cpp @@ -25,9 +25,4 @@ void *AllocateMemoryOrCrash(Terminator &terminator, std::size_t bytes) { } void FreeMemory(void *p) { std::free(p); } - -void FreeMemoryAndNullify(void *&p) { - std::free(p); - p = nullptr; -} } diff --git a/runtime/memory.h b/runtime/memory.h index 3e65a98fb224..d41f5f95407e 100644 --- a/runtime/memory.h +++ b/runtime/memory.h @@ -18,15 +18,22 @@ namespace Fortran::runtime { class Terminator; -void *AllocateMemoryOrCrash(Terminator &, std::size_t bytes); -template A &AllocateOrCrash(Terminator &t) { +[[nodiscard]] void *AllocateMemoryOrCrash(Terminator &, std::size_t bytes); +template[[nodiscard]] A &AllocateOrCrash(Terminator &t) { return *reinterpret_cast(AllocateMemoryOrCrash(t, sizeof(A))); } void FreeMemory(void *); -void FreeMemoryAndNullify(void *&); +template void FreeMemory(A *p) { + FreeMemory(reinterpret_cast(p)); +} +template void FreeMemoryAndNullify(A *&p) { + FreeMemory(p); + p = nullptr; +} template struct New { - template A &operator()(Terminator &terminator, X &&... x) { + template + [[nodiscard]] A &operator()(Terminator &terminator, X &&... x) { return *new (AllocateMemoryOrCrash(terminator, sizeof(A))) A{std::forward(x)...}; } @@ -37,6 +44,22 @@ template struct OwningPtrDeleter { }; template using OwningPtr = std::unique_ptr>; + +template struct Allocator { + using value_type = A; + explicit Allocator(Terminator &t) : terminator{t} {} + template + explicit constexpr Allocator(const Allocator &that) noexcept + : terminator{that.terminator} {} + Allocator(const Allocator &) = default; + Allocator(Allocator &&) = default; + [[nodiscard]] constexpr A *allocate(std::size_t n) { + return reinterpret_cast( + AllocateMemoryOrCrash(terminator, n * sizeof(A))); + } + constexpr void deallocate(A *p, std::size_t) { FreeMemory(p); } + Terminator &terminator; +}; } #endif // FORTRAN_RUNTIME_MEMORY_H_ diff --git a/runtime/numeric-output.h b/runtime/numeric-output.h new file mode 100644 index 000000000000..a0f40c700b7b --- /dev/null +++ b/runtime/numeric-output.h @@ -0,0 +1,449 @@ +//===-- runtime/numeric-output.h --------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ +#define FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ + +// Output data editing templates implementing the FORMAT data editing +// descriptors E, EN, ES, EX, D, F, and G for REAL data (and COMPLEX +// components, I and G for INTEGER, and B/O/Z for both. +// See subclauses in 13.7.2.3 of Fortran 2018 for the +// detailed specifications of these descriptors. +// Drives the same binary-to-decimal formatting templates used +// by the f18 compiler. + +#include "format.h" +#include "flang/common/unsigned-const-division.h" +#include "flang/decimal/decimal.h" + +namespace Fortran::runtime::io { + +class IoStatementState; + +// Utility subroutines +static bool EmitRepeated(IoStatementState &io, char ch, int n) { + while (n-- > 0) { + if (!io.Emit(&ch, 1)) { + return false; + } + } + return true; +} + +static bool EmitField( + IoStatementState &io, const char *p, std::size_t length, int width) { + if (width <= 0) { + width = static_cast(length); + } + if (length > static_cast(width)) { + return EmitRepeated(io, '*', width); + } else { + return EmitRepeated(io, ' ', static_cast(width - length)) && + io.Emit(p, length); + } +} + +// I, B, O, Z, and (for INTEGER) G output editing. +// edit is const here so that a repeated edit descriptor may safely serve +// multiple array elements +static bool EditIntegerOutput( + IoStatementState &io, const DataEdit &edit, std::int64_t n) { + char buffer[66], *end = &buffer[sizeof buffer], *p = end; + std::uint64_t un{static_cast(n < 0 ? -n : n)}; + int signChars{0}; + switch (edit.descriptor) { + case 'G': + case 'I': + if (n < 0 || (edit.modes.editingFlags & signPlus)) { + signChars = 1; // '-' or '+' + } + while (un > 0) { + auto quotient{common::DivideUnsignedBy(un)}; + *--p = '0' + un - 10 * quotient; + un = quotient; + } + break; + case 'B': + for (; un > 0; un >>= 1) { + *--p = '0' + (un & 1); + } + break; + case 'O': + for (; un > 0; un >>= 3) { + *--p = '0' + (un & 7); + } + break; + case 'Z': + for (; un > 0; un >>= 4) { + int digit = un & 0xf; + *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; + } + break; + default: + io.Crash( + "Data edit descriptor '%c' may not be used with an INTEGER data item", + edit.descriptor); + return false; + } + + int digits = end - p; + int leadingZeroes{0}; + int editWidth{edit.width.value_or(0)}; + if (edit.digits && digits <= *edit.digits) { // Iw.m + if (*edit.digits == 0 && n == 0) { + // Iw.0 with zero value: output field must be blank. For I0.0 + // and a zero value, emit one blank character. + signChars = 0; // in case of SP + editWidth = std::max(1, editWidth); + } else { + leadingZeroes = *edit.digits - digits; + } + } else if (n == 0) { + leadingZeroes = 1; + } + int total{signChars + leadingZeroes + digits}; + if (edit.width > 0 && total > editWidth) { + return EmitRepeated(io, '*', editWidth); + } + if (total < editWidth) { + EmitRepeated(io, '*', editWidth - total); + return false; + } + if (signChars) { + if (!io.Emit(n < 0 ? "-" : "+", 1)) { + return false; + } + } + return EmitRepeated(io, '0', leadingZeroes) && io.Emit(p, digits); +} + +// Encapsulates the state of a REAL output conversion. +template +class RealOutputEditing { +public: + RealOutputEditing(IoStatementState &io, FLOAT x) : io_{io}, x_{x} {} + bool Edit(const DataEdit &edit); + +private: + // The DataEdit arguments here are const references or copies so that + // the original DataEdit can safely serve multiple array elements if + // it has a repeat count. + bool EditEorDOutput(const DataEdit &); + bool EditFOutput(const DataEdit &); + DataEdit EditForGOutput(DataEdit); // returns an E or F edit + bool EditEXOutput(const DataEdit &); + + bool IsZero() const { return x_ == 0; } + const char *FormatExponent(int, const DataEdit &edit, int &length); + + static enum decimal::FortranRounding SetRounding( + common::RoundingMode rounding) { + switch (rounding) { + case common::RoundingMode::TiesToEven: break; + case common::RoundingMode::Up: return decimal::RoundUp; + case common::RoundingMode::Down: return decimal::RoundDown; + case common::RoundingMode::ToZero: return decimal::RoundToZero; + case common::RoundingMode::TiesAwayFromZero: + return decimal::RoundCompatible; + } + return decimal::RoundNearest; // arranged thus to dodge bogus G++ warning + } + + static bool IsDecimalNumber(const char *p) { + if (!p) { + return false; + } + if (*p == '-' || *p == '+') { + ++p; + } + return *p >= '0' && *p <= '9'; + } + + decimal::ConversionToDecimalResult Convert( + int significantDigits, const DataEdit &, int flags = 0); + + IoStatementState &io_; + FLOAT x_; + char buffer_[bufferSize]; + int trailingBlanks_{0}; // created when G editing maps to F + char exponent_[16]; +}; + +template +decimal::ConversionToDecimalResult RealOutputEditing::Convert(int significantDigits, + const DataEdit &edit, int flags) { + if (edit.modes.editingFlags & signPlus) { + flags |= decimal::AlwaysSign; + } + auto converted{decimal::ConvertToDecimal(buffer_, bufferSize, + static_cast(flags), + significantDigits, SetRounding(edit.modes.roundingMode), + decimal::BinaryFloatingPointNumber(x_))}; + if (!converted.str) { // overflow + io_.Crash("RealOutputEditing::Convert : buffer size %zd was insufficient", + bufferSize); + } + return converted; +} + +// 13.7.2.3.3 in F'2018 +template +bool RealOutputEditing::EditEorDOutput(const DataEdit &edit) { + int editDigits{edit.digits.value_or(0)}; // 'd' field + int editWidth{edit.width.value_or(0)}; // 'w' field + int significantDigits{editDigits}; + int flags{0}; + if (editWidth == 0) { // "the processor selects the field width" + if (edit.digits.has_value()) { // E0.d + editWidth = editDigits + 6; // -.666E+ee + } else { // E0 + flags |= decimal::Minimize; + significantDigits = + bufferSize - 5; // sign, NUL, + 3 extra for EN scaling + } + } + bool isEN{edit.variation == 'N'}; + bool isES{edit.variation == 'S'}; + int scale{isEN || isES ? 1 : edit.modes.scale}; // 'kP' value + int zeroesAfterPoint{0}; + if (scale < 0) { + zeroesAfterPoint = -scale; + significantDigits = std::max(0, significantDigits - zeroesAfterPoint); + } else if (scale > 0) { + ++significantDigits; + scale = std::min(scale, significantDigits + 1); + } + // In EN editing, multiple attempts may be necessary, so it's in a loop. + while (true) { + decimal::ConversionToDecimalResult converted{ + Convert(significantDigits, edit, flags)}; + if (converted.length > 0 && !IsDecimalNumber(converted.str)) { // Inf, NaN + return EmitField(io_, converted.str, converted.length, editWidth); + } + if (!IsZero()) { + converted.decimalExponent -= scale; + } + if (isEN && scale < 3 && (converted.decimalExponent % 3) != 0) { + // EN mode: boost the scale and significant digits, try again; need + // an effective exponent field that's a multiple of three. + ++scale; + ++significantDigits; + continue; + } + // Format the exponent (see table 13.1 for all the cases) + int expoLength{0}; + const char *exponent{ + FormatExponent(converted.decimalExponent, edit, expoLength)}; + int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; + int convertedDigits{static_cast(converted.length) - signLength}; + int zeroesBeforePoint{std::max(0, scale - convertedDigits)}; + int digitsBeforePoint{std::max(0, scale - zeroesBeforePoint)}; + int digitsAfterPoint{convertedDigits - digitsBeforePoint}; + int trailingZeroes{flags & decimal::Minimize + ? 0 + : std::max(0, + significantDigits - (convertedDigits + zeroesBeforePoint))}; + int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + + 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes + + expoLength}; + int width{editWidth > 0 ? editWidth : totalLength}; + if (totalLength > width) { + return EmitRepeated(io_, '*', width); + } + if (totalLength < width && digitsBeforePoint == 0 && + zeroesBeforePoint == 0) { + zeroesBeforePoint = 1; + ++totalLength; + } + return EmitRepeated(io_, ' ', width - totalLength) && + io_.Emit(converted.str, signLength + digitsBeforePoint) && + EmitRepeated(io_, '0', zeroesBeforePoint) && + io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && + EmitRepeated(io_, '0', zeroesAfterPoint) && + io_.Emit( + converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && + EmitRepeated(io_, '0', trailingZeroes) && + io_.Emit(exponent, expoLength); + } +} + +// Formats the exponent (see table 13.1 for all the cases) +template +const char *RealOutputEditing::FormatExponent(int expo, const DataEdit &edit, int &length) { + char *eEnd{&exponent_[sizeof exponent_]}; + char *exponent{eEnd}; + for (unsigned e{static_cast(std::abs(expo))}; e > 0;) { + unsigned quotient{common::DivideUnsignedBy(e)}; + *--exponent = '0' + e - 10 * quotient; + e = quotient; + } + if (edit.expoDigits) { + if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 + while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { + *--exponent = '0'; + } + } else if (exponent == eEnd) { + *--exponent = '0'; // Ew.dE0 with zero-valued exponent + } + } else { // ensure at least two exponent digits + while (exponent + 2 > eEnd) { + *--exponent = '0'; + } + } + *--exponent = expo < 0 ? '-' : '+'; + if (edit.expoDigits || exponent + 3 == eEnd) { + *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' + } + length = eEnd - exponent; + return exponent; +} + +// 13.7.2.3.2 in F'2018 +template +bool RealOutputEditing::EditFOutput(const DataEdit &edit) { + int fracDigits{edit.digits.value_or(0)}; // 'd' field + int extraDigits{0}; + int editWidth{edit.width.value_or(0)}; // 'w' field + int flags{0}; + if (editWidth == 0) { // "the processor selects the field width" + if (!edit.digits.has_value()) { // F0 + flags |= decimal::Minimize; + fracDigits = bufferSize - 2; // sign & NUL + } + } + // Multiple conversions may be needed to get the right number of + // effective rounded fractional digits. + while (true) { + decimal::ConversionToDecimalResult converted{ + Convert(extraDigits + fracDigits, edit, flags)}; + if (converted.length > 0 && !IsDecimalNumber(converted.str)) { // Inf, NaN + return EmitField(io_, converted.str, converted.length, editWidth); + } + int scale{IsZero() ? -1 : edit.modes.scale}; + int expo{converted.decimalExponent - scale}; + if (expo > extraDigits) { + extraDigits = expo; + if (flags & decimal::Minimize) { + fracDigits = bufferSize - extraDigits - 2; // sign & NUL + } + continue; // try again + } + int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0}; + int convertedDigits{static_cast(converted.length) - signLength}; + int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))}; + int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)}; + int zeroesAfterPoint{std::max(0, -expo)}; + int digitsAfterPoint{convertedDigits - digitsBeforePoint}; + int trailingZeroes{flags & decimal::Minimize + ? 0 + : std::max(0, fracDigits - (zeroesAfterPoint + digitsAfterPoint))}; + if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint + + digitsAfterPoint + trailingZeroes == + 0) { + ++zeroesBeforePoint; // "." -> "0." + } + int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint + + 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes}; + int width{editWidth > 0 ? editWidth : totalLength}; + if (totalLength > width) { + return EmitRepeated(io_, '*', width); + } + if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { + zeroesBeforePoint = 1; + ++totalLength; + } + return EmitRepeated(io_, ' ', width - totalLength) && + io_.Emit(converted.str, signLength + digitsBeforePoint) && + EmitRepeated(io_, '0', zeroesBeforePoint) && + io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && + EmitRepeated(io_, '0', zeroesAfterPoint) && + io_.Emit( + converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && + EmitRepeated(io_, '0', trailingZeroes) && + EmitRepeated(io_, ' ', trailingBlanks_); + } +} + +// 13.7.5.2.3 in F'2018 +template +DataEdit RealOutputEditing::EditForGOutput(DataEdit edit) { + edit.descriptor = 'E'; + if (!edit.width.has_value() || + (*edit.width > 0 && edit.digits.value_or(-1) == 0)) { + return edit; // Gw.0 -> Ew.0 for w > 0 + } + decimal::ConversionToDecimalResult converted{Convert(1, edit)}; + if (!IsDecimalNumber(converted.str)) { // Inf, NaN + return edit; + } + int expo{IsZero() ? 1 : converted.decimalExponent}; // 's' + int significantDigits{edit.digits.value_or(decimalPrecision)}; // 'd' + if (expo < 0 || expo > significantDigits) { + return edit; // Ew.d + } + edit.descriptor = 'F'; + edit.modes.scale = 0; // kP is ignored for G when no exponent field + trailingBlanks_ = 0; + int editWidth{edit.width.value_or(0)}; + if (editWidth > 0) { + int expoDigits{edit.expoDigits.value_or(0)}; + trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n' + *edit.width = std::max(0, editWidth - trailingBlanks_); + } + if (edit.digits.has_value()) { + *edit.digits = std::max(0, *edit.digits - expo); + } + return edit; +} + +// 13.7.5.2.6 in F'2018 +template +bool RealOutputEditing::EditEXOutput(const DataEdit &) { + io_.Crash("EX output editing is not yet implemented"); // TODO +} + +template +bool RealOutputEditing::Edit(const DataEdit &edit) { + switch (edit.descriptor) { + case 'D': return EditEorDOutput(edit); + case 'E': + if (edit.variation == 'X') { + return EditEXOutput(edit); + } else { + return EditEorDOutput(edit); + } + case 'F': return EditFOutput(edit); + case 'B': + case 'O': + case 'Z': + return EditIntegerOutput(io_, edit, decimal::BinaryFloatingPointNumber{x_}.raw); + case 'G': return Edit(EditForGOutput(edit)); + default: + io_.Crash("Data edit descriptor '%c' may not be used with a REAL data item", + edit.descriptor); + return false; + } + return false; +} +} +#endif // FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ diff --git a/runtime/stop.cpp b/runtime/stop.cpp index 8bf665f82512..85bf9c4a14ac 100644 --- a/runtime/stop.cpp +++ b/runtime/stop.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "stop.h" +#include "io-error.h" #include "terminator.h" +#include "unit.h" #include #include #include @@ -66,4 +68,10 @@ static void DescribeIEEESignaledExceptions() { Fortran::runtime::NotifyOtherImagesOfFailImageStatement(); std::exit(EXIT_FAILURE); } + +[[noreturn]] void RTNAME(ProgramEndStatement)() { + Fortran::runtime::io::IoErrorHandler handler{"END statement"}; + Fortran::runtime::io::ExternalFile::CloseAll(handler); + std::exit(EXIT_SUCCESS); +} } diff --git a/runtime/stop.h b/runtime/stop.h index b3d8e32c5de4..8fb1d8d4cffd 100644 --- a/runtime/stop.h +++ b/runtime/stop.h @@ -21,6 +21,7 @@ NORETURN void RTNAME(StopStatement)(int code DEFAULT_VALUE(EXIT_SUCCESS), NORETURN void RTNAME(StopStatementText)(const char *, bool isErrorStop DEFAULT_VALUE(false), bool quiet DEFAULT_VALUE(false)); NORETURN void RTNAME(FailImageStatement)(NO_ARGUMENTS); +NORETURN void RTNAME(ProgramEndStatement)(NO_ARGUMENTS); EXTERN_C_END diff --git a/runtime/tools.h b/runtime/tools.h index 184f6af63f8e..d1b90b1ad3c9 100644 --- a/runtime/tools.h +++ b/runtime/tools.h @@ -8,7 +8,12 @@ #ifndef FORTRAN_RUNTIME_TOOLS_H_ #define FORTRAN_RUNTIME_TOOLS_H_ + #include "memory.h" +#include +#include +#include + namespace Fortran::runtime { class Terminator; @@ -21,5 +26,11 @@ OwningPtr SaveDefaultCharacter(const char *, std::size_t, Terminator &); // or -1 when it has no match. int IdentifyValue( const char *value, std::size_t length, const char *possibilities[]); + +// A std::map<> customized to use the runtime's memory allocator +template +using MapAllocator = Allocator, VALUE>>; +template> +using Map = std::map>; } #endif // FORTRAN_RUNTIME_TOOLS_H_ diff --git a/runtime/unit.cpp b/runtime/unit.cpp new file mode 100644 index 000000000000..f7a342ccbb73 --- /dev/null +++ b/runtime/unit.cpp @@ -0,0 +1,137 @@ +//===-- runtime/unit.cpp ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "unit.h" +#include "lock.h" +#include "memory.h" +#include "tools.h" +#include +#include + +namespace Fortran::runtime::io { + +static Lock mapLock; +static Terminator mapTerminator; +static Map unitMap{MapAllocator{mapTerminator}}; + +ExternalFile *ExternalFile::LookUp(int unit) { + CriticalSection criticalSection{mapLock}; + auto iter{unitMap.find(unit)}; + return iter == unitMap.end() ? nullptr : &iter->second; +} + +ExternalFile &ExternalFile::LookUpOrCrash(int unit, Terminator &terminator) { + CriticalSection criticalSection{mapLock}; + ExternalFile *file{LookUp(unit)}; + if (!file) { + terminator.Crash("Not an open I/O unit number: %d", unit); + } + return *file; +} + +ExternalFile &ExternalFile::Create(int unit, Terminator &terminator) { + CriticalSection criticalSection{mapLock}; + auto pair{unitMap.emplace(unit, unit)}; + if (!pair.second) { + terminator.Crash("Already opened I/O unit number: %d", unit); + } + return pair.first->second; +} + +void ExternalFile::CloseUnit(IoErrorHandler &handler) { + CriticalSection criticalSection{mapLock}; + Flush(handler); + auto iter{unitMap.find(unitNumber_)}; + if (iter != unitMap.end()) { + unitMap.erase(iter); + } +} + +void ExternalFile::InitializePredefinedUnits(Terminator &terminator) { + ExternalFile &out{ExternalFile::Create(6, terminator)}; + out.Predefine(1); + out.set_mayRead(false); + out.set_mayWrite(true); + out.set_mayPosition(false); + ExternalFile &in{ExternalFile::Create(5, terminator)}; + in.Predefine(0); + in.set_mayRead(true); + in.set_mayWrite(false); + in.set_mayPosition(false); + // TODO: Set UTF-8 mode from the environment +} + +void ExternalFile::CloseAll(IoErrorHandler &handler) { + CriticalSection criticalSection{mapLock}; + while (!unitMap.empty()) { + auto &pair{*unitMap.begin()}; + pair.second.CloseUnit(handler); + } +} + +bool ExternalFile::SetPositionInRecord(std::int64_t n, IoErrorHandler &handler) { + n = std::max(std::int64_t{0}, n); + bool ok{true}; + if (n > recordLength.value_or(n)) { + handler.SignalEor(); + n = *recordLength; + ok = false; + } + if (n > furthestPositionInRecord) { + if (!isReading_ && ok) { + WriteFrame(recordOffsetInFile, n, handler); + std::fill_n(Frame() + furthestPositionInRecord, n - furthestPositionInRecord, ' '); + } + furthestPositionInRecord = n; + } + positionInRecord = n; + return ok; +} + +bool ExternalFile::Emit(const char *data, std::size_t bytes, IoErrorHandler &handler) { + auto furthestAfter{std::max(furthestPositionInRecord, positionInRecord + static_cast(bytes))}; + WriteFrame(recordOffsetInFile, furthestAfter, handler); + std::memcpy(Frame() + positionInRecord, data, bytes); + positionInRecord += bytes; + furthestPositionInRecord = furthestAfter; + return true; +} + +void ExternalFile::SetLeftTabLimit() { + leftTabLimit = furthestPositionInRecord; + positionInRecord = furthestPositionInRecord; +} + +bool ExternalFile::NextOutputRecord(IoErrorHandler &handler) { + bool ok{true}; + if (recordLength.has_value()) { // fill fixed-size record + ok &= SetPositionInRecord(*recordLength, handler); + } else if (!unformatted && !isReading_) { + ok &= SetPositionInRecord(furthestPositionInRecord, handler) && + Emit("\n", 1, handler); + } + recordOffsetInFile += furthestPositionInRecord; + ++currentRecordNumber; + positionInRecord = 0; + positionInRecord = furthestPositionInRecord = 0; + leftTabLimit.reset(); + return ok; +} + +bool ExternalFile::HandleAbsolutePosition(std::int64_t n, IoErrorHandler &handler) { + return SetPositionInRecord(std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0), handler); +} + +bool ExternalFile::HandleRelativePosition(std::int64_t n, IoErrorHandler &handler) { + return HandleAbsolutePosition(positionInRecord + n, handler); +} + +void ExternalFile::EndIoStatement() { + u_.emplace(); +} +} diff --git a/runtime/unit.h b/runtime/unit.h new file mode 100644 index 000000000000..a6b80b22587e --- /dev/null +++ b/runtime/unit.h @@ -0,0 +1,114 @@ +//===-- runtime/unit.h ------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Fortran I/O units + +#ifndef FORTRAN_RUNTIME_IO_UNIT_H_ +#define FORTRAN_RUNTIME_IO_UNIT_H_ + +#include "buffer.h" +#include "descriptor.h" +#include "file.h" +#include "format.h" +#include "io-error.h" +#include "io-stmt.h" +#include "lock.h" +#include "memory.h" +#include "terminator.h" +#include +#include +#include +#include + +namespace Fortran::runtime::io { + +enum class Access { Sequential, Direct, Stream }; + +inline bool IsRecordFile(Access a) { return a != Access::Stream; } + +// These characteristics of a connection are immutable after being +// established in an OPEN statement. +struct ConnectionAttributes { + Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' + std::optional recordLength; // RECL= when fixed-length + bool unformatted{false}; // FORM='UNFORMATTED' + bool isUTF8{false}; // ENCODING='UTF-8' + bool asynchronousAllowed{false}; // ASYNCHRONOUS='YES' +}; + +struct ConnectionState : public ConnectionAttributes { + // Positions in a record file (sequential or direct, but not stream) + std::int64_t recordOffsetInFile{0}; + std::int64_t currentRecordNumber{1}; // 1 is first + std::int64_t positionInRecord{0}; // offset in current record + std::int64_t furthestPositionInRecord{0}; // max(positionInRecord) + std::optional leftTabLimit; // offset in current record + // nextRecord value captured after ENDFILE/REWIND/BACKSPACE statement + // on a sequential access file + std::optional endfileRecordNumber; + // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT + MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP +}; + +class InternalUnit : public ConnectionState, public IoErrorHandler { +public: + InternalUnit(Descriptor &, const char *sourceFile, int sourceLine) + : IoErrorHandler{sourceFile, sourceLine} { +// TODO pmk descriptor_.Establish(...); + descriptor_.GetLowerBounds(at_); + recordLength = descriptor_.ElementBytes(); + endfileRecordNumber = descriptor_.Elements(); + } + ~InternalUnit() { + if (!doNotFree_) { + std::free(this); + } + } + +private: + bool doNotFree_{false}; + Descriptor descriptor_; + SubscriptValue at_[maxRank]; +}; + +class ExternalFile : public ConnectionState, // TODO: privatize these + public OpenFile, + public FileFrame { +public: + explicit ExternalFile(int unitNumber) : unitNumber_{unitNumber} {} + static ExternalFile *LookUp(int unit); + static ExternalFile &LookUpOrCrash(int unit, Terminator &); + static ExternalFile &Create(int unit, Terminator &); + static void InitializePredefinedUnits(Terminator &); + static void CloseAll(IoErrorHandler &); + + void CloseUnit(IoErrorHandler &); + + // TODO: accessors & mutators for many OPEN() specifiers + template A &BeginIoStatement(X&&... xs) { + // TODO: lock_.Take() here, and keep it until EndIoStatement()? + // Nested I/O from derived types wouldn't work, though. + return u_.emplace(std::forward(xs)...); + } + void EndIoStatement(); + + bool SetPositionInRecord(std::int64_t, IoErrorHandler &); + bool Emit(const char *, std::size_t bytes, IoErrorHandler &); + void SetLeftTabLimit(); + bool NextOutputRecord(IoErrorHandler &); + bool HandleAbsolutePosition(std::int64_t, IoErrorHandler &); + bool HandleRelativePosition(std::int64_t, IoErrorHandler &); +private: + int unitNumber_{-1}; + Lock lock_; + bool isReading_{false}; + std::variant> u_; +}; + +} +#endif // FORTRAN_RUNTIME_IO_UNIT_H_ diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index fda3776cc906..feadddfa880a 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -29,3 +29,11 @@ target_link_libraries(hello-world ) add_test(HelloWorld hello-world) + +add_executable(external-hello-world + external-hello.cpp +) + +target_link_libraries(external-hello-world + FortranRuntime +) diff --git a/test/runtime/external-hello.cpp b/test/runtime/external-hello.cpp new file mode 100644 index 000000000000..af7151f6c44e --- /dev/null +++ b/test/runtime/external-hello.cpp @@ -0,0 +1,15 @@ +#include "../../runtime/io-api.h" +#include "../../runtime/main.h" +#include "../../runtime/stop.h" +#include + +using namespace Fortran::runtime::io; + +int main(int argc, const char *argv[], const char *envp[]) { + static const char *format{"(12HHELLO, WORLD)"}; + RTNAME(ProgramStart)(argc, argv, envp); + auto *io{IONAME(BeginExternalFormattedOutput)(format, std::strlen(format))}; + IONAME(EndIoStatement)(io); + RTNAME(ProgramEndStatement)(); + return 0; +} diff --git a/test/runtime/format.cpp b/test/runtime/format.cpp index 937a4434b8d7..31e3261d8f88 100644 --- a/test/runtime/format.cpp +++ b/test/runtime/format.cpp @@ -1,4 +1,5 @@ -// Test basic FORMAT string traversal +// Tests basic FORMAT string traversal + #include "../runtime/format.h" #include "../runtime/terminator.h" #include @@ -17,17 +18,20 @@ using Results = std::list; // Test harness context for format control struct TestFormatContext : virtual public Terminator, public FormatContext { TestFormatContext() : Terminator{"format.cpp", 1} {} - void Emit(const char *, std::size_t); - void HandleSlash(int = 1); - void HandleRelativePosition(int); - void HandleAbsolutePosition(int); + bool Emit(const char *, std::size_t); + bool Emit(const char16_t *, std::size_t); + bool Emit(const char32_t *, std::size_t); + bool HandleSlash(int = 1); + bool HandleRelativePosition(std::int64_t); + bool HandleAbsolutePosition(std::int64_t); void Report(const DataEdit &); void Check(Results &); Results results; }; // Override the runtime's Crash() for testing purposes -[[noreturn]] void Fortran::runtime::Terminator::Crash(const char *message, ...) { +[[noreturn]] void Fortran::runtime::Terminator::Crash( + const char *message, ...) { std::va_list ap; va_start(ap, message); char buffer[1000]; @@ -36,27 +40,39 @@ struct TestFormatContext : virtual public Terminator, public FormatContext { throw std::string{buffer}; } -void TestFormatContext::Emit(const char *s, std::size_t len) { +bool TestFormatContext::Emit(const char *s, std::size_t len) { std::string str{s, len}; results.push_back("'"s + str + '\''); + return true; +} +bool TestFormatContext::Emit(const char16_t *, std::size_t) { + Crash("TestFormatContext::Emit(const char16_t *) called"); + return false; +} +bool TestFormatContext::Emit(const char32_t *, std::size_t) { + Crash("TestFormatContext::Emit(const char32_t *) called"); + return false; } -void TestFormatContext::HandleSlash(int n) { +bool TestFormatContext::HandleSlash(int n) { while (n-- > 0) { results.emplace_back("/"); } + return true; } -void TestFormatContext::HandleAbsolutePosition(int n) { +bool TestFormatContext::HandleAbsolutePosition(std::int64_t n) { results.push_back("T"s + std::to_string(n)); + return true; } -void TestFormatContext::HandleRelativePosition(int n) { +bool TestFormatContext::HandleRelativePosition(std::int64_t n) { if (n < 0) { results.push_back("TL"s + std::to_string(-n)); } else { results.push_back(std::to_string(n) + 'X'); } + return true; } void TestFormatContext::Report(const DataEdit &edit) { @@ -67,7 +83,9 @@ void TestFormatContext::Report(const DataEdit &edit) { if (edit.variation) { str += edit.variation; } - str += std::to_string(edit.width); + if (edit.width) { + str += std::to_string(*edit.width); + } if (edit.digits) { str += "."s + std::to_string(*edit.digits); } diff --git a/test/runtime/hello.cpp b/test/runtime/hello.cpp index 9c52a01b26a9..86354a36de5e 100644 --- a/test/runtime/hello.cpp +++ b/test/runtime/hello.cpp @@ -1,4 +1,4 @@ -// Basic tests of I/O API +// Basic sanity tests of I/O API; exhaustive testing will be done in Fortran #include "../../runtime/io-api.h" #include @@ -8,22 +8,334 @@ using namespace Fortran::runtime::io; static int failures{0}; -int main() { +static void test(const char *format, const char *expect, std::string &&got) { + std::string want{expect}; + want.resize(got.length(), ' '); + if (got != want) { + std::cerr << '\'' << format << "' failed;\n got '" << got + << "',\nexpected '" << want << "'\n"; + ++failures; + } +} + +static void hello() { char buffer[32]; - const char *format1{"(12HHELLO, WORLD)"}; - auto cookie{IONAME(BeginInternalFormattedOutput)(buffer, sizeof buffer, format1, std::strlen(format1))}; + const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"}; + auto cookie{IONAME(BeginInternalFormattedOutput)( + buffer, sizeof buffer, format, std::strlen(format))}; + IONAME(OutputAscii)(cookie, "WORLD", 5); + IONAME(OutputInteger64)(cookie, 678); + IONAME(OutputInteger64)(cookie, 0xfeedface); + IONAME(OutputLogical)(cookie, true); if (auto status{IONAME(EndIoStatement)(cookie)}) { - std::cerr << "format1 failed, status " << static_cast(status) << '\n'; + std::cerr << '\'' << format << "' failed, status " + << static_cast(status) << '\n'; ++failures; + } else { + test(format, "HELLO, WORLD 678 0xFEEDFACE T", + std::string{buffer, sizeof buffer}); } - std::string got1{buffer, sizeof buffer}; - std::string expect1{"HELLO, WORLD"}; - expect1.resize(got1.length(), ' '); - if (got1 != expect1) { - std::cerr << "format1 failed, got '" << got1 << "', expected '" << expect1 << "'\n"; +} + +static void realTest(const char *format, double x, const char *expect) { + char buffer[800]; + auto cookie{IONAME(BeginInternalFormattedOutput)( + buffer, sizeof buffer, format, std::strlen(format))}; + IONAME(OutputReal64)(cookie, x); + if (auto status{IONAME(EndIoStatement)(cookie)}) { + std::cerr << '\'' << format << "' failed, status " + << static_cast(status) << '\n'; ++failures; + } else { + test(format, expect, std::string{buffer, sizeof buffer}); + } +} + +int main() { + hello(); + + static const char *zeroes[][2]{ + {"(E32.17,';')", " 0.00000000000000000E+00;"}, + {"(F32.17,';')", " 0.00000000000000000;"}, + {"(G32.17,';')", " 0.0000000000000000 ;"}, + {"(DC,E32.17,';')", " 0,00000000000000000E+00;"}, + {"(DC,F32.17,';')", " 0,00000000000000000;"}, + {"(DC,G32.17,';')", " 0,0000000000000000 ;"}, + {"(D32.17,';')", " 0.00000000000000000D+00;"}, + {"(E32.17E1,';')", " 0.00000000000000000E+0;"}, + {"(G32.17E1,';')", " 0.0000000000000000 ;"}, + {"(E32.17E0,';')", " 0.00000000000000000E+0;"}, + {"(G32.17E0,';')", " 0.0000000000000000 ;"}, + {"(1P,E32.17,';')", " 0.00000000000000000E+00;"}, + {"(1P,F32.17,';')", " 0.00000000000000000;"}, + {"(1P,G32.17,';')", " 0.0000000000000000 ;"}, + {"(2P,E32.17,';')", " 00.0000000000000000E+00;"}, + {"(-1P,E32.17,';')", " 0.00000000000000000E+00;"}, + {"(G0,';')", "0.;"}, {}}; + for (int j{0}; zeroes[j][0]; ++j) { + realTest(zeroes[j][0], 0.0, zeroes[j][1]); + } + + static const char *ones[][2]{ + {"(E32.17,';')", " 0.10000000000000000E+01;"}, + {"(F32.17,';')", " 1.00000000000000000;"}, + {"(G32.17,';')", " 1.0000000000000000 ;"}, + {"(E32.17E1,';')", " 0.10000000000000000E+1;"}, + {"(G32.17E1,';')", " 1.0000000000000000 ;"}, + {"(E32.17E0,';')", " 0.10000000000000000E+1;"}, + {"(G32.17E0,';')", " 1.0000000000000000 ;"}, + {"(E32.17E4,';')", " 0.10000000000000000E+0001;"}, + {"(G32.17E4,';')", " 1.0000000000000000 ;"}, + {"(1P,E32.17,';')", " 1.00000000000000000E+00;"}, + {"(1P,F32.17,';')", " 0.10000000000000000;"}, + {"(1P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(ES32.17,';')", " 1.00000000000000000E+00;"}, + {"(2P,E32.17,';')", " 10.0000000000000000E-01;"}, + {"(2P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(-1P,E32.17,';')", " 0.01000000000000000E+02;"}, + {"(-1P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(G0,';')", "1.;"}, {}}; + for (int j{0}; ones[j][0]; ++j) { + realTest(ones[j][0], 1.0, ones[j][1]); } + realTest("(E32.17,';')", -1.0, " -0.10000000000000000E+01;"); + realTest("(F32.17,';')", -1.0, " -1.00000000000000000;"); + realTest("(G32.17,';')", -1.0, " -1.0000000000000000 ;"); + realTest("(G0,';')", -1.0, "-1.;"); + + volatile union { + double d; + std::uint64_t n; + } u; + u.n = 0x8000000000000000; // -0 + realTest("(E9.1,';')", u.d, " -0.0E+00;"); + realTest("(F4.0,';')", u.d, " -0.;"); + realTest("(G8.0,';')", u.d, "-0.0E+00;"); + realTest("(G8.1,';')", u.d, " -0. ;"); + realTest("(G0,';')", u.d, "-0.;"); + u.n = 0x7ff0000000000000; // +Inf + realTest("(E9.1,';')", u.d, " Inf;"); + realTest("(F9.1,';')", u.d, " Inf;"); + realTest("(G9.1,';')", u.d, " Inf;"); + realTest("(SP,E9.1,';')", u.d, " +Inf;"); + realTest("(SP,F9.1,';')", u.d, " +Inf;"); + realTest("(SP,G9.1,';')", u.d, " +Inf;"); + realTest("(G0,';')", u.d, "Inf;"); + u.n = 0xfff0000000000000; // -Inf + realTest("(E9.1,';')", u.d, " -Inf;"); + realTest("(F9.1,';')", u.d, " -Inf;"); + realTest("(G9.1,';')", u.d, " -Inf;"); + realTest("(G0,';')", u.d, "-Inf;"); + u.n = 0x7ff0000000000001; // NaN + realTest("(E9.1,';')", u.d, " NaN;"); + realTest("(F9.1,';')", u.d, " NaN;"); + realTest("(G9.1,';')", u.d, " NaN;"); + realTest("(G0,';')", u.d, "NaN;"); + u.n = 0xfff0000000000001; // NaN (sign irrelevant) + realTest("(E9.1,';')", u.d, " NaN;"); + realTest("(F9.1,';')", u.d, " NaN;"); + realTest("(G9.1,';')", u.d, " NaN;"); + realTest("(SP,E9.1,';')", u.d, " NaN;"); + realTest("(SP,F9.1,';')", u.d, " NaN;"); + realTest("(SP,G9.1,';')", u.d, " NaN;"); + realTest("(G0,';')", u.d, "NaN;"); + + u.n = 0x3fb999999999999a; // 0.1 rounded + realTest("(E62.55,';')", u.d, + " 0.1000000000000000055511151231257827021181583404541015625E+00;"); + realTest("(E0.0,';')", u.d, "0.E+00;"); + realTest("(E0.55,';')", u.d, + "0.1000000000000000055511151231257827021181583404541015625E+00;"); + realTest("(E0,';')", u.d, ".1E+00;"); + realTest("(F58.55,';')", u.d, + " 0.1000000000000000055511151231257827021181583404541015625;"); + realTest("(F0.0,';')", u.d, "0.;"); + realTest("(F0.55,';')", u.d, + ".1000000000000000055511151231257827021181583404541015625;"); + realTest("(F0,';')", u.d, ".1;"); + realTest("(G62.55,';')", u.d, + " 0.1000000000000000055511151231257827021181583404541015625 ;"); + realTest("(G0.0,';')", u.d, "0.;"); + realTest("(G0.55,';')", u.d, + ".1000000000000000055511151231257827021181583404541015625;"); + realTest("(G0,';')", u.d, ".1;"); + + u.n = 0x3ff8000000000000; // 1.5 + realTest("(E9.2,';')", u.d, " 0.15E+01;"); + realTest("(F4.1,';')", u.d, " 1.5;"); + realTest("(G7.1,';')", u.d, " 2. ;"); + realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RN,F3.0,';')", u.d, " 2.;"); + realTest("(RN,G7.0,';')", u.d, " 0.E+01;"); + realTest("(RN,G7.1,';')", u.d, " 2. ;"); + realTest("(RD,E8.1,';')", u.d, " 0.1E+01;"); + realTest("(RD,F3.0,';')", u.d, " 1.;"); + realTest("(RD,G7.0,';')", u.d, " 0.E+01;"); + realTest("(RD,G7.1,';')", u.d, " 1. ;"); + realTest("(RU,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RU,G7.0,';')", u.d, " 0.E+01;"); + realTest("(RU,G7.1,';')", u.d, " 2. ;"); + realTest("(RZ,E8.1,';')", u.d, " 0.1E+01;"); + realTest("(RZ,F3.0,';')", u.d, " 1.;"); + realTest("(RZ,G7.0,';')", u.d, " 0.E+01;"); + realTest("(RZ,G7.1,';')", u.d, " 1. ;"); + realTest("(RC,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RC,F3.0,';')", u.d, " 2.;"); + realTest("(RC,G7.0,';')", u.d, " 0.E+01;"); + realTest("(RC,G7.1,';')", u.d, " 2. ;"); + + // TODO continue F and G editing tests on these data + + u.n = 0xbff8000000000000; // -1.5 + realTest("(E9.2,';')", u.d, "-0.15E+01;"); + realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); + realTest("(RD,E8.1,';')", u.d, "-0.2E+01;"); + realTest("(RU,E8.1,';')", u.d, "-0.1E+01;"); + realTest("(RZ,E8.1,';')", u.d, "-0.1E+01;"); + realTest("(RC,E8.1,';')", u.d, "-0.2E+01;"); + + u.n = 0x4004000000000000; // 2.5 + realTest("(E9.2,';')", u.d, " 0.25E+01;"); + realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RD,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RU,E8.1,';')", u.d, " 0.3E+01;"); + realTest("(RZ,E8.1,';')", u.d, " 0.2E+01;"); + realTest("(RC,E8.1,';')", u.d, " 0.3E+01;"); + + u.n = 0xc004000000000000; // -2.5 + realTest("(E9.2,';')", u.d, "-0.25E+01;"); + realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); + realTest("(RD,E8.1,';')", u.d, "-0.3E+01;"); + realTest("(RU,E8.1,';')", u.d, "-0.2E+01;"); + realTest("(RZ,E8.1,';')", u.d, "-0.2E+01;"); + realTest("(RC,E8.1,';')", u.d, "-0.3E+01;"); + + u.n = 1; // least positive nonzero subnormal + realTest("(E32.17,';')", u.d, " 0.49406564584124654-323;"); + realTest("(ES32.17,';')", u.d, " 4.94065645841246544-324;"); + realTest("(EN32.17,';')", u.d, " 4.94065645841246544-324;"); + realTest("(E759.752,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "75386825064197182655334472656250-323;"); + realTest("(G0,';')", u.d, ".5-323;"); + realTest("(E757.750,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"); + realTest("(RN,E757.750,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"); + realTest("(RD,E757.750,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"); + realTest("(RU,E757.750,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726563-323;"); + realTest("(RC,E757.750,';')", u.d, + " 0." + "494065645841246544176568792868221372365059802614324764425585682500675507" + "270208751865299836361635992379796564695445717730926656710355939796398774" + "796010781878126300713190311404527845817167848982103688718636056998730723" + "050006387409153564984387312473397273169615140031715385398074126238565591" + "171026658556686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355238611550134" + "803526493472019379026810710749170333222684475333572083243193609238289345" + "836806010601150616980975307834227731832924790498252473077637592724787465" + "608477820373446969953364701797267771758512566055119913150489110145103786" + "273816725095583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726563-323;"); + + u.n = 0x10000000000000; // least positive nonzero normal + realTest("(E723.716,';')", u.d, + " 0." + "222507385850720138309023271733240406421921598046233183055332741688720443" + "481391819585428315901251102056406733973103581100515243416155346010885601" + "238537771882113077799353200233047961014744258363607192156504694250373420" + "837525080665061665815894872049117996859163964850063590877011830487479978" + "088775374994945158045160505091539985658247081864511353793580499211598108" + "576605199243335211435239014879569960959128889160299264151106346631339366" + "347758651302937176204732563178148566435087212282863764204484681140761391" + "147706280168985324411002416144742161856716615054015428508471675290190316" + "132277889672970737312333408698898317506783884692609277397797285865965494" + "10913690954061364675687023986783152906809846172109246253967285156250-" + "307;"); + realTest("(G0,';')", u.d, ".22250738585072014-307;"); + + u.n = 0x7fefffffffffffffuLL; // greatest finite + realTest("(E32.17,';')", u.d, " 0.17976931348623157+309;"); + realTest("(E317.310,';')", u.d, + " 0." + "179769313486231570814527423731704356798070567525844996598917476803157260" + "780028538760589558632766878171540458953514382464234321326889464182768467" + "546703537516986049910576551282076245490090389328944075868508455133942304" + "583236903222948165808559332123348274797826204144723168738177180919299881" + "2504040261841248583680+309;"); + realTest("(ES317.310,';')", u.d, + " 1." + "797693134862315708145274237317043567980705675258449965989174768031572607" + "800285387605895586327668781715404589535143824642343213268894641827684675" + "467035375169860499105765512820762454900903893289440758685084551339423045" + "832369032229481658085593321233482747978262041447231687381771809192998812" + "5040402618412485836800+308;"); + realTest("(EN319.310,';')", u.d, + " 179." + "769313486231570814527423731704356798070567525844996598917476803157260780" + "028538760589558632766878171540458953514382464234321326889464182768467546" + "703537516986049910576551282076245490090389328944075868508455133942304583" + "236903222948165808559332123348274797826204144723168738177180919299881250" + "4040261841248583680000+306;"); + realTest("(G0,';')", u.d, ".17976931348623157+309;"); + if (failures == 0) { std::cout << "PASS\n"; } else { From 572a57d3d0d785bb3f2aad9e890ef498c1214309 Mon Sep 17 00:00:00 2001 From: "Jinxin (Brian) Yang" Date: Wed, 5 Feb 2020 10:13:43 -0800 Subject: [PATCH 020/345] [OpenMP] Predetermined rules for loop index variables (#962) This refers to three rules in OpenMP 4.5 Spec 2.15.1.1: * The loop iteration variable(s) in the associated do-loop(s) of a do, parallel do, taskloop, or distribute construct is (are) private. * The loop iteration variable in the associated do-loop of a simd construct with just one associated do-loop is linear with a linear-step that is the increment of the associated do-loop. * The loop iteration variables in the associated do-loops of a simd construct with multiple associated do-loops are lastprivate. A simple example: ``` implicit none integer :: N = 1024 integer i, j, k !$omp parallel do collapse(3) do i=1, N <- i is private do j=1, N <- j is private do k=1, N <- k is private enddo enddo enddo end ``` If `collapse` clause is not present, the associated do-loop for construct `parallel do` is only `i` loop. With `collapse(n)`, `i`, `j`, and `k` are all associated do-loops and the loop index variables are private to the OpenMP construct: ``` implicit none !DEF: /MainProgram1/n ObjectEntity INTEGER(4) integer :: n = 1024 !DEF: /MainProgram1/i ObjectEntity INTEGER(4) !DEF: /MainProgram1/j ObjectEntity INTEGER(4) !DEF: /MainProgram1/k ObjectEntity INTEGER(4) integer i, j, k !$omp parallel do collapse(3) !DEF: /MainProgram1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) !REF: /MainProgram1/n do i=1,n !DEF: /MainProgram1/Block1/j (OmpPrivate) HostAssoc INTEGER(4) !REF: /MainProgram1/n do j=1,n !DEF: /MainProgram1/Block1/k (OmpPrivate) HostAssoc INTEGER(4) !REF: /MainProgram1/n do k=1,n end do end do end do end program ``` This implementation assumes that the structural checks for do-loops are done at this point, for example the `n` in `collapse(n)` should be no more than the number of actual perfectly nested do-loops, etc.. --- lib/semantics/check-omp-structure.cpp | 43 ----- lib/semantics/check-omp-structure.h | 43 +++++ lib/semantics/resolve-names.cpp | 145 +++++++++++++++- test/semantics/CMakeLists.txt | 1 + test/semantics/omp-clause-validity01.f90 | 10 +- test/semantics/omp-device-constructs.f90 | 6 +- test/semantics/omp-symbol01.f90 | 8 +- test/semantics/omp-symbol04.f90 | 2 +- test/semantics/omp-symbol06.f90 | 2 +- test/semantics/omp-symbol08.f90 | 209 +++++++++++++++++++++++ 10 files changed, 414 insertions(+), 55 deletions(-) create mode 100644 test/semantics/omp-symbol08.f90 diff --git a/lib/semantics/check-omp-structure.cpp b/lib/semantics/check-omp-structure.cpp index 8d22d422e909..c8493c05b22c 100644 --- a/lib/semantics/check-omp-structure.cpp +++ b/lib/semantics/check-omp-structure.cpp @@ -13,49 +13,6 @@ namespace Fortran::semantics { -static constexpr OmpDirectiveSet parallelSet{ - OmpDirective::DISTRIBUTE_PARALLEL_DO, - OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL, - OmpDirective::PARALLEL_DO, OmpDirective::PARALLEL_DO_SIMD, - OmpDirective::PARALLEL_SECTIONS, OmpDirective::PARALLEL_WORKSHARE, - OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO, - OmpDirective::TARGET_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; -static constexpr OmpDirectiveSet doSet{OmpDirective::DISTRIBUTE_PARALLEL_DO, - OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO, - OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO, OmpDirective::DO_SIMD, - OmpDirective::TARGET_PARALLEL_DO, OmpDirective::TARGET_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; -static constexpr OmpDirectiveSet simdSet{ - OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::DISTRIBUTE_SIMD, - OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO_SIMD, OmpDirective::SIMD, - OmpDirective::TARGET_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD, OmpDirective::TARGET_SIMD, - OmpDirective::TASKLOOP_SIMD, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TEAMS_DISTRIBUTE_SIMD}; -static constexpr OmpDirectiveSet doSimdSet{ - OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO_SIMD, - OmpDirective::DO_SIMD, OmpDirective::TARGET_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; -static constexpr OmpDirectiveSet taskloopSet{ - OmpDirective::TASKLOOP, OmpDirective::TASKLOOP_SIMD}; -static constexpr OmpDirectiveSet targetSet{OmpDirective::TARGET, - OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO, - OmpDirective::TARGET_PARALLEL_DO_SIMD, OmpDirective::TARGET_SIMD, - OmpDirective::TARGET_TEAMS, OmpDirective::TARGET_TEAMS_DISTRIBUTE, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, - OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD}; - std::string OmpStructureChecker::ContextDirectiveAsFortran() { auto dir{EnumToString(GetContext().directive)}; std::replace(dir.begin(), dir.end(), '_', ' '); diff --git a/lib/semantics/check-omp-structure.h b/lib/semantics/check-omp-structure.h index cfe4a2f0cda5..b20c32550769 100644 --- a/lib/semantics/check-omp-structure.h +++ b/lib/semantics/check-omp-structure.h @@ -46,6 +46,49 @@ ENUM_CLASS(OmpClause, ALIGNED, COLLAPSE, COPYIN, COPYPRIVATE, DEFAULT, using OmpClauseSet = common::EnumSet; +static constexpr OmpDirectiveSet parallelSet{ + OmpDirective::DISTRIBUTE_PARALLEL_DO, + OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL, + OmpDirective::PARALLEL_DO, OmpDirective::PARALLEL_DO_SIMD, + OmpDirective::PARALLEL_SECTIONS, OmpDirective::PARALLEL_WORKSHARE, + OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO, + OmpDirective::TARGET_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; +static constexpr OmpDirectiveSet doSet{OmpDirective::DISTRIBUTE_PARALLEL_DO, + OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO, + OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO, OmpDirective::DO_SIMD, + OmpDirective::TARGET_PARALLEL_DO, OmpDirective::TARGET_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; +static constexpr OmpDirectiveSet doSimdSet{ + OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO_SIMD, + OmpDirective::DO_SIMD, OmpDirective::TARGET_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD}; +static constexpr OmpDirectiveSet taskloopSet{ + OmpDirective::TASKLOOP, OmpDirective::TASKLOOP_SIMD}; +static constexpr OmpDirectiveSet targetSet{OmpDirective::TARGET, + OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO, + OmpDirective::TARGET_PARALLEL_DO_SIMD, OmpDirective::TARGET_SIMD, + OmpDirective::TARGET_TEAMS, OmpDirective::TARGET_TEAMS_DISTRIBUTE, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD}; +static constexpr OmpDirectiveSet simdSet{ + OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::DISTRIBUTE_SIMD, + OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO_SIMD, OmpDirective::SIMD, + OmpDirective::TARGET_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD, OmpDirective::TARGET_SIMD, + OmpDirective::TASKLOOP_SIMD, + OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, + OmpDirective::TEAMS_DISTRIBUTE_SIMD}; + class OmpStructureChecker : public virtual BaseChecker { public: OmpStructureChecker(SemanticsContext &context) : context_{context} {} diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index 5ab1424b246f..a7b76d61843c 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -1148,6 +1148,11 @@ class OmpAttributeVisitor { template bool Pre(const A &) { return true; } template void Post(const A &) {} + bool Pre(const parser::SpecificationPart &x) { + Walk(std::get>(x.t)); + return false; + } + bool Pre(const parser::OpenMPBlockConstruct &); void Post(const parser::OpenMPBlockConstruct &) { PopContext(); } void Post(const parser::OmpBeginBlockDirective &) { @@ -1199,6 +1204,7 @@ class OmpAttributeVisitor { // variables on Data-sharing attribute clauses std::map objectWithDSA; bool withinConstruct{false}; + std::size_t associatedLoopLevel{0}; }; // back() is the top of the stack OmpContext &GetContext() { @@ -1226,6 +1232,10 @@ class OmpAttributeVisitor { auto it{GetContext().objectWithDSA.find(&symbol)}; return it != GetContext().objectWithDSA.end(); } + void SetContextAssociatedLoopLevel(std::size_t level) { + GetContext().associatedLoopLevel = level; + } + std::size_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { const auto pair{ @@ -1260,6 +1270,11 @@ class OmpAttributeVisitor { } bool HasDataSharingAttributeObject(const Symbol &); + const parser::DoConstruct *GetDoConstructIf( + const parser::ExecutionPartConstruct &); + // Predetermined DSA rules + void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &); + void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); Symbol *ResolveOmp(const parser::Name &, Symbol::Flag); @@ -5935,10 +5950,20 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { const auto &beginLoopDir{std::get(x.t)}; const auto &beginDir{std::get(beginLoopDir.t)}; + const auto &clauseList{std::get(beginLoopDir.t)}; switch (beginDir.v) { case parser::OmpLoopDirective::Directive::Distribute: PushContext(beginDir.source, OmpDirective::DISTRIBUTE); break; + case parser::OmpLoopDirective::Directive::DistributeParallelDo: + PushContext(beginDir.source, OmpDirective::DISTRIBUTE_PARALLEL_DO); + break; + case parser::OmpLoopDirective::Directive::DistributeParallelDoSimd: + PushContext(beginDir.source, OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::DistributeSimd: + PushContext(beginDir.source, OmpDirective::DISTRIBUTE_SIMD); + break; case parser::OmpLoopDirective::Directive::Do: PushContext(beginDir.source, OmpDirective::DO); break; @@ -5954,20 +5979,136 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { case parser::OmpLoopDirective::Directive::Simd: PushContext(beginDir.source, OmpDirective::SIMD); break; + case parser::OmpLoopDirective::Directive::TargetParallelDo: + PushContext(beginDir.source, OmpDirective::TARGET_PARALLEL_DO); + break; + case parser::OmpLoopDirective::Directive::TargetParallelDoSimd: + PushContext(beginDir.source, OmpDirective::TARGET_PARALLEL_DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::TargetTeamsDistribute: + PushContext(beginDir.source, OmpDirective::TARGET_TEAMS_DISTRIBUTE); + break; + case parser::OmpLoopDirective::Directive::TargetTeamsDistributeParallelDo: + PushContext( + beginDir.source, OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO); + break; + case parser::OmpLoopDirective::Directive::TargetTeamsDistributeParallelDoSimd: + PushContext(beginDir.source, + OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::TargetTeamsDistributeSimd: + PushContext(beginDir.source, OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD); + break; + case parser::OmpLoopDirective::Directive::TargetSimd: + PushContext(beginDir.source, OmpDirective::TARGET_SIMD); + break; case parser::OmpLoopDirective::Directive::Taskloop: PushContext(beginDir.source, OmpDirective::TASKLOOP); break; case parser::OmpLoopDirective::Directive::TaskloopSimd: PushContext(beginDir.source, OmpDirective::TASKLOOP_SIMD); break; - default: - // TODO others + case parser::OmpLoopDirective::Directive::TeamsDistribute: + PushContext(beginDir.source, OmpDirective::TEAMS_DISTRIBUTE); + break; + case parser::OmpLoopDirective::Directive::TeamsDistributeParallelDo: + PushContext(beginDir.source, OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO); + break; + case parser::OmpLoopDirective::Directive::TeamsDistributeParallelDoSimd: + PushContext( + beginDir.source, OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD); + break; + case parser::OmpLoopDirective::Directive::TeamsDistributeSimd: + PushContext(beginDir.source, OmpDirective::TEAMS_DISTRIBUTE_SIMD); break; } ClearDataSharingAttributeObjects(); + SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); + PrivatizeAssociatedLoopIndex(x); return true; } +const parser::DoConstruct *OmpAttributeVisitor::GetDoConstructIf( + const parser::ExecutionPartConstruct &x) { + if (auto *y{std::get_if(&x.u)}) { + if (auto *z{std::get_if>(&y->u)}) { + return &z->value(); + } + } + return nullptr; +} + +std::size_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( + const parser::OmpClauseList &x) { + std::size_t orderedLevel{0}; + std::size_t collapseLevel{0}; + for (const auto &clause : x.v) { + if (const auto *orderedClause{ + std::get_if(&clause.u)}) { + if (const auto v{ + evaluate::ToInt64(resolver_.EvaluateIntExpr(orderedClause->v))}) { + orderedLevel = *v; + } + } + if (const auto *collapseClause{ + std::get_if(&clause.u)}) { + if (const auto v{evaluate::ToInt64( + resolver_.EvaluateIntExpr(collapseClause->v))}) { + collapseLevel = *v; + } + } + } + + if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { + return orderedLevel; + } else if (!orderedLevel && collapseLevel) { + return collapseLevel; + } // orderedLevel < collapseLevel is an error handled in structural checks + return 1; // default is outermost loop +} + +// 2.15.1.1 Data-sharing Attribute Rules - Predetermined +// - The loop iteration variable(s) in the associated do-loop(s) of a do, +// parallel do, taskloop, or distribute construct is (are) private. +// - The loop iteration variable in the associated do-loop of a simd construct +// with just one associated do-loop is linear with a linear-step that is the +// increment of the associated do-loop. +// - The loop iteration variables in the associated do-loops of a simd +// construct with multiple associated do-loops are lastprivate. +// +// TODO: This assumes that the do-loops association for collapse/ordered +// clause has been performed (the number of nested do-loops >= n). +void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex( + const parser::OpenMPLoopConstruct &x) { + std::size_t level{GetContext().associatedLoopLevel}; + Symbol::Flag ivDSA{Symbol::Flag::OmpPrivate}; + if (simdSet.test(GetContext().directive)) { + if (level == 1) { + ivDSA = Symbol::Flag::OmpLinear; + } else { + ivDSA = Symbol::Flag::OmpLastPrivate; + } + } + + auto &outer{std::get>(x.t)}; + for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { + // go through all the nested do-loops and resolve index variables + auto &loopControl{loop->GetLoopControl().value()}; + using Bounds = parser::LoopControl::Bounds; + const Bounds &bounds{std::get(loopControl.u)}; + const parser::Name &iv{bounds.name.thing}; + if (auto *symbol{ResolveOmp(iv, ivDSA)}) { + iv.symbol = symbol; // adjust the symbol within region + AddToContextObjectWithDSA(*symbol, ivDSA); + } + + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + } + CHECK(level == 0); +} + bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { const auto &beginSectionsDir{ std::get(x.t)}; diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index 5dfbcb2bf928..2dd11981577b 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -229,6 +229,7 @@ set(SYMBOL_TESTS omp-symbol05.f90 omp-symbol06.f90 omp-symbol07.f90 + omp-symbol08.f90 kinds01.f90 kinds03.f90 procinterface01.f90 diff --git a/test/semantics/omp-clause-validity01.f90 b/test/semantics/omp-clause-validity01.f90 index 08abc1f032ce..d624564cd20b 100644 --- a/test/semantics/omp-clause-validity01.f90 +++ b/test/semantics/omp-clause-validity01.f90 @@ -43,7 +43,9 @@ !ERROR: COLLAPSE clause is not allowed on the PARALLEL directive !$omp parallel collapse(2) do i = 1, N - a = 3.14 + do j = 1, N + a = 3.14 + enddo enddo !$omp end parallel @@ -143,7 +145,7 @@ enddo !ERROR: The parameter of the ORDERED clause must be greater than or equal to the parameter of the COLLAPSE clause - !$omp do collapse(num) ordered(1+2+3+4) + !$omp do collapse(num-14) ordered(1) do i = 1, N do j = 1, N do k = 1, N @@ -309,7 +311,9 @@ !ERROR: NOGROUP clause is not allowed on the DO SIMD directive !$omp do simd ordered(2) NOGROUP do i = 1, N - a = 3.14 + do j = 1, N + a = 3.14 + enddo enddo !$omp end parallel diff --git a/test/semantics/omp-device-constructs.f90 b/test/semantics/omp-device-constructs.f90 index 118e49c9afc9..e87cb119dba4 100644 --- a/test/semantics/omp-device-constructs.f90 +++ b/test/semantics/omp-device-constructs.f90 @@ -149,7 +149,11 @@ program main !ERROR: At most one COLLAPSE clause can appear on the DISTRIBUTE directive !$omp distribute collapse(2) collapse(3) do i = 1, N - a = 3.14 + do j = 1, N + do k = 1, N + a = 3.14 + enddo + enddo enddo !$omp end distribute !$omp end target diff --git a/test/semantics/omp-symbol01.f90 b/test/semantics/omp-symbol01.f90 index 2286bcc5a35e..eca885565fba 100644 --- a/test/semantics/omp-symbol01.f90 +++ b/test/semantics/omp-symbol01.f90 @@ -45,21 +45,21 @@ program mm !DEF: /mm/c (Implicit) ObjectEntity REAL(4) c = 2.0 !$omp parallel do private(a,t,/c/) shared(c) - !DEF: /mm/i (Implicit) ObjectEntity INTEGER(4) + !DEF: /mm/Block1/i (OmpPrivate) HostAssoc INTEGER(4) do i=1,10 !DEF: /mm/Block1/a (OmpPrivate) HostAssoc REAL(4) !REF: /mm/b - !REF: /mm/i + !REF: /mm/Block1/i a = a+b(i) !DEF: /mm/Block1/t (OmpPrivate) HostAssoc TYPE(myty) !REF: /md/myty/a - !REF: /mm/i + !REF: /mm/Block1/i t%a = i !DEF: /mm/Block1/y (OmpPrivate) HostAssoc REAL(4) y = 0. !DEF: /mm/Block1/x (OmpPrivate) HostAssoc REAL(4) !REF: /mm/Block1/a - !REF: /mm/i + !REF: /mm/Block1/i !REF: /mm/Block1/y x = a+i+y !REF: /mm/c diff --git a/test/semantics/omp-symbol04.f90 b/test/semantics/omp-symbol04.f90 index d97768d9396c..9daacfb49854 100644 --- a/test/semantics/omp-symbol04.f90 +++ b/test/semantics/omp-symbol04.f90 @@ -12,7 +12,7 @@ !DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 2. !$omp do private(a) - !DEF: /MainProgram1/i (Implicit) ObjectEntity INTEGER(4) + !DEF: /MainProgram1/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) do i=1,10 !DEF: /MainProgram1/Block1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 1. diff --git a/test/semantics/omp-symbol06.f90 b/test/semantics/omp-symbol06.f90 index d343d084c44d..3b82fc88e598 100644 --- a/test/semantics/omp-symbol06.f90 +++ b/test/semantics/omp-symbol06.f90 @@ -8,7 +8,7 @@ !DEF: /MainProgram1/a (Implicit) ObjectEntity REAL(4) a = 1. !$omp parallel do firstprivate(a) lastprivate(a) - !DEF: /MainProgram1/i (Implicit) ObjectEntity INTEGER(4) + !DEF: /MainProgram1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) do i=1,10 !DEF: /MainProgram1/Block1/a (OmpFirstPrivate, OmpLastPrivate) HostAssoc REAL(4) a = 2. diff --git a/test/semantics/omp-symbol08.f90 b/test/semantics/omp-symbol08.f90 new file mode 100644 index 000000000000..eaf8ff2eaa9d --- /dev/null +++ b/test/semantics/omp-symbol08.f90 @@ -0,0 +1,209 @@ +!OPTIONS: -fopenmp + +! 2.15.1.1 Predetermined rules for associated do-loops index variable +! a) The loop iteration variable(s) in the associated do-loop(s) of a do, +! parallel do, taskloop, or distribute construct is (are) private. +! b) The loop iteration variable in the associated do-loop of a simd construct +! with just one associated do-loop is linear with a linear-step that is the +! increment of the associated do-loop. +! c) The loop iteration variables in the associated do-loops of a simd +! construct with multiple associated do-loops are lastprivate. +! - TBD + +! All the tests assume that the do-loops association for collapse/ordered +! clause has been performed (the number of nested do-loops >= n). + +! Rule a) +! TODO: nested constructs (k should be private too) +!DEF: /test_do (Subroutine) Subprogram +subroutine test_do + implicit none + !DEF: /test_do/a ObjectEntity REAL(4) + real a(20,20,20) + !DEF: /test_do/i ObjectEntity INTEGER(4) + !DEF: /test_do/j ObjectEntity INTEGER(4) + !DEF: /test_do/k ObjectEntity INTEGER(4) + integer i, j, k +!$omp parallel + !REF: /test_do/i + i = 99 +!$omp do collapse(2) + !DEF: /test_do/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + do i=1,5 + !DEF: /test_do/Block1/Block1/j (OmpPrivate) HostAssoc INTEGER(4) + do j=6,10 + !REF: /test_do/a + a(1,1,1) = 0. + !REF: /test_do/k + do k=11,15 + !REF: /test_do/a + !REF: /test_do/k + !REF: /test_do/Block1/Block1/j + !REF: /test_do/Block1/Block1/i + a(k,j,i) = 1. + end do + end do + end do +!$omp end parallel +end subroutine test_do + +! Rule a) +!DEF: /test_pardo (Subroutine) Subprogram +subroutine test_pardo + implicit none + !DEF: /test_pardo/a ObjectEntity REAL(4) + real a(20,20,20) + !DEF: /test_pardo/i ObjectEntity INTEGER(4) + !DEF: /test_pardo/j ObjectEntity INTEGER(4) + !DEF: /test_pardo/k ObjectEntity INTEGER(4) + integer i, j, k +!$omp parallel do collapse(2) private(k) ordered(2) + !DEF: /test_pardo/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + do i=1,5 + !DEF: /test_pardo/Block1/j (OmpPrivate) HostAssoc INTEGER(4) + do j=6,10 + !REF: /test_pardo/a + a(1,1,1) = 0. + !DEF: /test_pardo/Block1/k (OmpPrivate) HostAssoc INTEGER(4) + do k=11,15 + !REF: /test_pardo/a + !REF: /test_pardo/Block1/k + !REF: /test_pardo/Block1/j + !REF: /test_pardo/Block1/i + a(k,j,i) = 1. + end do + end do + end do +end subroutine test_pardo + +! Rule a) +!DEF: /test_taskloop (Subroutine) Subprogram +subroutine test_taskloop + implicit none + !DEF: /test_taskloop/a ObjectEntity REAL(4) + real a(5,5) + !DEF: /test_taskloop/i ObjectEntity INTEGER(4) + !DEF: /test_taskloop/j ObjectEntity INTEGER(4) + integer i, j +!$omp taskloop private(j) + !DEF: /test_taskloop/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + do i=1,5 + !DEF: /test_taskloop/Block1/j (OmpPrivate) HostAssoc INTEGER(4) + !REF: /test_taskloop/Block1/i + do j=1,i + !REF: /test_taskloop/a + !REF: /test_taskloop/Block1/j + !REF: /test_taskloop/Block1/i + a(j,i) = 3.14 + end do + end do +!$omp end taskloop +end subroutine test_taskloop + +! Rule a); OpenMP 4.5 Examples teams.2.f90 +! TODO: reduction; data-mapping attributes +!DEF: /dotprod (Subroutine) Subprogram +!DEF: /dotprod/b ObjectEntity REAL(4) +!DEF: /dotprod/c ObjectEntity REAL(4) +!DEF: /dotprod/n ObjectEntity INTEGER(4) +!DEF: /dotprod/block_size ObjectEntity INTEGER(4) +!DEF: /dotprod/num_teams ObjectEntity INTEGER(4) +!DEF: /dotprod/block_threads ObjectEntity INTEGER(4) +subroutine dotprod (b, c, n, block_size, num_teams, block_threads) + implicit none + !REF: /dotprod/n + integer n + !REF: /dotprod/b + !REF: /dotprod/n + !REF: /dotprod/c + !DEF: /dotprod/sum ObjectEntity REAL(4) + real b(n), c(n), sum + !REF: /dotprod/block_size + !REF: /dotprod/num_teams + !REF: /dotprod/block_threads + !DEF: /dotprod/i ObjectEntity INTEGER(4) + !DEF: /dotprod/i0 ObjectEntity INTEGER(4) + integer block_size, num_teams, block_threads, i, i0 + !REF: /dotprod/sum + sum = 0.0e0 +!$omp target map(to:b,c) map(tofrom:sum) +!$omp teams num_teams(num_teams) thread_limit(block_threads) reduction(+:sum) +!$omp distribute + !DEF: /dotprod/Block1/Block1/Block1/i0 (OmpPrivate) HostAssoc INTEGER(4) + !REF: /dotprod/n + !REF: /dotprod/block_size + do i0=1,n,block_size +!$omp parallel do reduction(+:sum) + !DEF: /dotprod/Block1/Block1/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !REF: /dotprod/i0 + !DEF: /dotprod/min INTRINSIC (Function) ProcEntity + !REF: /dotprod/block_size + !REF: /dotprod/n + do i=i0,min(i0+block_size, n) + !REF: /dotprod/sum + !REF: /dotprod/b + !REF: /dotprod/Block1/Block1/Block1/Block1/i + !REF: /dotprod/c + sum = sum+b(i)*c(i) + end do + end do +!$omp end teams +!$omp end target + !REF: /dotprod/sum + print *, sum +end subroutine dotprod + +! Rule b) +! TODO: nested constructs (j, k should be private too) +!DEF: /test_simd (Subroutine) Subprogram +subroutine test_simd + implicit none + !DEF: /test_simd/a ObjectEntity REAL(4) + real a(20,20,20) + !DEF: /test_simd/i ObjectEntity INTEGER(4) + !DEF: /test_simd/j ObjectEntity INTEGER(4) + !DEF: /test_simd/k ObjectEntity INTEGER(4) + integer i, j, k +!$omp parallel do simd + !DEF: /test_simd/Block1/i (OmpLinear) HostAssoc INTEGER(4) + do i=1,5 + !REF: /test_simd/j + do j=6,10 + !REF: /test_simd/k + do k=11,15 + !REF: /test_simd/a + !REF: /test_simd/k + !REF: /test_simd/j + !REF: /test_simd/Block1/i + a(k,j,i) = 3.14 + end do + end do + end do +end subroutine test_simd + +! Rule c) +!DEF: /test_simd_multi (Subroutine) Subprogram +subroutine test_simd_multi + implicit none + !DEF: /test_simd_multi/a ObjectEntity REAL(4) + real a(20,20,20) + !DEF: /test_simd_multi/i ObjectEntity INTEGER(4) + !DEF: /test_simd_multi/j ObjectEntity INTEGER(4) + !DEF: /test_simd_multi/k ObjectEntity INTEGER(4) + integer i, j, k +!$omp parallel do simd collapse(3) + !DEF: /test_simd_multi/Block1/i (OmpLastPrivate) HostAssoc INTEGER(4) + do i=1,5 + !DEF: /test_simd_multi/Block1/j (OmpLastPrivate) HostAssoc INTEGER(4) + do j=6,10 + !DEF: /test_simd_multi/Block1/k (OmpLastPrivate) HostAssoc INTEGER(4) + do k=11,15 + !REF: /test_simd_multi/a + !REF: /test_simd_multi/Block1/k + !REF: /test_simd_multi/Block1/j + !REF: /test_simd_multi/Block1/i + a(k,j,i) = 3.14 + end do + end do + end do +end subroutine test_simd_multi From a1a95bfcd1811d697f1386fe57af664b8a16ffb5 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Thu, 6 Feb 2020 12:26:51 -0800 Subject: [PATCH 021/345] Semantic checks for C702 C702 (R701) A colon shall not be used as a type-param-value except in the declaration of an entity that has the POINTER or ALLOCATABLE attribute. I added code to the visitor for a TypeDeclarationStmt to check for the 'LEN' type parameter for strings and to loop over the type parameters for derived types. I also ran into a few situations where previous tests had erroneously used a colon for type parameters without either the POINTER or ALLOCATABLE attribute and fixed them up. --- lib/semantics/resolve-names.cpp | 27 +++++++++++++++++-- test/evaluate/folding05.f90 | Bin 9530 -> 9530 bytes test/semantics/CMakeLists.txt | 1 + test/semantics/allocate03.f90 | 2 +- test/semantics/allocate09.f90 | 8 +++--- test/semantics/modfile28.f90 | 12 ++++----- test/semantics/resolve37.f90 | 2 ++ test/semantics/resolve69.f90 | 45 ++++++++++++++++++++++++++++++++ 8 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 test/semantics/resolve69.f90 diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index a7b76d61843c..a964f85943a1 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -720,7 +720,7 @@ class DeclarationVisitor : public ArraySpecVisitor, 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::TypeDeclarationStmt &); void Post(const parser::IntegerTypeSpec &); void Post(const parser::IntrinsicTypeSpec::Real &); void Post(const parser::IntrinsicTypeSpec::Complex &); @@ -2889,6 +2889,29 @@ bool DeclarationVisitor::CheckAccessibleComponent( return false; } +void DeclarationVisitor::Post(const parser::TypeDeclarationStmt &) { + if (!GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { // C702 + if (const auto *typeSpec{GetDeclTypeSpec()}) { + if (typeSpec->category() == DeclTypeSpec::Character) { + if (typeSpec->characterTypeSpec().length().isDeferred()) { + Say("The type parameter LEN cannot be deferred without" + " the POINTER or ALLOCATABLE attribute"_err_en_US); + } + } else if (const DerivedTypeSpec * derivedSpec{typeSpec->AsDerived()}) { + for (const auto &pair : derivedSpec->parameters()) { + if (pair.second.isDeferred()) { + Say(currStmtSource().value(), + "The value of type parameter '%s' cannot be deferred" + " without the POINTER or ALLOCATABLE attribute"_err_en_US, + pair.first); + } + } + } + } + } + EndDecl(); +} + void DeclarationVisitor::Post(const parser::DimensionStmt::Declaration &x) { const auto &name{std::get(x.t)}; DeclareObjectEntity(name, Attrs{}); @@ -3522,7 +3545,7 @@ bool DeclarationVisitor::Pre(const parser::DataComponentDefStmt &x) { // so POINTER & ALLOCATABLE enable forward references to derived types. Walk(std::get>(x.t)); set_allowForwardReferenceToDerivedType( - GetAttrs().test(Attr::POINTER) || GetAttrs().test(Attr::ALLOCATABLE)); + GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})); Walk(std::get(x.t)); set_allowForwardReferenceToDerivedType(false); Walk(std::get>(x.t)); diff --git a/test/evaluate/folding05.f90 b/test/evaluate/folding05.f90 index d6d8a45673822b31959a962829fd3a147889a315..5e5e5c576976724256e3c150f56be5df2efaf023 100644 GIT binary patch delta 35 mcmdnxwaaV6NoGc^&8L_rG6Ja>)*3b-b(7l>Bq+@)*3b-b(7l>Bq+@ 0xff are serialized into UTF-8; ! each of those bytes then gets encoded into UTF-8 for the module file. -character(kind=1,len=:), parameter :: c1 = 1_"Hi! 你好!" -character(kind=4,len=:), parameter :: c4a(*) = [4_"一", 4_"二", 4_"三", 4_"四", 4_"五"] +character(kind=1,len=*), parameter :: c1 = 1_"Hi! 你好!" +character(kind=4,len=*), parameter :: c4a(*) = [4_"一", 4_"二", 4_"三", 4_"四", 4_"五"] integer, parameter :: lc4 = len(c4) integer, parameter :: lc1 = len(c1) end module m !Expect: m.mod !module m -!character(:,4),parameter::c4=4_"Hi! \344\275\240\345\245\275!" -!character(:,1),parameter::c1=1_"Hi! \344\275\240\345\245\275!" -!character(:,4),parameter::c4a(1_8:*)=[CHARACTER(KIND=4,LEN=1)::4_"\344\270\200",4_"\344\272\214",4_"\344\270\211",4_"\345\233\233",4_"\344\272\224"] +!character(*,4),parameter::c4=4_"Hi! \344\275\240\345\245\275!" +!character(*,1),parameter::c1=1_"Hi! \344\275\240\345\245\275!" +!character(*,4),parameter::c4a(1_8:*)=[CHARACTER(KIND=4,LEN=1)::4_"\344\270\200",4_"\344\272\214",4_"\344\270\211",4_"\345\233\233",4_"\344\272\224"] !integer(4),parameter::lc4=7_4 !intrinsic::len !integer(4),parameter::lc1=11_4 diff --git a/test/semantics/resolve37.f90 b/test/semantics/resolve37.f90 index e2d5f124bed4..a33e3700a932 100644 --- a/test/semantics/resolve37.f90 +++ b/test/semantics/resolve37.f90 @@ -1,3 +1,5 @@ +! C701 The type-param-value for a kind type parameter shall be a constant +! expression. This constraint looks like a mistake in the standard. integer, parameter :: k = 8 real, parameter :: l = 8.0 integer :: n = 2 diff --git a/test/semantics/resolve69.f90 b/test/semantics/resolve69.f90 new file mode 100644 index 000000000000..5950f66e4741 --- /dev/null +++ b/test/semantics/resolve69.f90 @@ -0,0 +1,45 @@ +subroutine s1() + ! C701 (R701) The type-param-value for a kind type parameter shall be a + ! constant expression. + ! C702 (R701) A colon shall not be used as a type-param-value except in the + ! declaration of an entity that has the POINTER or ALLOCATABLE attribute. + integer, parameter :: constVal = 1 + integer :: nonConstVal = 1 +!ERROR: Invalid specification expression: reference to local entity 'nonconstval' + character(nonConstVal) :: colonString1 + character(len=20, kind=constVal + 1) :: constKindString + character(len=:, kind=constVal + 1), pointer :: constKindString1 +!ERROR: The type parameter LEN cannot be deferred without the POINTER or ALLOCATABLE attribute + character(len=:, kind=constVal + 1) :: constKindString2 +!ERROR: Must be a constant value + character(len=20, kind=nonConstVal) :: nonConstKindString +!ERROR: The type parameter LEN cannot be deferred without the POINTER or ALLOCATABLE attribute + character(len=:) :: deferredString +!ERROR: The type parameter LEN cannot be deferred without the POINTER or ALLOCATABLE attribute + character(:) :: colonString2 + !OK because of the allocatable attribute + character(:), allocatable :: colonString3 + + type derived(typeKind, typeLen) + integer, kind :: typeKind + integer, len :: typeLen + end type derived + + type (derived(constVal, 3)) :: constDerivedKind +!ERROR: Value of kind type parameter 'typekind' (nonconstval) is not a scalar INTEGER constant +!ERROR: Invalid specification expression: reference to local entity 'nonconstval' + type (derived(nonConstVal, 3)) :: nonConstDerivedKind + + !OK because all type-params are constants + type (derived(3, constVal)) :: constDerivedLen + +!ERROR: Invalid specification expression: reference to local entity 'nonconstval' + type (derived(3, nonConstVal)) :: nonConstDerivedLen +!ERROR: The value of type parameter 'typelen' cannot be deferred without the POINTER or ALLOCATABLE attribute + type (derived(3, :)) :: colonDerivedLen +!ERROR: The value of type parameter 'typekind' cannot be deferred without the POINTER or ALLOCATABLE attribute +!ERROR: The value of type parameter 'typelen' cannot be deferred without the POINTER or ALLOCATABLE attribute + type (derived( :, :)) :: colonDerivedLen1 + type (derived( :, :)), pointer :: colonDerivedLen2 + type (derived(4, :)), pointer :: colonDerivedLen3 +end subroutine s1 From 16543d22f74e9421ecb4078818f4c1970bac0a5d Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 6 Feb 2020 03:27:36 -0800 Subject: [PATCH 022/345] Fix issues comming from clang-10 warnings - Remove SubprogramDetails copy ctor - Prevent copies in range based loops over symbols - Remove unsued var --- include/flang/semantics/symbol.h | 4 ---- lib/semantics/mod-file.cpp | 4 ++-- lib/semantics/resolve-labels.cpp | 2 +- lib/semantics/resolve-names.cpp | 2 +- lib/semantics/semantics.cpp | 2 +- runtime/buffer.h | 1 - 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/include/flang/semantics/symbol.h b/include/flang/semantics/symbol.h index faef1ca7af2b..a27a935bdb00 100644 --- a/include/flang/semantics/symbol.h +++ b/include/flang/semantics/symbol.h @@ -52,10 +52,6 @@ class MainProgramDetails { class SubprogramDetails { public: - SubprogramDetails() {} - SubprogramDetails(const SubprogramDetails &that) - : dummyArgs_{that.dummyArgs_}, result_{that.result_} {} - bool isFunction() const { return result_ != nullptr; } bool isInterface() const { return isInterface_; } void set_isInterface(bool value = true) { isInterface_ = value; } diff --git a/lib/semantics/mod-file.cpp b/lib/semantics/mod-file.cpp index 857c52b2b461..1f89c56e9870 100644 --- a/lib/semantics/mod-file.cpp +++ b/lib/semantics/mod-file.cpp @@ -915,10 +915,10 @@ void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) { if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) { DoSymbol(extends->name(), extends->typeSymbol()); } - for (const auto pair : derived->parameters()) { + for (const auto &pair : derived->parameters()) { DoParamValue(pair.second); } - for (const auto pair : *typeSymbol.scope()) { + for (const auto &pair : *typeSymbol.scope()) { const Symbol &comp{*pair.second}; DoSymbol(comp); } diff --git a/lib/semantics/resolve-labels.cpp b/lib/semantics/resolve-labels.cpp index 87cacb8376c3..723762706a85 100644 --- a/lib/semantics/resolve-labels.cpp +++ b/lib/semantics/resolve-labels.cpp @@ -824,7 +824,7 @@ void CheckBranchesIntoDoBody(const SourceStmtList &branches, if (HasScope(branchTarget.proxyForScope)) { const auto &fromPosition{branch.parserCharBlock}; const auto &toPosition{branchTarget.parserCharBlock}; - for (const auto body : loopBodies) { + for (const auto &body : loopBodies) { if (!InBody(fromPosition, body) && InBody(toPosition, body)) { context.Say(fromPosition, "branch into loop body from outside"_en_US) .Attach(body.first, "the loop branched into"_en_US); diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index a964f85943a1..197a32b4fee2 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -4099,7 +4099,7 @@ void DeclarationVisitor::SetSaveAttr(Symbol &symbol) { // Check types of common block objects, now that they are known. void DeclarationVisitor::CheckCommonBlocks() { // check for empty common blocks - for (const auto pair : currScope().commonBlocks()) { + for (const auto &pair : currScope().commonBlocks()) { const auto &symbol{*pair.second}; if (symbol.get().objects().empty() && symbol.attrs().test(Attr::BIND_C)) { diff --git a/lib/semantics/semantics.cpp b/lib/semantics/semantics.cpp index 1f9958e08469..8c5bece6f72e 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/semantics/semantics.cpp @@ -294,7 +294,7 @@ void Semantics::DumpSymbols(std::ostream &os) { void Semantics::DumpSymbolsSources(std::ostream &os) const { NameToSymbolMap symbols; GetSymbolNames(context_.globalScope(), symbols); - for (const auto pair : symbols) { + for (const auto &pair : symbols) { const Symbol &symbol{pair.second}; if (auto sourceInfo{cooked_.GetSourcePositionRange(symbol.name())}) { os << symbol.name().ToString() << ": " << sourceInfo->first.file.path() diff --git a/runtime/buffer.h b/runtime/buffer.h index a7b31848df8d..ec39bacd6ec6 100644 --- a/runtime/buffer.h +++ b/runtime/buffer.h @@ -66,7 +66,6 @@ template class FileFrame { } else { // [cde........ab] -> [abcde........] auto n{start_ + length_ - size_}; // 3 for cde - auto gap{size_ - length_}; // 13 - 5 = 8 RUNTIME_CHECK(handler, length_ >= n); std::memmove(buffer_ + n, buffer_ + start_, length_ - n); // cdeab LeftShiftBufferCircularly(buffer_, length_, n); // abcde From c342575a9e0217fa3252b70b8ef3eac0236a58e3 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Mon, 10 Feb 2020 10:55:24 -0800 Subject: [PATCH 023/345] Fix compilation error on macOS The call to `std::min` failed to compile with GCC on macOS due to type inference because `std::size_t` is `long unsigned int` but `std::int64_t` is `long long int`. --- runtime/buffer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/buffer.h b/runtime/buffer.h index a7b31848df8d..8a1fd9b39df5 100644 --- a/runtime/buffer.h +++ b/runtime/buffer.h @@ -41,8 +41,7 @@ template class FileFrame { FileOffset FrameAt() const { return fileOffset_ + frame_; } char *Frame() const { return buffer_ + start_ + frame_; } std::size_t FrameLength() const { - return std::min( - static_cast(length_ - frame_), size_ - (start_ + frame_)); + return std::min(length_ - frame_, size_ - (start_ + frame_)); } // Returns a short frame at a non-fatal EOF. Can return a long frame as well. From 75adddd504577a295f0c906aa049bd47ce2b4f2e Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Mon, 3 Feb 2020 10:31:30 -0800 Subject: [PATCH 024/345] Updated the description of `evaluate::Expr` types --- documentation/ImplementingASemanticCheck.md | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/documentation/ImplementingASemanticCheck.md b/documentation/ImplementingASemanticCheck.md index cc5ff7f62667..736202abaa1f 100644 --- a/documentation/ImplementingASemanticCheck.md +++ b/documentation/ImplementingASemanticCheck.md @@ -214,22 +214,21 @@ existing framework used in DO construct semantic checking that traversed an be able to use a similar framework to traverse an `evaluate::Expr` node to find all of the `evaluate::ActualArgument` nodes. -Note that there are two distinct data types in the compiler called `Expr`. One -is in the `parser` namespace. `parser::Expr` is defined in the file +Note that the compiler has multiple types called `Expr`. One is in the +`parser` namespace. `parser::Expr` is defined in the file `include/flang/parser/parse-tree.h`. It represents a parsed expression that maps directly to the source code and has fields that specify any operators in the expression, the operands, and the source position of the expression. -The second `Expr` type is in the `evaluate` namespace. The `evaluate` -namespace contains many types associated with semantic checking of expressions. -`evaluate::Expr` is defined in the file `include/flang/evaluate/expression.h`. -It represents an expression after it has undergone semantic checking and -contains information that is only available after semantic analysis. This -information includes the Fortran type of the expression, whether it's a -reference to a function, whether it's an actual argument, etc. After an -expression has undergone semantic analysis, the field `typedExpr` in the -`parser::Expr` node is filled in with a pointer to the analyzed expression in -`evaluate::Expr`. +Additionally, in the namespace `evaluate`, there are `evaluate::Expr` +template classes defined in the file `include/flang/evaluate/expression.h`. +These are parameterized over the various types of Fortran and constitute a +suite of strongly-typed representations of valid Fortran expressions of type +`T` that have been fully elaborated with conversion operations and subjected to +constant folding. After an expression has undergone semantic analysis, the +field `typedExpr` in the `parser::Expr` node is filled in with a pointer that +owns an instance of `evaluate::Expr`, the most general representation +of an analyzed expression. All of the declarations associated with both FUNCTION and SUBROUTINE calls are in `include/flang/evaluate/call.h`. An `evaluate::FunctionRef` inherits from From 49a64c4c2374e930f6890b270289a6f49ba63edd Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Mon, 10 Feb 2020 13:24:32 -0800 Subject: [PATCH 025/345] Semantic checks for constraints on types I implemented and added tests for constraints C703, C704, C705, C706, and C796. In some cases, the code and/or test already existed, and all I did was add a notation indicating the associated constraint. --- lib/semantics/check-declarations.cpp | 2 +- lib/semantics/resolve-names.cpp | 33 ++++++++++++++-- test/semantics/CMakeLists.txt | 1 + test/semantics/allocate04.f90 | 2 +- test/semantics/resolve52.f90 | 1 + test/semantics/resolve69.f90 | 9 +++++ test/semantics/resolve70.f90 | 58 ++++++++++++++++++++++++++++ test/semantics/structconst01.f90 | 4 +- 8 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 test/semantics/resolve70.f90 diff --git a/lib/semantics/check-declarations.cpp b/lib/semantics/check-declarations.cpp index a96bc6fb056b..7cd81c91c348 100644 --- a/lib/semantics/check-declarations.cpp +++ b/lib/semantics/check-declarations.cpp @@ -495,7 +495,7 @@ void CheckHelper::CheckDerivedType( } if (const DeclTypeSpec * parent{FindParentTypeSpec(symbol)}) { const DerivedTypeSpec *parentDerived{parent->AsDerived()}; - if (!IsExtensibleType(parentDerived)) { + if (!IsExtensibleType(parentDerived)) { // C705 messages_.Say("The parent type is not extensible"_err_en_US); } if (!symbol.attrs().test(Attr::ABSTRACT) && parentDerived && diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index 197a32b4fee2..f075fa30a4b8 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -343,6 +343,7 @@ class DeclTypeSpecVisitor : public AttrsVisitor { } KindExpr GetKindParamExpr( TypeCategory, const std::optional &); + void CheckForAbstractType(const Symbol &typeSymbol); private: State state_; @@ -731,7 +732,9 @@ class DeclarationVisitor : public ArraySpecVisitor, void Post(const parser::LengthSelector &); bool Pre(const parser::KindParam &); bool Pre(const parser::DeclarationTypeSpec::Type &); + void Post(const parser::DeclarationTypeSpec::Type &); bool Pre(const parser::DeclarationTypeSpec::Class &); + void Post(const parser::DeclarationTypeSpec::Class &); bool Pre(const parser::DeclarationTypeSpec::Record &); void Post(const parser::DerivedTypeSpec &); bool Pre(const parser::DerivedTypeDef &); @@ -1590,9 +1593,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) { case DeclTypeSpec::Character: typeSpec.declTypeSpec = spec; break; case DeclTypeSpec::TypeDerived: if (const DerivedTypeSpec * derived{spec->AsDerived()}) { - if (derived->typeSymbol().attrs().test(Attr::ABSTRACT)) { - Say("ABSTRACT derived type may not be used here"_err_en_US); - } + CheckForAbstractType(derived->typeSymbol()); // C703 typeSpec.declTypeSpec = spec; } break; @@ -1613,6 +1614,12 @@ void DeclTypeSpecVisitor::MakeNumericType(TypeCategory category, int kind) { SetDeclTypeSpec(context().MakeNumericType(category, kind)); } +void DeclTypeSpecVisitor::CheckForAbstractType(const Symbol &typeSymbol) { + if (typeSymbol.attrs().test(Attr::ABSTRACT)) { + Say("ABSTRACT derived type may not be used here"_err_en_US); + } +} + void DeclTypeSpecVisitor::Post(const parser::DeclarationTypeSpec::ClassStar &) { SetDeclTypeSpec(context().globalScope().MakeClassStarType()); } @@ -3287,11 +3294,29 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Type &) { return true; } +void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Type &type) { + const parser::Name &derivedName{std::get(type.derived.t)}; + if (const Symbol * derivedSymbol{derivedName.symbol}) { + CheckForAbstractType(*derivedSymbol); // C706 + } +} + bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Class &) { SetDeclTypeSpecCategory(DeclTypeSpec::Category::ClassDerived); return true; } +void DeclarationVisitor::Post( + const parser::DeclarationTypeSpec::Class &parsedClass) { + const auto &typeName{std::get(parsedClass.derived.t)}; + if (auto spec{ResolveDerivedType(typeName)}; + spec && !IsExtensibleType(&*spec)) { // C705 + SayWithDecl(typeName, *typeName.symbol, + "Non-extensible derived type '%s' may not be used with CLASS" + " keyword"_err_en_US); + } +} + bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Record &) { // TODO return true; @@ -4501,7 +4526,7 @@ ParamValue DeclarationVisitor::GetParamValue( const parser::TypeParamValue &x, common::TypeParamAttr attr) { return std::visit( common::visitors{ - [=](const parser::ScalarIntExpr &x) { + [=](const parser::ScalarIntExpr &x) { // C704 return ParamValue{EvaluateIntExpr(x), attr}; }, [=](const parser::Star &) { return ParamValue::Assumed(attr); }, diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index c69569bc33e3..4873a0f50ebd 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -100,6 +100,7 @@ set(ERROR_TESTS resolve67.f90 resolve68.f90 resolve69.f90 + resolve70.f90 stop01.f90 structconst01.f90 structconst02.f90 diff --git a/test/semantics/allocate04.f90 b/test/semantics/allocate04.f90 index 4ccf01413c88..3b7ce25bf00e 100644 --- a/test/semantics/allocate04.f90 +++ b/test/semantics/allocate04.f90 @@ -47,7 +47,7 @@ subroutine C933_b(n) allocate(p3%y) !ERROR: Either type-spec or source-expr must appear in ALLOCATE when allocatable object is of abstract type allocate(p4(2)%y) - !WRONG allocate(Base:: u1) !C703 + !WRONG allocate(Base:: u1) ! No error expected allocate(real:: u1, u2(2)) diff --git a/test/semantics/resolve52.f90 b/test/semantics/resolve52.f90 index 3df7f7fae628..3ee41dd3503f 100644 --- a/test/semantics/resolve52.f90 +++ b/test/semantics/resolve52.f90 @@ -114,6 +114,7 @@ module m7 end type contains subroutine s(x) + !ERROR: Non-extensible derived type 't' may not be used with CLASS keyword class(t) :: x end end diff --git a/test/semantics/resolve69.f90 b/test/semantics/resolve69.f90 index 5950f66e4741..bf08c3a706b0 100644 --- a/test/semantics/resolve69.f90 +++ b/test/semantics/resolve69.f90 @@ -1,8 +1,14 @@ subroutine s1() ! C701 (R701) The type-param-value for a kind type parameter shall be a ! constant expression. + ! ! C702 (R701) A colon shall not be used as a type-param-value except in the ! declaration of an entity that has the POINTER or ALLOCATABLE attribute. + ! + ! C704 (R703) In a declaration-type-spec, every type-param-value that is + ! not a colon or an asterisk shall be a specification expression. + ! Section 10.1.11 defines specification expressions + ! integer, parameter :: constVal = 1 integer :: nonConstVal = 1 !ERROR: Invalid specification expression: reference to local entity 'nonconstval' @@ -20,6 +26,9 @@ subroutine s1() !OK because of the allocatable attribute character(:), allocatable :: colonString3 +!ERROR: Must have INTEGER type, but is REAL(4) + character(3.5) :: badParamValue + type derived(typeKind, typeLen) integer, kind :: typeKind integer, len :: typeLen diff --git a/test/semantics/resolve70.f90 b/test/semantics/resolve70.f90 new file mode 100644 index 000000000000..b771fd0677be --- /dev/null +++ b/test/semantics/resolve70.f90 @@ -0,0 +1,58 @@ +! C703 (R702) The derived-type-spec shall not specify an abstract type (7.5.7). +! This constraint refers to the derived-type-spec in a type-spec. A type-spec +! can appear in an ALLOCATE statement, an ac-spec for an array constructor, and +! in the type specifier of a TYPE GUARD statement +! +! C706 TYPE(derived-type-spec) shall not specify an abstract type (7.5.7). +! This is for a declaration-type-spec +! +! C796 (R756) The derived-type-spec shall not specify an abstract type (7.5.7). +! +! C705 (R703) In a declaration-type-spec that uses the CLASS keyword, +! derived-type-spec shall specify an extensible type (7.5.7). +subroutine s() + type, abstract :: abstractType + end type abstractType + + type, extends(abstractType) :: concreteType + end type concreteType + + ! declaration-type-spec + !ERROR: ABSTRACT derived type may not be used here + type (abstractType), allocatable :: abstractVar + + ! ac-spec for an array constructor + !ERROR: ABSTRACT derived type may not be used here + !ERROR: ABSTRACT derived type may not be used here + type (abstractType), parameter :: abstractArray(*) = (/ abstractType :: /) + + class(*), allocatable :: selector + + ! Structure constructor + !ERROR: ABSTRACT derived type may not be used here + !ERROR: ABSTRACT derived type 'abstracttype' may not be used in a structure constructor + type (abstractType) :: abstractVar1 = abstractType() + + ! Allocate statement + !ERROR: ABSTRACT derived type may not be used here + allocate(abstractType :: abstractVar) + + select type(selector) + ! Type specifier for a type guard statement + !ERROR: ABSTRACT derived type may not be used here + type is (abstractType) + end select +end subroutine s + +subroutine s1() + type :: extensible + end type + type, bind(c) :: inextensible + end type + + ! This one's OK + class(extensible) :: y + + !ERROR: Non-extensible derived type 'inextensible' may not be used with CLASS keyword + class(inextensible) :: x +end subroutine s1 diff --git a/test/semantics/structconst01.f90 b/test/semantics/structconst01.f90 index 5a4bd7bad346..a83286c422ab 100644 --- a/test/semantics/structconst01.f90 +++ b/test/semantics/structconst01.f90 @@ -3,6 +3,8 @@ ! errors meant to be caught by expression semantic analysis, as well as ! acceptable use cases. ! Type parameters are used here to make the parses unambiguous. +! C796 (R756) The derived-type-spec shall not specify an abstract type (7.5.7). +! This refers to a derived-type-spec used in a structure constructor module module1 type :: type1(j) @@ -29,7 +31,7 @@ subroutine type2arg(x) type(type2(0,0)), intent(in) :: x end subroutine type2arg subroutine abstractarg(x) - type(abstract(0)), intent(in) :: x + class(abstract(0)), intent(in) :: x end subroutine abstractarg subroutine errors call type1arg(type1(0)()) From 9c4bba11cf2329575ea9ee446f69e9caa797135c Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 4 Feb 2020 16:55:45 -0800 Subject: [PATCH 026/345] Progress on Fortran I/O runtime Use internal units for internal I/O state Replace use of virtual functions reference_wrapper Internal formatted output to array descriptor Delete dead code Begin list-directed internal output Refactorings and renamings for clarity List-directed external I/O (character) COMPLEX list-directed output Control list items First cut at unformatted I/O More OPEN statement work; rename class to ExternalFileUnit Complete OPEN (exc. for POSITION=), add CLOSE() OPEN(POSITION=) Flush buffers on crash and for terminal output; clean up Documentation Fix backquote in documentation Fix typo in comment Begin implementation of input Refactor binary floating-point properties to a new header, simplify numeric output editing Dodge spurious GCC 7.2 build warning Address review comments --- documentation/FortranForCProgrammers.md | 6 +- documentation/IORuntimeInternals.md | 341 ++++++++++ include/flang/common/real.h | 86 +++ include/flang/decimal/binary-floating-point.h | 39 +- include/flang/decimal/decimal.h | 9 + include/flang/evaluate/common.h | 4 +- include/flang/evaluate/complex.h | 2 +- include/flang/evaluate/integer.h | 2 +- include/flang/evaluate/real.h | 58 +- include/flang/evaluate/type.h | 2 +- lib/decimal/big-radix-floating-point.h | 3 +- lib/decimal/binary-to-decimal.cpp | 24 +- lib/decimal/decimal-to-binary.cpp | 6 +- lib/evaluate/characteristics.cpp | 29 - lib/evaluate/complex.cpp | 2 +- lib/evaluate/real.cpp | 52 +- module/iso_fortran_env.f90 | 3 +- runtime/CMakeLists.txt | 3 + runtime/buffer.h | 12 +- runtime/connection.cpp | 19 + runtime/connection.h | 50 ++ runtime/descriptor.cpp | 5 + runtime/descriptor.h | 1 + runtime/environment.cpp | 4 +- runtime/environment.h | 5 +- runtime/file.cpp | 110 ++-- runtime/file.h | 38 +- runtime/format-implementation.h | 355 ++++++++++ runtime/format.cpp | 390 ++--------- runtime/format.h | 72 +- runtime/internal-unit.cpp | 129 ++++ runtime/internal-unit.h | 46 ++ runtime/io-api.cpp | 614 +++++++++++++++++- runtime/io-api.h | 15 +- runtime/io-error.h | 3 +- runtime/io-stmt.cpp | 402 ++++++++---- runtime/io-stmt.h | 312 +++++++-- runtime/lock.h | 2 +- runtime/main.cpp | 3 +- runtime/memory.cpp | 2 +- runtime/memory.h | 11 +- runtime/numeric-output.cpp | 152 +++++ runtime/numeric-output.h | 324 ++++----- runtime/stop.cpp | 2 +- runtime/terminator.cpp | 21 +- runtime/terminator.h | 12 +- runtime/tools.cpp | 2 +- runtime/tools.h | 3 +- runtime/unit.cpp | 118 +++- runtime/unit.h | 106 ++- test/evaluate/real.cpp | 2 +- test/runtime/external-hello.cpp | 15 +- test/runtime/format.cpp | 29 +- test/runtime/hello.cpp | 48 +- 54 files changed, 2928 insertions(+), 1177 deletions(-) create mode 100644 documentation/IORuntimeInternals.md create mode 100644 include/flang/common/real.h create mode 100644 runtime/connection.cpp create mode 100644 runtime/connection.h create mode 100644 runtime/format-implementation.h create mode 100644 runtime/internal-unit.cpp create mode 100644 runtime/internal-unit.h create mode 100644 runtime/numeric-output.cpp diff --git a/documentation/FortranForCProgrammers.md b/documentation/FortranForCProgrammers.md index db8345477ba5..6038c7ce348a 100644 --- a/documentation/FortranForCProgrammers.md +++ b/documentation/FortranForCProgrammers.md @@ -1,9 +1,9 @@ - Fortran For C Programmers diff --git a/documentation/IORuntimeInternals.md b/documentation/IORuntimeInternals.md new file mode 100644 index 000000000000..70dd0941ac76 --- /dev/null +++ b/documentation/IORuntimeInternals.md @@ -0,0 +1,341 @@ + + +Fortran I/O Runtime Library Internal Design +=========================================== + +This note is meant to be an overview of the design of the *implementation* +of the f18 Fortran compiler's runtime support library for I/O statements. + +The *interface* to the I/O runtime support library is defined in the +C++ header file `runtime/io-api.h`. +This interface was designed to minimize the amount of complexity exposed +to its clients, which are of course the sequences of calls generated by +the compiler to implement each I/O statement. +By keeping this interface as simple as possible, we hope that we have +lowered the risk of future incompatible changes that would necessitate +recompilation of Fortran codes in order to link with later versions of +the runtime library. +As one will see in `io-api.h`, the interface is also directly callable +from C and C++ programs. + +The I/O facilities of the Fortran 2018 language are specified in the +language standard in its clauses 12 (I/O statements) and 13 (`FORMAT`). +It's a complicated collection of language features: + * Files can comprise *records* or *streams*. + * Records can be fixed-length or variable-length. + * Record files can be accessed sequentially or directly (random access). + * Files can be *formatted*, or *unformatted* raw bits. + * `CHARACTER` scalars and arrays can be used as if they were +fixed-length formatted sequential record files. + * Formatted I/O can be under control of a `FORMAT` statement +or `FMT=` specifier, *list-directed* with default formatting chosen +by the runtime, or `NAMELIST`, in which a collection of variables +can be given a name and passed as a group to the runtime library. + * Sequential records of a file can be partially processed by one +or more *non-advancing* I/O statements and eventually completed by +another. + * `FORMAT` strings can manipulate the position in the current +record arbitrarily, causing re-reading or overwriting. + * Floating-point output formatting supports more rounding modes +than the IEEE standard for floating-point arithmetic. + +The Fortran I/O runtime support library is written in C++17, and +uses some C++17 standard library facilities, but it is intended +to not have any link-time dependences on the C++ runtime support +library or any LLVM libraries. +This is important because there are at least two C++ runtime support +libraries, and we don't want Fortran application builders to have to +build multiple versions of their codes; neither do we want to require +them to ship LLVM libraries along with their products. + +Consequently, dynamic memory allocation in the Fortran runtime +uses only C's `malloc()` and `free()` functions, and the few +C++ standard class templates that we instantiate in the library have been +modified with optional template arguments that override their +allocators and deallocators. + +Conversions between the many binary floating-point formats supported +by f18 and their decimal representations are performed with the same +template library of fast conversion algorithms used to interpret +floating-point values in Fortran source programs and to emit them +to module files. + +Overview of Classes +=================== + +A suite of C++ classes and class templates are composed to construct +the Fortran I/O runtime support library. +They (mostly) reside in the C++ namespace `Fortran::runtime::io`. +They are summarized here in a bottom-up order of dependence. + +The header and C++ implementation source file names of these +classes are in the process of being vigorously rearranged and +modified; use `grep` or an IDE to discover these classes in +the source for now. (Sorry!) + +`Terminator` +---------- +A general facility for the entire library, `Terminator` latches a +source program statement location in terms of an unowned pointer to +its source file path name and line number and uses them to construct +a fatal error message if needed. +It is used for both user program errors and internal runtime library crashes. + +`IoErrorHandler` +-------------- +When I/O error conditions arise at runtime that the Fortran program +might have the privilege to handle itself via `ERR=`, `END=`, or +`EOR=` labels and/or by an `IOSTAT=` variable, this subclass of +`Terminator` is used to either latch the error indication or to crash. +It sorts out priorities in the case of multiple errors and determines +the final `IOSTAT=` value at the end of an I/O statement. + +`MutableModes` +------------ +Fortran's formatted I/O statements are affected by a suite of +modes that can be configured by `OPEN` statements, overridden by +data transfer I/O statement control lists, and further overridden +between data items with control edit descriptors in a `FORMAT` string. +These modes are represented with a `MutableModes` instance, and these +are instantiated and copied where one would expect them to be in +order to properly isolate their modifications. +The modes in force at the time each data item is processed constitute +a member of each `DataEdit`. + +`DataEdit` +-------- +Represents a single data edit descriptor from a `FORMAT` statement +or `FMT=` character value, with some hidden extensions to also +support formatting of list-directed transfers. +It holds an instance of `MutableModes`, and also has a repetition +count for when an array appears as a data item in the *io-list*. +For simplicity and efficiency, each data edit descriptor is +encoded in the `DataEdit` as a simple capitalized character +(or two) and some optional field widths. + +`FormatControl<>` +--------------- +This class template traverses a `FORMAT` statement's contents (or `FMT=` +character value) to extract data edit descriptors like `E20.14` to +serve each item in an I/O data transfer statement's *io-list*, +making callbacks to an instance of its class template argument +along the way to effect character literal output and record +positioning. +The Fortran language standard defines formatted I/O as if the `FORMAT` +string were driving the traversal of the data items in the *io-list*, +but our implementation reverses that perspective to allow a more +convenient (for the compiler) I/O runtime support library API design +in which each data item is presented to the library with a distinct +type-dependent call. + +Clients of `FormatControl` instantiations call its `GetNextDataEdit()` +member function to acquire the next data edit descriptor to be processed +from the format, and `FinishOutput()` to flush out any remaining +output strings or record positionings at the end of the *io-list*. + +The `DefaultFormatControlCallbacks` structure summarizes the API +expected by `FormatControl` from its class template actual arguments. + +`OpenFile` +-------- +This class encapsulates all (I hope) the operating system interfaces +used to interact with the host's filesystems for operations on +external units. +Asynchronous I/O interfaces are faked for now with synchronous +operations and deferred results. + +`ConnectionState` +--------------- +An active connection to an external or internal unit maintains +the common parts of its state in this subclass of `ConnectionAttributes`. +The base class holds state that should not change during the +lifetime of the connection, while the subclass maintains state +that may change during I/O statement execution. + +`InternalDescriptorUnit` +---------------------- +When I/O is being performed from/to a Fortran `CHARACTER` array +rather than an external file, this class manages the standard +interoperable descriptor used to access its elements as records. +It has the necessary interfaces to serve as an actual argument +to the `FormatControl` class template. + +`FileFrame<>` +----------- +This CRTP class template isolates all of the complexity involved between +an external unit's `OpenFile` and the buffering requirements +imposed by the capabilities of Fortran `FORMAT` control edit +descriptors that allow repositioning within the current record. +Its interface enables its clients to define a "frame" (my term, +not Fortran's) that is a contiguous range of bytes that are +or may soon be in the file. +This frame is defined as a file offset and a byte size. +The `FileFrame` instance manages an internal circular buffer +with two essential guarantees: + +1. The most recently requested frame is present in the buffer +and contiguous in memory. +1. Any extra data after the frame that may have been read from +the external unit will be preserved, so that it's safe to +read from a socket, pipe, or tape and not have to worry about +repositioning and rereading. + +In end-of-file situations, it's possible that a request to read +a frame may come up short. + +As a CRTP class template, `FileFrame` accesses the raw filesystem +facilities it needs from `*this`. + +`ExternalFileUnit` +---------------- +This class mixes in `ConnectionState`, `OpenFile`, and +`FileFrame` to represent the state of an open +(or soon to be opened) external file descriptor as a Fortran +I/O unit. +It has the contextual APIs required to serve as a template actual +argument to `FormatControl`. +And it contains a `std::variant<>` suitable for holding the +state of the active I/O statement in progress on the unit +(see below). + +`ExternalFileUnit` instances reside in a `Map` that is allocated +as a static variable and indexed by Fortran unit number. +Static member functions `LookUp()`, `LookUpOrCrash()`, and `LookUpOrCreate()` +probe the map to convert Fortran `UNIT=` numbers from I/O statements +into references to active units. + +`IoStatementBase` +--------------- +The subclasses of `IoStatementBase` each encapsulate and maintain +the state of one active Fortran I/O statement across the several +I/O runtime library API function calls it may comprise. +The subclasses handle the distinctions between internal vs. external I/O, +formatted vs. list-directed vs. unformatted I/O, input vs. output, +and so on. + +`IoStatementBase` inherits default `FORMAT` processing callbacks and +an `IoErrorHandler`. +Each of the `IoStatementBase` classes that pertain to formatted I/O +support the contextual callback interfaces needed by `FormatControl`, +overriding the default callbacks of the base class, which crash if +called inappropriately (e.g., if a `CLOSE` statement somehow +passes a data item from an *io-list*). + +The lifetimes of these subclasses' instances each begin with a user +program call to an I/O API routine with a name like `BeginExternalListOutput()` +and persist until `EndIoStatement()` is called. + +To reduce dynamic memory allocation, *external* I/O statements allocate +their per-statement state class instances in space reserved in the +`ExternalFileUnit` instance. +Internal I/O statements currently use dynamic allocation, but +the I/O API supports a means whereby the code generated for the Fortran +program may supply stack space to the I/O runtime support library +for this purpose. + +`IoStatementState` +---------------- +F18's Fortran I/O runtime support library defines and implements an API +that uses a sequence of function calls to implement each Fortran I/O +statement. +The state of each I/O statement in progress is maintained in some +subclass of `IoStatementBase`, as noted above. +The purpose of `IoStatementState` is to provide generic access +to the specific state classes without recourse to C++ `virtual` +functions or function pointers, language features that may not be +available to us in some important execution environments. +`IoStatementState` comprises a `std::variant<>` of wrapped references +to the various possibilities, and uses `std::visit()` to +access them as needed by the I/O API calls that process each specifier +in the I/O *control-list* and each item in the *io-list*. + +Pointers to `IoStatementState` instances are the `Cookie` type returned +in the I/O API for `Begin...` I/O statement calls, passed back for +the *control-list* specifiers and *io-list* data items, and consumed +by the `EndIoStatement()` call at the end of the statement. + +Storage for `IoStatementState` is reserved in `ExternalFileUnit` for +external I/O units, and in the various final subclasses for internal +I/O statement states otherwise. + +Since Fortran permits a `CLOSE` statement to reference a nonexistent +unit, the library has to treat that (expected to be rare) situation +as a weird variation of internal I/O since there's no `ExternalFileUnit` +available to hold its `IoStatementBase` subclass or `IoStatementState`. + +A Narrative Overview Of `PRINT *, 'HELLO, WORLD'` +================================================= +1. When the compiled Fortran program begins execution at the `main()` +entry point exported from its main program, it calls `ProgramStart()` +with its arguments and environment. `ProgramStart()` calls +`ExternalFileUnit::InitializePredefinedUnits()` to create and +initialize Fortran units 5 and 6 and connect them with the +standard input and output file descriptors (respectively). +1. The generated code calls `BeginExternalListOutput()` to +start the sequence of calls that implement the `PRINT` statement. +The default unit code is converted to 6 and passed to +`ExternalFileUnit::LookUpOrCrash()`, which returns a reference to +unit 6's instance. +1. We check that the unit was opened for formatted I/O. +1. `ExternalFileUnit::BeginIoStatement<>()` is called to initialize +an instance of `ExternalListIoStatementState` in the unit, +point to it with an `IoStatementState`, and return a reference to +that object whose address will be the `Cookie` for this statement. +1. The generated code calls `OutputAscii()` with that cookie and the +address and length of the string. +1. `OutputAscii()` confirms that the cookie corresponds to an output +statement and determines that it's list-directed. +1. `ListDirectedStatementState::EmitLeadingSpaceOrAdvance()` +emits the required initial space on the new current output record +by calling `IoStatementState::GetConnectionState()` to locate +the connection state, determining from the record position state +that the space is necessary, and calling `IoStatementState::Emit()` +to cough it out. That call is redirected to `ExternalFileUnit::Emit()`, +which calls `FileFrame::WriteFrame()` to extend +the frame of the current record and then `memcpy()` to fill its +first byte with the space. +1. Back in `OutputAscii()`, the mutable modes and connection state +of the `IoStatementState` are queried to see whether we're in an +`WRITE(UNIT=,FMT=,DELIM=)` statement with a delimited specifier. +If we were, the library would emit the appropriate quote marks, +double up any instances of that character in the text, and split the +text over multiple records if it's long. +1. But we don't have a delimiter, so `OutputAscii()` just carves +up the text into record-sized chunks and emits them. There's just +one chunk for our short `CHARACTER` string value in this example. +It's passed to `IoStatementState::Emit()`, which (as above) is +redirected to `ExternalFileUnit::Emit()`, which interacts with the +frame to extend the frame and `memcpy` data into the buffer. +1. A flag is set in `ListDirectedStatementState` to remember +that the last item emitted in this list-directed output statement +was an undelimited `CHARACTER` value, so that if the next item is +also an undelimited `CHARACTER`, no interposing space will be emitted +between them. +1. `OutputAscii()` return `true` to its caller. +1. The generated code calls `EndIoStatement()`, which is redirected to +`ExternalIoStatementState`'s override of that function. +As this is not a non-advancing I/O statement, `ExternalFileUnit::AdvanceRecord()` +is called to end the record. Since this is a sequential formatted +file, a newline is emitted. +1. If unit 6 is connected to a terminal, the buffer is flushed. +`FileFrame::Flush()` drives `ExternalFileUnit::Write()` +to push out the data in maximal contiguous chunks, dealing with any +short writes that might occur, and collecting I/O errors along the way. +This statement has no `ERR=` label or `IOSTAT=` specifier, so errors +arriving at `IoErrorHandler::SignalErrno()` will cause an immediate +crash. +1. `ExternalIoStatementBase::EndIoStatement()` is called. +It gets the final `IOSTAT=` value from `IoStatementBase::EndIoStatement()`, +tells the `ExternalFileUnit` that no I/O statement remains active, and +returns the I/O status value back to the program. +1. Eventually, the program calls `ProgramEndStatement()`, which +calls `ExternalFileUnit::CloseAll()`, which flushes and closes all +open files. If the standard output were not a terminal, the output +would be written now with the same sequence of calls as above. +1. `exit(EXIT_SUCCESS)`. diff --git a/include/flang/common/real.h b/include/flang/common/real.h new file mode 100644 index 000000000000..d15de663a92b --- /dev/null +++ b/include/flang/common/real.h @@ -0,0 +1,86 @@ +//===-- include/flang/common/real.h -----------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_COMMON_REAL_H_ +#define FORTRAN_COMMON_REAL_H_ + +// Characteristics of IEEE-754 & related binary floating-point numbers. +// The various representations are distinguished by their binary precisions +// (number of explicit significand bits and any implicit MSB in the fraction). + +#include + +namespace Fortran::common { + +// Total representation size in bits for each type +static constexpr int BitsForBinaryPrecision(int binaryPrecision) { + switch (binaryPrecision) { + case 8: return 16; // IEEE single (truncated): 1+8+7 + case 11: return 16; // IEEE half precision: 1+5+10 + case 24: return 32; // IEEE single precision: 1+8+23 + case 53: return 64; // IEEE double precision: 1+11+52 + case 64: return 80; // x87 extended precision: 1+15+64 + case 106: return 128; // "double-double": 2*(1+11+52) + case 112: return 128; // IEEE quad precision: 1+16+111 + default: return -1; + } +} + +// Number of significant decimal digits in the fraction of the +// exact conversion of the least nonzero (subnormal) value +// in each type; i.e., a 128-bit quad value can be formatted +// exactly with FORMAT(E0.22981). +static constexpr int MaxDecimalConversionDigits(int binaryPrecision) { + switch (binaryPrecision) { + case 8: return 93; + case 11: return 17; + case 24: return 105; + case 53: return 751; + case 64: return 11495; + case 106: return 2 * 751; + case 112: return 22981; + default: return -1; + } +} + +template class RealDetails { +private: + // Converts bit widths to whole decimal digits + static constexpr int LogBaseTwoToLogBaseTen(int logb2) { + constexpr std::int64_t LogBaseTenOfTwoTimesTenToThe12th{301029995664}; + constexpr std::int64_t TenToThe12th{1000000000000}; + std::int64_t logb10{ + (logb2 * LogBaseTenOfTwoTimesTenToThe12th) / TenToThe12th}; + return static_cast(logb10); + } + +public: + static constexpr int binaryPrecision{BINARY_PRECISION}; + static constexpr int bits{BitsForBinaryPrecision(binaryPrecision)}; + static constexpr bool isImplicitMSB{binaryPrecision != 64 /*x87*/}; + static constexpr int significandBits{binaryPrecision - isImplicitMSB}; + static constexpr int exponentBits{bits - significandBits - 1 /*sign*/}; + static constexpr int maxExponent{(1 << exponentBits) - 1}; + static constexpr int exponentBias{maxExponent / 2}; + + static constexpr int decimalPrecision{ + LogBaseTwoToLogBaseTen(binaryPrecision - 1)}; + static constexpr int decimalRange{LogBaseTwoToLogBaseTen(exponentBias - 1)}; + + // Number of significant decimal digits in the fraction of the + // exact conversion of the least nonzero subnormal. + static constexpr int maxDecimalConversionDigits{ + MaxDecimalConversionDigits(binaryPrecision)}; + + static_assert(binaryPrecision > 0); + static_assert(exponentBits > 1); + static_assert(exponentBits <= 16); +}; + +} +#endif // FORTRAN_COMMON_REAL_H_ diff --git a/include/flang/decimal/binary-floating-point.h b/include/flang/decimal/binary-floating-point.h index 3da4a336c50e..bf467c5cbb70 100644 --- a/include/flang/decimal/binary-floating-point.h +++ b/include/flang/decimal/binary-floating-point.h @@ -12,6 +12,7 @@ // Access and manipulate the fields of an IEEE-754 binary // floating-point value via a generalized template. +#include "flang/common/real.h" #include "flang/common/uint128.h" #include #include @@ -20,34 +21,24 @@ namespace Fortran::decimal { -static constexpr int BitsForPrecision(int prec) { - switch (prec) { - case 8: return 16; - case 11: return 16; - case 24: return 32; - case 53: return 64; - case 64: return 80; - case 112: return 128; - default: return -1; - } -} +template +struct BinaryFloatingPointNumber + : public common::RealDetails { -// LOG10(2.)*1E12 -static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664}; + using Details = common::RealDetails; + using Details::bits; + using Details::decimalPrecision; + using Details::decimalRange; + using Details::exponentBias; + using Details::exponentBits; + using Details::isImplicitMSB; + using Details::maxDecimalConversionDigits; + using Details::maxExponent; + using Details::significandBits; -template struct BinaryFloatingPointNumber { - static constexpr int precision{PRECISION}; - static constexpr int bits{BitsForPrecision(precision)}; using RawType = common::HostUnsignedIntType; static_assert(CHAR_BIT * sizeof(RawType) >= bits); - static constexpr bool implicitMSB{precision != 64 /*x87*/}; - static constexpr int significandBits{precision - implicitMSB}; - static constexpr int exponentBits{bits - 1 - significandBits}; - static constexpr int maxExponent{(1 << exponentBits) - 1}; - static constexpr int exponentBias{maxExponent / 2}; static constexpr RawType significandMask{(RawType{1} << significandBits) - 1}; - static constexpr int RANGE{static_cast( - (exponentBias - 1) * ScaledLogBaseTenOfTwo / 1000000000000)}; constexpr BinaryFloatingPointNumber() {} // zero constexpr BinaryFloatingPointNumber( @@ -76,7 +67,7 @@ template struct BinaryFloatingPointNumber { constexpr RawType Significand() const { return raw & significandMask; } constexpr RawType Fraction() const { RawType sig{Significand()}; - if (implicitMSB && BiasedExponent() > 0) { + if (isImplicitMSB && BiasedExponent() > 0) { sig |= RawType{1} << significandBits; } return sig; diff --git a/include/flang/decimal/decimal.h b/include/flang/decimal/decimal.h index 812d08fe8d09..c9aad161f4dd 100644 --- a/include/flang/decimal/decimal.h +++ b/include/flang/decimal/decimal.h @@ -62,6 +62,15 @@ enum DecimalConversionFlags { AlwaysSign = 2, /* emit leading '+' if not negative */ }; +/* + * When allocating decimal conversion output buffers, use the maximum + * number of significant decimal digits in the representation of the + * least nonzero value, and add this extra space for a sign, a NUL, and + * some extra due to the library working internally in base 10**16 + * and computing its output size in multiples of 16. + */ +#define EXTRA_DECIMAL_CONVERSION_SPACE (1 + 1 + 16 - 1) + #ifdef __cplusplus template ConversionToDecimalResult ConvertToDecimal(char *, size_t, diff --git a/include/flang/evaluate/common.h b/include/flang/evaluate/common.h index f24e93d7cd33..b7ea530e712f 100644 --- a/include/flang/evaluate/common.h +++ b/include/flang/evaluate/common.h @@ -130,9 +130,9 @@ struct Rounding { static constexpr Rounding defaultRounding; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -constexpr bool IsHostLittleEndian{false}; +constexpr bool isHostLittleEndian{false}; #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -constexpr bool IsHostLittleEndian{true}; +constexpr bool isHostLittleEndian{true}; #else #error host endianness is not known #endif diff --git a/include/flang/evaluate/complex.h b/include/flang/evaluate/complex.h index 201cbcea60ab..16559e9f0962 100644 --- a/include/flang/evaluate/complex.h +++ b/include/flang/evaluate/complex.h @@ -95,7 +95,7 @@ extern template class Complex, 11>>; extern template class Complex, 8>>; extern template class Complex, 24>>; extern template class Complex, 53>>; -extern template class Complex, 64, false>>; +extern template class Complex, 64>>; extern template class Complex, 112>>; } #endif // FORTRAN_EVALUATE_COMPLEX_H_ diff --git a/include/flang/evaluate/integer.h b/include/flang/evaluate/integer.h index 1bff2bb7b7ca..46478f7e6106 100644 --- a/include/flang/evaluate/integer.h +++ b/include/flang/evaluate/integer.h @@ -49,7 +49,7 @@ namespace Fortran::evaluate::value { // Member functions that correspond to Fortran intrinsic functions are // named accordingly in ALL CAPS so that they can be referenced easily in // the language standard. -template, typename BIGPART = HostUnsignedInt> diff --git a/include/flang/evaluate/real.h b/include/flang/evaluate/real.h index 84e2d556baf3..bcc73cb54b73 100644 --- a/include/flang/evaluate/real.h +++ b/include/flang/evaluate/real.h @@ -12,6 +12,7 @@ #include "formatting.h" #include "integer.h" #include "rounding-bits.h" +#include "flang/common/real.h" #include "flang/evaluate/common.h" #include #include @@ -30,26 +31,25 @@ static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664}; // Models IEEE binary floating-point numbers (IEEE 754-2008, // ISO/IEC/IEEE 60559.2011). The first argument to this // class template must be (or look like) an instance of Integer<>; -// the second specifies the number of effective bits in the fraction; -// the third, if true, indicates that the most significant position of the -// fraction is an implicit bit whose value is assumed to be 1 in a finite -// normal number. -template class Real { +// the second specifies the number of effective bits (binary precision) +// in the fraction. +template +class Real : public common::RealDetails { public: using Word = WORD; + static constexpr int binaryPrecision{PREC}; + using Details = common::RealDetails; + using Details::exponentBias; + using Details::exponentBits; + using Details::isImplicitMSB; + using Details::maxExponent; + using Details::significandBits; + static constexpr int bits{Word::bits}; - static constexpr int precision{PREC}; - using Fraction = Integer; // all bits made explicit - static constexpr bool implicitMSB{IMPLICIT_MSB}; - static constexpr int significandBits{precision - implicitMSB}; - static constexpr int exponentBits{bits - significandBits - 1 /*sign*/}; - static_assert(precision > 0); - static_assert(exponentBits > 1); - static_assert(exponentBits <= 16); - static constexpr int maxExponent{(1 << exponentBits) - 1}; - static constexpr int exponentBias{maxExponent / 2}; - - template friend class Real; + static_assert(bits >= Details::bits); + using Fraction = Integer; // all bits made explicit + + template friend class Real; constexpr Real() {} // +0.0 constexpr Real(const Real &) = default; @@ -130,12 +130,13 @@ template class Real { static constexpr Real EPSILON() { Real epsilon; - epsilon.Normalize(false, exponentBias - precision, Fraction::MASKL(1)); + epsilon.Normalize( + false, exponentBias - binaryPrecision, Fraction::MASKL(1)); return epsilon; } static constexpr Real HUGE() { Real huge; - huge.Normalize(false, maxExponent - 1, Fraction::MASKR(precision)); + huge.Normalize(false, maxExponent - 1, Fraction::MASKR(binaryPrecision)); return huge; } static constexpr Real TINY() { @@ -144,11 +145,9 @@ template class Real { return tiny; } - static constexpr int DIGITS{precision}; - static constexpr int PRECISION{static_cast( - (precision - 1) * ScaledLogBaseTenOfTwo / 1000000000000)}; - static constexpr int RANGE{static_cast( - (exponentBias - 1) * ScaledLogBaseTenOfTwo / 1000000000000)}; + static constexpr int DIGITS{binaryPrecision}; + static constexpr int PRECISION{Details::decimalPrecision}; + static constexpr int RANGE{Details::decimalRange}; static constexpr int MAXEXPONENT{maxExponent - 1 - exponentBias}; static constexpr int MINEXPONENT{1 - exponentBias}; @@ -190,7 +189,7 @@ template class Real { } ValueWithRealFlags result; int exponent{exponentBias + absN.bits - leadz - 1}; - int bitsNeeded{absN.bits - (leadz + implicitMSB)}; + int bitsNeeded{absN.bits - (leadz + isImplicitMSB)}; int bitsLost{bitsNeeded - significandBits}; if (bitsLost <= 0) { Fraction fraction{Fraction::ConvertUnsigned(absN).value}; @@ -224,7 +223,8 @@ template class Real { result.flags.set( RealFlag::Overflow, exponent >= exponentBias + result.value.bits); result.flags |= intPart.flags; - int shift{exponent - exponentBias - precision + 1}; // positive -> left + int shift{ + exponent - exponentBias - binaryPrecision + 1}; // positive -> left result.value = result.value.ConvertUnsigned(intPart.value.GetFraction().SHIFTR(-shift)) .value.SHIFTL(shift); @@ -252,7 +252,7 @@ template class Real { } ValueWithRealFlags result; int exponent{exponentBias + x.UnbiasedExponent()}; - int bitsLost{A::precision - precision}; + int bitsLost{A::binaryPrecision - binaryPrecision}; if (exponent < 1) { bitsLost += 1 - exponent; exponent = 1; @@ -282,7 +282,7 @@ template class Real { // Extracts the fraction; any implied bit is made explicit. constexpr Fraction GetFraction() const { Fraction result{Fraction::ConvertUnsigned(word_).value}; - if constexpr (!implicitMSB) { + if constexpr (!isImplicitMSB) { return result; } else { int exponent{Exponent()}; @@ -366,7 +366,7 @@ extern template class Real, 11>; // IEEE half format extern template class Real, 8>; // the "other" half format extern template class Real, 24>; // IEEE single extern template class Real, 53>; // IEEE double -extern template class Real, 64, false>; // 80387 extended precision +extern template class Real, 64>; // 80387 extended precision extern template class Real, 112>; // IEEE quad // N.B. No "double-double" support. } diff --git a/include/flang/evaluate/type.h b/include/flang/evaluate/type.h index 29dde4eeb1a4..a558928d4893 100644 --- a/include/flang/evaluate/type.h +++ b/include/flang/evaluate/type.h @@ -268,7 +268,7 @@ class Type : public TypeBase { template<> class Type : public TypeBase { public: - using Scalar = value::Real, 64, false>; + using Scalar = value::Real, 64>; }; // REAL(KIND=16) is IEEE quad precision (128 bits) diff --git a/lib/decimal/big-radix-floating-point.h b/lib/decimal/big-radix-floating-point.h index 51eb9ec8c5b6..35f0a2e8c31f 100644 --- a/lib/decimal/big-radix-floating-point.h +++ b/lib/decimal/big-radix-floating-point.h @@ -58,7 +58,8 @@ template class BigRadixFloatingPointNumber { // The base-2 logarithm of the least significant bit that can arise // in a subnormal IEEE floating-point number. - static constexpr int minLog2AnyBit{-Real::exponentBias - Real::precision}; + static constexpr int minLog2AnyBit{ + -Real::exponentBias - Real::binaryPrecision}; // The number of Digits needed to represent the smallest subnormal. static constexpr int maxDigits{3 - minLog2AnyBit / log10Radix}; diff --git a/lib/decimal/binary-to-decimal.cpp b/lib/decimal/binary-to-decimal.cpp index ba061856b089..d15aab5ff638 100644 --- a/lib/decimal/binary-to-decimal.cpp +++ b/lib/decimal/binary-to-decimal.cpp @@ -25,7 +25,7 @@ BigRadixFloatingPointNumber::BigRadixFloatingPointNumber( } int twoPow{x.UnbiasedExponent()}; twoPow -= x.bits - 1; - if (!x.implicitMSB) { + if (!x.isImplicitMSB) { ++twoPow; } int lshift{x.exponentBits}; @@ -317,7 +317,7 @@ void BigRadixFloatingPointNumber -ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, +ConversionToDecimalResult ConvertToDecimal(char *buffer, std::size_t size, enum DecimalConversionFlags flags, int digits, enum FortranRounding rounding, BinaryFloatingPointNumber x) { if (x.IsNaN()) { @@ -355,34 +355,34 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, } } -template ConversionToDecimalResult ConvertToDecimal<8>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<8>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<8>); -template ConversionToDecimalResult ConvertToDecimal<11>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<11>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<11>); -template ConversionToDecimalResult ConvertToDecimal<24>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<24>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<24>); -template ConversionToDecimalResult ConvertToDecimal<53>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<53>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<53>); -template ConversionToDecimalResult ConvertToDecimal<64>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<64>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<64>); -template ConversionToDecimalResult ConvertToDecimal<112>(char *, size_t, +template ConversionToDecimalResult ConvertToDecimal<112>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<112>); extern "C" { -ConversionToDecimalResult ConvertFloatToDecimal(char *buffer, size_t size, +ConversionToDecimalResult ConvertFloatToDecimal(char *buffer, std::size_t size, enum DecimalConversionFlags flags, int digits, enum FortranRounding rounding, float x) { return Fortran::decimal::ConvertToDecimal(buffer, size, flags, digits, rounding, Fortran::decimal::BinaryFloatingPointNumber<24>(x)); } -ConversionToDecimalResult ConvertDoubleToDecimal(char *buffer, size_t size, +ConversionToDecimalResult ConvertDoubleToDecimal(char *buffer, std::size_t size, enum DecimalConversionFlags flags, int digits, enum FortranRounding rounding, double x) { return Fortran::decimal::ConvertToDecimal(buffer, size, flags, digits, @@ -390,8 +390,8 @@ ConversionToDecimalResult ConvertDoubleToDecimal(char *buffer, size_t size, } #if __x86_64__ -ConversionToDecimalResult ConvertLongDoubleToDecimal(char *buffer, size_t size, - enum DecimalConversionFlags flags, int digits, +ConversionToDecimalResult ConvertLongDoubleToDecimal(char *buffer, + std::size_t size, enum DecimalConversionFlags flags, int digits, enum FortranRounding rounding, long double x) { return Fortran::decimal::ConvertToDecimal(buffer, size, flags, digits, rounding, Fortran::decimal::BinaryFloatingPointNumber<64>(x)); diff --git a/lib/decimal/decimal-to-binary.cpp b/lib/decimal/decimal-to-binary.cpp index de15833098f5..a07cd570d8c3 100644 --- a/lib/decimal/decimal-to-binary.cpp +++ b/lib/decimal/decimal-to-binary.cpp @@ -122,7 +122,7 @@ bool BigRadixFloatingPointNumber::ParseNumber( // The decimal->binary conversion routine will cope with // returning 0 or Inf, but we must ensure that "expo" didn't // overflow back around to something legal. - expo = 10 * Real::RANGE; + expo = 10 * Real::decimalRange; exponent_ = 0; } p = q; // exponent was valid @@ -256,7 +256,7 @@ ConversionToBinaryResult IntermediateFloat::ToBinary( using Raw = typename Binary::RawType; Raw raw = static_cast(isNegative) << (Binary::bits - 1); raw |= static_cast(expo) << Binary::significandBits; - if constexpr (Binary::implicitMSB) { + if constexpr (Binary::isImplicitMSB) { fraction &= ~topBit; } raw |= fraction; @@ -278,7 +278,7 @@ BigRadixFloatingPointNumber::ConvertToBinary() { // it sits to the *left* of the digits: i.e., x = .D * 10.**E exponent_ += digits_ * log10Radix; // Sanity checks for ridiculous exponents - static constexpr int crazy{2 * Real::RANGE + log10Radix}; + static constexpr int crazy{2 * Real::decimalRange + log10Radix}; if (exponent_ < -crazy) { // underflow to +/-0. return {Real{SignBit()}, Inexact}; } else if (exponent_ > crazy) { // overflow to +/-Inf. diff --git a/lib/evaluate/characteristics.cpp b/lib/evaluate/characteristics.cpp index ac18b5a1730b..3197c46863fa 100644 --- a/lib/evaluate/characteristics.cpp +++ b/lib/evaluate/characteristics.cpp @@ -121,35 +121,6 @@ std::optional TypeAndShape::Characterize( } } -#if 0 // pmk -std::optional TypeAndShape::Characterize( - const Expr &expr, FoldingContext &context) { - if (const auto *symbol{UnwrapWholeSymbolDataRef(expr)}) { - if (const auto *object{ - symbol->detailsIf()}) { - return Characterize(*object); - } else if (const auto *assoc{ - symbol->detailsIf()}) { - return Characterize(*assoc, context); - } - } - if (auto type{expr.GetType()}) { - if (auto shape{GetShape(context, expr)}) { - TypeAndShape result{*type, std::move(*shape)}; - if (type->category() == TypeCategory::Character) { - if (const auto *chExpr{UnwrapExpr>(expr)}) { - if (auto length{chExpr->LEN()}) { - result.set_LEN(Expr{std::move(*length)}); - } - } - } - return result; - } - } - return std::nullopt; -} -#endif // pmk - bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages, const TypeAndShape &that, const char *thisIs, const char *thatIs, bool isElemental) const { diff --git a/lib/evaluate/complex.cpp b/lib/evaluate/complex.cpp index 210fd1fb54c5..a2dca42e4e0b 100644 --- a/lib/evaluate/complex.cpp +++ b/lib/evaluate/complex.cpp @@ -100,6 +100,6 @@ template class Complex, 11>>; template class Complex, 8>>; template class Complex, 24>>; template class Complex, 53>>; -template class Complex, 64, false>>; +template class Complex, 64>>; template class Complex, 112>>; } diff --git a/lib/evaluate/real.cpp b/lib/evaluate/real.cpp index ec9ab1dd4373..29ad1e0aa5a3 100644 --- a/lib/evaluate/real.cpp +++ b/lib/evaluate/real.cpp @@ -15,8 +15,7 @@ namespace Fortran::evaluate::value { -template -Relation Real::Compare(const Real &y) const { +template Relation Real::Compare(const Real &y) const { if (IsNotANumber() || y.IsNotANumber()) { // NaN vs x, x vs NaN return Relation::Unordered; } else if (IsInfinite()) { @@ -53,8 +52,8 @@ Relation Real::Compare(const Real &y) const { } } -template -ValueWithRealFlags> Real::Add( +template +ValueWithRealFlags> Real::Add( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { @@ -133,8 +132,8 @@ ValueWithRealFlags> Real::Add( return result; } -template -ValueWithRealFlags> Real::Multiply( +template +ValueWithRealFlags> Real::Multiply( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { @@ -193,8 +192,8 @@ ValueWithRealFlags> Real::Multiply( return result; } -template -ValueWithRealFlags> Real::Divide( +template +ValueWithRealFlags> Real::Divide( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { @@ -261,8 +260,8 @@ ValueWithRealFlags> Real::Divide( return result; } -template -ValueWithRealFlags> Real::ToWholeNumber( +template +ValueWithRealFlags> Real::ToWholeNumber( common::RoundingMode mode) const { ValueWithRealFlags result{*this}; if (IsNotANumber()) { @@ -271,7 +270,7 @@ ValueWithRealFlags> Real::ToWholeNumber( } else if (IsInfinite()) { result.flags.set(RealFlag::Overflow); } else { - constexpr int noClipExponent{exponentBias + precision - 1}; + constexpr int noClipExponent{exponentBias + binaryPrecision - 1}; if (Exponent() < noClipExponent) { Real adjust; // ABS(EPSILON(adjust)) == 0.5 adjust.Normalize(IsSignBitSet(), noClipExponent, Fraction::MASKL(1)); @@ -287,8 +286,8 @@ ValueWithRealFlags> Real::ToWholeNumber( return result; } -template -RealFlags Real::Normalize(bool negative, int exponent, +template +RealFlags Real::Normalize(bool negative, int exponent, const Fraction &fraction, Rounding rounding, RoundingBits *roundingBits) { int lshift{fraction.LEADZ()}; if (lshift == fraction.bits /* fraction is zero */ && @@ -337,7 +336,7 @@ RealFlags Real::Normalize(bool negative, int exponent, } } } - if constexpr (implicitMSB) { + if constexpr (isImplicitMSB) { word_ = word_.IBCLR(significandBits); } word_ = word_.IOR(Word{exponent}.SHIFTL(significandBits)); @@ -347,8 +346,8 @@ RealFlags Real::Normalize(bool negative, int exponent, return {}; } -template -RealFlags Real::Round( +template +RealFlags Real::Round( Rounding rounding, const RoundingBits &bits, bool multiply) { int origExponent{Exponent()}; RealFlags flags; @@ -363,7 +362,7 @@ RealFlags Real::Round( int newExponent{origExponent}; if (sum.carry) { // The fraction was all ones before rounding; sum.value is now zero - sum.value = sum.value.IBSET(precision - 1); + sum.value = sum.value.IBSET(binaryPrecision - 1); if (++newExponent >= maxExponent) { flags.set(RealFlag::Overflow); // rounded away to an infinity } @@ -388,8 +387,8 @@ RealFlags Real::Round( return flags; } -template -void Real::NormalizeAndRound(ValueWithRealFlags &result, +template +void Real::NormalizeAndRound(ValueWithRealFlags &result, bool isNegative, int exponent, const Fraction &fraction, Rounding rounding, RoundingBits roundingBits, bool multiply) { result.flags |= result.value.Normalize( @@ -423,17 +422,16 @@ inline RealFlags MapFlags(decimal::ConversionResultFlags flags) { return result; } -template -ValueWithRealFlags> Real::Read( +template +ValueWithRealFlags> Real::Read( const char *&p, Rounding rounding) { auto converted{ decimal::ConvertToBinary

(p, MapRoundingMode(rounding.mode))}; - const auto *value{reinterpret_cast *>(&converted.binary)}; + const auto *value{reinterpret_cast *>(&converted.binary)}; return {*value, MapFlags(converted.flags)}; } -template -std::string Real::DumpHexadecimal() const { +template std::string Real::DumpHexadecimal() const { if (IsNotANumber()) { return "NaN 0x"s + word_.Hexadecimal(); } else if (IsNegative()) { @@ -479,8 +477,8 @@ std::string Real::DumpHexadecimal() const { } } -template -std::ostream &Real::AsFortran( +template +std::ostream &Real::AsFortran( std::ostream &o, int kind, bool minimal) const { if (IsNotANumber()) { o << "(0._" << kind << "/0.)"; @@ -521,6 +519,6 @@ template class Real, 11>; template class Real, 8>; template class Real, 24>; template class Real, 53>; -template class Real, 64, false>; +template class Real, 64>; template class Real, 112>; } diff --git a/module/iso_fortran_env.f90 b/module/iso_fortran_env.f90 index 01676cd7f894..957c3ec88131 100644 --- a/module/iso_fortran_env.f90 +++ b/module/iso_fortran_env.f90 @@ -128,7 +128,8 @@ module iso_fortran_env integer, parameter :: current_team = -1, initial_team = -2, parent_team = -3 - integer, parameter :: input_unit = 5, output_unit = 6, error_unit = 0 + integer, parameter :: input_unit = 5, output_unit = 6 + integer, parameter :: error_unit = output_unit integer, parameter :: iostat_end = -1, iostat_eor = -2 integer, parameter :: iostat_inquire_internal_unit = -1 diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 4c1ecf0be736..571775ce8984 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -9,16 +9,19 @@ add_library(FortranRuntime ISO_Fortran_binding.cpp buffer.cpp + connection.cpp derived-type.cpp descriptor.cpp environment.cpp file.cpp format.cpp + internal-unit.cpp io-api.cpp io-error.cpp io-stmt.cpp main.cpp memory.cpp + numeric-output.cpp stop.cpp terminator.cpp tools.cpp diff --git a/runtime/buffer.h b/runtime/buffer.h index 57c740fabfa8..a956a3bbae1d 100644 --- a/runtime/buffer.h +++ b/runtime/buffer.h @@ -97,14 +97,13 @@ template class FileFrame { } dirty_ = true; frame_ = at - fileOffset_; - length_ = std::max(length_, static_cast(frame_ + bytes)); + length_ = std::max(length_, frame_ + bytes); } void Flush(IoErrorHandler &handler) { if (dirty_) { while (length_ > 0) { - std::size_t chunk{std::min(static_cast(length_), - static_cast(size_ - start_))}; + std::size_t chunk{std::min(length_, size_ - start_)}; std::size_t put{ Store().Write(fileOffset_, buffer_ + start_, chunk, handler)}; length_ -= put; @@ -121,15 +120,14 @@ template class FileFrame { private: STORE &Store() { return static_cast(*this); } - void Reallocate(std::size_t bytes, Terminator &terminator) { + void Reallocate(std::size_t bytes, const Terminator &terminator) { if (bytes > size_) { char *old{buffer_}; auto oldSize{size_}; size_ = std::max(bytes, minBuffer); buffer_ = reinterpret_cast(AllocateMemoryOrCrash(terminator, size_)); - auto chunk{ - std::min(length_, static_cast(oldSize - start_))}; + auto chunk{std::min(length_, oldSize - start_)}; std::memcpy(buffer_, old + start_, chunk); start_ = 0; std::memcpy(buffer_ + chunk, old, length_ - chunk); @@ -143,7 +141,7 @@ template class FileFrame { dirty_ = false; } - void DiscardLeadingBytes(std::size_t n, Terminator &terminator) { + void DiscardLeadingBytes(std::size_t n, const Terminator &terminator) { RUNTIME_CHECK(terminator, length_ >= n); length_ -= n; if (length_ == 0) { diff --git a/runtime/connection.cpp b/runtime/connection.cpp new file mode 100644 index 000000000000..ff15a40819ab --- /dev/null +++ b/runtime/connection.cpp @@ -0,0 +1,19 @@ +//===-- runtime/connection.cpp ----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "connection.h" +#include "environment.h" + +namespace Fortran::runtime::io { + +std::size_t ConnectionState::RemainingSpaceInRecord() const { + return recordLength.value_or( + executionEnvironment.listDirectedOutputLineLengthLimit) - + positionInRecord; +} +} diff --git a/runtime/connection.h b/runtime/connection.h new file mode 100644 index 000000000000..85372dfa610d --- /dev/null +++ b/runtime/connection.h @@ -0,0 +1,50 @@ +//===-- runtime/connection.h ------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Fortran I/O connection state (internal & external) + +#ifndef FORTRAN_RUNTIME_IO_CONNECTION_H_ +#define FORTRAN_RUNTIME_IO_CONNECTION_H_ + +#include "format.h" +#include +#include + +namespace Fortran::runtime::io { + +enum class Access { Sequential, Direct, Stream }; + +inline bool IsRecordFile(Access a) { return a != Access::Stream; } + +// These characteristics of a connection are immutable after being +// established in an OPEN statement. +struct ConnectionAttributes { + Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' + std::optional recordLength; // RECL= when fixed-length + bool isUnformatted{false}; // FORM='UNFORMATTED' + bool isUTF8{false}; // ENCODING='UTF-8' +}; + +struct ConnectionState : public ConnectionAttributes { + std::size_t RemainingSpaceInRecord() const; + // Positions in a record file (sequential or direct, but not stream) + std::int64_t recordOffsetInFile{0}; + std::int64_t currentRecordNumber{1}; // 1 is first + std::int64_t positionInRecord{0}; // offset in current record + std::int64_t furthestPositionInRecord{0}; // max(positionInRecord) + bool nonAdvancing{false}; // ADVANCE='NO' + // Set at end of non-advancing I/O data transfer + std::optional leftTabLimit; // offset in current record + // currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement + // on a sequential access file + std::optional endfileRecordNumber; + // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT + MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP +}; +} +#endif // FORTRAN_RUNTIME_IO_CONNECTION_H_ diff --git a/runtime/descriptor.cpp b/runtime/descriptor.cpp index c8895dd6d246..ca065246d522 100644 --- a/runtime/descriptor.cpp +++ b/runtime/descriptor.cpp @@ -10,9 +10,14 @@ #include "flang/common/idioms.h" #include #include +#include namespace Fortran::runtime { +Descriptor::Descriptor(const Descriptor &that) { + std::memcpy(this, &that, that.SizeInBytes()); +} + Descriptor::~Descriptor() { if (raw_.attribute != CFI_attribute_pointer) { Deallocate(); diff --git a/runtime/descriptor.h b/runtime/descriptor.h index 3a4f2ce3a29c..bb8a428c83ec 100644 --- a/runtime/descriptor.h +++ b/runtime/descriptor.h @@ -125,6 +125,7 @@ class Descriptor { raw_.base_addr = nullptr; raw_.f18Addendum = false; } + Descriptor(const Descriptor &); ~Descriptor(); diff --git a/runtime/environment.cpp b/runtime/environment.cpp index 5ce55ab47459..735312b9f57c 100644 --- a/runtime/environment.cpp +++ b/runtime/environment.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "environment.h" +#include #include #include @@ -19,7 +20,8 @@ void ExecutionEnvironment::Configure( argv = av; envp = env; listDirectedOutputLineLengthLimit = 79; // PGI default - defaultOutputRoundingMode = common::RoundingMode::TiesToEven; // RP=RN + defaultOutputRoundingMode = + decimal::FortranRounding::RoundNearest; // RP(==RN) if (auto *x{std::getenv("FORT_FMT_RECL")}) { char *end; diff --git a/runtime/environment.h b/runtime/environment.h index 25a98959b741..056a13829b2a 100644 --- a/runtime/environment.h +++ b/runtime/environment.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_RUNTIME_ENVIRONMENT_H_ #define FORTRAN_RUNTIME_ENVIRONMENT_H_ -#include "flang/common/Fortran.h" +#include "flang/decimal/decimal.h" namespace Fortran::runtime { struct ExecutionEnvironment { @@ -19,8 +19,9 @@ struct ExecutionEnvironment { const char **argv; const char **envp; int listDirectedOutputLineLengthLimit; - common::RoundingMode defaultOutputRoundingMode; + enum decimal::FortranRounding defaultOutputRoundingMode; }; extern ExecutionEnvironment executionEnvironment; } + #endif // FORTRAN_RUNTIME_ENVIRONMENT_H_ diff --git a/runtime/file.cpp b/runtime/file.cpp index f9c18c741734..9ee4ae3c4318 100644 --- a/runtime/file.cpp +++ b/runtime/file.cpp @@ -9,7 +9,6 @@ #include "file.h" #include "magic-numbers.h" #include "memory.h" -#include "tools.h" #include #include #include @@ -18,49 +17,22 @@ namespace Fortran::runtime::io { -void OpenFile::Open(const char *path, std::size_t pathLength, - const char *status, std::size_t statusLength, const char *action, - std::size_t actionLength, IoErrorHandler &handler) { - CriticalSection criticalSection{lock_}; - RUNTIME_CHECK(handler, fd_ < 0); // TODO handle re-openings - int flags{0}; - static const char *actions[]{"READ", "WRITE", "READWRITE", nullptr}; - switch (IdentifyValue(action, actionLength, actions)) { - case 0: - flags = O_RDONLY; - mayRead_ = true; - mayWrite_ = false; - break; - case 1: - flags = O_WRONLY; - mayRead_ = false; - mayWrite_ = true; - break; - case 2: - mayRead_ = true; - mayWrite_ = true; - flags = O_RDWR; - break; - default: - handler.Crash( - "Invalid ACTION='%.*s'", action, static_cast(actionLength)); - } - if (!status) { - status = "UNKNOWN", statusLength = 7; - } - static const char *statuses[]{ - "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; - switch (IdentifyValue(status, statusLength, statuses)) { - case 0: // STATUS='OLD' - if (!path && fd_ >= 0) { - // TODO: Update OpenFile in situ; can ACTION be changed? +void OpenFile::set_path(OwningPtr &&path, std::size_t bytes) { + path_ = std::move(path); + pathLength_ = bytes; +} + +void OpenFile::Open( + OpenStatus status, Position position, IoErrorHandler &handler) { + int flags{mayRead_ ? mayWrite_ ? O_RDWR : O_RDONLY : O_WRONLY}; + switch (status) { + case OpenStatus::Old: + if (fd_ >= 0) { return; } break; - case 1: // STATUS='NEW' - flags |= O_CREAT | O_EXCL; - break; - case 2: // STATUS='SCRATCH' + case OpenStatus::New: flags |= O_CREAT | O_EXCL; break; + case OpenStatus::Scratch: if (path_.get()) { handler.Crash("FILE= must not appear with STATUS='SCRATCH'"); path_.reset(); @@ -74,27 +46,22 @@ void OpenFile::Open(const char *path, std::size_t pathLength, ::unlink(path); } return; - case 3: // STATUS='REPLACE' - flags |= O_CREAT | O_TRUNC; - break; - case 4: // STATUS='UNKNOWN' + case OpenStatus::Replace: flags |= O_CREAT | O_TRUNC; break; + case OpenStatus::Unknown: if (fd_ >= 0) { return; } flags |= O_CREAT; break; - default: - handler.Crash( - "Invalid STATUS='%.*s'", status, static_cast(statusLength)); } // If we reach this point, we're opening a new file if (fd_ >= 0) { - if (::close(fd_) != 0) { + if (fd_ <= 2) { + // don't actually close a standard file descriptor, we might need it + } else if (::close(fd_) != 0) { handler.SignalErrno(); } } - path_ = SaveDefaultCharacter(path, pathLength, handler); - pathLength_ = pathLength; if (!path_.get()) { handler.Crash( "FILE= is required unless STATUS='OLD' and unit is connected"); @@ -105,6 +72,10 @@ void OpenFile::Open(const char *path, std::size_t pathLength, } pending_.reset(); knownSize_.reset(); + if (position == Position::Append && !RawSeekToEnd()) { + handler.SignalErrno(); + } + isTerminal_ = ::isatty(fd_) == 1; } void OpenFile::Predefine(int fd) { @@ -118,25 +89,18 @@ void OpenFile::Predefine(int fd) { pending_.reset(); } -void OpenFile::Close( - const char *status, std::size_t statusLength, IoErrorHandler &handler) { +void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) { CriticalSection criticalSection{lock_}; CheckOpen(handler); pending_.reset(); knownSize_.reset(); - static const char *statuses[]{"KEEP", "DELETE", nullptr}; - switch (IdentifyValue(status, statusLength, statuses)) { - case 0: break; - case 1: + switch (status) { + case CloseStatus::Keep: break; + case CloseStatus::Delete: if (path_.get()) { ::unlink(path_.get()); } break; - default: - if (status) { - handler.Crash( - "Invalid STATUS='%.*s'", status, static_cast(statusLength)); - } } path_.reset(); if (fd_ >= 0) { @@ -319,7 +283,7 @@ void OpenFile::WaitAll(IoErrorHandler &handler) { } } -void OpenFile::CheckOpen(Terminator &terminator) { +void OpenFile::CheckOpen(const Terminator &terminator) { RUNTIME_CHECK(terminator, fd_ >= 0); } @@ -337,13 +301,27 @@ bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) { bool OpenFile::RawSeek(FileOffset at) { #ifdef _LARGEFILE64_SOURCE - return ::lseek64(fd_, at, SEEK_SET) == 0; + return ::lseek64(fd_, at, SEEK_SET) == at; #else - return ::lseek(fd_, at, SEEK_SET) == 0; + return ::lseek(fd_, at, SEEK_SET) == at; #endif } -int OpenFile::PendingResult(Terminator &terminator, int iostat) { +bool OpenFile::RawSeekToEnd() { +#ifdef _LARGEFILE64_SOURCE + std::int64_t at{::lseek64(fd_, 0, SEEK_END)}; +#else + std::int64_t at{::lseek(fd_, 0, SEEK_END)}; +#endif + if (at >= 0) { + knownSize_ = at; + return true; + } else { + return false; + } +} + +int OpenFile::PendingResult(const Terminator &terminator, int iostat) { int id{nextId_++}; pending_.reset(&New{}(terminator, id, iostat, std::move(pending_))); return id; diff --git a/runtime/file.h b/runtime/file.h index d5e521756653..9ed1c250364a 100644 --- a/runtime/file.h +++ b/runtime/file.h @@ -19,25 +19,33 @@ namespace Fortran::runtime::io { +enum class OpenStatus { Old, New, Scratch, Replace, Unknown }; +enum class CloseStatus { Keep, Delete }; +enum class Position { AsIs, Rewind, Append }; + class OpenFile { public: using FileOffset = std::int64_t; - FileOffset position() const { return position_; } - - void Open(const char *path, std::size_t pathLength, const char *status, - std::size_t statusLength, const char *action, std::size_t actionLength, - IoErrorHandler &); - void Predefine(int fd); - void Close(const char *action, std::size_t actionLength, IoErrorHandler &); - - int fd() const { return fd_; } + Lock &lock() { return lock_; } + const char *path() const { return path_.get(); } + void set_path(OwningPtr &&, std::size_t bytes); + std::size_t pathLength() const { return pathLength_; } bool mayRead() const { return mayRead_; } - bool mayWrite() const { return mayWrite_; } - bool mayPosition() const { return mayPosition_; } void set_mayRead(bool yes) { mayRead_ = yes; } + bool mayWrite() const { return mayWrite_; } void set_mayWrite(bool yes) { mayWrite_ = yes; } + bool mayAsynchronous() const { return mayAsynchronous_; } + void set_mayAsynchronous(bool yes) { mayAsynchronous_ = yes; } + bool mayPosition() const { return mayPosition_; } void set_mayPosition(bool yes) { mayPosition_ = yes; } + FileOffset position() const { return position_; } + bool isTerminal() const { return isTerminal_; } + + bool IsOpen() const { return fd_ >= 0; } + void Open(OpenStatus, Position, IoErrorHandler &); + void Predefine(int fd); + void Close(CloseStatus, IoErrorHandler &); // Reads data into memory; returns amount acquired. Synchronous. // Partial reads (less than minBytes) signify end-of-file. If the @@ -69,10 +77,11 @@ class OpenFile { }; // lock_ must be held for these - void CheckOpen(Terminator &); + void CheckOpen(const Terminator &); bool Seek(FileOffset, IoErrorHandler &); bool RawSeek(FileOffset); - int PendingResult(Terminator &, int); + bool RawSeekToEnd(); + int PendingResult(const Terminator &, int); Lock lock_; int fd_{-1}; @@ -81,8 +90,11 @@ class OpenFile { bool mayRead_{false}; bool mayWrite_{false}; bool mayPosition_{false}; + bool mayAsynchronous_{false}; FileOffset position_{0}; std::optional knownSize_; + bool isTerminal_{false}; + int nextId_; OwningPtr pending_; }; diff --git a/runtime/format-implementation.h b/runtime/format-implementation.h new file mode 100644 index 000000000000..cb5fc2dfd8b5 --- /dev/null +++ b/runtime/format-implementation.h @@ -0,0 +1,355 @@ +//===-- runtime/format-implementation.h -------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Implements out-of-line member functions of template class FormatControl + +#ifndef FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ +#define FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ + +#include "format.h" +#include "io-stmt.h" +#include "main.h" +#include "flang/common/format.h" +#include "flang/decimal/decimal.h" +#include + +namespace Fortran::runtime::io { + +template +FormatControl::FormatControl(const Terminator &terminator, + const CharType *format, std::size_t formatLength, int maxHeight) + : maxHeight_{static_cast(maxHeight)}, format_{format}, + formatLength_{static_cast(formatLength)} { + if (maxHeight != maxHeight_) { + terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); + } + if (formatLength != static_cast(formatLength_)) { + terminator.Crash( + "internal Fortran runtime error: formatLength %zd", formatLength); + } + stack_[0].start = offset_; + stack_[0].remaining = Iteration::unlimited; // 13.4(8) +} + +template +int FormatControl::GetMaxParenthesisNesting( + const Terminator &terminator, const CharType *format, + std::size_t formatLength) { + using Validator = common::FormatValidator; + typename Validator::Reporter reporter{ + [&](const common::FormatMessage &message) { + terminator.Crash(message.text, message.arg); + return false; // crashes on error above + }}; + Validator validator{format, formatLength, reporter}; + validator.Check(); + return validator.maxNesting(); +} + +template +int FormatControl::GetIntField( + const Terminator &terminator, CharType firstCh) { + CharType ch{firstCh ? firstCh : PeekNext()}; + if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) { + terminator.Crash( + "Invalid FORMAT: integer expected at '%c'", static_cast(ch)); + } + int result{0}; + bool negate{ch == '-'}; + if (negate) { + firstCh = '\0'; + ch = PeekNext(); + } + while (ch >= '0' && ch <= '9') { + if (result > + std::numeric_limits::max() / 10 - (static_cast(ch) - '0')) { + terminator.Crash("FORMAT integer field out of range"); + } + result = 10 * result + ch - '0'; + if (firstCh) { + firstCh = '\0'; + } else { + ++offset_; + } + ch = PeekNext(); + } + if (negate && (result *= -1) > 0) { + terminator.Crash("FORMAT integer field out of range"); + } + return result; +} + +template +static void HandleControl(CONTEXT &context, char ch, char next, int n) { + MutableModes &modes{context.mutableModes()}; + switch (ch) { + case 'B': + if (next == 'Z') { + modes.editingFlags |= blankZero; + return; + } + if (next == 'N') { + modes.editingFlags &= ~blankZero; + return; + } + break; + case 'D': + if (next == 'C') { + modes.editingFlags |= decimalComma; + return; + } + if (next == 'P') { + modes.editingFlags &= ~decimalComma; + return; + } + break; + case 'P': + if (!next) { + modes.scale = n; // kP - decimal scaling by 10**k + return; + } + break; + case 'R': + switch (next) { + case 'N': modes.round = decimal::RoundNearest; return; + case 'Z': modes.round = decimal::RoundToZero; return; + case 'U': modes.round = decimal::RoundUp; return; + case 'D': modes.round = decimal::RoundDown; return; + case 'C': modes.round = decimal::RoundCompatible; return; + case 'P': + modes.round = executionEnvironment.defaultOutputRoundingMode; + return; + default: break; + } + break; + case 'X': + if (!next) { + context.HandleRelativePosition(n); + return; + } + break; + case 'S': + if (next == 'P') { + modes.editingFlags |= signPlus; + return; + } + if (!next || next == 'S') { + modes.editingFlags &= ~signPlus; + return; + } + break; + case 'T': { + if (!next) { // Tn + context.HandleAbsolutePosition(n - 1); // convert 1-based to 0-based + return; + } + if (next == 'L' || next == 'R') { // TLn & TRn + context.HandleRelativePosition(next == 'L' ? -n : n); + return; + } + } break; + default: break; + } + if (next) { + context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next); + } else { + context.Crash("Unknown '%c' edit descriptor in FORMAT", ch); + } +} + +// Locates the next data edit descriptor in the format. +// Handles all repetition counts and control edit descriptors. +// Generally assumes that the format string has survived the common +// format validator gauntlet. +template +int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { + int unlimitedLoopCheck{-1}; + while (true) { + std::optional repeat; + bool unlimited{false}; + CharType ch{Capitalize(GetNextChar(context))}; + while (ch == ',' || ch == ':') { + // Skip commas, and don't complain if they're missing; the format + // validator does that. + if (stop && ch == ':') { + return 0; + } + ch = Capitalize(GetNextChar(context)); + } + if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { + repeat = GetIntField(context, ch); + ch = GetNextChar(context); + } else if (ch == '*') { + unlimited = true; + ch = GetNextChar(context); + if (ch != '(') { + context.Crash("Invalid FORMAT: '*' may appear only before '('"); + } + } + if (ch == '(') { + if (height_ >= maxHeight_) { + context.Crash("FORMAT stack overflow: too many nested parentheses"); + } + stack_[height_].start = offset_ - 1; // the '(' + if (unlimited || height_ == 0) { + stack_[height_].remaining = Iteration::unlimited; + unlimitedLoopCheck = offset_ - 1; + } else if (repeat) { + if (*repeat <= 0) { + *repeat = 1; // error recovery + } + stack_[height_].remaining = *repeat - 1; + } else { + stack_[height_].remaining = 0; + } + ++height_; + } else if (height_ == 0) { + context.Crash("FORMAT lacks initial '('"); + } else if (ch == ')') { + if (height_ == 1) { + if (stop) { + return 0; // end of FORMAT and no data items remain + } + context.AdvanceRecord(); // implied / before rightmost ) + } + if (stack_[height_ - 1].remaining == Iteration::unlimited) { + offset_ = stack_[height_ - 1].start + 1; + if (offset_ == unlimitedLoopCheck) { + context.Crash( + "Unlimited repetition in FORMAT lacks data edit descriptors"); + } + } else if (stack_[height_ - 1].remaining-- > 0) { + offset_ = stack_[height_ - 1].start + 1; + } else { + --height_; + } + } else if (ch == '\'' || ch == '"') { + // Quoted 'character literal' + CharType quote{ch}; + auto start{offset_}; + while (offset_ < formatLength_ && format_[offset_] != quote) { + ++offset_; + } + if (offset_ >= formatLength_) { + context.Crash("FORMAT missing closing quote on character literal"); + } + ++offset_; + std::size_t chars{ + static_cast(&format_[offset_] - &format_[start])}; + if (PeekNext() == quote) { + // subtle: handle doubled quote character in a literal by including + // the first in the output, then treating the second as the start + // of another character literal. + } else { + --chars; + } + context.Emit(format_ + start, chars); + } else if (ch == 'H') { + // 9HHOLLERITH + if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { + context.Crash("Invalid width on Hollerith in FORMAT"); + } + context.Emit(format_ + offset_, static_cast(*repeat)); + offset_ += *repeat; + } else if (ch >= 'A' && ch <= 'Z') { + int start{offset_ - 1}; + CharType next{Capitalize(PeekNext())}; + if (next >= 'A' && next <= 'Z') { + ++offset_; + } else { + next = '\0'; + } + if (ch == 'E' || + (!next && + (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' || + ch == 'F' || ch == 'D' || ch == 'G' || ch == 'L'))) { + // Data edit descriptor found + offset_ = start; + return repeat && *repeat > 0 ? *repeat : 1; + } else { + // Control edit descriptor + if (ch == 'T') { // Tn, TLn, TRn + repeat = GetIntField(context); + } + HandleControl(context, static_cast(ch), static_cast(next), + repeat ? *repeat : 1); + } + } else if (ch == '/') { + context.AdvanceRecord(repeat && *repeat > 0 ? *repeat : 1); + } else { + context.Crash("Invalid character '%c' in FORMAT", static_cast(ch)); + } + } +} + +template +DataEdit FormatControl::GetNextDataEdit( + Context &context, int maxRepeat) { + + // TODO: DT editing + + // Return the next data edit descriptor + int repeat{CueUpNextDataEdit(context)}; + auto start{offset_}; + DataEdit edit; + edit.descriptor = static_cast(Capitalize(GetNextChar(context))); + if (edit.descriptor == 'E') { + edit.variation = static_cast(Capitalize(PeekNext())); + if (edit.variation >= 'A' && edit.variation <= 'Z') { + ++offset_; + } + } + + if (edit.descriptor == 'A') { // width is optional for A[w] + auto ch{PeekNext()}; + if (ch >= '0' && ch <= '9') { + edit.width = GetIntField(context); + } + } else { + edit.width = GetIntField(context); + } + edit.modes = context.mutableModes(); + if (PeekNext() == '.') { + ++offset_; + edit.digits = GetIntField(context); + CharType ch{PeekNext()}; + if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { + ++offset_; + edit.expoDigits = GetIntField(context); + } + } + + // Handle repeated nonparenthesized edit descriptors + if (repeat > 1) { + stack_[height_].start = start; // after repeat count + stack_[height_].remaining = repeat; // full count + ++height_; + } + edit.repeat = 1; + if (height_ > 1) { + int start{stack_[height_ - 1].start}; + if (format_[start] != '(') { + if (stack_[height_ - 1].remaining > maxRepeat) { + edit.repeat = maxRepeat; + stack_[height_ - 1].remaining -= maxRepeat; + offset_ = start; // repeat same edit descriptor next time + } else { + edit.repeat = stack_[height_ - 1].remaining; + --height_; + } + } + } + return edit; +} + +template +void FormatControl::FinishOutput(Context &context) { + CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); +} +} +#endif // FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ diff --git a/runtime/format.cpp b/runtime/format.cpp index f31139ebb5ac..91a6b6749514 100644 --- a/runtime/format.cpp +++ b/runtime/format.cpp @@ -6,356 +6,48 @@ // //===----------------------------------------------------------------------===// -#include "format.h" -#include "io-stmt.h" -#include "main.h" -#include "flang/common/format.h" -#include "flang/decimal/decimal.h" -#include +#include "format-implementation.h" namespace Fortran::runtime::io { -template -FormatControl::FormatControl(Terminator &terminator, const CHAR *format, - std::size_t formatLength, int maxHeight) - : maxHeight_{static_cast(maxHeight)}, format_{format}, - formatLength_{static_cast(formatLength)} { - if (maxHeight != maxHeight_) { - terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); - } - if (formatLength != static_cast(formatLength_)) { - terminator.Crash( - "internal Fortran runtime error: formatLength %zd", formatLength); - } - stack_[0].start = offset_; - stack_[0].remaining = Iteration::unlimited; // 13.4(8) -} - -template -int FormatControl::GetMaxParenthesisNesting( - Terminator &terminator, const CHAR *format, std::size_t formatLength) { - using Validator = common::FormatValidator; - typename Validator::Reporter reporter{ - [&](const common::FormatMessage &message) { - terminator.Crash(message.text, message.arg); - return false; // crashes on error above - }}; - Validator validator{format, formatLength, reporter}; - validator.Check(); - return validator.maxNesting(); -} - -template -int FormatControl::GetIntField(Terminator &terminator, CHAR firstCh) { - CHAR ch{firstCh ? firstCh : PeekNext()}; - if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) { - terminator.Crash( - "Invalid FORMAT: integer expected at '%c'", static_cast(ch)); - } - int result{0}; - bool negate{ch == '-'}; - if (negate) { - firstCh = '\0'; - ch = PeekNext(); - } - while (ch >= '0' && ch <= '9') { - if (result > - std::numeric_limits::max() / 10 - (static_cast(ch) - '0')) { - terminator.Crash("FORMAT integer field out of range"); - } - result = 10 * result + ch - '0'; - if (firstCh) { - firstCh = '\0'; - } else { - ++offset_; - } - ch = PeekNext(); - } - if (negate && (result *= -1) > 0) { - terminator.Crash("FORMAT integer field out of range"); - } - return result; -} - -static void HandleControl(FormatContext &context, char ch, char next, int n) { - MutableModes &modes{context.mutableModes()}; - switch (ch) { - case 'B': - if (next == 'Z') { - modes.editingFlags |= blankZero; - return; - } - if (next == 'N') { - modes.editingFlags &= ~blankZero; - return; - } - break; - case 'D': - if (next == 'C') { - modes.editingFlags |= decimalComma; - return; - } - if (next == 'P') { - modes.editingFlags &= ~decimalComma; - return; - } - break; - case 'P': - if (!next) { - modes.scale = n; // kP - decimal scaling by 10**k - return; - } - break; - case 'R': - switch (next) { - case 'N': modes.roundingMode = common::RoundingMode::TiesToEven; return; - case 'Z': modes.roundingMode = common::RoundingMode::ToZero; return; - case 'U': modes.roundingMode = common::RoundingMode::Up; return; - case 'D': modes.roundingMode = common::RoundingMode::Down; return; - case 'C': - modes.roundingMode = common::RoundingMode::TiesAwayFromZero; - return; - case 'P': - modes.roundingMode = executionEnvironment.defaultOutputRoundingMode; - return; - default: break; - } - break; - case 'X': - if (!next) { - context.HandleRelativePosition(n); - return; - } - break; - case 'S': - if (next == 'P') { - modes.editingFlags |= signPlus; - return; - } - if (!next || next == 'S') { - modes.editingFlags &= ~signPlus; - return; - } - break; - case 'T': { - if (!next) { // Tn - context.HandleAbsolutePosition(n); - return; - } - if (next == 'L' || next == 'R') { // TLn & TRn - context.HandleRelativePosition(next == 'L' ? -n : n); - return; - } - } break; - default: break; - } - if (next) { - context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next); - } else { - context.Crash("Unknown '%c' edit descriptor in FORMAT", ch); - } -} - -// Locates the next data edit descriptor in the format. -// Handles all repetition counts and control edit descriptors. -// Generally assumes that the format string has survived the common -// format validator gauntlet. -template -int FormatControl::CueUpNextDataEdit(FormatContext &context, bool stop) { - int unlimitedLoopCheck{-1}; - while (true) { - std::optional repeat; - bool unlimited{false}; - CHAR ch{Capitalize(GetNextChar(context))}; - while (ch == ',' || ch == ':') { - // Skip commas, and don't complain if they're missing; the format - // validator does that. - if (stop && ch == ':') { - return 0; - } - ch = Capitalize(GetNextChar(context)); - } - if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { - repeat = GetIntField(context, ch); - ch = GetNextChar(context); - } else if (ch == '*') { - unlimited = true; - ch = GetNextChar(context); - if (ch != '(') { - context.Crash("Invalid FORMAT: '*' may appear only before '('"); - } - } - if (ch == '(') { - if (height_ >= maxHeight_) { - context.Crash("FORMAT stack overflow: too many nested parentheses"); - } - stack_[height_].start = offset_ - 1; // the '(' - if (unlimited || height_ == 0) { - stack_[height_].remaining = Iteration::unlimited; - unlimitedLoopCheck = offset_ - 1; - } else if (repeat) { - if (*repeat <= 0) { - *repeat = 1; // error recovery - } - stack_[height_].remaining = *repeat - 1; - } else { - stack_[height_].remaining = 0; - } - ++height_; - } else if (height_ == 0) { - context.Crash("FORMAT lacks initial '('"); - } else if (ch == ')') { - if (height_ == 1) { - if (stop) { - return 0; // end of FORMAT and no data items remain - } - context.HandleSlash(); // implied / before rightmost ) - } - if (stack_[height_ - 1].remaining == Iteration::unlimited) { - offset_ = stack_[height_ - 1].start + 1; - if (offset_ == unlimitedLoopCheck) { - context.Crash( - "Unlimited repetition in FORMAT lacks data edit descriptors"); - } - } else if (stack_[height_ - 1].remaining-- > 0) { - offset_ = stack_[height_ - 1].start + 1; - } else { - --height_; - } - } else if (ch == '\'' || ch == '"') { - // Quoted 'character literal' - CHAR quote{ch}; - auto start{offset_}; - while (offset_ < formatLength_ && format_[offset_] != quote) { - ++offset_; - } - if (offset_ >= formatLength_) { - context.Crash("FORMAT missing closing quote on character literal"); - } - ++offset_; - std::size_t chars{ - static_cast(&format_[offset_] - &format_[start])}; - if (PeekNext() == quote) { - // subtle: handle doubled quote character in a literal by including - // the first in the output, then treating the second as the start - // of another character literal. - } else { - --chars; - } - context.Emit(format_ + start, chars); - } else if (ch == 'H') { - // 9HHOLLERITH - if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { - context.Crash("Invalid width on Hollerith in FORMAT"); - } - context.Emit(format_ + offset_, static_cast(*repeat)); - offset_ += *repeat; - } else if (ch >= 'A' && ch <= 'Z') { - int start{offset_ - 1}; - CHAR next{Capitalize(PeekNext())}; - if (next >= 'A' && next <= 'Z') { - ++offset_; - } else { - next = '\0'; - } - if (ch == 'E' || - (!next && - (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' || - ch == 'F' || ch == 'D' || ch == 'G' || ch == 'L'))) { - // Data edit descriptor found - offset_ = start; - return repeat && *repeat > 0 ? *repeat : 1; - } else { - // Control edit descriptor - if (ch == 'T') { // Tn, TLn, TRn - repeat = GetIntField(context); - } - HandleControl(context, static_cast(ch), static_cast(next), - repeat ? *repeat : 1); - } - } else if (ch == '/') { - context.HandleSlash(repeat && *repeat > 0 ? *repeat : 1); - } else { - context.Crash("Invalid character '%c' in FORMAT", static_cast(ch)); - } - } -} - -template -void FormatControl::GetNext( - FormatContext &context, DataEdit &edit, int maxRepeat) { - - // TODO: DT editing - - // Return the next data edit descriptor - int repeat{CueUpNextDataEdit(context)}; - auto start{offset_}; - edit.descriptor = static_cast(Capitalize(GetNextChar(context))); - if (edit.descriptor == 'E') { - edit.variation = static_cast(Capitalize(PeekNext())); - if (edit.variation >= 'A' && edit.variation <= 'Z') { - ++offset_; - } else { - edit.variation = '\0'; - } - } else { - edit.variation = '\0'; - } - - if (edit.descriptor == 'A') { // width is optional for A[w] - auto ch{PeekNext()}; - if (ch >= '0' && ch <= '9') { - edit.width = GetIntField(context); - } else { - edit.width.reset(); - } - } else { - edit.width = GetIntField(context); - } - edit.modes = context.mutableModes(); - if (PeekNext() == '.') { - ++offset_; - edit.digits = GetIntField(context); - CHAR ch{PeekNext()}; - if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { - ++offset_; - edit.expoDigits = GetIntField(context); - } else { - edit.expoDigits.reset(); - } - } else { - edit.digits.reset(); - edit.expoDigits.reset(); - } - - // Handle repeated nonparenthesized edit descriptors - if (repeat > 1) { - stack_[height_].start = start; // after repeat count - stack_[height_].remaining = repeat; // full count - ++height_; - } - edit.repeat = 1; - if (height_ > 1) { - int start{stack_[height_ - 1].start}; - if (format_[start] != '(') { - if (stack_[height_ - 1].remaining > maxRepeat) { - edit.repeat = maxRepeat; - stack_[height_ - 1].remaining -= maxRepeat; - offset_ = start; // repeat same edit descriptor next time - } else { - edit.repeat = stack_[height_ - 1].remaining; - --height_; - } - } - } -} - -template -void FormatControl::FinishOutput(FormatContext &context) { - CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); -} - -template class FormatControl; -template class FormatControl; -template class FormatControl; +DataEdit DefaultFormatControlCallbacks::GetNextDataEdit(int) { + Crash("DefaultFormatControlCallbacks::GetNextDataEdit() called for " + "non-formatted I/O statement"); + return {}; +} +bool DefaultFormatControlCallbacks::Emit(const char *, std::size_t) { + Crash("DefaultFormatControlCallbacks::Emit(char) called for non-output I/O " + "statement"); + return {}; +} +bool DefaultFormatControlCallbacks::Emit(const char16_t *, std::size_t) { + Crash("DefaultFormatControlCallbacks::Emit(char16_t) called for non-output " + "I/O statement"); + return {}; +} +bool DefaultFormatControlCallbacks::Emit(const char32_t *, std::size_t) { + Crash("DefaultFormatControlCallbacks::Emit(char32_t) called for non-output " + "I/O statement"); + return {}; +} +bool DefaultFormatControlCallbacks::AdvanceRecord(int) { + Crash("DefaultFormatControlCallbacks::AdvanceRecord() called unexpectedly"); + return {}; +} +bool DefaultFormatControlCallbacks::HandleAbsolutePosition(std::int64_t) { + Crash("DefaultFormatControlCallbacks::HandleAbsolutePosition() called for " + "non-formatted " + "I/O statement"); + return {}; +} +bool DefaultFormatControlCallbacks::HandleRelativePosition(std::int64_t) { + Crash("DefaultFormatControlCallbacks::HandleRelativePosition() called for " + "non-formatted " + "I/O statement"); + return {}; +} + +template class FormatControl>; +template class FormatControl>; +template class FormatControl>; } diff --git a/runtime/format.h b/runtime/format.h index c954c7f3872f..c072b3b9805d 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -12,8 +12,10 @@ #define FORTRAN_RUNTIME_FORMAT_H_ #include "environment.h" +#include "io-error.h" #include "terminator.h" #include "flang/common/Fortran.h" +#include "flang/decimal/decimal.h" #include #include @@ -27,7 +29,7 @@ enum EditingFlags { struct MutableModes { std::uint8_t editingFlags{0}; // BN, DP, SS - common::RoundingMode roundingMode{ + enum decimal::FortranRounding round{ executionEnvironment .defaultOutputRoundingMode}; // RP/ROUND='PROCESSOR_DEFAULT' bool pad{false}; // PAD= mode on READ @@ -38,6 +40,16 @@ struct MutableModes { // A single edit descriptor extracted from a FORMAT struct DataEdit { char descriptor; // capitalized: one of A, I, B, O, Z, F, E(N/S/X), D, G + + // Special internal data edit descriptors to distinguish list-directed I/O + static constexpr char ListDirected{'g'}; // non-COMPLEX list-directed + static constexpr char ListDirectedRealPart{'r'}; // emit "(r," or "(r;" + static constexpr char ListDirectedImaginaryPart{'z'}; // emit "z)" + constexpr bool IsListDirected() const { + return descriptor == ListDirected || descriptor == ListDirectedRealPart || + descriptor == ListDirectedImaginaryPart; + } + char variation{'\0'}; // N, S, or X for EN, ES, EX std::optional width; // the 'w' field; optional for A std::optional digits; // the 'm' or 'd' field @@ -46,37 +58,35 @@ struct DataEdit { int repeat{1}; }; -class FormatContext : virtual public Terminator { -public: - FormatContext() {} - virtual ~FormatContext() {} - explicit FormatContext(const MutableModes &modes) : mutableModes_{modes} {} - virtual bool Emit(const char *, std::size_t) = 0; - virtual bool Emit(const char16_t *, std::size_t) = 0; - virtual bool Emit(const char32_t *, std::size_t) = 0; - virtual bool HandleSlash(int = 1) = 0; - virtual bool HandleRelativePosition(std::int64_t) = 0; - virtual bool HandleAbsolutePosition(std::int64_t) = 0; - MutableModes &mutableModes() { return mutableModes_; } - -private: - MutableModes mutableModes_; +// FormatControl requires that A have these member functions; +// these default implementations just crash if called. +struct DefaultFormatControlCallbacks : public IoErrorHandler { + using IoErrorHandler::IoErrorHandler; + DataEdit GetNextDataEdit(int = 1); + bool Emit(const char *, std::size_t); + bool Emit(const char16_t *, std::size_t); + bool Emit(const char32_t *, std::size_t); + bool AdvanceRecord(int = 1); + bool HandleAbsolutePosition(std::int64_t); + bool HandleRelativePosition(std::int64_t); }; // Generates a sequence of DataEdits from a FORMAT statement or // default-CHARACTER string. Driven by I/O item list processing. // Errors are fatal. See clause 13.4 in Fortran 2018 for background. -template class FormatControl { +template class FormatControl { public: + using Context = CONTEXT; + using CharType = typename Context::CharType; + FormatControl() {} - // TODO: make 'format' a reference here and below - FormatControl(Terminator &, const CHAR *format, std::size_t formatLength, - int maxHeight = maxMaxHeight); + FormatControl(const Terminator &, const CharType *format, + std::size_t formatLength, int maxHeight = maxMaxHeight); // Determines the max parenthesis nesting level by scanning and validating // the FORMAT string. static int GetMaxParenthesisNesting( - Terminator &, const CHAR *format, std::size_t formatLength); + const Terminator &, const CharType *format, std::size_t formatLength); // For attempting to allocate in a user-supplied stack area static std::size_t GetNeededSize(int maxHeight) { @@ -86,10 +96,10 @@ template class FormatControl { // Extracts the next data edit descriptor, handling control edit descriptors // along the way. - void GetNext(FormatContext &, DataEdit &, int maxRepeat = 1); + DataEdit GetNextDataEdit(Context &, int maxRepeat = 1); // Emit any remaining character literals after the last data item. - void FinishOutput(FormatContext &); + void FinishOutput(Context &); private: static constexpr std::uint8_t maxMaxHeight{100}; @@ -105,27 +115,27 @@ template class FormatControl { ++offset_; } } - CHAR PeekNext() { + CharType PeekNext() { SkipBlanks(); return offset_ < formatLength_ ? format_[offset_] : '\0'; } - CHAR GetNextChar(Terminator &terminator) { + CharType GetNextChar(const Terminator &terminator) { SkipBlanks(); if (offset_ >= formatLength_) { terminator.Crash("FORMAT missing at least one ')'"); } return format_[offset_++]; } - int GetIntField(Terminator &, CHAR firstCh = '\0'); + int GetIntField(const Terminator &, CharType firstCh = '\0'); // Advances through the FORMAT until the next data edit // descriptor has been found; handles control edit descriptors // along the way. Returns the repeat count that appeared // before the descriptor (defaulting to 1) and leaves offset_ // pointing to the data edit. - int CueUpNextDataEdit(FormatContext &, bool stop = false); + int CueUpNextDataEdit(Context &, bool stop = false); - static constexpr CHAR Capitalize(CHAR ch) { + static constexpr CharType Capitalize(CharType ch) { return ch >= 'a' && ch <= 'z' ? ch + 'A' - 'a' : ch; } @@ -134,16 +144,12 @@ template class FormatControl { // user program for internal I/O. const std::uint8_t maxHeight_{maxMaxHeight}; std::uint8_t height_{0}; - const CHAR *format_{nullptr}; + const CharType *format_{nullptr}; int formatLength_{0}; int offset_{0}; // next item is at format_[offset_] // must be last, may be incomplete Iteration stack_[maxMaxHeight]; }; - -extern template class FormatControl; -extern template class FormatControl; -extern template class FormatControl; } #endif // FORTRAN_RUNTIME_FORMAT_H_ diff --git a/runtime/internal-unit.cpp b/runtime/internal-unit.cpp new file mode 100644 index 000000000000..737f0856e33f --- /dev/null +++ b/runtime/internal-unit.cpp @@ -0,0 +1,129 @@ +//===-- runtime/internal-unit.cpp -------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "internal-unit.h" +#include "descriptor.h" +#include "io-error.h" +#include +#include + +namespace Fortran::runtime::io { + +template +InternalDescriptorUnit::InternalDescriptorUnit( + Scalar scalar, std::size_t length) { + recordLength = length; + endfileRecordNumber = 2; + void *pointer{reinterpret_cast(const_cast(scalar))}; + descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr, + CFI_attribute_pointer); +} + +template +InternalDescriptorUnit::InternalDescriptorUnit( + const Descriptor &that, const Terminator &terminator) { + RUNTIME_CHECK(terminator, that.type().IsCharacter()); + Descriptor &d{descriptor()}; + RUNTIME_CHECK( + terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0)); + new (&d) Descriptor{that}; + d.Check(); + recordLength = d.ElementBytes(); + endfileRecordNumber = d.Elements() + 1; + d.GetLowerBounds(at_); +} + +template void InternalDescriptorUnit::EndIoStatement() { + if constexpr (!isInput) { + // blank fill + while (currentRecordNumber < endfileRecordNumber.value_or(0)) { + char *record{descriptor().template Element(at_)}; + std::fill_n(record + furthestPositionInRecord, + recordLength.value_or(0) - furthestPositionInRecord, ' '); + furthestPositionInRecord = 0; + ++currentRecordNumber; + descriptor().IncrementSubscripts(at_); + } + } +} + +template +bool InternalDescriptorUnit::Emit( + const char *data, std::size_t bytes, IoErrorHandler &handler) { + if constexpr (isInput) { + handler.Crash( + "InternalDescriptorUnit::Emit() called for an input statement"); + return false; + } + if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { + handler.SignalEnd(); + return false; + } + char *record{descriptor().template Element(at_)}; + auto furthestAfter{std::max(furthestPositionInRecord, + positionInRecord + static_cast(bytes))}; + bool ok{true}; + if (furthestAfter > static_cast(recordLength.value_or(0))) { + handler.SignalEor(); + furthestAfter = recordLength.value_or(0); + bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); + ok = false; + } + std::memcpy(record + positionInRecord, data, bytes); + positionInRecord += bytes; + furthestPositionInRecord = furthestAfter; + return ok; +} + +template +bool InternalDescriptorUnit::AdvanceRecord(IoErrorHandler &handler) { + if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { + handler.SignalEnd(); + return false; + } + if (!HandleAbsolutePosition(recordLength.value_or(0), handler)) { + return false; + } + ++currentRecordNumber; + descriptor().IncrementSubscripts(at_); + positionInRecord = 0; + furthestPositionInRecord = 0; + return true; +} + +template +bool InternalDescriptorUnit::HandleAbsolutePosition( + std::int64_t n, IoErrorHandler &handler) { + n = std::max(0, n); + bool ok{true}; + if (n > static_cast(recordLength.value_or(n))) { + handler.SignalEor(); + n = *recordLength; + ok = false; + } + if (n > furthestPositionInRecord && ok) { + if constexpr (!isInput) { + char *record{descriptor().template Element(at_)}; + std::fill_n( + record + furthestPositionInRecord, n - furthestPositionInRecord, ' '); + } + furthestPositionInRecord = n; + } + positionInRecord = n; + return ok; +} + +template +bool InternalDescriptorUnit::HandleRelativePosition( + std::int64_t n, IoErrorHandler &handler) { + return HandleAbsolutePosition(positionInRecord + n, handler); +} + +template class InternalDescriptorUnit; +template class InternalDescriptorUnit; +} diff --git a/runtime/internal-unit.h b/runtime/internal-unit.h new file mode 100644 index 000000000000..837ddc6f588f --- /dev/null +++ b/runtime/internal-unit.h @@ -0,0 +1,46 @@ +//===-- runtime/internal-unit.h ---------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Fortran internal I/O "units" + +#ifndef FORTRAN_RUNTIME_IO_INTERNAL_UNIT_H_ +#define FORTRAN_RUNTIME_IO_INTERNAL_UNIT_H_ + +#include "connection.h" +#include "descriptor.h" +#include +#include + +namespace Fortran::runtime::io { + +class IoErrorHandler; + +// Points to (but does not own) a CHARACTER scalar or array for internal I/O. +// Does not buffer. +template class InternalDescriptorUnit : public ConnectionState { +public: + using Scalar = std::conditional_t; + InternalDescriptorUnit(Scalar, std::size_t); + InternalDescriptorUnit(const Descriptor &, const Terminator &); + void EndIoStatement(); + + bool Emit(const char *, std::size_t bytes, IoErrorHandler &); + bool AdvanceRecord(IoErrorHandler &); + bool HandleAbsolutePosition(std::int64_t, IoErrorHandler &); + bool HandleRelativePosition(std::int64_t, IoErrorHandler &); + +private: + Descriptor &descriptor() { return staticDescriptor_.descriptor(); } + StaticDescriptor staticDescriptor_; + SubscriptValue at_[maxRank]; +}; + +extern template class InternalDescriptorUnit; +extern template class InternalDescriptorUnit; +} +#endif // FORTRAN_RUNTIME_IO_INTERNAL_UNIT_H_ diff --git a/runtime/io-api.cpp b/runtime/io-api.cpp index d5840a03b75d..969315a49fa7 100644 --- a/runtime/io-api.cpp +++ b/runtime/io-api.cpp @@ -1,4 +1,4 @@ -//===-- runtime/io.cpp ------------------------------------------*- C++ -*-===// +//===-- runtime/io-api.cpp --------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,24 +9,76 @@ // Implements the I/O statement API #include "io-api.h" +#include "environment.h" #include "format.h" #include "io-stmt.h" #include "memory.h" #include "numeric-output.h" #include "terminator.h" +#include "tools.h" #include "unit.h" #include #include namespace Fortran::runtime::io { +Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &descriptor, + void ** /*scratchArea*/, std::size_t /*scratchBytes*/, + const char *sourceFile, int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return &New>{}( + oom, descriptor, sourceFile, sourceLine) + .ioStatementState(); +} + +Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor, + const char *format, std::size_t formatLength, void ** /*scratchArea*/, + std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return &New>{}( + oom, descriptor, format, formatLength, sourceFile, sourceLine) + .ioStatementState(); +} + +Cookie IONAME(BeginInternalListOutput)(char *internal, + std::size_t internalLength, void ** /*scratchArea*/, + std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return &New>{}( + oom, internal, internalLength, sourceFile, sourceLine) + .ioStatementState(); +} + Cookie IONAME(BeginInternalFormattedOutput)(char *internal, std::size_t internalLength, const char *format, std::size_t formatLength, void ** /*scratchArea*/, std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { Terminator oom{sourceFile, sourceLine}; return &New>{}(oom, internal, - internalLength, format, formatLength, sourceFile, sourceLine); + internalLength, format, formatLength, sourceFile, sourceLine) + .ioStatementState(); +} + +Cookie IONAME(BeginInternalFormattedInput)(char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + void ** /*scratchArea*/, std::size_t /*scratchBytes*/, + const char *sourceFile, int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return &New>{}(oom, internal, + internalLength, format, formatLength, sourceFile, sourceLine) + .ioStatementState(); +} + +Cookie IONAME(BeginExternalListOutput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + Terminator terminator{sourceFile, sourceLine}; + int unit{unitNumber == DefaultUnit ? 6 : unitNumber}; + ExternalFileUnit &file{ExternalFileUnit::LookUpOrCrash(unit, terminator)}; + if (file.isUnformatted) { + terminator.Crash("List-directed output attempted to unformatted file"); + } + return &file.BeginIoStatement>( + file, sourceFile, sourceLine); } Cookie IONAME(BeginExternalFormattedOutput)(const char *format, @@ -34,53 +86,557 @@ Cookie IONAME(BeginExternalFormattedOutput)(const char *format, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; int unit{unitNumber == DefaultUnit ? 6 : unitNumber}; - ExternalFile &file{ExternalFile::LookUpOrCrash(unit, terminator)}; - return &file.BeginIoStatement>( - file, format, formatLength, sourceFile, sourceLine); + ExternalFileUnit &file{ExternalFileUnit::LookUpOrCrash(unit, terminator)}; + if (file.isUnformatted) { + terminator.Crash("Formatted output attempted to unformatted file"); + } + IoStatementState &io{ + file.BeginIoStatement>( + file, format, formatLength, sourceFile, sourceLine)}; + return &io; +} + +Cookie IONAME(BeginUnformattedOutput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + Terminator terminator{sourceFile, sourceLine}; + ExternalFileUnit &file{ + ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; + if (!file.isUnformatted) { + terminator.Crash("Unformatted output attempted to formatted file"); + } + IoStatementState &io{ + file.BeginIoStatement>( + file, sourceFile, sourceLine)}; + if (file.access == Access::Sequential && !file.recordLength.has_value()) { + // Filled in by UnformattedIoStatementState::EndIoStatement() + io.Emit("\0\0\0\0", 4); // placeholder for record length header + } + return &io; +} + +Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + bool wasExtant{false}; + ExternalFileUnit &unit{ + ExternalFileUnit::LookUpOrCreate(unitNumber, &wasExtant)}; + return &unit.BeginIoStatement( + unit, wasExtant, sourceFile, sourceLine); +} + +Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) + const char *sourceFile, int sourceLine) { + return IONAME(BeginOpenUnit)( + ExternalFileUnit::NewUnit(), sourceFile, sourceLine); +} + +Cookie IONAME(BeginClose)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { + return &unit->BeginIoStatement( + *unit, sourceFile, sourceLine); + } else { + // CLOSE(UNIT=bad unit) is just a no-op + Terminator oom{sourceFile, sourceLine}; + return &New{}(oom, sourceFile, sourceLine) + .ioStatementState(); + } +} + +// Control list items + +void IONAME(EnableHandlers)( + Cookie cookie, bool hasIoStat, bool hasErr, bool hasEnd, bool hasEor) { + IoErrorHandler &handler{cookie->GetIoErrorHandler()}; + if (hasIoStat) { + handler.HasIoStat(); + } + if (hasErr) { + handler.HasErrLabel(); + } + if (hasEnd) { + handler.HasEndLabel(); + } + if (hasEor) { + handler.HasEorLabel(); + } +} + +static bool YesOrNo(const char *keyword, std::size_t length, const char *what, + const Terminator &terminator) { + static const char *keywords[]{"YES", "NO", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: return true; + case 1: return false; + default: + terminator.Crash( + "Invalid %s='%.*s'", what, static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetAdvance)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + connection.nonAdvancing = + !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler()); + return true; +} + +bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + static const char *keywords[]{"NULL", "ZERO", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: connection.modes.editingFlags &= ~blankZero; return true; + case 1: connection.modes.editingFlags |= blankZero; return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid BLANK='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetDecimal)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + static const char *keywords[]{"COMMA", "POINT", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: connection.modes.editingFlags |= decimalComma; return true; + case 1: connection.modes.editingFlags &= ~decimalComma; return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid DECIMAL='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: connection.modes.delim = '\''; return true; + case 1: connection.modes.delim = '"'; return true; + case 2: connection.modes.delim = '\0'; return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid DELIM='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + connection.modes.pad = + YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler()); + return true; +} + +// TODO: SetPos (stream I/O) +// TODO: SetRec (direct I/O) + +bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE", + "PROCESSOR_DEFINED", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: connection.modes.round = decimal::RoundUp; return true; + case 1: connection.modes.round = decimal::RoundDown; return true; + case 2: connection.modes.round = decimal::RoundToZero; return true; + case 3: connection.modes.round = decimal::RoundNearest; return true; + case 4: connection.modes.round = decimal::RoundCompatible; return true; + case 5: + connection.modes.round = executionEnvironment.defaultOutputRoundingMode; + return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid ROUND='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + static const char *keywords[]{"PLUS", "YES", "PROCESSOR_DEFINED", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: connection.modes.editingFlags |= signPlus; return true; + case 1: + case 2: // processor default is SS + connection.modes.editingFlags &= ~signPlus; + return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid SIGN='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetAccess() called when not in an OPEN statement"); + } + ConnectionState &connection{open->GetConnectionState()}; + Access access{connection.access}; + static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: access = Access::Sequential; break; + case 1: access = Access::Direct; break; + case 2: access = Access::Stream; break; + default: + open->Crash("Invalid ACCESS='%.*s'", static_cast(length), keyword); + } + if (access != connection.access) { + if (open->wasExtant()) { + open->Crash("ACCESS= may not be changed on an open unit"); + } + connection.access = access; + } + return true; +} + +bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetAction() called when not in an OPEN statement"); + } + bool mayRead{true}; + bool mayWrite{true}; + static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: mayWrite = false; break; + case 1: mayRead = false; break; + case 2: break; + default: + open->Crash("Invalid ACTION='%.*s'", static_cast(length), keyword); + return false; + } + if (mayRead != open->unit().mayRead() || + mayWrite != open->unit().mayWrite()) { + if (open->wasExtant()) { + open->Crash("ACTION= may not be changed on an open unit"); + } + open->unit().set_mayRead(mayRead); + open->unit().set_mayWrite(mayWrite); + } + return true; +} + +bool IONAME(SetAsynchronous)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetAsynchronous() called when not in an OPEN statement"); + } + static const char *keywords[]{"YES", "NO", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: open->unit().set_mayAsynchronous(true); return true; + case 1: open->unit().set_mayAsynchronous(false); return true; + default: + open->Crash( + "Invalid ASYNCHRONOUS='%.*s'", static_cast(length), keyword); + return false; + } +} + +bool IONAME(SetEncoding)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetEncoding() called when not in an OPEN statement"); + } + bool isUTF8{false}; + static const char *keywords[]{"UTF-8", "DEFAULT", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: isUTF8 = true; break; + case 1: isUTF8 = false; break; + default: + open->Crash("Invalid ENCODING='%.*s'", static_cast(length), keyword); + } + if (isUTF8 != open->unit().isUTF8) { + if (open->wasExtant()) { + open->Crash("ENCODING= may not be changed on an open unit"); + } + open->unit().isUTF8 = isUTF8; + } + return true; +} + +bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetEncoding() called when not in an OPEN statement"); + } + bool isUnformatted{false}; + static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; + switch (IdentifyValue(keyword, length, keywords)) { + case 0: isUnformatted = false; break; + case 1: isUnformatted = true; break; + default: + open->Crash("Invalid FORM='%.*s'", static_cast(length), keyword); + } + if (isUnformatted != open->unit().isUnformatted) { + if (open->wasExtant()) { + open->Crash("FORM= may not be changed on an open unit"); + } + open->unit().isUnformatted = isUnformatted; + } + return true; +} + +bool IONAME(SetPosition)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetPosition() called when not in an OPEN statement"); + } + static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr}; + switch (IdentifyValue(keyword, length, positions)) { + case 0: open->set_position(Position::AsIs); return true; + case 1: open->set_position(Position::Rewind); return true; + case 2: open->set_position(Position::Append); return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid POSITION='%.*s'", static_cast(length), keyword); + } + return true; +} + +bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "SetRecl() called when not in an OPEN statement"); + } + if (open->wasExtant() && open->unit().recordLength.has_value() && + *open->unit().recordLength != n) { + open->Crash("RECL= may not be changed for an open unit"); + } + open->unit().recordLength = n; + return true; +} + +bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + if (auto *open{io.get_if()}) { + static const char *statuses[]{ + "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; + switch (IdentifyValue(keyword, length, statuses)) { + case 0: open->set_status(OpenStatus::Old); return true; + case 1: open->set_status(OpenStatus::New); return true; + case 2: open->set_status(OpenStatus::Scratch); return true; + case 3: open->set_status(OpenStatus::Replace); return true; + case 4: open->set_status(OpenStatus::Unknown); return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid STATUS='%.*s'", static_cast(length), keyword); + } + return false; + } + if (auto *close{io.get_if()}) { + static const char *statuses[]{"KEEP", "DELETE", nullptr}; + switch (IdentifyValue(keyword, length, statuses)) { + case 0: close->set_status(CloseStatus::Keep); return true; + case 1: close->set_status(CloseStatus::Delete); return true; + default: + io.GetIoErrorHandler().Crash( + "Invalid STATUS='%.*s'", static_cast(length), keyword); + } + return false; + } + if (io.get_if()) { + return true; // don't bother validating STATUS= in a no-op CLOSE + } + io.GetIoErrorHandler().Crash( + "SetStatus() called when not in an OPEN or CLOSE statement"); +} + +bool IONAME(SetFile)( + Cookie cookie, const char *path, std::size_t chars, int kind) { + IoStatementState &io{*cookie}; + if (auto *open{io.get_if()}) { + open->set_path(path, chars, kind); + return true; + } + io.GetIoErrorHandler().Crash( + "SetFile() called when not in an OPEN statement"); + return false; +} + +static bool SetInteger(int &x, int kind, int value) { + switch (kind) { + case 1: reinterpret_cast(x) = value; return true; + case 2: reinterpret_cast(x) = value; return true; + case 4: x = value; return true; + case 8: reinterpret_cast(x) = value; return true; + default: return false; + } +} + +bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + if (!open) { + io.GetIoErrorHandler().Crash( + "GetNewUnit() called when not in an OPEN statement"); + } + if (!SetInteger(unit, kind, open->unit().unitNumber())) { + open->Crash("GetNewUnit(): Bad INTEGER kind(%d) for result"); + } + return true; +} + +// Data transfers +// TODO: Input + +bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &) { + IoStatementState &io{*cookie}; + io.GetIoErrorHandler().Crash( + "OutputDescriptor: not yet implemented"); // TODO +} + +bool IONAME(OutputUnformattedBlock)( + Cookie cookie, const char *x, std::size_t length) { + IoStatementState &io{*cookie}; + if (auto *unf{io.get_if>()}) { + return unf->Emit(x, length); + } + io.GetIoErrorHandler().Crash("OutputUnformatted() called for an I/O " + "statement that is not unformatted output"); + return false; } bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { IoStatementState &io{*cookie}; - DataEdit edit; - io.GetNext(edit); - return EditIntegerOutput(io, edit, n); + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "OutputInteger64() called for a non-output I/O statement"); + return false; + } + return EditIntegerOutput(io, io.GetNextDataEdit(), n); } bool IONAME(OutputReal64)(Cookie cookie, double x) { IoStatementState &io{*cookie}; - DataEdit edit; - io.GetNext(edit); - return RealOutputEditing{io, x}.Edit(edit); + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "OutputReal64() called for a non-output I/O statement"); + return false; + } + return RealOutputEditing<53>{io, x}.Edit(io.GetNextDataEdit()); +} + +bool IONAME(OutputComplex64)(Cookie cookie, double r, double z) { + IoStatementState &io{*cookie}; + if (io.get_if>()) { + DataEdit real, imaginary; + real.descriptor = DataEdit::ListDirectedRealPart; + imaginary.descriptor = DataEdit::ListDirectedImaginaryPart; + return RealOutputEditing<53>{io, r}.Edit(real) && + RealOutputEditing<53>{io, z}.Edit(imaginary); + } + return IONAME(OutputReal64)(cookie, r) && IONAME(OutputReal64)(cookie, z); } bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { IoStatementState &io{*cookie}; - DataEdit edit; - io.GetNext(edit); - if (edit.descriptor != 'A' && edit.descriptor != 'G') { - io.Crash( - "Data edit descriptor '%c' may not be used with a CHARACTER data item", - edit.descriptor); + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "OutputAscii() called for a non-output I/O statement"); return false; } - int len{static_cast(length)}; - int width{edit.width.value_or(len)}; - return EmitRepeated(io, ' ', std::max(0, width - len)) && - io.Emit(x, std::min(width, len)); + bool ok{true}; + if (auto *list{io.get_if>()}) { + // List-directed default CHARACTER output + ok &= list->EmitLeadingSpaceOrAdvance(io, length, true); + MutableModes &modes{io.mutableModes()}; + ConnectionState &connection{io.GetConnectionState()}; + if (modes.delim) { + ok &= io.Emit(&modes.delim, 1); + for (std::size_t j{0}; j < length; ++j) { + if (list->NeedAdvance(connection, 2)) { + ok &= io.Emit(&modes.delim, 1) && io.AdvanceRecord() && + io.Emit(&modes.delim, 1); + } + if (x[j] == modes.delim) { + ok &= io.EmitRepeated(modes.delim, 2); + } else { + ok &= io.Emit(&x[j], 1); + } + } + ok &= io.Emit(&modes.delim, 1); + } else { + std::size_t put{0}; + while (put < length) { + auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())}; + ok &= io.Emit(x + put, chunk); + put += chunk; + if (put < length) { + ok &= io.AdvanceRecord() && io.Emit(" ", 1); + } + } + list->lastWasUndelimitedCharacter = true; + } + } else { + // Formatted default CHARACTER output + DataEdit edit{io.GetNextDataEdit()}; + if (edit.descriptor != 'A' && edit.descriptor != 'G') { + io.GetIoErrorHandler().Crash("Data edit descriptor '%c' may not be used " + "with a CHARACTER data item", + edit.descriptor); + return false; + } + int len{static_cast(length)}; + int width{edit.width.value_or(len)}; + ok &= io.EmitRepeated(' ', std::max(0, width - len)) && + io.Emit(x, std::min(width, len)); + } + return ok; } bool IONAME(OutputLogical)(Cookie cookie, bool truth) { IoStatementState &io{*cookie}; - DataEdit edit; - io.GetNext(edit); - if (edit.descriptor != 'L' && edit.descriptor != 'G') { - io.Crash( - "Data edit descriptor '%c' may not be used with a LOGICAL data item", - edit.descriptor); + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "OutputLogical() called for a non-output I/O statement"); return false; } - return EmitRepeated(io, ' ', std::max(0, edit.width.value_or(1) - 1)) && - io.Emit(truth ? "T" : "F", 1); + if (auto *unf{io.get_if>()}) { + char x = truth; + return unf->Emit(&x, 1); + } + bool ok{true}; + if (auto *list{io.get_if>()}) { + ok &= list->EmitLeadingSpaceOrAdvance(io, 1); + } else { + DataEdit edit{io.GetNextDataEdit()}; + if (edit.descriptor != 'L' && edit.descriptor != 'G') { + io.GetIoErrorHandler().Crash( + "Data edit descriptor '%c' may not be used with a LOGICAL data item", + edit.descriptor); + return false; + } + ok &= io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)); + } + return ok && io.Emit(truth ? "T" : "F", 1); } enum Iostat IONAME(EndIoStatement)(Cookie cookie) { diff --git a/runtime/io-api.h b/runtime/io-api.h index 1c1f81ea4c6f..417c0b5a3981 100644 --- a/runtime/io-api.h +++ b/runtime/io-api.h @@ -51,8 +51,7 @@ constexpr std::size_t RecommendedInternalIoScratchAreaBytes( } // Internal I/O to/from character arrays &/or non-default-kind character -// requires a descriptor, which must remain unchanged until the I/O -// statement is complete. +// requires a descriptor, which is copied. Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); @@ -172,8 +171,8 @@ Cookie IONAME(BeginInquireIoLength)( // } // } // if (EndIoStatement(cookie) == FORTRAN_RUTIME_IOSTAT_END) goto label666; -void IONAME(EnableHandlers)(Cookie, bool HasIostat = false, bool HasErr = false, - bool HasEnd = false, bool HasEor = false); +void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false, + bool hasEnd = false, bool hasEor = false); // Control list options. These return false on a error that the // Begin...() call has specified will be handled by the caller. @@ -253,12 +252,10 @@ bool IONAME(SetStatus)(Cookie, const char *, std::size_t); // SetFile() may pass a CHARACTER argument of non-default kind, // and such filenames are converted to UTF-8 before being // presented to the filesystem. -bool IONAME(SetFile)(Cookie, const char *, std::size_t, int kind = 1); +bool IONAME(SetFile)(Cookie, const char *, std::size_t chars, int kind = 1); -// GetNewUnit() must not be called until after all Set...() -// connection list specifiers have been called after -// BeginOpenNewUnit(). -bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4); // NEWUNIT= +// Acquires the runtime-created unit number for OPEN(NEWUNIT=) +bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4); // READ(SIZE=), after all input items bool IONAME(GetSize)(Cookie, std::int64_t, int kind = 8); diff --git a/runtime/io-error.h b/runtime/io-error.h index 6cab725186e3..80f5fa817910 100644 --- a/runtime/io-error.h +++ b/runtime/io-error.h @@ -18,9 +18,10 @@ namespace Fortran::runtime::io { -class IoErrorHandler : virtual public Terminator { +class IoErrorHandler : public Terminator { public: using Terminator::Terminator; + explicit IoErrorHandler(const Terminator &that) : Terminator{that} {} void Begin(const char *sourceFileName, int sourceLine); void HasIoStat() { flags_ |= hasIoStat; } void HasErrLabel() { flags_ |= hasErr; } diff --git a/runtime/io-stmt.cpp b/runtime/io-stmt.cpp index e54a67a328e2..adc9bae6c150 100644 --- a/runtime/io-stmt.cpp +++ b/runtime/io-stmt.cpp @@ -7,130 +7,66 @@ //===----------------------------------------------------------------------===// #include "io-stmt.h" +#include "connection.h" +#include "format.h" #include "memory.h" +#include "tools.h" #include "unit.h" #include #include +#include namespace Fortran::runtime::io { -IoStatementState::IoStatementState(const char *sourceFile, int sourceLine) - : IoErrorHandler{sourceFile, sourceLine} {} +int IoStatementBase::EndIoStatement() { return GetIoStat(); } -int IoStatementState::EndIoStatement() { return GetIoStat(); } - -// Defaults -void IoStatementState::GetNext(DataEdit &, int) { - Crash("GetNext() called for I/O statement that is not a formatted data " - "transfer statement"); -} -bool IoStatementState::Emit(const char *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::Emit(const char16_t *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::Emit(const char32_t *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::HandleSlash(int) { - Crash("HandleSlash() called for I/O statement that is not a formatted data " - "transfer statement"); - return false; -} -bool IoStatementState::HandleRelativePosition(std::int64_t) { - Crash("HandleRelativePosition() called for I/O statement that is not a " - "formatted data transfer statement"); - return false; -} -bool IoStatementState::HandleAbsolutePosition(std::int64_t) { - Crash("HandleAbsolutePosition() called for I/O statement that is not a " - "formatted data transfer statement"); - return false; +DataEdit IoStatementBase::GetNextDataEdit(int) { + Crash("IoStatementBase::GetNextDataEdit() called for non-formatted I/O " + "statement"); } template -FixedRecordIoStatementState::FixedRecordIoStatementState( - Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) - : IoStatementState{sourceFile, sourceLine}, buffer_{buffer}, length_{length} { -} +InternalIoStatementState::InternalIoStatementState( + Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length} {} + +template +InternalIoStatementState::InternalIoStatementState( + const Descriptor &d, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {} template -bool FixedRecordIoStatementState::Emit( - const CHAR *data, std::size_t chars) { +bool InternalIoStatementState::Emit( + const CharType *data, std::size_t chars) { if constexpr (isInput) { - IoStatementState::Emit(data, chars); // default Crash() + Crash("InternalIoStatementState::Emit() called for input statement"); return false; - } else if (at_ + chars > length_) { - SignalEor(); - if (at_ < length_) { - std::memcpy(buffer_ + at_, data, (length_ - at_) * sizeof(CHAR)); - at_ = furthest_ = length_; - } - return false; - } else { - std::memcpy(buffer_ + at_, data, chars * sizeof(CHAR)); - at_ += chars; - furthest_ = std::max(furthest_, at_); - return true; } + return unit_.Emit(data, chars, *this); } template -bool FixedRecordIoStatementState::HandleAbsolutePosition( - std::int64_t n) { - if (n < 0) { - n = 0; - } - n += leftTabLimit_; - bool ok{true}; - if (static_cast(n) > length_) { - SignalEor(); - n = length_; - ok = false; - } - if constexpr (!isInput) { - if (static_cast(n) > furthest_) { - std::fill_n(buffer_ + furthest_, n - furthest_, static_cast(' ')); +bool InternalIoStatementState::AdvanceRecord(int n) { + while (n-- > 0) { + if (!unit_.AdvanceRecord(*this)) { + return false; } } - at_ = n; - furthest_ = std::max(furthest_, at_); - return ok; -} - -template -bool FixedRecordIoStatementState::HandleRelativePosition( - std::int64_t n) { - return HandleAbsolutePosition(n + at_ - leftTabLimit_); + return true; } template -int FixedRecordIoStatementState::EndIoStatement() { +int InternalIoStatementState::EndIoStatement() { if constexpr (!isInput) { - HandleAbsolutePosition(length_ - leftTabLimit_); // fill + unit_.EndIoStatement(); // fill } - return GetIoStat(); -} - -template -int InternalIoStatementState::EndIoStatement() { - auto result{FixedRecordIoStatementState::EndIoStatement()}; + auto result{IoStatementBase::EndIoStatement()}; if (free_) { FreeMemory(this); } return result; } -template -InternalIoStatementState::InternalIoStatementState( - Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) - : FixedRecordIoStatementState( - buffer, length, sourceFile, sourceLine) {} - template InternalFormattedIoStatementState::InternalFormattedIoStatementState(Buffer buffer, std::size_t length, @@ -138,63 +74,289 @@ InternalFormattedIoStatementState{buffer, length, sourceFile, sourceLine}, - format_{*this, format, formatLength} {} + ioStatementState_{*this}, format_{*this, format, formatLength} {} + +template +InternalFormattedIoStatementState::InternalFormattedIoStatementState(const Descriptor &d, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : InternalIoStatementState{d, sourceFile, sourceLine}, + ioStatementState_{*this}, format_{*this, format, formatLength} {} template int InternalFormattedIoStatementState::EndIoStatement() { - format_.FinishOutput(*this); + if constexpr (!isInput) { + format_.FinishOutput(*this); + } return InternalIoStatementState::EndIoStatement(); } template -ExternalFormattedIoStatementState::ExternalFormattedIoStatementState(ExternalFile &file, - const CHAR *format, std::size_t formatLength, const char *sourceFile, - int sourceLine) - : IoStatementState{sourceFile, sourceLine}, file_{file}, format_{*this, - format, - formatLength} {} +bool InternalFormattedIoStatementState::HandleAbsolutePosition( + std::int64_t n) { + return unit_.HandleAbsolutePosition(n, *this); +} template -bool ExternalFormattedIoStatementState::Emit( - const CHAR *data, std::size_t chars) { - // TODO: UTF-8 encoding of 2- and 4-byte characters - return file_.Emit(data, chars * sizeof(CHAR), *this); +bool InternalFormattedIoStatementState::HandleRelativePosition( + std::int64_t n) { + return unit_.HandleRelativePosition(n, *this); } template -bool ExternalFormattedIoStatementState::HandleSlash(int n) { +InternalListIoStatementState::InternalListIoStatementState( + Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) + : InternalIoStatementState{buffer, length, sourceFile, + sourceLine}, + ioStatementState_{*this} {} + +template +InternalListIoStatementState::InternalListIoStatementState( + const Descriptor &d, const char *sourceFile, int sourceLine) + : InternalIoStatementState{d, sourceFile, sourceLine}, + ioStatementState_{*this} {} + +ExternalIoStatementBase::ExternalIoStatementBase( + ExternalFileUnit &unit, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {} + +MutableModes &ExternalIoStatementBase::mutableModes() { return unit_.modes; } + +ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; } + +int ExternalIoStatementBase::EndIoStatement() { + if (unit_.nonAdvancing) { + unit_.leftTabLimit = unit_.furthestPositionInRecord; + unit_.nonAdvancing = false; + } else { + unit_.leftTabLimit.reset(); + } + auto result{IoStatementBase::EndIoStatement()}; + unit_.EndIoStatement(); // annihilates *this in unit_.u_ + return result; +} + +void OpenStatementState::set_path( + const char *path, std::size_t length, int kind) { + if (kind != 1) { // TODO + Crash("OPEN: FILE= with unimplemented: CHARACTER(KIND=%d)", kind); + } + std::size_t bytes{length * kind}; // TODO: UTF-8 encoding of Unicode path + path_ = SaveDefaultCharacter(path, bytes, *this); + pathLength_ = length; +} + +int OpenStatementState::EndIoStatement() { + if (wasExtant_ && status_ != OpenStatus::Old) { + Crash("OPEN statement for connected unit must have STATUS='OLD'"); + } + unit().OpenUnit(status_, position_, std::move(path_), pathLength_, *this); + return IoStatementBase::EndIoStatement(); +} + +int CloseStatementState::EndIoStatement() { + unit().CloseUnit(status_, *this); + return IoStatementBase::EndIoStatement(); +} + +int NoopCloseStatementState::EndIoStatement() { + auto result{IoStatementBase::EndIoStatement()}; + FreeMemory(this); + return result; +} + +template int ExternalIoStatementState::EndIoStatement() { + if constexpr (!isInput) { + if (!unit().nonAdvancing) { + unit().AdvanceRecord(*this); + } + unit().FlushIfTerminal(*this); + } + return ExternalIoStatementBase::EndIoStatement(); +} + +template +bool ExternalIoStatementState::Emit( + const char *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + return unit().Emit(data, chars * sizeof(*data), *this); +} + +template +bool ExternalIoStatementState::Emit( + const char16_t *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + // TODO: UTF-8 encoding + return unit().Emit( + reinterpret_cast(data), chars * sizeof(*data), *this); +} + +template +bool ExternalIoStatementState::Emit( + const char32_t *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + // TODO: UTF-8 encoding + return unit().Emit( + reinterpret_cast(data), chars * sizeof(*data), *this); +} + +template +bool ExternalIoStatementState::AdvanceRecord(int n) { while (n-- > 0) { - if (!file_.NextOutputRecord(*this)) { + if (!unit().AdvanceRecord(*this)) { return false; } } return true; } -template -bool ExternalFormattedIoStatementState::HandleAbsolutePosition( - std::int64_t n) { - return file_.HandleAbsolutePosition(n, *this); +template +bool ExternalIoStatementState::HandleAbsolutePosition(std::int64_t n) { + return unit().HandleAbsolutePosition(n, *this); } -template -bool ExternalFormattedIoStatementState::HandleRelativePosition( - std::int64_t n) { - return file_.HandleRelativePosition(n, *this); +template +bool ExternalIoStatementState::HandleRelativePosition(std::int64_t n) { + return unit().HandleRelativePosition(n, *this); } +template +ExternalFormattedIoStatementState::ExternalFormattedIoStatementState(ExternalFileUnit &unit, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : ExternalIoStatementState{unit, sourceFile, sourceLine}, + mutableModes_{unit.modes}, format_{*this, format, formatLength} {} + template int ExternalFormattedIoStatementState::EndIoStatement() { format_.FinishOutput(*this); - if constexpr (!isInput) { - file_.NextOutputRecord(*this); // TODO: non-advancing I/O + return ExternalIoStatementState::EndIoStatement(); +} + +DataEdit IoStatementState::GetNextDataEdit(int n) { + return std::visit([&](auto &x) { return x.get().GetNextDataEdit(n); }, u_); +} + +bool IoStatementState::Emit(const char *data, std::size_t n) { + return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_); +} + +bool IoStatementState::AdvanceRecord(int n) { + return std::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_); +} + +int IoStatementState::EndIoStatement() { + return std::visit([](auto &x) { return x.get().EndIoStatement(); }, u_); +} + +ConnectionState &IoStatementState::GetConnectionState() { + return std::visit( + [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); }, + u_); +} + +MutableModes &IoStatementState::mutableModes() { + return std::visit( + [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_); +} + +IoErrorHandler &IoStatementState::GetIoErrorHandler() const { + return std::visit( + [](auto &x) -> IoErrorHandler & { + return static_cast(x.get()); + }, + u_); +} + +bool IoStatementState::EmitRepeated(char ch, std::size_t n) { + return std::visit( + [=](auto &x) { + for (std::size_t j{0}; j < n; ++j) { + if (!x.get().Emit(&ch, 1)) { + return false; + } + } + return true; + }, + u_); +} + +bool IoStatementState::EmitField( + const char *p, std::size_t length, std::size_t width) { + if (width <= 0) { + width = static_cast(length); } - int result{GetIoStat()}; - file_.EndIoStatement(); // annihilates *this in file_.u_ - return result; + if (length > static_cast(width)) { + return EmitRepeated('*', width); + } else { + return EmitRepeated(' ', static_cast(width - length)) && + Emit(p, length); + } +} + +bool ListDirectedStatementState::NeedAdvance( + const ConnectionState &connection, std::size_t width) const { + return connection.positionInRecord > 0 && + width > connection.RemainingSpaceInRecord(); +} + +bool ListDirectedStatementState::EmitLeadingSpaceOrAdvance( + IoStatementState &io, std::size_t length, bool isCharacter) { + if (length == 0) { + return true; + } + const ConnectionState &connection{io.GetConnectionState()}; + int space{connection.positionInRecord == 0 || + !(isCharacter && lastWasUndelimitedCharacter)}; + lastWasUndelimitedCharacter = false; + if (NeedAdvance(connection, space + length)) { + return io.AdvanceRecord(); + } + if (space) { + return io.Emit(" ", 1); + } + return true; +} + +template +int UnformattedIoStatementState::EndIoStatement() { + auto &ext{static_cast &>(*this)}; + ExternalFileUnit &unit{ext.unit()}; + if (unit.access == Access::Sequential && !unit.recordLength.has_value()) { + // Overwrite the first four bytes of the record with its length, + // and also append the length. These four bytes were skipped over + // in BeginUnformattedOutput(). + // TODO: Break very large records up into subrecords with negative + // headers &/or footers + union { + std::uint32_t u; + char c[sizeof u]; + } u; + u.u = unit.furthestPositionInRecord - sizeof u.c; + // TODO: Convert record length to little-endian on big-endian host? + if (!(ext.Emit(u.c, sizeof u.c) && ext.HandleAbsolutePosition(0) && + ext.Emit(u.c, sizeof u.c) && ext.AdvanceRecord())) { + return false; + } + } + return ext.EndIoStatement(); } +template class InternalIoStatementState; +template class InternalIoStatementState; template class InternalFormattedIoStatementState; +template class InternalFormattedIoStatementState; +template class InternalListIoStatementState; +template class ExternalIoStatementState; template class ExternalFormattedIoStatementState; +template class ExternalListIoStatementState; +template class UnformattedIoStatementState; } diff --git a/runtime/io-stmt.h b/runtime/io-stmt.h index 002f38e82596..17549388b060 100644 --- a/runtime/io-stmt.h +++ b/runtime/io-stmt.h @@ -6,112 +6,312 @@ // //===----------------------------------------------------------------------===// -// Represents state of an I/O statement in progress +// Representations of the state of an I/O statement in progress #ifndef FORTRAN_RUNTIME_IO_STMT_H_ #define FORTRAN_RUNTIME_IO_STMT_H_ #include "descriptor.h" +#include "file.h" #include "format.h" +#include "internal-unit.h" #include "io-error.h" +#include #include +#include namespace Fortran::runtime::io { -class ExternalFile; +struct ConnectionState; +class ExternalFileUnit; -class IoStatementState : public IoErrorHandler, public FormatContext { +class OpenStatementState; +class CloseStatementState; +class NoopCloseStatementState; +template +class InternalFormattedIoStatementState; +template class InternalListIoStatementState; +template +class ExternalFormattedIoStatementState; +template class ExternalListIoStatementState; +template class UnformattedIoStatementState; + +// The Cookie type in the I/O API is a pointer (for C) to this class. +class IoStatementState { public: - IoStatementState(const char *sourceFile, int sourceLine); - virtual ~IoStatementState() {} - - virtual int EndIoStatement(); - - // Default (crashing) callback overrides for FormatContext - virtual void GetNext(DataEdit &, int maxRepeat = 1); - virtual bool Emit(const char *, std::size_t); - virtual bool Emit(const char16_t *, std::size_t); - virtual bool Emit(const char32_t *, std::size_t); - virtual bool HandleSlash(int); - virtual bool HandleRelativePosition(std::int64_t); - virtual bool HandleAbsolutePosition(std::int64_t); -}; + template explicit IoStatementState(A &x) : u_{x} {} -template -class FixedRecordIoStatementState : public IoStatementState { -protected: - using Buffer = std::conditional_t; + // These member functions each project themselves into the active alternative. + // They're used by per-data-item routines in the I/O API(e.g., OutputReal64) + // to interact with the state of the I/O statement in progress. + // This design avoids virtual member functions and function pointers, + // which may not have good support in some use cases. + DataEdit GetNextDataEdit(int = 1); + bool Emit(const char *, std::size_t); + bool AdvanceRecord(int = 1); + int EndIoStatement(); + ConnectionState &GetConnectionState(); + MutableModes &mutableModes(); -public: - FixedRecordIoStatementState( - Buffer, std::size_t, const char *sourceFile, int sourceLine); + // N.B.: this also works with base classes + template A *get_if() const { + return std::visit( + [](auto &x) -> A * { + if constexpr (std::is_convertible_v) { + return &x.get(); + } + return nullptr; + }, + u_); + } + IoErrorHandler &GetIoErrorHandler() const; - virtual bool Emit(const CHAR *, std::size_t chars /* not bytes */); - // TODO virtual void HandleSlash(int); - virtual bool HandleRelativePosition(std::int64_t); - virtual bool HandleAbsolutePosition(std::int64_t); - virtual int EndIoStatement(); + bool EmitRepeated(char, std::size_t); + bool EmitField(const char *, std::size_t length, std::size_t width); private: - Buffer buffer_{nullptr}; - std::size_t length_; // RECL= or internal I/O character variable length - std::size_t leftTabLimit_{0}; // nonzero only when non-advancing - std::size_t at_{0}; - std::size_t furthest_{0}; + std::variant, + std::reference_wrapper, + std::reference_wrapper, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>> + u_; +}; + +// Base class for all per-I/O statement state classes. +// Inherits IoErrorHandler from its base. +struct IoStatementBase : public DefaultFormatControlCallbacks { + using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks; + int EndIoStatement(); + DataEdit GetNextDataEdit(int = 1); // crashing default +}; + +struct InputStatementState {}; +struct OutputStatementState {}; +template +using IoDirectionState = + std::conditional_t; + +struct FormattedStatementState {}; + +template struct ListDirectedStatementState {}; +template<> struct ListDirectedStatementState { + static std::size_t RemainingSpaceInRecord(const ConnectionState &); + bool NeedAdvance(const ConnectionState &, std::size_t) const; + bool EmitLeadingSpaceOrAdvance( + IoStatementState &, std::size_t, bool isCharacter = false); + bool lastWasUndelimitedCharacter{false}; }; template -class InternalIoStatementState - : public FixedRecordIoStatementState { +class InternalIoStatementState : public IoStatementBase, + public IoDirectionState { public: - using typename FixedRecordIoStatementState::Buffer; + using CharType = CHAR; + using Buffer = std::conditional_t; InternalIoStatementState(Buffer, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); - virtual int EndIoStatement(); + InternalIoStatementState( + const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); + int EndIoStatement(); + bool Emit(const CharType *, std::size_t chars /* not bytes */); + bool AdvanceRecord(int = 1); + ConnectionState &GetConnectionState() { return unit_; } + MutableModes &mutableModes() { return unit_.modes; } protected: bool free_{true}; + InternalDescriptorUnit unit_; }; -template +template class InternalFormattedIoStatementState - : public InternalIoStatementState { + : public InternalIoStatementState, + public FormattedStatementState { public: - using typename InternalIoStatementState::Buffer; + using CharType = CHAR; + using typename InternalIoStatementState::Buffer; InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, - const CHAR *format, std::size_t formatLength, + const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); - void GetNext(DataEdit &edit, int maxRepeat = 1) { - format_.GetNext(*this, edit, maxRepeat); + InternalFormattedIoStatementState(const Descriptor &, const CharType *format, + std::size_t formatLength, const char *sourceFile = nullptr, + int sourceLine = 0); + IoStatementState &ioStatementState() { return ioStatementState_; } + int EndIoStatement(); + DataEdit GetNextDataEdit(int maxRepeat = 1) { + return format_.GetNextDataEdit(*this, maxRepeat); } + bool HandleRelativePosition(std::int64_t); + bool HandleAbsolutePosition(std::int64_t); + +private: + IoStatementState ioStatementState_; // points to *this + using InternalIoStatementState::unit_; + // format_ *must* be last; it may be partial someday + FormatControl format_; +}; + +template +class InternalListIoStatementState + : public InternalIoStatementState, + public ListDirectedStatementState { +public: + using CharType = CHAR; + using typename InternalIoStatementState::Buffer; + InternalListIoStatementState(Buffer internal, std::size_t internalLength, + const char *sourceFile = nullptr, int sourceLine = 0); + InternalListIoStatementState( + const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); + IoStatementState &ioStatementState() { return ioStatementState_; } + DataEdit GetNextDataEdit(int maxRepeat = 1) { + DataEdit edit; + edit.descriptor = DataEdit::ListDirected; + edit.repeat = maxRepeat; + edit.modes = InternalIoStatementState::mutableModes(); + return edit; + } + +private: + using InternalIoStatementState::unit_; + IoStatementState ioStatementState_; // points to *this +}; + +class ExternalIoStatementBase : public IoStatementBase { +public: + ExternalIoStatementBase( + ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); + ExternalFileUnit &unit() { return unit_; } + MutableModes &mutableModes(); + ConnectionState &GetConnectionState(); int EndIoStatement(); private: - FormatControl format_; // must be last, may be partial + ExternalFileUnit &unit_; }; -template -class ExternalFormattedIoStatementState : public IoStatementState { +template +class ExternalIoStatementState : public ExternalIoStatementBase, + public IoDirectionState { public: - ExternalFormattedIoStatementState(ExternalFile &, const CHAR *format, + using ExternalIoStatementBase::ExternalIoStatementBase; + int EndIoStatement(); + bool Emit(const char *, std::size_t chars /* not bytes */); + bool Emit(const char16_t *, std::size_t chars /* not bytes */); + bool Emit(const char32_t *, std::size_t chars /* not bytes */); + bool AdvanceRecord(int = 1); + bool HandleRelativePosition(std::int64_t); + bool HandleAbsolutePosition(std::int64_t); +}; + +template +class ExternalFormattedIoStatementState + : public ExternalIoStatementState, + public FormattedStatementState { +public: + using CharType = CHAR; + ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); - void GetNext(DataEdit &edit, int maxRepeat = 1) { - format_.GetNext(*this, edit, maxRepeat); + MutableModes &mutableModes() { return mutableModes_; } + int EndIoStatement(); + DataEdit GetNextDataEdit(int maxRepeat = 1) { + return format_.GetNextDataEdit(*this, maxRepeat); } - bool Emit(const CHAR *, std::size_t chars /* not bytes */); - bool HandleSlash(int); - bool HandleRelativePosition(std::int64_t); - bool HandleAbsolutePosition(std::int64_t); + +private: + // These are forked from ConnectionState's modes at the beginning + // of each formatted I/O statement so they may be overridden by control + // edit descriptors during the statement. + MutableModes mutableModes_; + FormatControl format_; +}; + +template +class ExternalListIoStatementState + : public ExternalIoStatementState, + public ListDirectedStatementState { +public: + using ExternalIoStatementState::ExternalIoStatementState; + DataEdit GetNextDataEdit(int maxRepeat = 1) { + DataEdit edit; + edit.descriptor = DataEdit::ListDirected; + edit.repeat = maxRepeat; + edit.modes = ExternalIoStatementState::mutableModes(); + return edit; + } +}; + +template +class UnformattedIoStatementState : public ExternalIoStatementState { +public: + using ExternalIoStatementState::ExternalIoStatementState; + int EndIoStatement(); +}; + +class OpenStatementState : public ExternalIoStatementBase { +public: + OpenStatementState(ExternalFileUnit &unit, bool wasExtant, + const char *sourceFile = nullptr, int sourceLine = 0) + : ExternalIoStatementBase{unit, sourceFile, sourceLine}, wasExtant_{ + wasExtant} {} + bool wasExtant() const { return wasExtant_; } + void set_status(OpenStatus status) { status_ = status; } + void set_path(const char *, std::size_t, int kind); // FILE= + void set_position(Position position) { position_ = position; } // POSITION= + int EndIoStatement(); + +private: + bool wasExtant_; + OpenStatus status_{OpenStatus::Unknown}; + Position position_{Position::AsIs}; + OwningPtr path_; + std::size_t pathLength_; +}; + +class CloseStatementState : public ExternalIoStatementBase { +public: + CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr, + int sourceLine = 0) + : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} + void set_status(CloseStatus status) { status_ = status; } + int EndIoStatement(); + +private: + CloseStatus status_{CloseStatus::Keep}; +}; + +class NoopCloseStatementState : public IoStatementBase { +public: + NoopCloseStatementState(const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{*this} {} + IoStatementState &ioStatementState() { return ioStatementState_; } + void set_status(CloseStatus) {} // discards + MutableModes &mutableModes() { return connection_.modes; } + ConnectionState &GetConnectionState() { return connection_; } int EndIoStatement(); private: - ExternalFile &file_; - FormatControl format_; + IoStatementState ioStatementState_; // points to *this + ConnectionState connection_; }; +extern template class InternalIoStatementState; +extern template class InternalIoStatementState; extern template class InternalFormattedIoStatementState; +extern template class InternalFormattedIoStatementState; +extern template class InternalListIoStatementState; +extern template class ExternalIoStatementState; extern template class ExternalFormattedIoStatementState; +extern template class ExternalListIoStatementState; +extern template class UnformattedIoStatementState; +extern template class FormatControl>; +extern template class FormatControl>; +extern template class FormatControl>; } #endif // FORTRAN_RUNTIME_IO_STMT_H_ diff --git a/runtime/lock.h b/runtime/lock.h index 19f0cea79b01..a26c96542c88 100644 --- a/runtime/lock.h +++ b/runtime/lock.h @@ -23,7 +23,7 @@ class Lock { bool Try() { return pthread_mutex_trylock(&mutex_) != 0; } void Drop() { pthread_mutex_unlock(&mutex_); } - void CheckLocked(Terminator &terminator) { + void CheckLocked(const Terminator &terminator) { if (Try()) { Drop(); terminator.Crash("Lock::CheckLocked() failed"); diff --git a/runtime/main.cpp b/runtime/main.cpp index 8c2caa570df5..e7f4200b2777 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -33,7 +33,6 @@ void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { std::atexit(Fortran::runtime::NotifyOtherImagesOfNormalEnd); Fortran::runtime::executionEnvironment.Configure(argc, argv, envp); ConfigureFloatingPoint(); - Fortran::runtime::Terminator terminator{"ProgramStart()"}; - Fortran::runtime::io::ExternalFile::InitializePredefinedUnits(terminator); + Fortran::runtime::io::ExternalFileUnit::InitializePredefinedUnits(); } } diff --git a/runtime/memory.cpp b/runtime/memory.cpp index ac456a5dd9d1..84fd35da48ef 100644 --- a/runtime/memory.cpp +++ b/runtime/memory.cpp @@ -12,7 +12,7 @@ namespace Fortran::runtime { -void *AllocateMemoryOrCrash(Terminator &terminator, std::size_t bytes) { +void *AllocateMemoryOrCrash(const Terminator &terminator, std::size_t bytes) { if (void *p{std::malloc(bytes)}) { return p; } diff --git a/runtime/memory.h b/runtime/memory.h index d41f5f95407e..1bd5bca1b78a 100644 --- a/runtime/memory.h +++ b/runtime/memory.h @@ -18,8 +18,9 @@ namespace Fortran::runtime { class Terminator; -[[nodiscard]] void *AllocateMemoryOrCrash(Terminator &, std::size_t bytes); -template[[nodiscard]] A &AllocateOrCrash(Terminator &t) { +[[nodiscard]] void *AllocateMemoryOrCrash( + const Terminator &, std::size_t bytes); +template[[nodiscard]] A &AllocateOrCrash(const Terminator &t) { return *reinterpret_cast(AllocateMemoryOrCrash(t, sizeof(A))); } void FreeMemory(void *); @@ -33,7 +34,7 @@ template void FreeMemoryAndNullify(A *&p) { template struct New { template - [[nodiscard]] A &operator()(Terminator &terminator, X &&... x) { + [[nodiscard]] A &operator()(const Terminator &terminator, X &&... x) { return *new (AllocateMemoryOrCrash(terminator, sizeof(A))) A{std::forward(x)...}; } @@ -47,7 +48,7 @@ template using OwningPtr = std::unique_ptr>; template struct Allocator { using value_type = A; - explicit Allocator(Terminator &t) : terminator{t} {} + explicit Allocator(const Terminator &t) : terminator{t} {} template explicit constexpr Allocator(const Allocator &that) noexcept : terminator{that.terminator} {} @@ -58,7 +59,7 @@ template struct Allocator { AllocateMemoryOrCrash(terminator, n * sizeof(A))); } constexpr void deallocate(A *p, std::size_t) { FreeMemory(p); } - Terminator &terminator; + const Terminator &terminator; }; } diff --git a/runtime/numeric-output.cpp b/runtime/numeric-output.cpp new file mode 100644 index 000000000000..daef7aba879a --- /dev/null +++ b/runtime/numeric-output.cpp @@ -0,0 +1,152 @@ +//===-- runtime/numeric-output.cpp ------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "numeric-output.h" +#include "flang/common/unsigned-const-division.h" + +namespace Fortran::runtime::io { + +bool EditIntegerOutput( + IoStatementState &io, const DataEdit &edit, std::int64_t n) { + char buffer[66], *end = &buffer[sizeof buffer], *p = end; + std::uint64_t un{static_cast(n < 0 ? -n : n)}; + int signChars{0}; + switch (edit.descriptor) { + case DataEdit::ListDirected: + case 'G': + case 'I': + if (n < 0 || (edit.modes.editingFlags & signPlus)) { + signChars = 1; // '-' or '+' + } + while (un > 0) { + auto quotient{common::DivideUnsignedBy(un)}; + *--p = '0' + un - 10 * quotient; + un = quotient; + } + break; + case 'B': + for (; un > 0; un >>= 1) { + *--p = '0' + (un & 1); + } + break; + case 'O': + for (; un > 0; un >>= 3) { + *--p = '0' + (un & 7); + } + break; + case 'Z': + for (; un > 0; un >>= 4) { + int digit = un & 0xf; + *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; + } + break; + default: + io.GetIoErrorHandler().Crash( + "Data edit descriptor '%c' may not be used with an INTEGER data item", + edit.descriptor); + return false; + } + + int digits = end - p; + int leadingZeroes{0}; + int editWidth{edit.width.value_or(0)}; + if (edit.digits && digits <= *edit.digits) { // Iw.m + if (*edit.digits == 0 && n == 0) { + // Iw.0 with zero value: output field must be blank. For I0.0 + // and a zero value, emit one blank character. + signChars = 0; // in case of SP + editWidth = std::max(1, editWidth); + } else { + leadingZeroes = *edit.digits - digits; + } + } else if (n == 0) { + leadingZeroes = 1; + } + int total{signChars + leadingZeroes + digits}; + if (editWidth > 0 && total > editWidth) { + return io.EmitRepeated('*', editWidth); + } + int leadingSpaces{std::max(0, editWidth - total)}; + if (edit.IsListDirected()) { + if (static_cast(total) > + io.GetConnectionState().RemainingSpaceInRecord() && + !io.AdvanceRecord()) { + return false; + } + leadingSpaces = 1; + } + return io.EmitRepeated(' ', leadingSpaces) && + io.Emit(n < 0 ? "-" : "+", signChars) && + io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits); +} + +// Formats the exponent (see table 13.1 for all the cases) +const char *RealOutputEditingBase::FormatExponent( + int expo, const DataEdit &edit, int &length) { + char *eEnd{&exponent_[sizeof exponent_]}; + char *exponent{eEnd}; + for (unsigned e{static_cast(std::abs(expo))}; e > 0;) { + unsigned quotient{common::DivideUnsignedBy(e)}; + *--exponent = '0' + e - 10 * quotient; + e = quotient; + } + if (edit.expoDigits) { + if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 + while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { + *--exponent = '0'; + } + } else if (exponent == eEnd) { + *--exponent = '0'; // Ew.dE0 with zero-valued exponent + } + } else { // ensure at least two exponent digits + while (exponent + 2 > eEnd) { + *--exponent = '0'; + } + } + *--exponent = expo < 0 ? '-' : '+'; + if (edit.expoDigits || exponent + 3 == eEnd) { + *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' + } + length = eEnd - exponent; + return exponent; +} + +bool RealOutputEditingBase::EmitPrefix( + const DataEdit &edit, std::size_t length, std::size_t width) { + if (edit.IsListDirected()) { + int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart + ? 2 + : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0 : 1}; + int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart || + edit.descriptor == DataEdit::ListDirectedImaginaryPart + ? 1 + : 0}; + length += prefixLength + suffixLength; + ConnectionState &connection{io_.GetConnectionState()}; + return (connection.positionInRecord == 0 || + length <= connection.RemainingSpaceInRecord() || + io_.AdvanceRecord()) && + io_.Emit(" (", prefixLength); + } else if (width > length) { + return io_.EmitRepeated(' ', width - length); + } else { + return true; + } +} + +bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) { + if (edit.descriptor == DataEdit::ListDirectedRealPart) { + return io_.Emit(edit.modes.editingFlags & decimalComma ? ";" : ",", 1); + } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { + return io_.Emit(")", 1); + } else { + return true; + } +} + +} diff --git a/runtime/numeric-output.h b/runtime/numeric-output.h index a0f40c700b7b..f8c5437ca31b 100644 --- a/runtime/numeric-output.h +++ b/runtime/numeric-output.h @@ -14,191 +14,99 @@ // components, I and G for INTEGER, and B/O/Z for both. // See subclauses in 13.7.2.3 of Fortran 2018 for the // detailed specifications of these descriptors. -// Drives the same binary-to-decimal formatting templates used -// by the f18 compiler. +// List-directed output (13.10.4) for numeric types is also done here. +// Drives the same fast binary-to-decimal formatting templates used +// in the f18 front-end. #include "format.h" -#include "flang/common/unsigned-const-division.h" +#include "io-stmt.h" #include "flang/decimal/decimal.h" namespace Fortran::runtime::io { class IoStatementState; -// Utility subroutines -static bool EmitRepeated(IoStatementState &io, char ch, int n) { - while (n-- > 0) { - if (!io.Emit(&ch, 1)) { - return false; - } - } - return true; -} +// I, B, O, Z, and G output editing for INTEGER. +// edit is const here (and elsewhere in this header) so that one +// edit descriptor with a repeat factor may safely serve to edit +// multiple elements of an array. +bool EditIntegerOutput(IoStatementState &, const DataEdit &, std::int64_t); -static bool EmitField( - IoStatementState &io, const char *p, std::size_t length, int width) { - if (width <= 0) { - width = static_cast(length); - } - if (length > static_cast(width)) { - return EmitRepeated(io, '*', width); - } else { - return EmitRepeated(io, ' ', static_cast(width - length)) && - io.Emit(p, length); - } -} +// Encapsulates the state of a REAL output conversion. +class RealOutputEditingBase { +protected: + explicit RealOutputEditingBase(IoStatementState &io) : io_{io} {} -// I, B, O, Z, and (for INTEGER) G output editing. -// edit is const here so that a repeated edit descriptor may safely serve -// multiple array elements -static bool EditIntegerOutput( - IoStatementState &io, const DataEdit &edit, std::int64_t n) { - char buffer[66], *end = &buffer[sizeof buffer], *p = end; - std::uint64_t un{static_cast(n < 0 ? -n : n)}; - int signChars{0}; - switch (edit.descriptor) { - case 'G': - case 'I': - if (n < 0 || (edit.modes.editingFlags & signPlus)) { - signChars = 1; // '-' or '+' - } - while (un > 0) { - auto quotient{common::DivideUnsignedBy(un)}; - *--p = '0' + un - 10 * quotient; - un = quotient; - } - break; - case 'B': - for (; un > 0; un >>= 1) { - *--p = '0' + (un & 1); - } - break; - case 'O': - for (; un > 0; un >>= 3) { - *--p = '0' + (un & 7); + static bool IsDecimalNumber(const char *p) { + if (!p) { + return false; } - break; - case 'Z': - for (; un > 0; un >>= 4) { - int digit = un & 0xf; - *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; + if (*p == '-' || *p == '+') { + ++p; } - break; - default: - io.Crash( - "Data edit descriptor '%c' may not be used with an INTEGER data item", - edit.descriptor); - return false; + return *p >= '0' && *p <= '9'; } - int digits = end - p; - int leadingZeroes{0}; - int editWidth{edit.width.value_or(0)}; - if (edit.digits && digits <= *edit.digits) { // Iw.m - if (*edit.digits == 0 && n == 0) { - // Iw.0 with zero value: output field must be blank. For I0.0 - // and a zero value, emit one blank character. - signChars = 0; // in case of SP - editWidth = std::max(1, editWidth); - } else { - leadingZeroes = *edit.digits - digits; - } - } else if (n == 0) { - leadingZeroes = 1; - } - int total{signChars + leadingZeroes + digits}; - if (edit.width > 0 && total > editWidth) { - return EmitRepeated(io, '*', editWidth); - } - if (total < editWidth) { - EmitRepeated(io, '*', editWidth - total); - return false; - } - if (signChars) { - if (!io.Emit(n < 0 ? "-" : "+", 1)) { - return false; - } - } - return EmitRepeated(io, '0', leadingZeroes) && io.Emit(p, digits); -} + const char *FormatExponent(int, const DataEdit &edit, int &length); + bool EmitPrefix(const DataEdit &, std::size_t length, std::size_t width); + bool EmitSuffix(const DataEdit &); -// Encapsulates the state of a REAL output conversion. -template -class RealOutputEditing { + IoStatementState &io_; + int trailingBlanks_{0}; // created when Gw editing maps to Fw + char exponent_[16]; +}; + +template +class RealOutputEditing : public RealOutputEditingBase { public: - RealOutputEditing(IoStatementState &io, FLOAT x) : io_{io}, x_{x} {} - bool Edit(const DataEdit &edit); + template + RealOutputEditing(IoStatementState &io, A x) + : RealOutputEditingBase{io}, x_{x} {} + bool Edit(const DataEdit &); private: + using BinaryFloatingPoint = + decimal::BinaryFloatingPointNumber; + // The DataEdit arguments here are const references or copies so that - // the original DataEdit can safely serve multiple array elements if + // the original DataEdit can safely serve multiple array elements when // it has a repeat count. bool EditEorDOutput(const DataEdit &); bool EditFOutput(const DataEdit &); DataEdit EditForGOutput(DataEdit); // returns an E or F edit bool EditEXOutput(const DataEdit &); + bool EditListDirectedOutput(const DataEdit &); - bool IsZero() const { return x_ == 0; } - const char *FormatExponent(int, const DataEdit &edit, int &length); - - static enum decimal::FortranRounding SetRounding( - common::RoundingMode rounding) { - switch (rounding) { - case common::RoundingMode::TiesToEven: break; - case common::RoundingMode::Up: return decimal::RoundUp; - case common::RoundingMode::Down: return decimal::RoundDown; - case common::RoundingMode::ToZero: return decimal::RoundToZero; - case common::RoundingMode::TiesAwayFromZero: - return decimal::RoundCompatible; - } - return decimal::RoundNearest; // arranged thus to dodge bogus G++ warning - } - - static bool IsDecimalNumber(const char *p) { - if (!p) { - return false; - } - if (*p == '-' || *p == '+') { - ++p; - } - return *p >= '0' && *p <= '9'; - } + bool IsZero() const { return x_.IsZero(); } decimal::ConversionToDecimalResult Convert( int significantDigits, const DataEdit &, int flags = 0); - IoStatementState &io_; - FLOAT x_; - char buffer_[bufferSize]; - int trailingBlanks_{0}; // created when G editing maps to F - char exponent_[16]; + BinaryFloatingPoint x_; + char buffer_[BinaryFloatingPoint::maxDecimalConversionDigits + + EXTRA_DECIMAL_CONVERSION_SPACE]; }; -template -decimal::ConversionToDecimalResult RealOutputEditing::Convert(int significantDigits, - const DataEdit &edit, int flags) { +template +decimal::ConversionToDecimalResult RealOutputEditing::Convert( + int significantDigits, const DataEdit &edit, int flags) { if (edit.modes.editingFlags & signPlus) { flags |= decimal::AlwaysSign; } - auto converted{decimal::ConvertToDecimal(buffer_, bufferSize, - static_cast(flags), - significantDigits, SetRounding(edit.modes.roundingMode), - decimal::BinaryFloatingPointNumber(x_))}; + auto converted{decimal::ConvertToDecimal(buffer_, + sizeof buffer_, static_cast(flags), + significantDigits, edit.modes.round, x_)}; if (!converted.str) { // overflow - io_.Crash("RealOutputEditing::Convert : buffer size %zd was insufficient", - bufferSize); + io_.GetIoErrorHandler().Crash( + "RealOutputEditing::Convert : buffer size %zd was insufficient", + sizeof buffer_); } return converted; } // 13.7.2.3.3 in F'2018 -template -bool RealOutputEditing::EditEorDOutput(const DataEdit &edit) { +template +bool RealOutputEditing::EditEorDOutput(const DataEdit &edit) { int editDigits{edit.digits.value_or(0)}; // 'd' field int editWidth{edit.width.value_or(0)}; // 'w' field int significantDigits{editDigits}; @@ -209,7 +117,7 @@ bool RealOutputEditing 0 && !IsDecimalNumber(converted.str)) { // Inf, NaN - return EmitField(io_, converted.str, converted.length, editWidth); + return EmitPrefix(edit, converted.length, editWidth) && + io_.Emit(converted.str, converted.length) && EmitSuffix(edit); } if (!IsZero()) { converted.decimalExponent -= scale; @@ -258,63 +167,28 @@ bool RealOutputEditing 0 ? editWidth : totalLength}; if (totalLength > width) { - return EmitRepeated(io_, '*', width); + return io_.EmitRepeated('*', width); } if (totalLength < width && digitsBeforePoint == 0 && zeroesBeforePoint == 0) { zeroesBeforePoint = 1; ++totalLength; } - return EmitRepeated(io_, ' ', width - totalLength) && + return EmitPrefix(edit, totalLength, width) && io_.Emit(converted.str, signLength + digitsBeforePoint) && - EmitRepeated(io_, '0', zeroesBeforePoint) && + io_.EmitRepeated('0', zeroesBeforePoint) && io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && - EmitRepeated(io_, '0', zeroesAfterPoint) && + io_.EmitRepeated('0', zeroesAfterPoint) && io_.Emit( converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && - EmitRepeated(io_, '0', trailingZeroes) && - io_.Emit(exponent, expoLength); - } -} - -// Formats the exponent (see table 13.1 for all the cases) -template -const char *RealOutputEditing::FormatExponent(int expo, const DataEdit &edit, int &length) { - char *eEnd{&exponent_[sizeof exponent_]}; - char *exponent{eEnd}; - for (unsigned e{static_cast(std::abs(expo))}; e > 0;) { - unsigned quotient{common::DivideUnsignedBy(e)}; - *--exponent = '0' + e - 10 * quotient; - e = quotient; - } - if (edit.expoDigits) { - if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 - while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { - *--exponent = '0'; - } - } else if (exponent == eEnd) { - *--exponent = '0'; // Ew.dE0 with zero-valued exponent - } - } else { // ensure at least two exponent digits - while (exponent + 2 > eEnd) { - *--exponent = '0'; - } + io_.EmitRepeated('0', trailingZeroes) && + io_.Emit(exponent, expoLength) && EmitSuffix(edit); } - *--exponent = expo < 0 ? '-' : '+'; - if (edit.expoDigits || exponent + 3 == eEnd) { - *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' - } - length = eEnd - exponent; - return exponent; } // 13.7.2.3.2 in F'2018 -template -bool RealOutputEditing::EditFOutput(const DataEdit &edit) { +template +bool RealOutputEditing::EditFOutput(const DataEdit &edit) { int fracDigits{edit.digits.value_or(0)}; // 'd' field int extraDigits{0}; int editWidth{edit.width.value_or(0)}; // 'w' field @@ -322,7 +196,7 @@ bool RealOutputEditing 0 && !IsDecimalNumber(converted.str)) { // Inf, NaN - return EmitField(io_, converted.str, converted.length, editWidth); + return EmitPrefix(edit, converted.length, editWidth) && + io_.Emit(converted.str, converted.length) && EmitSuffix(edit); } int scale{IsZero() ? -1 : edit.modes.scale}; int expo{converted.decimalExponent - scale}; if (expo > extraDigits) { extraDigits = expo; if (flags & decimal::Minimize) { - fracDigits = bufferSize - extraDigits - 2; // sign & NUL + fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL } continue; // try again } @@ -360,29 +235,27 @@ bool RealOutputEditing 0 ? editWidth : totalLength}; if (totalLength > width) { - return EmitRepeated(io_, '*', width); + return io_.EmitRepeated('*', width); } if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) { zeroesBeforePoint = 1; ++totalLength; } - return EmitRepeated(io_, ' ', width - totalLength) && + return EmitPrefix(edit, totalLength, width) && io_.Emit(converted.str, signLength + digitsBeforePoint) && - EmitRepeated(io_, '0', zeroesBeforePoint) && + io_.EmitRepeated('0', zeroesBeforePoint) && io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) && - EmitRepeated(io_, '0', zeroesAfterPoint) && + io_.EmitRepeated('0', zeroesAfterPoint) && io_.Emit( converted.str + signLength + digitsBeforePoint, digitsAfterPoint) && - EmitRepeated(io_, '0', trailingZeroes) && - EmitRepeated(io_, ' ', trailingBlanks_); + io_.EmitRepeated('0', trailingZeroes) && + io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit); } } // 13.7.5.2.3 in F'2018 -template -DataEdit RealOutputEditing::EditForGOutput(DataEdit edit) { +template +DataEdit RealOutputEditing::EditForGOutput(DataEdit edit) { edit.descriptor = 'E'; if (!edit.width.has_value() || (*edit.width > 0 && edit.digits.value_or(-1) == 0)) { @@ -393,7 +266,8 @@ DataEdit RealOutputEditing significantDigits) { return edit; // Ew.d } @@ -412,18 +286,32 @@ DataEdit RealOutputEditing +bool RealOutputEditing::EditListDirectedOutput( + const DataEdit &edit) { + decimal::ConversionToDecimalResult converted{Convert(1, edit)}; + if (!IsDecimalNumber(converted.str)) { // Inf, NaN + return EditEorDOutput(edit); + } + int expo{converted.decimalExponent}; + if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) { + DataEdit copy{edit}; + copy.modes.scale = 1; // 1P + return EditEorDOutput(copy); + } + return EditFOutput(edit); +} + // 13.7.5.2.6 in F'2018 -template -bool RealOutputEditing::EditEXOutput(const DataEdit &) { - io_.Crash("EX output editing is not yet implemented"); // TODO +template +bool RealOutputEditing::EditEXOutput(const DataEdit &) { + io_.GetIoErrorHandler().Crash( + "EX output editing is not yet implemented"); // TODO } -template -bool RealOutputEditing::Edit(const DataEdit &edit) { +template +bool RealOutputEditing::Edit(const DataEdit &edit) { switch (edit.descriptor) { case 'D': return EditEorDOutput(edit); case 'E': @@ -436,14 +324,20 @@ bool RealOutputEditing{x_}.raw); + return EditIntegerOutput( + io_, edit, decimal::BinaryFloatingPointNumber{x_}.raw); case 'G': return Edit(EditForGOutput(edit)); default: - io_.Crash("Data edit descriptor '%c' may not be used with a REAL data item", + if (edit.IsListDirected()) { + return EditListDirectedOutput(edit); + } + io_.GetIoErrorHandler().Crash( + "Data edit descriptor '%c' may not be used with a REAL data item", edit.descriptor); return false; } return false; } + } #endif // FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ diff --git a/runtime/stop.cpp b/runtime/stop.cpp index 85bf9c4a14ac..46ad558dfe4c 100644 --- a/runtime/stop.cpp +++ b/runtime/stop.cpp @@ -71,7 +71,7 @@ static void DescribeIEEESignaledExceptions() { [[noreturn]] void RTNAME(ProgramEndStatement)() { Fortran::runtime::io::IoErrorHandler handler{"END statement"}; - Fortran::runtime::io::ExternalFile::CloseAll(handler); + Fortran::runtime::io::ExternalFileUnit::CloseAll(handler); std::exit(EXIT_SUCCESS); } } diff --git a/runtime/terminator.cpp b/runtime/terminator.cpp index c516af3c854a..74594ba65d84 100644 --- a/runtime/terminator.cpp +++ b/runtime/terminator.cpp @@ -12,13 +12,14 @@ namespace Fortran::runtime { -[[noreturn]] void Terminator::Crash(const char *message, ...) { +[[noreturn]] void Terminator::Crash(const char *message, ...) const { va_list ap; va_start(ap, message); CrashArgs(message, ap); } -[[noreturn]] void Terminator::CrashArgs(const char *message, va_list &ap) { +[[noreturn]] void Terminator::CrashArgs( + const char *message, va_list &ap) const { std::fputs("\nfatal Fortran runtime error", stderr); if (sourceFileName_) { std::fprintf(stderr, "(%s", sourceFileName_); @@ -31,23 +32,19 @@ namespace Fortran::runtime { std::vfprintf(stderr, message, ap); fputc('\n', stderr); va_end(ap); + io::FlushOutputOnCrash(*this); NotifyOtherImagesOfErrorTermination(); std::abort(); } [[noreturn]] void Terminator::CheckFailed( - const char *predicate, const char *file, int line) { + const char *predicate, const char *file, int line) const { Crash("Internal error: RUNTIME_CHECK(%s) failed at %s(%d)", predicate, file, line); } -void NotifyOtherImagesOfNormalEnd() { - // TODO -} -void NotifyOtherImagesOfFailImageStatement() { - // TODO -} -void NotifyOtherImagesOfErrorTermination() { - // TODO -} +// TODO: These will be defined in the coarray runtime library +void NotifyOtherImagesOfNormalEnd() {} +void NotifyOtherImagesOfFailImageStatement() {} +void NotifyOtherImagesOfErrorTermination() {} } diff --git a/runtime/terminator.h b/runtime/terminator.h index 5fe381e5167a..8cfc5cc8b123 100644 --- a/runtime/terminator.h +++ b/runtime/terminator.h @@ -21,16 +21,17 @@ namespace Fortran::runtime { class Terminator { public: Terminator() {} + Terminator(const Terminator &) = default; explicit Terminator(const char *sourceFileName, int sourceLine = 0) : sourceFileName_{sourceFileName}, sourceLine_{sourceLine} {} void SetLocation(const char *sourceFileName = nullptr, int sourceLine = 0) { sourceFileName_ = sourceFileName; sourceLine_ = sourceLine; } - [[noreturn]] void Crash(const char *message, ...); - [[noreturn]] void CrashArgs(const char *message, va_list &); + [[noreturn]] void Crash(const char *message, ...) const; + [[noreturn]] void CrashArgs(const char *message, va_list &) const; [[noreturn]] void CheckFailed( - const char *predicate, const char *file, int line); + const char *predicate, const char *file, int line) const; private: const char *sourceFileName_{nullptr}; @@ -47,4 +48,9 @@ void NotifyOtherImagesOfNormalEnd(); void NotifyOtherImagesOfFailImageStatement(); void NotifyOtherImagesOfErrorTermination(); } + +namespace Fortran::runtime::io { +void FlushOutputOnCrash(const Terminator &); +} + #endif // FORTRAN_RUNTIME_TERMINATOR_H_ diff --git a/runtime/tools.cpp b/runtime/tools.cpp index 43a0f68b0fe8..b254baf07b46 100644 --- a/runtime/tools.cpp +++ b/runtime/tools.cpp @@ -12,7 +12,7 @@ namespace Fortran::runtime { OwningPtr SaveDefaultCharacter( - const char *s, std::size_t length, Terminator &terminator) { + const char *s, std::size_t length, const Terminator &terminator) { if (s) { auto *p{static_cast(AllocateMemoryOrCrash(terminator, length + 1))}; std::memcpy(p, s, length); diff --git a/runtime/tools.h b/runtime/tools.h index d1b90b1ad3c9..99571782dc07 100644 --- a/runtime/tools.h +++ b/runtime/tools.h @@ -18,7 +18,8 @@ namespace Fortran::runtime { class Terminator; -OwningPtr SaveDefaultCharacter(const char *, std::size_t, Terminator &); +OwningPtr SaveDefaultCharacter( + const char *, std::size_t, const Terminator &); // For validating and recognizing default CHARACTER values in a // case-insensitive manner. Returns the zero-based index into the diff --git a/runtime/unit.cpp b/runtime/unit.cpp index f7a342ccbb73..277d36b39a08 100644 --- a/runtime/unit.cpp +++ b/runtime/unit.cpp @@ -10,55 +10,98 @@ #include "lock.h" #include "memory.h" #include "tools.h" -#include +#include #include namespace Fortran::runtime::io { static Lock mapLock; static Terminator mapTerminator; -static Map unitMap{MapAllocator{mapTerminator}}; +static Map unitMap{ + MapAllocator{mapTerminator}}; +static ExternalFileUnit *defaultOutput{nullptr}; + +void FlushOutputOnCrash(const Terminator &terminator) { + if (defaultOutput) { + IoErrorHandler handler{terminator}; + handler.HasIoStat(); // prevent nested crash if flush has error + defaultOutput->Flush(handler); + } +} -ExternalFile *ExternalFile::LookUp(int unit) { +ExternalFileUnit *ExternalFileUnit::LookUp(int unit) { CriticalSection criticalSection{mapLock}; auto iter{unitMap.find(unit)}; return iter == unitMap.end() ? nullptr : &iter->second; } -ExternalFile &ExternalFile::LookUpOrCrash(int unit, Terminator &terminator) { +ExternalFileUnit &ExternalFileUnit::LookUpOrCrash( + int unit, const Terminator &terminator) { CriticalSection criticalSection{mapLock}; - ExternalFile *file{LookUp(unit)}; + ExternalFileUnit *file{LookUp(unit)}; if (!file) { terminator.Crash("Not an open I/O unit number: %d", unit); } return *file; } -ExternalFile &ExternalFile::Create(int unit, Terminator &terminator) { +ExternalFileUnit &ExternalFileUnit::LookUpOrCreate(int unit, bool *wasExtant) { CriticalSection criticalSection{mapLock}; auto pair{unitMap.emplace(unit, unit)}; - if (!pair.second) { - terminator.Crash("Already opened I/O unit number: %d", unit); + if (wasExtant) { + *wasExtant = !pair.second; } return pair.first->second; } -void ExternalFile::CloseUnit(IoErrorHandler &handler) { +int ExternalFileUnit::NewUnit() { + CriticalSection criticalSection{mapLock}; + static int nextNewUnit{-1000}; // see 12.5.6.12 in Fortran 2018 + return --nextNewUnit; +} + +void ExternalFileUnit::OpenUnit(OpenStatus status, Position position, + OwningPtr &&newPath, std::size_t newPathLength, + IoErrorHandler &handler) { + CriticalSection criticalSection{lock()}; + if (IsOpen()) { + if (status == OpenStatus::Old && + (!newPath.get() || + (path() && pathLength() == newPathLength && + std::memcmp(path(), newPath.get(), newPathLength) == 0))) { + // OPEN of existing unit, STATUS='OLD', not new FILE= + newPath.reset(); + return; + } + // Otherwise, OPEN on open unit with new FILE= implies CLOSE + Flush(handler); + Close(CloseStatus::Keep, handler); + } + set_path(std::move(newPath), newPathLength); + Open(status, position, handler); +} + +void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { + { + CriticalSection criticalSection{lock()}; + Flush(handler); + Close(status, handler); + } CriticalSection criticalSection{mapLock}; - Flush(handler); auto iter{unitMap.find(unitNumber_)}; if (iter != unitMap.end()) { unitMap.erase(iter); } } -void ExternalFile::InitializePredefinedUnits(Terminator &terminator) { - ExternalFile &out{ExternalFile::Create(6, terminator)}; +void ExternalFileUnit::InitializePredefinedUnits() { + ExternalFileUnit &out{ExternalFileUnit::LookUpOrCreate(6)}; out.Predefine(1); out.set_mayRead(false); out.set_mayWrite(true); out.set_mayPosition(false); - ExternalFile &in{ExternalFile::Create(5, terminator)}; + defaultOutput = &out; + ExternalFileUnit &in{ExternalFileUnit::LookUpOrCreate(5)}; in.Predefine(0); in.set_mayRead(true); in.set_mayWrite(false); @@ -66,18 +109,20 @@ void ExternalFile::InitializePredefinedUnits(Terminator &terminator) { // TODO: Set UTF-8 mode from the environment } -void ExternalFile::CloseAll(IoErrorHandler &handler) { +void ExternalFileUnit::CloseAll(IoErrorHandler &handler) { CriticalSection criticalSection{mapLock}; + defaultOutput = nullptr; while (!unitMap.empty()) { auto &pair{*unitMap.begin()}; - pair.second.CloseUnit(handler); + pair.second.CloseUnit(CloseStatus::Keep, handler); } } -bool ExternalFile::SetPositionInRecord(std::int64_t n, IoErrorHandler &handler) { - n = std::max(std::int64_t{0}, n); +bool ExternalFileUnit::SetPositionInRecord( + std::int64_t n, IoErrorHandler &handler) { + n = std::max(0, n); bool ok{true}; - if (n > recordLength.value_or(n)) { + if (n > static_cast(recordLength.value_or(n))) { handler.SignalEor(); n = *recordLength; ok = false; @@ -85,7 +130,8 @@ bool ExternalFile::SetPositionInRecord(std::int64_t n, IoErrorHandler &handler) if (n > furthestPositionInRecord) { if (!isReading_ && ok) { WriteFrame(recordOffsetInFile, n, handler); - std::fill_n(Frame() + furthestPositionInRecord, n - furthestPositionInRecord, ' '); + std::fill_n(Frame() + furthestPositionInRecord, + n - furthestPositionInRecord, ' '); } furthestPositionInRecord = n; } @@ -93,8 +139,10 @@ bool ExternalFile::SetPositionInRecord(std::int64_t n, IoErrorHandler &handler) return ok; } -bool ExternalFile::Emit(const char *data, std::size_t bytes, IoErrorHandler &handler) { - auto furthestAfter{std::max(furthestPositionInRecord, positionInRecord + static_cast(bytes))}; +bool ExternalFileUnit::Emit( + const char *data, std::size_t bytes, IoErrorHandler &handler) { + auto furthestAfter{std::max(furthestPositionInRecord, + positionInRecord + static_cast(bytes))}; WriteFrame(recordOffsetInFile, furthestAfter, handler); std::memcpy(Frame() + positionInRecord, data, bytes); positionInRecord += bytes; @@ -102,36 +150,46 @@ bool ExternalFile::Emit(const char *data, std::size_t bytes, IoErrorHandler &han return true; } -void ExternalFile::SetLeftTabLimit() { +void ExternalFileUnit::SetLeftTabLimit() { leftTabLimit = furthestPositionInRecord; positionInRecord = furthestPositionInRecord; } -bool ExternalFile::NextOutputRecord(IoErrorHandler &handler) { +bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) { bool ok{true}; if (recordLength.has_value()) { // fill fixed-size record ok &= SetPositionInRecord(*recordLength, handler); - } else if (!unformatted && !isReading_) { + } else if (!isUnformatted && !isReading_) { ok &= SetPositionInRecord(furthestPositionInRecord, handler) && - Emit("\n", 1, handler); + Emit("\n", 1, handler); } recordOffsetInFile += furthestPositionInRecord; ++currentRecordNumber; positionInRecord = 0; - positionInRecord = furthestPositionInRecord = 0; + furthestPositionInRecord = 0; leftTabLimit.reset(); return ok; } -bool ExternalFile::HandleAbsolutePosition(std::int64_t n, IoErrorHandler &handler) { - return SetPositionInRecord(std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0), handler); +bool ExternalFileUnit::HandleAbsolutePosition( + std::int64_t n, IoErrorHandler &handler) { + return SetPositionInRecord( + std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0), handler); } -bool ExternalFile::HandleRelativePosition(std::int64_t n, IoErrorHandler &handler) { +bool ExternalFileUnit::HandleRelativePosition( + std::int64_t n, IoErrorHandler &handler) { return HandleAbsolutePosition(positionInRecord + n, handler); } -void ExternalFile::EndIoStatement() { +void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { + if (isTerminal()) { + Flush(handler); + } +} + +void ExternalFileUnit::EndIoStatement() { + io_.reset(); u_.emplace(); } } diff --git a/runtime/unit.h b/runtime/unit.h index a6b80b22587e..62f664b8f32a 100644 --- a/runtime/unit.h +++ b/runtime/unit.h @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -// Fortran I/O units +// Fortran external I/O units #ifndef FORTRAN_RUNTIME_IO_UNIT_H_ #define FORTRAN_RUNTIME_IO_UNIT_H_ #include "buffer.h" -#include "descriptor.h" +#include "connection.h" #include "file.h" #include "format.h" #include "io-error.h" @@ -27,87 +27,57 @@ namespace Fortran::runtime::io { -enum class Access { Sequential, Direct, Stream }; - -inline bool IsRecordFile(Access a) { return a != Access::Stream; } - -// These characteristics of a connection are immutable after being -// established in an OPEN statement. -struct ConnectionAttributes { - Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' - std::optional recordLength; // RECL= when fixed-length - bool unformatted{false}; // FORM='UNFORMATTED' - bool isUTF8{false}; // ENCODING='UTF-8' - bool asynchronousAllowed{false}; // ASYNCHRONOUS='YES' -}; - -struct ConnectionState : public ConnectionAttributes { - // Positions in a record file (sequential or direct, but not stream) - std::int64_t recordOffsetInFile{0}; - std::int64_t currentRecordNumber{1}; // 1 is first - std::int64_t positionInRecord{0}; // offset in current record - std::int64_t furthestPositionInRecord{0}; // max(positionInRecord) - std::optional leftTabLimit; // offset in current record - // nextRecord value captured after ENDFILE/REWIND/BACKSPACE statement - // on a sequential access file - std::optional endfileRecordNumber; - // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT - MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP -}; - -class InternalUnit : public ConnectionState, public IoErrorHandler { +class ExternalFileUnit : public ConnectionState, + public OpenFile, + public FileFrame { public: - InternalUnit(Descriptor &, const char *sourceFile, int sourceLine) - : IoErrorHandler{sourceFile, sourceLine} { -// TODO pmk descriptor_.Establish(...); - descriptor_.GetLowerBounds(at_); - recordLength = descriptor_.ElementBytes(); - endfileRecordNumber = descriptor_.Elements(); - } - ~InternalUnit() { - if (!doNotFree_) { - std::free(this); - } - } + explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {} + int unitNumber() const { return unitNumber_; } -private: - bool doNotFree_{false}; - Descriptor descriptor_; - SubscriptValue at_[maxRank]; -}; - -class ExternalFile : public ConnectionState, // TODO: privatize these - public OpenFile, - public FileFrame { -public: - explicit ExternalFile(int unitNumber) : unitNumber_{unitNumber} {} - static ExternalFile *LookUp(int unit); - static ExternalFile &LookUpOrCrash(int unit, Terminator &); - static ExternalFile &Create(int unit, Terminator &); - static void InitializePredefinedUnits(Terminator &); + static ExternalFileUnit *LookUp(int unit); + static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &); + static ExternalFileUnit &LookUpOrCreate(int unit, bool *wasExtant = nullptr); + static int NewUnit(); + static void InitializePredefinedUnits(); static void CloseAll(IoErrorHandler &); - void CloseUnit(IoErrorHandler &); + void OpenUnit(OpenStatus, Position, OwningPtr &&path, + std::size_t pathLength, IoErrorHandler &); + void CloseUnit(CloseStatus, IoErrorHandler &); - // TODO: accessors & mutators for many OPEN() specifiers - template A &BeginIoStatement(X&&... xs) { - // TODO: lock_.Take() here, and keep it until EndIoStatement()? + template + IoStatementState &BeginIoStatement(X &&... xs) { + // TODO: lock().Take() here, and keep it until EndIoStatement()? // Nested I/O from derived types wouldn't work, though. - return u_.emplace(std::forward(xs)...); + A &state{u_.emplace(std::forward(xs)...)}; + if constexpr (!std::is_same_v) { + state.mutableModes() = ConnectionState::modes; + } + io_.emplace(state); + return *io_; } - void EndIoStatement(); - bool SetPositionInRecord(std::int64_t, IoErrorHandler &); bool Emit(const char *, std::size_t bytes, IoErrorHandler &); void SetLeftTabLimit(); - bool NextOutputRecord(IoErrorHandler &); + bool AdvanceRecord(IoErrorHandler &); bool HandleAbsolutePosition(std::int64_t, IoErrorHandler &); bool HandleRelativePosition(std::int64_t, IoErrorHandler &); + + void FlushIfTerminal(IoErrorHandler &); + void EndIoStatement(); + private: + bool SetPositionInRecord(std::int64_t, IoErrorHandler &); + int unitNumber_{-1}; - Lock lock_; bool isReading_{false}; - std::variant> u_; + // When an I/O statement is in progress on this unit, holds its state. + std::variant, + ExternalListIoStatementState, UnformattedIoStatementState> + u_; + // Points to the active alternative, if any, in u_, for use as a Cookie + std::optional io_; }; } diff --git a/test/evaluate/real.cpp b/test/evaluate/real.cpp index 919bc3ca87c7..85101e513726 100644 --- a/test/evaluate/real.cpp +++ b/test/evaluate/real.cpp @@ -91,7 +91,7 @@ template void basicTests(int rm, Rounding rounding) { TEST(nan.Compare(zero) == Relation::Unordered)(desc); TEST(nan.Compare(minusZero) == Relation::Unordered)(desc); TEST(nan.Compare(nan) == Relation::Unordered)(desc); - int significandBits{R::precision - R::implicitMSB}; + int significandBits{R::binaryPrecision - R::isImplicitMSB}; int exponentBits{R::bits - significandBits - 1}; std::uint64_t maxExponent{(std::uint64_t{1} << exponentBits) - 1}; MATCH(nan.Exponent(), maxExponent)(desc); diff --git a/test/runtime/external-hello.cpp b/test/runtime/external-hello.cpp index af7151f6c44e..400d345e1b39 100644 --- a/test/runtime/external-hello.cpp +++ b/test/runtime/external-hello.cpp @@ -6,9 +6,20 @@ using namespace Fortran::runtime::io; int main(int argc, const char *argv[], const char *envp[]) { - static const char *format{"(12HHELLO, WORLD)"}; RTNAME(ProgramStart)(argc, argv, envp); - auto *io{IONAME(BeginExternalFormattedOutput)(format, std::strlen(format))}; + auto *io{IONAME(BeginExternalListOutput)()}; + const char str[]{"Hello, world!"}; + IONAME(OutputAscii)(io, str, std::strlen(str)); + IONAME(OutputInteger64)(io, 678); + IONAME(OutputReal64)(io, 0.0); + IONAME(OutputReal64)(io, 2.0 / 3.0); + IONAME(OutputReal64)(io, 1.0e99); + IONAME(OutputReal64)(io, 1.0 / 0.0); + IONAME(OutputReal64)(io, -1.0 / 0.0); + IONAME(OutputReal64)(io, 0.0 / 0.0); + IONAME(OutputComplex64)(io, 123.0, -234.0); + IONAME(OutputLogical)(io, false); + IONAME(OutputLogical)(io, true); IONAME(EndIoStatement)(io); RTNAME(ProgramEndStatement)(); return 0; diff --git a/test/runtime/format.cpp b/test/runtime/format.cpp index 31e3261d8f88..05ec9d3e280b 100644 --- a/test/runtime/format.cpp +++ b/test/runtime/format.cpp @@ -1,37 +1,43 @@ // Tests basic FORMAT string traversal -#include "../runtime/format.h" +#include "../runtime/format-implementation.h" #include "../runtime/terminator.h" #include #include #include -#include #include +#include using namespace Fortran::runtime; using namespace Fortran::runtime::io; using namespace std::literals::string_literals; static int failures{0}; -using Results = std::list; +using Results = std::vector; -// Test harness context for format control -struct TestFormatContext : virtual public Terminator, public FormatContext { +// A test harness context for testing FormatControl +class TestFormatContext : public Terminator { +public: + using CharType = char; TestFormatContext() : Terminator{"format.cpp", 1} {} bool Emit(const char *, std::size_t); bool Emit(const char16_t *, std::size_t); bool Emit(const char32_t *, std::size_t); - bool HandleSlash(int = 1); + bool AdvanceRecord(int = 1); bool HandleRelativePosition(std::int64_t); bool HandleAbsolutePosition(std::int64_t); void Report(const DataEdit &); void Check(Results &); Results results; + MutableModes &mutableModes() { return mutableModes_; } + +private: + MutableModes mutableModes_; }; // Override the runtime's Crash() for testing purposes [[noreturn]] void Fortran::runtime::Terminator::Crash( - const char *message, ...) { + const char *message, ...) const { std::va_list ap; va_start(ap, message); char buffer[1000]; @@ -54,7 +60,7 @@ bool TestFormatContext::Emit(const char32_t *, std::size_t) { return false; } -bool TestFormatContext::HandleSlash(int n) { +bool TestFormatContext::AdvanceRecord(int n) { while (n-- > 0) { results.emplace_back("/"); } @@ -115,12 +121,11 @@ void TestFormatContext::Check(Results &expect) { static void Test(int n, const char *format, Results &&expect, int repeat = 1) { TestFormatContext context; - FormatControl control{context, format, std::strlen(format)}; + FormatControl control{ + context, format, std::strlen(format)}; try { for (int j{0}; j < n; ++j) { - DataEdit edit; - control.GetNext(context, edit, repeat); - context.Report(edit); + context.Report(control.GetNextDataEdit(context, repeat)); } control.FinishOutput(context); } catch (const std::string &crash) { diff --git a/test/runtime/hello.cpp b/test/runtime/hello.cpp index 86354a36de5e..4bb65acd565e 100644 --- a/test/runtime/hello.cpp +++ b/test/runtime/hello.cpp @@ -1,9 +1,11 @@ // Basic sanity tests of I/O API; exhaustive testing will be done in Fortran +#include "../../runtime/descriptor.h" #include "../../runtime/io-api.h" #include #include +using namespace Fortran::runtime; using namespace Fortran::runtime::io; static int failures{0}; @@ -28,7 +30,7 @@ static void hello() { IONAME(OutputInteger64)(cookie, 0xfeedface); IONAME(OutputLogical)(cookie, true); if (auto status{IONAME(EndIoStatement)(cookie)}) { - std::cerr << '\'' << format << "' failed, status " + std::cerr << "hello: '" << format << "' failed, status " << static_cast(status) << '\n'; ++failures; } else { @@ -37,6 +39,49 @@ static void hello() { } } +static void multiline() { + char buffer[4][32]; + StaticDescriptor<1> staticDescriptor[2]; + Descriptor &whole{staticDescriptor[0].descriptor()}; + SubscriptValue extent[]{4}; + whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, + CFI_attribute_pointer); + // whole.Dump(std::cout); + whole.Check(); + Descriptor §ion{staticDescriptor[1].descriptor()}; + SubscriptValue lowers[]{0}, uppers[]{3}, strides[]{1}; + section.Establish(whole.type(), whole.ElementBytes(), nullptr, 1, extent, + CFI_attribute_pointer); + // section.Dump(std::cout); + section.Check(); + if (auto error{ + CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}) { + std::cerr << "multiline: CFI_section failed: " << error << '\n'; + ++failures; + return; + } + section.Dump(std::cout); + section.Check(); + const char *format{"('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,25X,'done')"}; + auto cookie{IONAME(BeginInternalArrayFormattedOutput)( + section, format, std::strlen(format))}; + IONAME(OutputAscii)(cookie, "WORLD", 5); + IONAME(OutputAscii)(cookie, "HELLO", 5); + IONAME(OutputInteger64)(cookie, 789); + if (auto status{IONAME(EndIoStatement)(cookie)}) { + std::cerr << "multiline: '" << format << "' failed, status " + << static_cast(status) << '\n'; + ++failures; + } else { + test(format, + ">HELLO, WORLD <" + " " + "789 done" + " ", + std::string{buffer[0], sizeof buffer}); + } +} + static void realTest(const char *format, double x, const char *expect) { char buffer[800]; auto cookie{IONAME(BeginInternalFormattedOutput)( @@ -53,6 +98,7 @@ static void realTest(const char *format, double x, const char *expect) { int main() { hello(); + multiline(); static const char *zeroes[][2]{ {"(E32.17,';')", " 0.00000000000000000E+00;"}, From 403faf847d0beacac26421bc82318aaef0e973c9 Mon Sep 17 00:00:00 2001 From: David Truby Date: Fri, 14 Feb 2020 04:46:29 +0000 Subject: [PATCH 027/345] Add zlib to drone files so that linking LLVM works. (#983) --- .drone.star | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index f08549f289d1..5911a5b15bc4 100644 --- a/.drone.star +++ b/.drone.star @@ -7,7 +7,7 @@ def clang(arch): "name": "test", "image": "ubuntu", "commands": [ - "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-dev libc++-8-dev libc++abi-8-dev", + "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-dev libc++-8-dev libc++abi-8-dev libz-dev", "mkdir build && cd build", 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', "ninja -j8", @@ -27,7 +27,7 @@ def gcc(arch): "name": "test", "image": "gcc", "commands": [ - "apt-get update && apt-get install -y cmake ninja-build llvm-dev", + "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev", "mkdir build && cd build", 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', "ninja -j8", From 5dd0b0bbe811a908374b2907bb38c75ca76127d2 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Tue, 11 Feb 2020 12:14:04 -0800 Subject: [PATCH 028/345] Semantic check for C708 An entity declared with the CLASS keyword shall be a dummy argument or have the ALLOCATABLE or POINTER attribute. Implementing this check revealed a problem in the test resolve44.cpp. It also showed that we were doing semantic checking on the entities created by the compiler for LOCAL and LOCAL_INIT locality-specs. So I changed the creation of symbols associated with LOCAL and LOCAL_INIT locality-specs to be host associated with the outer symbol rather than new object entities. In the process, I also changed things so that the `parser::Name` associated with the newly created symbols was set to the symbol rather than being set to nullptr. --- lib/semantics/check-declarations.cpp | 8 ++++++++ lib/semantics/resolve-names.cpp | 8 ++------ test/semantics/CMakeLists.txt | 1 + test/semantics/allocate01.f90 | 3 ++- test/semantics/allocate09.f90 | 8 +++++--- test/semantics/resolve44.f90 | 3 +++ test/semantics/resolve70.f90 | 4 ++-- test/semantics/resolve71.f90 | 23 +++++++++++++++++++++++ test/semantics/symbol09.f90 | 4 ++-- 9 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 test/semantics/resolve71.f90 diff --git a/lib/semantics/check-declarations.cpp b/lib/semantics/check-declarations.cpp index 7cd81c91c348..57f4d788bcc7 100644 --- a/lib/semantics/check-declarations.cpp +++ b/lib/semantics/check-declarations.cpp @@ -371,6 +371,14 @@ void CheckHelper::CheckObjectEntity( } } } + if (const DeclTypeSpec * type{details.type()}) { // C708 + if (type->IsPolymorphic() && + !(IsAllocatableOrPointer(symbol) || symbol.IsDummy())) { + messages_.Say("CLASS entity '%s' must be a dummy argument or have " + "ALLOCATABLE or POINTER attribute"_err_en_US, + symbol.name()); + } + } } // The six different kinds of array-specs: diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index f075fa30a4b8..61e9ed02711d 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -4314,12 +4314,8 @@ Symbol *DeclarationVisitor::DeclareLocalEntity(const parser::Name &name) { if (!PassesLocalityChecks(name, prev)) { return nullptr; } - name.symbol = nullptr; - Symbol &symbol{DeclareEntity(name, {})}; - if (auto *type{prev.GetType()}) { - symbol.SetType(*type); - symbol.set(Symbol::Flag::Implicit, prev.test(Symbol::Flag::Implicit)); - } + Symbol &symbol{MakeSymbol(name, HostAssocDetails{prev})}; + name.symbol = &symbol; return &symbol; } diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index 4873a0f50ebd..c29282442cd9 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -101,6 +101,7 @@ set(ERROR_TESTS resolve68.f90 resolve69.f90 resolve70.f90 + resolve71.f90 stop01.f90 structconst01.f90 structconst02.f90 diff --git a/test/semantics/allocate01.f90 b/test/semantics/allocate01.f90 index 137286e47cf6..6944e2b30090 100644 --- a/test/semantics/allocate01.f90 +++ b/test/semantics/allocate01.f90 @@ -16,7 +16,8 @@ module m contains function mfoo(x) - class(a_type) :: foo, x + class(a_type) :: x + class(a_type), allocatable :: foo foo = x end function subroutine mbar(x) diff --git a/test/semantics/allocate09.f90 b/test/semantics/allocate09.f90 index 49736f905743..e47cd8134b49 100644 --- a/test/semantics/allocate09.f90 +++ b/test/semantics/allocate09.f90 @@ -24,7 +24,7 @@ subroutine C946(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) real(kind=4) srcx, srcx_array(10) real(kind=8) srcx8, srcx8_array(10) - class(WithParam(4, 2)) src_a_4_2 + class(WithParam(4, 2)), allocatable :: src_a_4_2 type(WithParam(8, 2)) src_a_8_2 class(WithParam(4, :)), allocatable :: src_a_4_def class(WithParam(8, :)), allocatable :: src_a_8_def @@ -33,8 +33,10 @@ subroutine C946(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) type(WithParamExtent(8, 2, 8, 3)) src_b_8_2_8_3 class(WithParamExtent(8, :, 8, 3)), allocatable :: src_b_8_def_8_3 type(WithParamExtent2(k1=4, l1=5, k2=5, l2=6, l3=8 )) src_c_4_5_5_6_8_8 - class(WithParamExtent2(k1=4, l1=2, k2=5, l2=6, k3=5, l3=8)) src_c_4_2_5_6_5_8 - class(WithParamExtent2(k2=5, l2=6, k3=5, l3=8)) src_c_1_2_5_6_5_8 + class(WithParamExtent2(k1=4, l1=2, k2=5, l2=6, k3=5, l3=8)), & + allocatable :: src_c_4_2_5_6_5_8 + class(WithParamExtent2(k2=5, l2=6, k3=5, l3=8)), & + allocatable :: src_c_1_2_5_6_5_8 type(WithParamExtent2(k1=5, l1=5, k2=5, l2=6, l3=8 )) src_c_5_5_5_6_8_8 type(WithParamExtent2(k1=5, l1=2, k2=5, l2=6, k3=5, l3=8)) src_c_5_2_5_6_5_8 diff --git a/test/semantics/resolve44.f90 b/test/semantics/resolve44.f90 index af6a40dff6b4..f6e7a89ba5c3 100644 --- a/test/semantics/resolve44.f90 +++ b/test/semantics/resolve44.f90 @@ -7,6 +7,7 @@ program main type(recursive1), pointer :: ok1 type(recursive1), allocatable :: ok2 !ERROR: Recursive use of the derived type requires POINTER or ALLOCATABLE + !ERROR: CLASS entity 'bad2' must be a dummy argument or have ALLOCATABLE or POINTER attribute class(recursive1) :: bad2 class(recursive1), pointer :: ok3 class(recursive1), allocatable :: ok4 @@ -19,6 +20,7 @@ program main type(recursive2(kind,len)), pointer :: ok1 type(recursive2(kind,len)), allocatable :: ok2 !ERROR: Recursive use of the derived type requires POINTER or ALLOCATABLE + !ERROR: CLASS entity 'bad2' must be a dummy argument or have ALLOCATABLE or POINTER attribute class(recursive2(kind,len)) :: bad2 class(recursive2(kind,len)), pointer :: ok3 class(recursive2(kind,len)), allocatable :: ok4 @@ -31,6 +33,7 @@ program main type(recursive3), pointer :: ok1 type(recursive3), allocatable :: ok2 !ERROR: Recursive use of the derived type requires POINTER or ALLOCATABLE + !ERROR: CLASS entity 'bad2' must be a dummy argument or have ALLOCATABLE or POINTER attribute class(recursive3) :: bad2 class(recursive3), pointer :: ok3 class(recursive3), allocatable :: ok4 diff --git a/test/semantics/resolve70.f90 b/test/semantics/resolve70.f90 index b771fd0677be..8824ea4249af 100644 --- a/test/semantics/resolve70.f90 +++ b/test/semantics/resolve70.f90 @@ -51,8 +51,8 @@ subroutine s1() end type ! This one's OK - class(extensible) :: y + class(extensible), allocatable :: y !ERROR: Non-extensible derived type 'inextensible' may not be used with CLASS keyword - class(inextensible) :: x + class(inextensible), allocatable :: x end subroutine s1 diff --git a/test/semantics/resolve71.f90 b/test/semantics/resolve71.f90 new file mode 100644 index 000000000000..d570233d4633 --- /dev/null +++ b/test/semantics/resolve71.f90 @@ -0,0 +1,23 @@ +! C708 An entity declared with the CLASS keyword shall be a dummy argument +! or have the ALLOCATABLE or POINTER attribute. +subroutine s() + type :: parentType + end type + + class(parentType), pointer :: pvar + class(parentType), allocatable :: avar + class(*), allocatable :: starAllocatableVar + class(*), pointer :: starPointerVar + !ERROR: CLASS entity 'barevar' must be a dummy argument or have ALLOCATABLE or POINTER attribute + class(parentType) :: bareVar + !ERROR: CLASS entity 'starvar' must be a dummy argument or have ALLOCATABLE or POINTER attribute + class(*) :: starVar + + contains + subroutine inner(arg1, arg2, arg3, arg4, arg5) + class (parenttype) :: arg1, arg3 + type(parentType) :: arg2 + class (parenttype), pointer :: arg4 + class (parenttype), allocatable :: arg5 + end subroutine inner +end subroutine s diff --git a/test/semantics/symbol09.f90 b/test/semantics/symbol09.f90 index 480f7198c8b0..8dca1332a538 100644 --- a/test/semantics/symbol09.f90 +++ b/test/semantics/symbol09.f90 @@ -104,8 +104,8 @@ subroutine s6 !DEF: /s6/a ObjectEntity INTEGER(4) integer :: a(5) = 1 !DEF: /s6/Block1/i ObjectEntity INTEGER(4) - !DEF: /s6/Block1/j (LocalityLocal) ObjectEntity INTEGER(8) - !DEF: /s6/Block1/k (Implicit, LocalityLocalInit) ObjectEntity INTEGER(4) + !DEF: /s6/Block1/j (LocalityLocal) HostAssoc INTEGER(8) + !DEF: /s6/Block1/k (LocalityLocalInit) HostAssoc INTEGER(4) !DEF: /s6/Block1/a (LocalityShared) HostAssoc INTEGER(4) do concurrent(integer::i=1:5)local(j)local_init(k)shared(a) !REF: /s6/Block1/a From 00d8d5121cdaed09e584a0aea7d59baf322bfaa3 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Tue, 21 Jan 2020 07:41:52 -0800 Subject: [PATCH 029/345] Add clang-format files for FIR source (LLVM style) Note: This commit does not reflect an actual work log, it is a feature based split of the changes done in the FIR experimental branch. The related work log can be found in the commits between: https://github.com/schweitzpgi/f18/commit/8c320e3bf2c3e9cdac66c81db3bf4634bf972e1d and: https://github.com/schweitzpgi/f18/commit/9b9ea05f9a75608c7bb5372c56bf7b9363569a69 --- include/fir/.clang-format | 2 ++ include/flang/lower/.clang-format | 2 ++ include/flang/optimizer/.clang-format | 2 ++ lib/fir/.clang-format | 2 ++ lib/lower/.clang-format | 2 ++ lib/optimizer/.clang-format | 2 ++ tools/bbc/.clang-format | 2 ++ tools/tco/.clang-format | 2 ++ 8 files changed, 16 insertions(+) create mode 100644 include/fir/.clang-format create mode 100644 include/flang/lower/.clang-format create mode 100644 include/flang/optimizer/.clang-format create mode 100644 lib/fir/.clang-format create mode 100644 lib/lower/.clang-format create mode 100644 lib/optimizer/.clang-format create mode 100644 tools/bbc/.clang-format create mode 100644 tools/tco/.clang-format diff --git a/include/fir/.clang-format b/include/fir/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/lower/.clang-format b/include/flang/lower/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/flang/lower/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/optimizer/.clang-format b/include/flang/optimizer/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/flang/optimizer/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/fir/.clang-format b/lib/fir/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/lower/.clang-format b/lib/lower/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/lower/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/optimizer/.clang-format b/lib/optimizer/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/optimizer/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/tools/bbc/.clang-format b/tools/bbc/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/tools/bbc/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/tools/tco/.clang-format b/tools/tco/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/tools/tco/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes From edb0943bca4b81689f320bda341040bf255d6e2e Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Tue, 28 Jan 2020 04:58:30 -0800 Subject: [PATCH 030/345] Add Pre-FIR Tree structure to help lowering the parse-tree The Pre-FIR Tree structure is a transient data structure that is meant to be built from the parse tree just before lowering to FIR and that will be deleted just afterwards. It is not meant to perfrom optimization analysis and transformations. It only provides temporary information, such as label target information or parse tree parent nodes, that is meant to be used to lower the parse tree structure into FIR operations. A PFTBuilder class builds the Pre-Fir Tree from the parse-tree. A pretty printer is available to visualize this data structure. - Lit tests are added to: 1. that the PFT tree structure is as expected 2. that the PFT captures all intented nodes - Cmake changes: Prevent warnings inisde LLVM headers when compiling flang The issue is that some LLVM headers define functions where the usage of the parameters depend on environment ifdef. See for instance Size in: https://github.com/llvm/llvm-project/blob/5f940220bf9438e95ffa4a627ac1591be1e1ba6e/llvm/include/llvm/Support/Compiler.h#L574 Because flang is build with -Werror and -Wunused-parameter is default in clang, this may breaks build in some environments (like with clang9 on macos). A solution would be to add -Wno-unused-parameter to flang CmakLists.txt, but it is wished to keep this warning on flang sources for quality purposes. Fixing LLVM headers is not an easy task and `[[maybe_unused]]` is C++17 and cannot be used yet in LLVM headers. Hence, this fix simply silence warnings coming from LLVM headers by telling CMake they are to be considered as if they were system headers. - drone.io changes: remove llvm 6.0 from clang config in drone.io and link flang with libstdc++ instead of libc++ llvm-dev resolved to llvm-6.0 in clang builds on drone.io. llvm 6.0 too old. LLVM packages are linked with libstdc++ standard library whereas libc++ was used for flang. This caused link time failure when building clang. Change frone.io to build flang with libc++. Note: This commit does not reflect an actual work log, it is a feature based split of the changes done in the FIR experimental branch. The related work log can be found in the commits between: 864898cbe509d032abfe1172ec367dbd3dd92bc1 and 137c23da9c64cf90584cf81fd646053a69e91f63 Other changes come from https://github.com/flang-compiler/f18/pull/959 review. --- .drone.star | 4 +- CMakeLists.txt | 9 +- include/flang/lower/PFTBuilder.h | 394 ++++++++++++++ include/flang/parser/dump-parse-tree.h | 6 +- lib/CMakeLists.txt | 1 + lib/lower/CMakeLists.txt | 13 + lib/lower/PFTBuilder.cpp | 697 +++++++++++++++++++++++++ test-lit/CMakeLists.txt | 2 + test-lit/lit.cfg.py | 5 +- test-lit/lit.site.cfg.py.in | 1 + test-lit/lower/pre-fir-tree01.f90 | 130 +++++ test-lit/lower/pre-fir-tree02.f90 | 334 ++++++++++++ test-lit/lower/pre-fir-tree03.f90 | 60 +++ test-lit/lower/pre-fir-tree04.f90 | 70 +++ tools/f18/CMakeLists.txt | 2 + tools/f18/f18.cpp | 14 + 16 files changed, 1735 insertions(+), 7 deletions(-) create mode 100644 include/flang/lower/PFTBuilder.h create mode 100644 lib/lower/CMakeLists.txt create mode 100644 lib/lower/PFTBuilder.cpp create mode 100644 test-lit/lower/pre-fir-tree01.f90 create mode 100644 test-lit/lower/pre-fir-tree02.f90 create mode 100644 test-lit/lower/pre-fir-tree03.f90 create mode 100644 test-lit/lower/pre-fir-tree04.f90 diff --git a/.drone.star b/.drone.star index 5911a5b15bc4..47dfca7c2460 100644 --- a/.drone.star +++ b/.drone.star @@ -7,9 +7,9 @@ def clang(arch): "name": "test", "image": "ubuntu", "commands": [ - "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-dev libc++-8-dev libc++abi-8-dev libz-dev", + "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev", "mkdir build && cd build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', "ninja -j8", "ctest --output-on-failure -j24", ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 999fd1f252c4..2b7a823f7ec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,14 @@ include(AddLLVM) # https://stackoverflow.com/questions/41924375/llvm-how-to-specify-all-link-libraries-as-input-to-llvm-map-components-to-libna # https://stackoverflow.com/questions/33948633/how-do-i-link-when-building-with-llvm-libraries -include_directories(${LLVM_INCLUDE_DIRS}) +# Add LLVM include files as if they were SYSTEM because there are complex unused +# parameter issues that may or may not appear depending on the environments and +# compilers (ifdefs are involved). This allows warnings from LLVM headers to be +# ignored while keeping -Wunused-parameter a fatal error inside f18 code base. +# This may have to be fine-tuned if flang headers are consider part of this +# LLVM_INCLUDE_DIRS when merging in the monorepo (Warning from flang headers +# should not be suppressed). +include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) # LLVM_LIT_EXTERNAL store in cache so it could be used by AddLLVM.cmake diff --git a/include/flang/lower/PFTBuilder.h b/include/flang/lower/PFTBuilder.h new file mode 100644 index 000000000000..0b1345ef8bee --- /dev/null +++ b/include/flang/lower/PFTBuilder.h @@ -0,0 +1,394 @@ +//===-- include/flang/lower/PFTBuilder.h ------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_LOWER_PFT_BUILDER_H_ +#define FORTRAN_LOWER_PFT_BUILDER_H_ + +#include "flang/common/template.h" +#include "flang/parser/parse-tree.h" +#include "llvm/Support/raw_ostream.h" +#include + +/// Build a light-weight tree over the parse-tree to help with lowering to FIR. +/// It is named Pre-FIR Tree (PFT) to underline it has no other usage than +/// helping lowering to FIR. +/// The PFT will capture pointers back into the parse tree, so the parse tree +/// data structure may not be changed between the construction of the +/// PFT and all of its uses. +/// +/// The PFT captures a structured view of the program. The program is a list of +/// units. Function like units will contain lists of evaluations. Evaluations +/// are either statements or constructs, where a construct contains a list of +/// evaluations. The resulting PFT structure can then be used to create FIR. + +namespace Fortran::lower { +namespace pft { + +struct Evaluation; +struct Program; +struct ModuleLikeUnit; +struct FunctionLikeUnit; + +// TODO: A collection of Evaluations can obviously be any of the container +// types; leaving this as a std::list _for now_ because we reserve the right to +// insert PFT nodes in any order in O(1) time. +using EvaluationCollection = std::list; + +struct ParentType { + template + ParentType(A &parent) : p{&parent} {} + const std::variant + p; +}; + +/// Flags to describe the impact of parse-trees nodes on the program +/// control flow. These annotations to parse-tree nodes are later used to +/// build the control flow graph when lowering to FIR. +enum class CFGAnnotation { + None, // Node does not impact control flow. + Goto, // Node acts like a goto on the control flow. + CondGoto, // Node acts like a conditional goto on the control flow. + IndGoto, // Node acts like an indirect goto on the control flow. + IoSwitch, // Node is an IO statement with ERR, END, or EOR specifier. + Switch, // Node acts like a switch on the control flow. + Iterative, // Node creates iterations in the control flow. + FirStructuredOp, // Node is a structured loop. + Return, // Node triggers a return from the current procedure. + Terminate // Node terminates the program. +}; + +/// Compiler-generated jump +/// +/// This is used to convert implicit control-flow edges to explicit form in the +/// decorated PFT +struct CGJump { + CGJump(Evaluation &to) : target{to} {} + Evaluation ⌖ +}; + +/// Classify the parse-tree nodes from ExecutablePartConstruct + +using ActionStmts = std::tuple< + parser::AllocateStmt, parser::AssignmentStmt, parser::BackspaceStmt, + parser::CallStmt, parser::CloseStmt, parser::ContinueStmt, + parser::CycleStmt, parser::DeallocateStmt, parser::EndfileStmt, + parser::EventPostStmt, parser::EventWaitStmt, parser::ExitStmt, + parser::FailImageStmt, parser::FlushStmt, parser::FormTeamStmt, + parser::GotoStmt, parser::IfStmt, parser::InquireStmt, parser::LockStmt, + parser::NullifyStmt, parser::OpenStmt, parser::PointerAssignmentStmt, + parser::PrintStmt, parser::ReadStmt, parser::ReturnStmt, parser::RewindStmt, + parser::StopStmt, parser::SyncAllStmt, parser::SyncImagesStmt, + parser::SyncMemoryStmt, parser::SyncTeamStmt, parser::UnlockStmt, + parser::WaitStmt, parser::WhereStmt, parser::WriteStmt, + parser::ComputedGotoStmt, parser::ForallStmt, parser::ArithmeticIfStmt, + parser::AssignStmt, parser::AssignedGotoStmt, parser::PauseStmt>; + +using OtherStmts = std::tuple; + +using Constructs = + std::tuple; + +using ConstructStmts = std::tuple< + parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, + parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, + parser::EndSelectStmt, parser::ChangeTeamStmt, parser::EndChangeTeamStmt, + parser::CriticalStmt, parser::EndCriticalStmt, parser::NonLabelDoStmt, + parser::EndDoStmt, parser::IfThenStmt, parser::ElseIfStmt, parser::ElseStmt, + parser::EndIfStmt, parser::SelectRankStmt, parser::SelectRankCaseStmt, + parser::SelectTypeStmt, parser::TypeGuardStmt, parser::WhereConstructStmt, + parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, + parser::ForallConstructStmt, parser::EndForallStmt>; + +template +constexpr static bool isActionStmt{common::HasMember}; + +template +constexpr static bool isConstruct{common::HasMember}; + +template +constexpr static bool isConstructStmt{common::HasMember}; + +template +constexpr static bool isOtherStmt{common::HasMember}; + +template +constexpr static bool isGenerated{std::is_same_v}; + +template +constexpr static bool isFunctionLike{common::HasMember< + A, std::tuple>}; + +/// Function-like units can contains lists of evaluations. These can be +/// (simple) statements or constructs, where a construct contains its own +/// evaluations. +struct Evaluation { + using EvalTuple = common::CombineTuples; + + /// Hide non-nullable pointers to the parse-tree node. + template + using MakeRefType = const A *const; + using EvalVariant = + common::CombineVariants, + std::variant>; + template + constexpr auto visit(A visitor) const { + return std::visit(common::visitors{ + [&](const auto *p) { return visitor(*p); }, + [&](auto &r) { return visitor(r); }, + }, + u); + } + template + constexpr const A *getIf() const { + if constexpr (!std::is_same_v) { + if (auto *ptr{std::get_if>(&u)}) { + return *ptr; + } + } else { + return std::get_if(&u); + } + return nullptr; + } + template + constexpr bool isA() const { + if constexpr (!std::is_same_v) { + return std::holds_alternative>(u); + } + return std::holds_alternative(u); + } + + Evaluation() = delete; + Evaluation(const Evaluation &) = delete; + Evaluation(Evaluation &&) = default; + + /// General ctor + template + Evaluation(const A &a, const ParentType &p, const parser::CharBlock &pos, + const std::optional &lab) + : u{&a}, parent{p}, pos{pos}, lab{lab} {} + + /// Compiler-generated jump + Evaluation(const CGJump &jump, const ParentType &p) + : u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {} + + /// Construct ctor + template + Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} { + static_assert(pft::isConstruct, "must be a construct"); + } + + constexpr bool isActionOrGenerated() const { + return visit(common::visitors{ + [](auto &r) { + using T = std::decay_t; + return isActionStmt || isGenerated; + }, + }); + } + + constexpr bool isStmt() const { + return visit(common::visitors{ + [](auto &r) { + using T = std::decay_t; + static constexpr bool isStmt{isActionStmt || isOtherStmt || + isConstructStmt}; + static_assert(!(isStmt && pft::isConstruct), + "statement classification is inconsistent"); + return isStmt; + }, + }); + } + constexpr bool isConstruct() const { return !isStmt(); } + + /// Set the type of originating control flow type for this evaluation. + void setCFG(CFGAnnotation a, Evaluation *cstr) { + cfg = a; + setBranches(cstr); + } + + /// Is this evaluation a control-flow origin? (The PFT must be annotated) + bool isControlOrigin() const { return cfg != CFGAnnotation::None; } + + /// Is this evaluation a control-flow target? (The PFT must be annotated) + bool isControlTarget() const { return isTarget; } + + /// Set the containsBranches flag iff this evaluation (a construct) contains + /// control flow + void setBranches() { containsBranches = true; } + + EvaluationCollection *getConstructEvals() { + auto *evals{subs.get()}; + if (isStmt() && !evals) { + return nullptr; + } + if (isConstruct() && evals) { + return evals; + } + llvm_unreachable("evaluation subs is inconsistent"); + return nullptr; + } + + /// Set that the construct `cstr` (if not a nullptr) has branches. + static void setBranches(Evaluation *cstr) { + if (cstr) + cstr->setBranches(); + } + + EvalVariant u; + ParentType parent; + parser::CharBlock pos; + std::optional lab; + std::unique_ptr subs; // construct sub-statements + CFGAnnotation cfg{CFGAnnotation::None}; + bool isTarget{false}; // this evaluation is a control target + bool containsBranches{false}; // construct contains branches +}; + +/// A program is a list of program units. +/// These units can be function like, module like, or block data +struct ProgramUnit { + template + ProgramUnit(const A &ptr, const ParentType &parent) + : p{&ptr}, parent{parent} {} + ProgramUnit(ProgramUnit &&) = default; + ProgramUnit(const ProgramUnit &) = delete; + + const std::variant< + const parser::MainProgram *, const parser::FunctionSubprogram *, + const parser::SubroutineSubprogram *, const parser::Module *, + const parser::Submodule *, const parser::SeparateModuleSubprogram *, + const parser::BlockData *> + p; + ParentType parent; +}; + +/// Function-like units have similar structure. They all can contain executable +/// statements as well as other function-like units (internal procedures and +/// function statements). +struct FunctionLikeUnit : public ProgramUnit { + // wrapper statements for function-like syntactic structures + using FunctionStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent); + FunctionLikeUnit(const parser::FunctionSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SubroutineSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SeparateModuleSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(FunctionLikeUnit &&) = default; + FunctionLikeUnit(const FunctionLikeUnit &) = delete; + + bool isMainProgram() { + return std::holds_alternative< + const parser::Statement *>(endStmt); + } + const parser::FunctionStmt *getFunction() { + return getA(); + } + const parser::SubroutineStmt *getSubroutine() { + return getA(); + } + const parser::MpSubprogramStmt *getMPSubp() { + return getA(); + } + + /// Anonymous programs do not have a begin statement + std::optional beginStmt; + FunctionStatement endStmt; + EvaluationCollection evals; // statements + std::list funcs; // internal procedures + +private: + template + const A *getA() { + if (beginStmt) { + if (auto p = + std::get_if *>(&beginStmt.value())) + return &(*p)->statement; + } + return nullptr; + } +}; + +/// Module-like units have similar structure. They all can contain a list of +/// function-like units. +struct ModuleLikeUnit : public ProgramUnit { + // wrapper statements for module-like syntactic structures + using ModuleStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + ModuleLikeUnit(const parser::Module &m, const ParentType &parent); + ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent); + ~ModuleLikeUnit() = default; + ModuleLikeUnit(ModuleLikeUnit &&) = default; + ModuleLikeUnit(const ModuleLikeUnit &) = delete; + + ModuleStatement beginStmt; + ModuleStatement endStmt; + std::list funcs; +}; + +struct BlockDataUnit : public ProgramUnit { + BlockDataUnit(const parser::BlockData &bd, const ParentType &parent); + BlockDataUnit(BlockDataUnit &&) = default; + BlockDataUnit(const BlockDataUnit &) = delete; +}; + +/// A Program is the top-level PFT +struct Program { + using Units = std::variant; + + Program() = default; + Program(Program &&) = default; + Program(const Program &) = delete; + + std::list &getUnits() { return units; } + +private: + std::list units; +}; + +} // namespace pft + +/// Create an PFT from the parse tree +std::unique_ptr createPFT(const parser::Program &root); + +/// Decorate the PFT with control flow annotations +/// +/// The PFT must be decorated with control-flow annotations to prepare it for +/// use in generating a CFG-like structure. +void annotateControl(pft::Program &); + +void dumpPFT(llvm::raw_ostream &o, pft::Program &); + +} // namespace Fortran::lower + +#endif // FORTRAN_LOWER_PFT_BUILDER_H_ diff --git a/include/flang/parser/dump-parse-tree.h b/include/flang/parser/dump-parse-tree.h index aca18137f955..a6181834c94f 100644 --- a/include/flang/parser/dump-parse-tree.h +++ b/include/flang/parser/dump-parse-tree.h @@ -40,11 +40,11 @@ class ParseTreeDumper { std::ostream &out, const AnalyzedObjectsAsFortran *asFortran = nullptr) : out_(out), asFortran_{asFortran} {} - constexpr const char *GetNodeName(const char *) { return "char *"; } + static constexpr const char *GetNodeName(const char *) { return "char *"; } #define NODE_NAME(T, N) \ - constexpr const char *GetNodeName(const T &) { return N; } + static constexpr const char *GetNodeName(const T &) { return N; } #define NODE_ENUM(T, E) \ - std::string GetNodeName(const T::E &x) { \ + static std::string GetNodeName(const T::E &x) { \ return #E " = "s + T::EnumToString(x); \ } #define NODE(T1, T2) NODE_NAME(T1::T2, #T2) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 343195bae2b0..35c3e139b1bf 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -9,5 +9,6 @@ add_subdirectory(common) add_subdirectory(evaluate) add_subdirectory(decimal) +add_subdirectory(lower) add_subdirectory(parser) add_subdirectory(semantics) diff --git a/lib/lower/CMakeLists.txt b/lib/lower/CMakeLists.txt new file mode 100644 index 000000000000..87131cd9fa53 --- /dev/null +++ b/lib/lower/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(FortranLower + PFTBuilder.cpp +) + +target_link_libraries(FortranLower + LLVMSupport +) + +install (TARGETS FortranLower + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/lib/lower/PFTBuilder.cpp b/lib/lower/PFTBuilder.cpp new file mode 100644 index 000000000000..d7998e259f78 --- /dev/null +++ b/lib/lower/PFTBuilder.cpp @@ -0,0 +1,697 @@ +//===-- lib/lower/PFTBuilder.cc -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/lower/PFTBuilder.h" +#include "flang/parser/dump-parse-tree.h" +#include "flang/parser/parse-tree-visitor.h" +#include "llvm/ADT/DenseMap.h" +#include +#include +#include + +namespace Fortran::lower { +namespace { + +/// Helpers to unveil parser node inside parser::Statement<>, +/// parser::UnlabeledStatement, and common::Indirection<> +template +struct RemoveIndirectionHelper { + using Type = A; + static constexpr const Type &unwrap(const A &a) { return a; } +}; +template +struct RemoveIndirectionHelper> { + using Type = A; + static constexpr const Type &unwrap(const common::Indirection &a) { + return a.value(); + } +}; + +template +const auto &removeIndirection(const A &a) { + return RemoveIndirectionHelper::unwrap(a); +} + +template +struct UnwrapStmt { + static constexpr bool isStmt{false}; +}; +template +struct UnwrapStmt> { + static constexpr bool isStmt{true}; + using Type = typename RemoveIndirectionHelper::Type; + constexpr UnwrapStmt(const parser::Statement &a) + : unwrapped{removeIndirection(a.statement)}, pos{a.source}, lab{a.label} { + } + const Type &unwrapped; + parser::CharBlock pos; + std::optional lab; +}; +template +struct UnwrapStmt> { + static constexpr bool isStmt{true}; + using Type = typename RemoveIndirectionHelper::Type; + constexpr UnwrapStmt(const parser::UnlabeledStatement &a) + : unwrapped{removeIndirection(a.statement)}, pos{a.source} {} + const Type &unwrapped; + parser::CharBlock pos; + std::optional lab; +}; + +/// The instantiation of a parse tree visitor (Pre and Post) is extremely +/// expensive in terms of compile and link time, so one goal here is to limit +/// the bridge to one such instantiation. +class PFTBuilder { +public: + PFTBuilder() : pgm{new pft::Program}, parents{*pgm.get()} {} + + /// Get the result + std::unique_ptr result() { return std::move(pgm); } + + template + constexpr bool Pre(const A &a) { + bool visit{true}; + if constexpr (pft::isFunctionLike) { + return enterFunc(a); + } else if constexpr (pft::isConstruct) { + return enterConstruct(a); + } else if constexpr (UnwrapStmt::isStmt) { + using T = typename UnwrapStmt::Type; + // Node "a" being visited has one of the following types: + // Statement, Statement, UnlabeledStatement, + // or UnlabeledStatement> + auto stmt{UnwrapStmt(a)}; + if constexpr (pft::isConstructStmt || pft::isOtherStmt) { + addEval(pft::Evaluation{stmt.unwrapped, parents.back(), stmt.pos, + stmt.lab}); + visit = false; + } else if constexpr (std::is_same_v) { + addEval(makeEvalAction(stmt.unwrapped, stmt.pos, stmt.lab)); + visit = false; + } + } + return visit; + } + + template + constexpr void Post(const A &) { + if constexpr (pft::isFunctionLike) { + exitFunc(); + } else if constexpr (pft::isConstruct) { + exitConstruct(); + } + } + + // Module like + bool Pre(const parser::Module &node) { return enterModule(node); } + bool Pre(const parser::Submodule &node) { return enterModule(node); } + + void Post(const parser::Module &) { exitModule(); } + void Post(const parser::Submodule &) { exitModule(); } + + // Block data + bool Pre(const parser::BlockData &node) { + addUnit(pft::BlockDataUnit{node, parents.back()}); + return false; + } + + // Get rid of production wrapper + bool Pre(const parser::UnlabeledStatement + &statement) { + addEval(std::visit( + [&](const auto &x) { + return pft::Evaluation{x, parents.back(), statement.source, {}}; + }, + statement.statement.u)); + return false; + } + bool Pre(const parser::Statement &statement) { + addEval(std::visit( + [&](const auto &x) { + return pft::Evaluation{x, parents.back(), statement.source, + statement.label}; + }, + statement.statement.u)); + return false; + } + bool Pre(const parser::WhereBodyConstruct &whereBody) { + return std::visit( + common::visitors{ + [&](const parser::Statement &stmt) { + // Not caught as other AssignmentStmt because it is not + // wrapped in a parser::ActionStmt. + addEval(pft::Evaluation{stmt.statement, parents.back(), + stmt.source, stmt.label}); + return false; + }, + [&](const auto &) { return true; }, + }, + whereBody.u); + } + +private: + // ActionStmt has a couple of non-conforming cases, which get handled + // explicitly here. The other cases use an Indirection, which we discard in + // the PFT. + pft::Evaluation makeEvalAction(const parser::ActionStmt &statement, + parser::CharBlock pos, + std::optional lab) { + return std::visit( + common::visitors{ + [&](const auto &x) { + return pft::Evaluation{removeIndirection(x), parents.back(), pos, + lab}; + }, + }, + statement.u); + } + + // When we enter a function-like structure, we want to build a new unit and + // set the builder's cursors to point to it. + template + bool enterFunc(const A &func) { + auto &unit = addFunc(pft::FunctionLikeUnit{func, parents.back()}); + funclist = &unit.funcs; + pushEval(&unit.evals); + parents.emplace_back(unit); + return true; + } + /// Make funclist to point to current parent function list if it exists. + void setFunctListToParentFuncs() { + if (!parents.empty()) { + std::visit(common::visitors{ + [&](pft::FunctionLikeUnit *p) { funclist = &p->funcs; }, + [&](pft::ModuleLikeUnit *p) { funclist = &p->funcs; }, + [&](auto *) { funclist = nullptr; }, + }, + parents.back().p); + } + } + + void exitFunc() { + popEval(); + parents.pop_back(); + setFunctListToParentFuncs(); + } + + // When we enter a construct structure, we want to build a new construct and + // set the builder's evaluation cursor to point to it. + template + bool enterConstruct(const A &construct) { + auto &con = addEval(pft::Evaluation{construct, parents.back()}); + con.subs.reset(new pft::EvaluationCollection); + pushEval(con.subs.get()); + parents.emplace_back(con); + return true; + } + + void exitConstruct() { + popEval(); + parents.pop_back(); + } + + // When we enter a module structure, we want to build a new module and + // set the builder's function cursor to point to it. + template + bool enterModule(const A &func) { + auto &unit = addUnit(pft::ModuleLikeUnit{func, parents.back()}); + funclist = &unit.funcs; + parents.emplace_back(unit); + return true; + } + + void exitModule() { + parents.pop_back(); + setFunctListToParentFuncs(); + } + + template + A &addUnit(A &&unit) { + pgm->getUnits().emplace_back(std::move(unit)); + return std::get(pgm->getUnits().back()); + } + + template + A &addFunc(A &&func) { + if (funclist) { + funclist->emplace_back(std::move(func)); + return funclist->back(); + } + return addUnit(std::move(func)); + } + + /// move the Evaluation to the end of the current list + pft::Evaluation &addEval(pft::Evaluation &&eval) { + assert(funclist && "not in a function"); + assert(evallist.size() > 0); + evallist.back()->emplace_back(std::move(eval)); + return evallist.back()->back(); + } + + /// push a new list on the stack of Evaluation lists + void pushEval(pft::EvaluationCollection *eval) { + assert(funclist && "not in a function"); + assert(eval && eval->empty() && "evaluation list isn't correct"); + evallist.emplace_back(eval); + } + + /// pop the current list and return to the last Evaluation list + void popEval() { + assert(funclist && "not in a function"); + evallist.pop_back(); + } + + std::unique_ptr pgm; + /// funclist points to FunctionLikeUnit::funcs list (resp. + /// ModuleLikeUnit::funcs) when building a FunctionLikeUnit (resp. + /// ModuleLikeUnit) to store internal procedures (resp. module procedures). + /// Otherwise (e.g. when building the top level Program), it is null. + std::list *funclist{nullptr}; + /// evallist is a stack of pointer to FunctionLikeUnit::evals (or + /// Evaluation::subs) that are being build. + std::vector evallist; + std::vector parents; +}; + +template +constexpr bool hasLabel(const A &stmt) { + auto isLabel{ + [](const auto &v) { return std::holds_alternative, + "All ConstructStmts impact on the control flow " + "should be explicitly handled"); + } + /* else do nothing */ + }, + }); + } +} + +/// Annotate the PFT with CFG source decorations (see CFGAnnotation) and mark +/// potential branch targets +inline void annotateFuncCFG(pft::FunctionLikeUnit &functionLikeUnit) { + annotateEvalListCFG(functionLikeUnit.evals, nullptr); + for (auto &internalFunc : functionLikeUnit.funcs) + annotateFuncCFG(internalFunc); +} + +class PFTDumper { +public: + void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) { + for (auto &unit : pft.getUnits()) { + std::visit(common::visitors{ + [&](pft::BlockDataUnit &unit) { + outputStream << getNodeIndex(unit) << " "; + outputStream << "BlockData: "; + outputStream << "\nEndBlockData\n\n"; + }, + [&](pft::FunctionLikeUnit &func) { + dumpFunctionLikeUnit(outputStream, func); + }, + [&](pft::ModuleLikeUnit &unit) { + dumpModuleLikeUnit(outputStream, unit); + }, + }, + unit); + } + resetIndexes(); + } + + llvm::StringRef evalName(pft::Evaluation &eval) { + return eval.visit(common::visitors{ + [](const pft::CGJump) { return "CGJump"; }, + [](const auto &parseTreeNode) { + return parser::ParseTreeDumper::GetNodeName(parseTreeNode); + }, + }); + } + + void dumpEvalList(llvm::raw_ostream &outputStream, + pft::EvaluationCollection &evaluationCollection, + int indent = 1) { + static const std::string white{" ++"}; + std::string indentString{white.substr(0, indent * 2)}; + for (pft::Evaluation &eval : evaluationCollection) { + outputStream << indentString << getNodeIndex(eval) << " "; + llvm::StringRef name{evalName(eval)}; + if (auto *subs{eval.getConstructEvals()}) { + outputStream << "<<" << name << ">>"; + outputStream << "\n"; + dumpEvalList(outputStream, *subs, indent + 1); + outputStream << indentString << "<>\n"; + } else { + outputStream << name; + outputStream << ": " << eval.pos.ToString() + "\n"; + } + } + } + + void dumpFunctionLikeUnit(llvm::raw_ostream &outputStream, + pft::FunctionLikeUnit &functionLikeUnit) { + outputStream << getNodeIndex(functionLikeUnit) << " "; + llvm::StringRef unitKind{}; + std::string name{}; + std::string header{}; + if (functionLikeUnit.beginStmt) { + std::visit( + common::visitors{ + [&](const parser::Statement *statement) { + unitKind = "Program"; + name = statement->statement.v.ToString(); + }, + [&](const parser::Statement *statement) { + unitKind = "Function"; + name = + std::get(statement->statement.t).ToString(); + header = statement->source.ToString(); + }, + [&](const parser::Statement *statement) { + unitKind = "Subroutine"; + name = + std::get(statement->statement.t).ToString(); + header = statement->source.ToString(); + }, + [&](const parser::Statement + *statement) { + unitKind = "MpSubprogram"; + name = statement->statement.v.ToString(); + header = statement->source.ToString(); + }, + [&](auto *) {}, + }, + *functionLikeUnit.beginStmt); + } else { + unitKind = "Program"; + name = ""; + } + outputStream << unitKind << ' ' << name; + if (header.size()) + outputStream << ": " << header; + outputStream << '\n'; + dumpEvalList(outputStream, functionLikeUnit.evals); + if (!functionLikeUnit.funcs.empty()) { + outputStream << "\nContains\n"; + for (auto &func : functionLikeUnit.funcs) + dumpFunctionLikeUnit(outputStream, func); + outputStream << "EndContains\n"; + } + outputStream << "End" << unitKind << ' ' << name << "\n\n"; + } + + void dumpModuleLikeUnit(llvm::raw_ostream &outputStream, + pft::ModuleLikeUnit &moduleLikeUnit) { + outputStream << getNodeIndex(moduleLikeUnit) << " "; + outputStream << "ModuleLike: "; + outputStream << "\nContains\n"; + for (auto &func : moduleLikeUnit.funcs) + dumpFunctionLikeUnit(outputStream, func); + outputStream << "EndContains\nEndModuleLike\n\n"; + } + + template + std::size_t getNodeIndex(const T &node) { + auto addr{static_cast(&node)}; + auto it{nodeIndexes.find(addr)}; + if (it != nodeIndexes.end()) { + return it->second; + } + nodeIndexes.try_emplace(addr, nextIndex); + return nextIndex++; + } + std::size_t getNodeIndex(const pft::Program &) { return 0; } + + void resetIndexes() { + nodeIndexes.clear(); + nextIndex = 1; + } + +private: + llvm::DenseMap nodeIndexes; + std::size_t nextIndex{1}; // 0 is the root +}; + +template +pft::FunctionLikeUnit::FunctionStatement getFunctionStmt(const T &func) { + return pft::FunctionLikeUnit::FunctionStatement{ + &std::get>(func.t)}; +} +template +pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) { + return pft::ModuleLikeUnit::ModuleStatement{ + &std::get>(mod.t)}; +} + +} // namespace + +pft::FunctionLikeUnit::FunctionLikeUnit(const parser::MainProgram &func, + const pft::ParentType &parent) + : ProgramUnit{func, parent} { + auto &ps{ + std::get>>(func.t)}; + if (ps.has_value()) { + const parser::Statement &statement{ps.value()}; + beginStmt = &statement; + } + endStmt = getFunctionStmt(func); +} + +pft::FunctionLikeUnit::FunctionLikeUnit(const parser::FunctionSubprogram &func, + const pft::ParentType &parent) + : ProgramUnit{func, parent}, + beginStmt{getFunctionStmt(func)}, + endStmt{getFunctionStmt(func)} {} + +pft::FunctionLikeUnit::FunctionLikeUnit( + const parser::SubroutineSubprogram &func, const pft::ParentType &parent) + : ProgramUnit{func, parent}, + beginStmt{getFunctionStmt(func)}, + endStmt{getFunctionStmt(func)} {} + +pft::FunctionLikeUnit::FunctionLikeUnit( + const parser::SeparateModuleSubprogram &func, const pft::ParentType &parent) + : ProgramUnit{func, parent}, + beginStmt{getFunctionStmt(func)}, + endStmt{getFunctionStmt(func)} {} + +pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Module &m, + const pft::ParentType &parent) + : ProgramUnit{m, parent}, beginStmt{getModuleStmt(m)}, + endStmt{getModuleStmt(m)} {} + +pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Submodule &m, + const pft::ParentType &parent) + : ProgramUnit{m, parent}, beginStmt{getModuleStmt( + m)}, + endStmt{getModuleStmt(m)} {} + +pft::BlockDataUnit::BlockDataUnit(const parser::BlockData &bd, + const pft::ParentType &parent) + : ProgramUnit{bd, parent} {} + +std::unique_ptr createPFT(const parser::Program &root) { + PFTBuilder walker; + Walk(root, walker); + return walker.result(); +} + +void annotateControl(pft::Program &pft) { + for (auto &unit : pft.getUnits()) { + std::visit(common::visitors{ + [](pft::BlockDataUnit &) {}, + [](pft::FunctionLikeUnit &func) { annotateFuncCFG(func); }, + [](pft::ModuleLikeUnit &unit) { + for (auto &func : unit.funcs) + annotateFuncCFG(func); + }, + }, + unit); + } +} + +/// Dump a PFT. +void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) { + PFTDumper{}.dumpPFT(outputStream, pft); +} + +} // namespace Fortran::lower diff --git a/test-lit/CMakeLists.txt b/test-lit/CMakeLists.txt index 0819e57b81f3..111814303b0b 100644 --- a/test-lit/CMakeLists.txt +++ b/test-lit/CMakeLists.txt @@ -1,6 +1,8 @@ # Test runner infrastructure for Flang. This configures the Flang test trees # for use by Lit, and delegates to LLVM's lit test handlers. +set(FLANG_INTRINSIC_MODULES_DIR ${FLANG_BINARY_DIR}/tools/f18/include) + configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py diff --git a/test-lit/lit.cfg.py b/test-lit/lit.cfg.py index c27e6a616b85..3ca3c8ad23f7 100644 --- a/test-lit/lit.cfg.py +++ b/test-lit/lit.cfg.py @@ -61,9 +61,12 @@ # to search to ensure that we get the tools just built and not some random # tools that might happen to be in the user's PATH. tool_dirs = [config.llvm_tools_dir, config.flang_tools_dir] +flang_includes = "-I" + config.flang_intrinsic_modules_dir tools = [ToolSubst('%flang', command=FindTool('flang'), unresolved='fatal'), - ToolSubst('%f18', command=FindTool('f18'), unresolved='fatal')] + ToolSubst('%f18', command=FindTool('f18'), unresolved='fatal'), + ToolSubst('%f18_with_includes', command=FindTool('f18'), + extra_args=[flang_includes], unresolved='fatal')] llvm_config.add_tool_substitutions(tools, tool_dirs) diff --git a/test-lit/lit.site.cfg.py.in b/test-lit/lit.site.cfg.py.in index ad31bf1594f9..d00f3856fb41 100644 --- a/test-lit/lit.site.cfg.py.in +++ b/test-lit/lit.site.cfg.py.in @@ -6,6 +6,7 @@ config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.flang_obj_root = "@FLANG_BINARY_DIR@" config.flang_src_dir = "@FLANG_SOURCE_DIR@" config.flang_tools_dir = "@FLANG_TOOLS_DIR@" +config.flang_intrinsic_modules_dir = "@FLANG_INTRINSIC_MODULES_DIR@" config.python_executable = "@PYTHON_EXECUTABLE@" # Support substitution of the tools_dir with user parameters. This is diff --git a/test-lit/lower/pre-fir-tree01.f90 b/test-lit/lower/pre-fir-tree01.f90 new file mode 100644 index 000000000000..97f15eea052e --- /dev/null +++ b/test-lit/lower/pre-fir-tree01.f90 @@ -0,0 +1,130 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test structure of the Pre-FIR tree + +! CHECK: Subroutine foo +subroutine foo() + ! CHECK: <> + ! CHECK: NonLabelDoStmt + do i=1,5 + ! CHECK: PrintStmt + print *, "hey" + ! CHECK: <> + ! CHECK: NonLabelDoStmt + do j=1,5 + ! CHECK: PrintStmt + print *, "hello", i, j + ! CHECK: EndDoStmt + end do + ! CHECK: <> + ! CHECK: EndDoStmt + end do + ! CHECK: <> +end subroutine +! CHECK: EndSubroutine foo + +! CHECK: BlockData +block data + integer, parameter :: n = 100 + integer, dimension(n) :: a, b, c + common /arrays/ a, b, c +end +! CHECK: EndBlockData + +! CHECK: ModuleLike +module test_mod +interface + ! check specification parts are not part of the PFT. + ! CHECK-NOT: node + module subroutine dump() + end subroutine +end interface + integer :: xdim + real, allocatable :: pressure(:) +contains + ! CHECK: Subroutine foo + subroutine foo() + contains + ! CHECK: Subroutine subfoo + subroutine subfoo() + end subroutine + ! CHECK: EndSubroutine subfoo + ! CHECK: Function subfoo2 + function subfoo2() + end function + ! CHECK: EndFunction subfoo2 + end subroutine + ! CHECK: EndSubroutine foo + + ! CHECK: Function foo2 + function foo2(i, j) + integer i, j, foo2 + ! CHECK: AssignmentStmt + foo2 = i + j + contains + ! CHECK: Subroutine subfoo + subroutine subfoo() + end subroutine + ! CHECK: EndSubroutine subfoo + end function + ! CHECK: EndFunction foo2 +end module +! CHECK: EndModuleLike + +! CHECK: ModuleLike +submodule (test_mod) test_mod_impl +contains + ! CHECK: Subroutine foo + subroutine foo() + contains + ! CHECK: Subroutine subfoo + subroutine subfoo() + end subroutine + ! CHECK: EndSubroutine subfoo + ! CHECK: Function subfoo2 + function subfoo2() + end function + ! CHECK: EndFunction subfoo2 + end subroutine + ! CHECK: EndSubroutine foo + ! CHECK: MpSubprogram dump + module procedure dump + ! CHECK: FormatStmt +11 format (2E16.4, I6) + ! CHECK: <> + ! CHECK: IfThenStmt + if (xdim > 100) then + ! CHECK: PrintStmt + print *, "test: ", xdim + ! CHECK: ElseStmt + else + ! CHECK: WriteStmt + write (*, 11) "test: ", xdim, pressure + ! CHECK: EndIfStmt + end if + ! CHECK: <> + end procedure +end submodule +! CHECK: EndModuleLike + +! CHECK: BlockData +block data named_block + integer i, j, k + common /indexes/ i, j, k +end +! CHECK: EndBlockData + +! CHECK: Function bar +function bar() +end function +! CHECK: EndFunction bar + +! CHECK: Program + ! check specification parts are not part of the PFT. + ! CHECK-NOT: node + use test_mod + real, allocatable :: x(:) + ! CHECK: AllocateStmt + allocate(x(foo2(10, 30))) +end +! CHECK: EndProgram diff --git a/test-lit/lower/pre-fir-tree02.f90 b/test-lit/lower/pre-fir-tree02.f90 new file mode 100644 index 000000000000..ec9077a550a2 --- /dev/null +++ b/test-lit/lower/pre-fir-tree02.f90 @@ -0,0 +1,334 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test Pre-FIR Tree captures all the intended nodes from the parse-tree +! Coarray and OpenMP related nodes are tested in other files. + +! CHECK: Program test_prog +program test_prog + ! Check specification part is not part of the tree. + interface + subroutine incr(i) + integer, intent(inout) :: i + end subroutine + end interface + integer :: i, j, k + real, allocatable, target :: x(:) + real :: y(100) + ! CHECK-NOT: node + ! CHECK: <> + ! CHECK: NonLabelDoStmt + do i=1,5 + ! CHECK: PrintStmt + print *, "hey" + ! CHECK: <> + ! CHECK: NonLabelDoStmt + do j=1,5 + ! CHECK: PrintStmt + print *, "hello", i, j + ! CHECK: EndDoStmt + end do + ! CHECK: <> + ! CHECK: EndDoStmt + end do + ! CHECK: <> + + ! CHECK: <> + ! CHECK: AssociateStmt + associate (k => i + j) + ! CHECK: AllocateStmt + allocate(x(k)) + ! CHECK: EndAssociateStmt + end associate + ! CHECK: <> + + ! CHECK: <> + ! CHECK: BlockStmt + block + integer :: k, l + real, pointer :: p(:) + ! CHECK: PointerAssignmentStmt + p => x + ! CHECK: AssignmentStmt + k = size(p) + ! CHECK: AssignmentStmt + l = 1 + ! CHECK: <> + ! CHECK: SelectCaseStmt + select case (k) + ! CHECK: CaseStmt + case (:0) + ! CHECK: NullifyStmt + nullify(p) + ! CHECK: CaseStmt + case (1) + ! CHECK: <> + ! CHECK: IfThenStmt + if (p(1)>0.) then + ! CHECK: PrintStmt + print *, "+" + ! CHECK: ElseIfStmt + else if (p(1)==0.) then + ! CHECK: PrintStmt + print *, "0." + ! CHECK: ElseStmt + else + ! CHECK: PrintStmt + print *, "-" + ! CHECK: EndIfStmt + end if + ! CHECK: <> + ! CHECK: CaseStmt + case (2:10) + ! CHECK: CaseStmt + case default + ! Note: label-do-loop are canonicalized into do constructs + ! CHECK: <> + ! CHECK: NonLabelDoStmt + do 22 while(l<=k) + ! CHECK: IfStmt + if (p(l)<0.) p(l)=cos(p(l)) + ! CHECK: CallStmt +22 call incr(l) + ! CHECK: EndDoStmt + ! CHECK: <> + ! CHECK: CaseStmt + case (100:) + ! CHECK: EndSelectStmt + end select + ! CHECK: <> + ! CHECK: EndBlockStmt + end block + ! CHECK: <> + + ! CHECK-NOT: WhereConstruct + ! CHECK: WhereStmt + where (x > 1.) x = x/2. + + ! CHECK: <> + ! CHECK: WhereConstructStmt + where (x == 0.) + ! CHECK: AssignmentStmt + x = 0.01 + ! CHECK: MaskedElsewhereStmt + elsewhere (x < 0.5) + ! CHECK: AssignmentStmt + x = x*2. + ! CHECK: <> + where (y > 0.4) + ! CHECK: AssignmentStmt + y = y/2. + end where + ! CHECK: <> + ! CHECK: ElsewhereStmt + elsewhere + ! CHECK: AssignmentStmt + x = x + 1. + ! CHECK: EndWhereStmt + end where + ! CHECK: <> + + ! CHECK-NOT: ForAllConstruct + ! CHECK: ForallStmt + forall (i = 1:5) x(i) = y(i) + + ! CHECK: <> + ! CHECK: ForallConstructStmt + forall (i = 1:5) + ! CHECK: AssignmentStmt + x(i) = x(i) + y(10*i) + ! CHECK: EndForallStmt + end forall + ! CHECK: <> + + ! CHECK: DeallocateStmt + deallocate(x) +end + +! CHECK: ModuleLike +module test + type :: a_type + integer :: x + end type + type, extends(a_type) :: b_type + integer :: y + end type +contains + ! CHECK: Function foo + function foo(x) + real x(..) + integer :: foo + ! CHECK: <> + ! CHECK: SelectRankStmt + select rank(x) + ! CHECK: SelectRankCaseStmt + rank (0) + ! CHECK: AssignmentStmt + foo = 0 + ! CHECK: SelectRankCaseStmt + rank (*) + ! CHECK: AssignmentStmt + foo = -1 + ! CHECK: SelectRankCaseStmt + rank (1) + ! CHECK: AssignmentStmt + foo = 1 + ! CHECK: SelectRankCaseStmt + rank default + ! CHECK: AssignmentStmt + foo = 2 + ! CHECK: EndSelectStmt + end select + ! CHECK: <> + end function + + ! CHECK: Function bar + function bar(x) + class(*) :: x + ! CHECK: <> + ! CHECK: SelectTypeStmt + select type(x) + ! CHECK: TypeGuardStmt + type is (integer) + ! CHECK: AssignmentStmt + bar = 0 + ! CHECK: TypeGuardStmt + class is (a_type) + ! CHECK: AssignmentStmt + bar = 1 + ! CHECK: ReturnStmt + return + ! CHECK: TypeGuardStmt + class default + ! CHECK: AssignmentStmt + bar = -1 + ! CHECK: EndSelectStmt + end select + ! CHECK: <> + end function + + ! CHECK: Subroutine sub + subroutine sub(a) + real(4):: a + ! CompilerDirective + ! CHECK: <> + !DIR$ IGNORE_TKR a + end subroutine + + +end module + +! CHECK: Subroutine altreturn +subroutine altreturn(i, j, *, *) + ! CHECK: <> + if (i>j) then + ! CHECK: ReturnStmt + return 1 + else + ! CHECK: ReturnStmt + return 2 + end if + ! CHECK: <> +end subroutine + + +! Remaining TODO + +! CHECK: Subroutine iostmts +subroutine iostmts(filename, a, b, c) + character(*) :: filename + integer :: length + logical :: file_is_opened + real, a, b ,c + ! CHECK: InquireStmt + inquire(file=filename, opened=file_is_opened) + ! CHECK: <> + if (file_is_opened) then + ! CHECK: OpenStmt + open(10, FILE=filename) + end if + ! CHECK: <> + ! CHECK: ReadStmt + read(10, *) length + ! CHECK: RewindStmt + rewind 10 + ! CHECK: NamelistStmt + namelist /nlist/ a, b, c + ! CHECK: WriteStmt + write(10, NML=nlist) + ! CHECK: BackspaceStmt + backspace(10) + ! CHECK: FormatStmt +1 format (1PE12.4) + ! CHECK: WriteStmt + write (10, 1) a + ! CHECK: EndfileStmt + endfile 10 + ! CHECK: FlushStmt + flush 10 + ! CHECK: WaitStmt + wait(10) + ! CHECK: CloseStmt + close(10) +end subroutine + + +! CHECK: Subroutine sub2 +subroutine sub2() + integer :: i, j, k, l + i = 0 +1 j = i + ! CHECK: ContinueStmt +2 continue + i = i+1 +3 j = j+1 +! CHECK: ArithmeticIfStmt + if (j-i) 3, 4, 5 + ! CHECK: GotoStmt +4 goto 6 + +! FIXME: is name resolution on assigned goto broken/todo ? +! WILLCHECK: AssignStmt +!55 assign 6 to label +! WILLCHECK: AssignedGotoStmt +!66 go to label (5, 6) + +! CHECK: ComputedGotoStmt + go to (5, 6), 1 + mod(i, 2) +5 j = j + 1 +6 i = i + j/2 + + ! CHECK: <> + do1: do k=1,10 + ! CHECK: <> + do2: do l=5,20 + ! CHECK: CycleStmt + cycle do1 + ! CHECK: ExitStmt + exit do2 + end do do2 + ! CHECK: <> + end do do1 + ! CHECK: <> + + ! CHECK: PauseStmt + pause 7 + ! CHECK: StopStmt + stop +end subroutine + + +! CHECK: Subroutine sub3 +subroutine sub3() + print *, "normal" + ! CHECK: EntryStmt + entry sub4entry() + print *, "test" +end subroutine + +! CHECK: Subroutine sub4 +subroutine sub4(i, j) + integer :: i + print*, "test" + ! CHECK: DataStmt + data i /1/ +end subroutine diff --git a/test-lit/lower/pre-fir-tree03.f90 b/test-lit/lower/pre-fir-tree03.f90 new file mode 100644 index 000000000000..2eedfe7610ce --- /dev/null +++ b/test-lit/lower/pre-fir-tree03.f90 @@ -0,0 +1,60 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only -fopenmp %s | FileCheck %s + +! Test Pre-FIR Tree captures OpenMP related constructs + +! CHECK: Program test_omp +program test_omp + ! CHECK: PrintStmt + print *, "sequential" + + ! CHECK: <> + !$omp parallel + ! CHECK: PrintStmt + print *, "in omp //" + ! CHECK: <> + !$omp do + ! CHECK: <> + ! CHECK: LabelDoStmt + do i=1,100 + ! CHECK: PrintStmt + print *, "in omp do" + ! CHECK: EndDoStmt + end do + ! CHECK: <> + ! CHECK: OmpEndLoopDirective + !$omp end do + ! CHECK: <> + + ! CHECK: PrintStmt + print *, "not in omp do" + + ! CHECK: <> + !$omp do + ! CHECK: <> + ! CHECK: LabelDoStmt + do i=1,100 + ! CHECK: PrintStmt + print *, "in omp do" + ! CHECK: EndDoStmt + end do + ! CHECK: <> + ! CHECK: <> + ! CHECK-NOT: OmpEndLoopDirective + ! CHECK: PrintStmt + print *, "no in omp do" + !$omp end parallel + ! CHECK: <> + + ! CHECK: PrintStmt + print *, "sequential again" + + ! CHECK: <> + !$omp task + ! CHECK: PrintStmt + print *, "in task" + !$omp end task + ! CHECK: <> + + ! CHECK: PrintStmt + print *, "sequential again" +end program diff --git a/test-lit/lower/pre-fir-tree04.f90 b/test-lit/lower/pre-fir-tree04.f90 new file mode 100644 index 000000000000..3e8516e57bbb --- /dev/null +++ b/test-lit/lower/pre-fir-tree04.f90 @@ -0,0 +1,70 @@ +! RUN: %f18_with_includes -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test Pre-FIR Tree captures all the coarray related statements + +! CHECK: Subroutine test_coarray +Subroutine test_coarray + use iso_fortran_env, only: team_type, event_type, lock_type + type(team_type) :: t + type(event_type) :: done + type(lock_type) :: alock + real :: y[10,*] + integer :: counter[*] + logical :: is_master + ! CHECK: <> + change team(t, x[5,*] => y) + ! CHECK: AssignmentStmt + x = x[4, 1] + end team + ! CHECK: <> + ! CHECK: FormTeamStmt + form team(1, t) + + ! CHECK: <> + if (this_image() == 1) then + ! CHECK: EventPostStmt + event post (done) + else + ! CHECK: EventWaitStmt + event wait (done) + end if + ! CHECK: <> + + ! CHECK: <> + critical + ! CHECK: AssignmentStmt + counter[1] = counter[1] + 1 + end critical + ! CHECK: <> + + ! CHECK: LockStmt + lock(alock) + ! CHECK: PrintStmt + print *, "I have the lock" + ! CHECK: UnlockStmt + unlock(alock) + + ! CHECK: SyncAllStmt + sync all + ! CHECK: SyncMemoryStmt + sync memory + ! CHECK: SyncTeamStmt + sync team(t) + + ! CHECK: <> + if (this_image() == 1) then + ! CHECK: SyncImagesStmt + sync images(*) + else + ! CHECK: SyncImagesStmt + sync images(1) + end if + ! CHECK: <> + + ! CHECK: <> + if (y<0.) then + ! CHECK: FailImageStmt + fail image + end if + ! CHECK: <> +end diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 676549c95cf4..79f5c52d6a6c 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -18,6 +18,8 @@ target_link_libraries(f18 FortranParser FortranEvaluate FortranSemantics + LLVMSupport + FortranLower ) add_executable(f18-parse-demo diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index 56f008ba6fe1..b54d1a9e2d11 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -11,6 +11,7 @@ #include "flang/common/Fortran-features.h" #include "flang/common/default-kinds.h" #include "flang/evaluate/expression.h" +#include "flang/lower/PFTBuilder.h" #include "flang/parser/characters.h" #include "flang/parser/dump-parse-tree.h" #include "flang/parser/message.h" @@ -22,6 +23,7 @@ #include "flang/semantics/expression.h" #include "flang/semantics/semantics.h" #include "flang/semantics/unparse-with-symbols.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -92,6 +94,7 @@ struct DriverOptions { bool dumpUnparse{false}; bool dumpUnparseWithSymbols{false}; bool dumpParseTree{false}; + bool dumpPreFirTree{false}; bool dumpSymbols{false}; bool debugResolveNames{false}; bool debugNoSemantics{false}; @@ -308,6 +311,15 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, nullptr /* action before each statement */, &asFortran); return {}; } + if (driver.dumpPreFirTree) { + if (auto ast{Fortran::lower::createPFT(parseTree)}) { + Fortran::lower::annotateControl(*ast); + Fortran::lower::dumpPFT(llvm::outs(), *ast); + } else { + std::cerr << "Pre FIR Tree is NULL.\n"; + exitStatus = EXIT_FAILURE; + } + } if (driver.parseOnly) { return {}; } @@ -475,6 +487,8 @@ int main(int argc, char *const argv[]) { options.needProvenanceRangeToCharBlockMappings = true; } else if (arg == "-fdebug-dump-parse-tree") { driver.dumpParseTree = true; + } else if (arg == "-fdebug-pre-fir-tree") { + driver.dumpPreFirTree = true; } else if (arg == "-fdebug-dump-symbols") { driver.dumpSymbols = true; } else if (arg == "-fdebug-resolve-names") { From fb87d16a868112e26ade0ad696b2232d8cf3a524 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 18 Feb 2020 15:20:28 -0800 Subject: [PATCH 031/345] Reorganize evaluate::Assignment Every analyzed assignment represented by `evaluate::Assignment` has a LHS and RHS expression. These need to be checked uniformly in various places. So change Assignment always to have those data members, with the variant determining which kinds of assignment it is: intrinsic, user-defined, or pointer. --- include/flang/evaluate/expression.h | 34 +++++++--------- lib/evaluate/expression.cpp | 59 ++++++++++++---------------- lib/semantics/assignment.cpp | 48 ++++++++++------------ lib/semantics/expression.cpp | 57 +++++++++++++-------------- lib/semantics/pointer-assignment.cpp | 14 ++++--- lib/semantics/pointer-assignment.h | 4 +- 6 files changed, 99 insertions(+), 117 deletions(-) diff --git a/include/flang/evaluate/expression.h b/include/flang/evaluate/expression.h index b06119107ef8..019858a6e3da 100644 --- a/include/flang/evaluate/expression.h +++ b/include/flang/evaluate/expression.h @@ -811,28 +811,24 @@ template<> class Expr : public ExpressionBase { common::CombineVariants u; }; -// An assignment is either intrinsic (with lhs and rhs) or user-defined, -// represented as a ProcedureRef. A pointer assignment optionally also has -// a bounds-spec or bounds-remapping. +// An assignment is either intrinsic, user-defined (with a ProcedureRef to +// specify the procedure to call), or pointer assignment (with possibly empty +// BoundsSpec or non-empty BoundsRemapping). In all cases there are Exprs +// representing the LHS and RHS of the assignment. class Assignment { public: - UNION_CONSTRUCTORS(Assignment) - struct IntrinsicAssignment { - Expr lhs; - Expr rhs; - }; - struct PointerAssignment { - using BoundsSpec = std::vector>; - using BoundsRemapping = - std::vector, Expr>>; - PointerAssignment(Expr &&lhs, Expr &&rhs) - : lhs{std::move(lhs)}, rhs{std::move(rhs)} {} - Expr lhs; - Expr rhs; - std::variant bounds; - }; + Assignment(Expr &&lhs, Expr &&rhs) + : lhs(std::move(lhs)), rhs(std::move(rhs)) {} + + struct Intrinsic {}; + using BoundsSpec = std::vector>; + using BoundsRemapping = + std::vector, Expr>>; std::ostream &AsFortran(std::ostream &) const; - std::variant u; + + Expr lhs; + Expr rhs; + std::variant u; }; // This wrapper class is used, by means of a forward reference with diff --git a/lib/evaluate/expression.cpp b/lib/evaluate/expression.cpp index fbf20daba7af..c80599670f9e 100644 --- a/lib/evaluate/expression.cpp +++ b/lib/evaluate/expression.cpp @@ -168,40 +168,33 @@ GenericExprWrapper::~GenericExprWrapper() {} std::ostream &Assignment::AsFortran(std::ostream &o) const { std::visit( common::visitors{ - [&](const evaluate::Assignment::IntrinsicAssignment &x) { - x.rhs.AsFortran(x.lhs.AsFortran(o) << '='); + [&](const Assignment::Intrinsic &) { + rhs.AsFortran(lhs.AsFortran(o) << '='); }, - [&](const evaluate::ProcedureRef &x) { x.AsFortran(o << "CALL "); }, - [&](const evaluate::Assignment::PointerAssignment &x) { - x.lhs.AsFortran(o); - std::visit( - common::visitors{ - [&](const evaluate::Assignment::PointerAssignment:: - BoundsSpec &bounds) { - if (!bounds.empty()) { - char sep{'('}; - for (const auto &bound : bounds) { - bound.AsFortran(o << sep) << ':'; - sep = ','; - } - o << ')'; - } - }, - [&](const evaluate::Assignment::PointerAssignment:: - BoundsRemapping &bounds) { - if (!bounds.empty()) { - char sep{'('}; - for (const auto &bound : bounds) { - bound.first.AsFortran(o << sep) << ':'; - bound.second.AsFortran(o); - sep = ','; - } - o << ')'; - } - }, - }, - x.bounds); - x.rhs.AsFortran(o << " => "); + [&](const ProcedureRef &proc) { proc.AsFortran(o << "CALL "); }, + [&](const BoundsSpec &bounds) { + lhs.AsFortran(o); + if (!bounds.empty()) { + char sep{'('}; + for (const auto &bound : bounds) { + bound.AsFortran(o << sep) << ':'; + sep = ','; + } + o << ')'; + } + }, + [&](const BoundsRemapping &bounds) { + lhs.AsFortran(o); + if (!bounds.empty()) { + char sep{'('}; + for (const auto &bound : bounds) { + bound.first.AsFortran(o << sep) << ':'; + bound.second.AsFortran(o); + sep = ','; + } + o << ')'; + } + rhs.AsFortran(o << " => "); }, }, u); diff --git a/lib/semantics/assignment.cpp b/lib/semantics/assignment.cpp index 362df0465480..cb727efb2b66 100644 --- a/lib/semantics/assignment.cpp +++ b/lib/semantics/assignment.cpp @@ -141,50 +141,44 @@ class AssignmentContext { void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { // Assignment statement analysis is in expression.cpp where user-defined // assignments can be recognized and replaced. - if (const evaluate::Assignment * asst{GetAssignment(stmt)}) { - if (const auto *intrinsicAsst{ - std::get_if(&asst->u)}) { - CheckForImpureCall(intrinsicAsst->lhs); - CheckForImpureCall(intrinsicAsst->rhs); - if (forall_) { - // TODO: Warn if some name in forall_->activeNames or its outer - // contexts does not appear on LHS - } - CheckForPureContext(intrinsicAsst->lhs, intrinsicAsst->rhs, - std::get(stmt.t).source, false /* not => */); + if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) { + CheckForImpureCall(assignment->lhs); + CheckForImpureCall(assignment->rhs); + if (forall_) { + // TODO: Warn if some name in forall_->activeNames or its outer + // contexts does not appear on LHS } + CheckForPureContext(assignment->lhs, assignment->rhs, + std::get(stmt.t).source, false /* not => */); } // TODO: Fortran 2003 ALLOCATABLE assignment semantics (automatic // (re)allocation of LHS array when unallocated or nonconformable) } void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { - using PointerAssignment = evaluate::Assignment::PointerAssignment; CHECK(!where_); - const evaluate::Assignment *assign{GetAssignment(stmt)}; - if (!assign) { + const evaluate::Assignment *assignment{GetAssignment(stmt)}; + if (!assignment) { return; } - const auto &ptrAssign{std::get(assign->u)}; - const SomeExpr &lhs{ptrAssign.lhs}; - const SomeExpr &rhs{ptrAssign.rhs}; + const SomeExpr &lhs{assignment->lhs}; + const SomeExpr &rhs{assignment->rhs}; CheckForImpureCall(lhs); CheckForImpureCall(rhs); std::visit( - common::visitors{ - [&](const PointerAssignment::BoundsSpec &bounds) { - for (const auto &bound : bounds) { - CheckForImpureCall(SomeExpr{bound}); - } - }, - [&](const PointerAssignment::BoundsRemapping &bounds) { + common::visitors{[&](const evaluate::Assignment::BoundsSpec &bounds) { + for (const auto &bound : bounds) { + CheckForImpureCall(SomeExpr{bound}); + } + }, + [&](const evaluate::Assignment::BoundsRemapping &bounds) { for (const auto &bound : bounds) { CheckForImpureCall(SomeExpr{bound.first}); CheckForImpureCall(SomeExpr{bound.second}); } }, - }, - ptrAssign.bounds); + [](const auto &) { DIE("not valid for pointer assignment"); }}, + assignment->u); if (forall_) { // TODO: Warn if some name in forall_->activeNames or its outer // contexts does not appear on LHS @@ -193,7 +187,7 @@ void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { true /* isPointerAssignment */); auto restorer{context_.foldingContext().messages().SetLocation( context_.location().value())}; - CheckPointerAssignment(context_.foldingContext(), ptrAssign); + CheckPointerAssignment(context_.foldingContext(), *assignment); } void AssignmentContext::Analyze(const parser::WhereStmt &stmt) { diff --git a/lib/semantics/expression.cpp b/lib/semantics/expression.cpp index 59593ae90e8f..3aa03893290e 100644 --- a/lib/semantics/expression.cpp +++ b/lib/semantics/expression.cpp @@ -1919,10 +1919,13 @@ const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { x.typedAssignment.reset(new GenericAssignmentWrapper{}); } else { std::optional procRef{analyzer.TryDefinedAssignment()}; - x.typedAssignment.reset(new GenericAssignmentWrapper{procRef - ? Assignment{std::move(*procRef)} - : Assignment{Assignment::IntrinsicAssignment{ - Fold(analyzer.MoveExpr(0)), Fold(analyzer.MoveExpr(1))}}}); + Assignment assignment{ + Fold(analyzer.MoveExpr(0)), Fold(analyzer.MoveExpr(1))}; + if (procRef) { + assignment.u = std::move(*procRef); + } + x.typedAssignment.reset( + new GenericAssignmentWrapper{std::move(assignment)}); } } return common::GetPtrFromOptional(x.typedAssignment->v); @@ -1933,12 +1936,14 @@ const Assignment *ExpressionAnalyzer::Analyze( if (!x.typedAssignment) { MaybeExpr lhs{Analyze(std::get(x.t))}; MaybeExpr rhs{Analyze(std::get(x.t))}; - decltype(Assignment::PointerAssignment::bounds) pointerBounds; - std::visit( - common::visitors{ - [&](const std::list &list) { - if (!list.empty()) { - Assignment::PointerAssignment::BoundsRemapping bounds; + if (!lhs || !rhs) { + x.typedAssignment.reset(new GenericAssignmentWrapper{}); + } else { + Assignment assignment{std::move(*lhs), std::move(*rhs)}; + std::visit( + common::visitors{ + [&](const std::list &list) { + Assignment::BoundsRemapping bounds; for (const auto &elem : list) { auto lower{AsSubscript(Analyze(std::get<0>(elem.t)))}; auto upper{AsSubscript(Analyze(std::get<1>(elem.t)))}; @@ -1947,30 +1952,21 @@ const Assignment *ExpressionAnalyzer::Analyze( Fold(std::move(*lower)), Fold(std::move(*upper))); } } - pointerBounds = bounds; - } - }, - [&](const std::list &list) { - if (!list.empty()) { - Assignment::PointerAssignment::BoundsSpec bounds; + assignment.u = std::move(bounds); + }, + [&](const std::list &list) { + Assignment::BoundsSpec bounds; for (const auto &bound : list) { if (auto lower{AsSubscript(Analyze(bound.v))}) { bounds.emplace_back(Fold(std::move(*lower))); } } - pointerBounds = bounds; - } - }, - }, - std::get(x.t).u); - if (!lhs || !rhs) { - x.typedAssignment.reset(new GenericAssignmentWrapper{}); - } else { - Assignment::PointerAssignment assignment{ - Fold(std::move(*lhs)), Fold(std::move(*rhs))}; - assignment.bounds = pointerBounds; + assignment.u = std::move(bounds); + }, + }, + std::get(x.t).u); x.typedAssignment.reset( - new GenericAssignmentWrapper{Assignment{std::move(assignment)}}); + new GenericAssignmentWrapper{std::move(assignment)}); } } return common::GetPtrFromOptional(x.typedAssignment->v); @@ -2784,8 +2780,9 @@ std::optional ArgumentAnalyzer::GetDefinedAssignmentProc() { } } if (proc) { - actuals_[1]->Parenthesize(); - return ProcedureRef{ProcedureDesignator{*proc}, std::move(actuals_)}; + ActualArguments actualsCopy{actuals_}; + actualsCopy[1]->Parenthesize(); + return ProcedureRef{ProcedureDesignator{*proc}, std::move(actualsCopy)}; } else { return std::nullopt; } diff --git a/lib/semantics/pointer-assignment.cpp b/lib/semantics/pointer-assignment.cpp index 8e111eff4061..d3b3ec7f76ad 100644 --- a/lib/semantics/pointer-assignment.cpp +++ b/lib/semantics/pointer-assignment.cpp @@ -35,7 +35,6 @@ using evaluate::characteristics::Procedure; using evaluate::characteristics::TypeAndShape; using parser::MessageFixedText; using parser::MessageFormattedText; -using PointerAssignment = evaluate::Assignment::PointerAssignment; class PointerAssignmentChecker { public: @@ -348,17 +347,17 @@ parser::Message *PointerAssignmentChecker::Say(A &&... x) { // Verify that any bounds on the LHS of a pointer assignment are valid. // Return true if it is a bound-remapping so we can perform further checks. static bool CheckPointerBounds( - evaluate::FoldingContext &context, const PointerAssignment &assignment) { + evaluate::FoldingContext &context, const evaluate::Assignment &assignment) { auto &messages{context.messages()}; const SomeExpr &lhs{assignment.lhs}; const SomeExpr &rhs{assignment.rhs}; bool isBoundsRemapping{false}; std::size_t numBounds{std::visit( common::visitors{ - [&](const PointerAssignment::BoundsSpec &bounds) { + [&](const evaluate::Assignment::BoundsSpec &bounds) { return bounds.size(); }, - [&](const PointerAssignment::BoundsRemapping &bounds) { + [&](const evaluate::Assignment::BoundsRemapping &bounds) { isBoundsRemapping = true; evaluate::ExtentExpr lhsSizeExpr{1}; for (const auto &bound : bounds) { @@ -383,8 +382,11 @@ static bool CheckPointerBounds( } return bounds.size(); }, + [](const auto &) -> std::size_t { + DIE("not valid for pointer assignment"); + }, }, - assignment.bounds)}; + assignment.u)}; if (numBounds > 0) { if (lhs.Rank() != static_cast(numBounds)) { messages.Say("Pointer '%s' has rank %d but the number of bounds specified" @@ -401,7 +403,7 @@ static bool CheckPointerBounds( } void CheckPointerAssignment( - evaluate::FoldingContext &context, const PointerAssignment &assignment) { + evaluate::FoldingContext &context, const evaluate::Assignment &assignment) { const SomeExpr &lhs{assignment.lhs}; const SomeExpr &rhs{assignment.rhs}; const Symbol *pointer{GetLastSymbol(lhs)}; diff --git a/lib/semantics/pointer-assignment.h b/lib/semantics/pointer-assignment.h index 70c805683016..a9efc59994ac 100644 --- a/lib/semantics/pointer-assignment.h +++ b/lib/semantics/pointer-assignment.h @@ -26,8 +26,8 @@ namespace Fortran::semantics { class Symbol; -void CheckPointerAssignment(evaluate::FoldingContext &, - const evaluate::Assignment::PointerAssignment &); +void CheckPointerAssignment( + evaluate::FoldingContext &, const evaluate::Assignment &); void CheckPointerAssignment( evaluate::FoldingContext &, const Symbol &lhs, const SomeExpr &rhs); void CheckPointerAssignment(evaluate::FoldingContext &, From 69a845283b058a3644053ec58b00d3361f4d4a59 Mon Sep 17 00:00:00 2001 From: "Jinxin (Brian) Yang" Date: Tue, 18 Feb 2020 16:27:43 -0800 Subject: [PATCH 032/345] [OpenMP] Predetermined rule for sequential loop index (#976) This commit implements rule: A loop iteration variable for a sequential loop in a parallel or task generating construct is private in the innermost such construct that encloses the loop. A Simple example: ``` i = -1 <== Scope 0 j = -1 !$omp parallel <== Scope 1 print *,i,j <-- both are shared (Scope 0) !$omp parallel <== Scope 2 print *,i,j <-- a) i is shared (Scope 0), j is private (Scope 2) !$omp do <== Scope 3 do i=1, 10 <-- i is private (Scope 3) do j=1, 10 <-- b) j is private (Scope 2, not 3!) enddo enddo print *,i,j <-- c) i is shared (Scope 0), j is private (Scope 2) !$omp end parallel print *,i,j <-- both are shared (Scope 0) !$omp end parallel print *,i,j <-- both are shared (Scope 0) end ``` Ideally the above rule solves a), b), and c) but a) is left as a TODO because it is better to handle the data-sharing attribute conflicts along with the rules for "Predetermined DSA on Clauses". The basic idea is when visiting the `DoConstruct` node within an OpenMP construct, if the do-loop is not associated (like `i` loop is associated with `!$omp do`) AND the do-loop is in the parallel/task generating construct, resolve the loop index to be private to that innermost construct. In the above example, `j` loop is not associated (then it is sequential) and the innermost parallel/task generating construct that encloses the `j` loop is the `parallel` construct marked with `<== Scope 2`, so `j` is private to that construct. To do that, I also need to change the prototype of those `ResolveOmp*` functions to allow specifiying the `scope` because the new symbol for `j` should be created in Scope 2 and all the `symbol` field of `Name j` in that `parallel` construct should be fixed, such as c). --- include/flang/semantics/symbol.h | 2 +- lib/semantics/check-omp-structure.h | 2 + lib/semantics/resolve-names.cpp | 123 ++++++++++++++++++++++------ test/semantics/omp-symbol01.f90 | 2 +- test/semantics/omp-symbol04.f90 | 2 +- test/semantics/omp-symbol06.f90 | 2 +- test/semantics/omp-symbol08.f90 | 84 ++++++++++++++----- 7 files changed, 166 insertions(+), 51 deletions(-) diff --git a/include/flang/semantics/symbol.h b/include/flang/semantics/symbol.h index a27a935bdb00..5c45a8c5ad06 100644 --- a/include/flang/semantics/symbol.h +++ b/include/flang/semantics/symbol.h @@ -463,7 +463,7 @@ class Symbol { // OpenMP miscellaneous flags OmpCommonBlock, OmpReduction, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, OmpFlushed, OmpCriticalLock, - OmpIfSpecified, OmpNone); + OmpIfSpecified, OmpNone, OmpPreDetermined); using Flags = common::EnumSet; const Scope &owner() const { return *owner_; } diff --git a/lib/semantics/check-omp-structure.h b/lib/semantics/check-omp-structure.h index b20c32550769..e265b6bda909 100644 --- a/lib/semantics/check-omp-structure.h +++ b/lib/semantics/check-omp-structure.h @@ -88,6 +88,8 @@ static constexpr OmpDirectiveSet simdSet{ OmpDirective::TASKLOOP_SIMD, OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::TEAMS_DISTRIBUTE_SIMD}; +static constexpr OmpDirectiveSet taskGeneratingSet{ + OmpDirectiveSet{OmpDirective::TASK} | taskloopSet}; class OmpStructureChecker : public virtual BaseChecker { public: diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index 61e9ed02711d..b98b7bc14a8d 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -1167,6 +1167,7 @@ class OmpAttributeVisitor { void Post(const parser::OmpBeginLoopDirective &) { GetContext().withinConstruct = true; } + bool Pre(const parser::DoConstruct &); bool Pre(const parser::OpenMPSectionsConstruct &); void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } @@ -1224,27 +1225,34 @@ class OmpAttributeVisitor { void SetContextDirectiveEnum(OmpDirective dir) { GetContext().directive = dir; } - const Scope &currScope() { return GetContext().scope; } + Scope &currScope() { return GetContext().scope; } void SetContextDefaultDSA(Symbol::Flag flag) { GetContext().defaultDSA = flag; } + void AddToContextObjectWithDSA( + const Symbol &symbol, Symbol::Flag flag, OmpContext &context) { + context.objectWithDSA.emplace(&symbol, flag); + } void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { - GetContext().objectWithDSA.emplace(&symbol, flag); + AddToContextObjectWithDSA(symbol, flag, GetContext()); } bool IsObjectWithDSA(const Symbol &symbol) { auto it{GetContext().objectWithDSA.find(&symbol)}; return it != GetContext().objectWithDSA.end(); } + void SetContextAssociatedLoopLevel(std::size_t level) { GetContext().associatedLoopLevel = level; } std::size_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); - Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { - const auto pair{ - GetContext().scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; + Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) { + const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; return *pair.first->second; } + Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { + return MakeAssocSymbol(name, prev, currScope()); + } static const parser::Name *GetDesignatorNameIfDataRef( const parser::Designator &designator) { @@ -1277,14 +1285,17 @@ class OmpAttributeVisitor { const parser::ExecutionPartConstruct &); // Predetermined DSA rules void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &); + const parser::Name &GetLoopIndex(const parser::DoConstruct &); + void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); - Symbol *ResolveOmp(const parser::Name &, Symbol::Flag); - Symbol *ResolveOmp(Symbol &, Symbol::Flag); + Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); + Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); Symbol *ResolveOmpCommonBlockName(const parser::Name *); - Symbol *DeclarePrivateAccessEntity(const parser::Name &, Symbol::Flag); - Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag); + Symbol *DeclarePrivateAccessEntity( + const parser::Name &, Symbol::Flag, Scope &); + Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); void CheckMultipleAppearances( @@ -1380,6 +1391,7 @@ class ResolveNamesVisitor : public virtual ScopeHandler, void FinishSpecificationParts(const ProgramTree &); void FinishDerivedTypeInstantiation(Scope &); void ResolveExecutionParts(const ProgramTree &); + void ResolveOmpParts(const parser::ProgramUnit &); }; // ImplicitRules implementation @@ -5766,7 +5778,7 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { ResolveSpecificationParts(root); FinishSpecificationParts(root); ResolveExecutionParts(root); - OmpAttributeVisitor{context(), *this}.Walk(x); + ResolveOmpParts(x); return false; } @@ -6072,6 +6084,53 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { return true; } +const parser::Name &OmpAttributeVisitor::GetLoopIndex( + const parser::DoConstruct &x) { + auto &loopControl{x.GetLoopControl().value()}; + using Bounds = parser::LoopControl::Bounds; + const Bounds &bounds{std::get(loopControl.u)}; + return bounds.name.thing; +} + +void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( + const parser::Name &iv) { + auto targetIt{ompContext_.rbegin()}; + for (;; ++targetIt) { + if (targetIt == ompContext_.rend()) { + return; + } + if (parallelSet.test(targetIt->directive) || + taskGeneratingSet.test(targetIt->directive)) { + break; + } + } + if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { + targetIt++; + symbol->set(Symbol::Flag::OmpPreDetermined); + iv.symbol = symbol; // adjust the symbol within region + for (auto it{ompContext_.rbegin()}; it != targetIt; ++it) { + AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); + } + } +} + +// 2.15.1.1 Data-sharing Attribute Rules - Predetermined +// - A loop iteration variable for a sequential loop in a parallel +// or task generating construct is private in the innermost such +// construct that encloses the loop +bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { + if (!ompContext_.empty() && GetContext().withinConstruct) { + if (const auto &iv{GetLoopIndex(x)}; iv.symbol) { + if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) { + ResolveSeqLoopIndexInParallelOrTaskConstruct(iv); + } else { + // TODO: conflict checks with explicitly determined DSA + } + } + } + return true; +} + const parser::DoConstruct *OmpAttributeVisitor::GetDoConstructIf( const parser::ExecutionPartConstruct &x) { if (auto *y{std::get_if(&x.u)}) { @@ -6137,11 +6196,9 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex( auto &outer{std::get>(x.t)}; for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { // go through all the nested do-loops and resolve index variables - auto &loopControl{loop->GetLoopControl().value()}; - using Bounds = parser::LoopControl::Bounds; - const Bounds &bounds{std::get(loopControl.u)}; - const parser::Name &iv{bounds.name.thing}; - if (auto *symbol{ResolveOmp(iv, ivDSA)}) { + const parser::Name &iv{GetLoopIndex(*loop)}; + if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { + symbol->set(Symbol::Flag::OmpPreDetermined); iv.symbol = symbol; // adjust the symbol within region AddToContextObjectWithDSA(*symbol, ivDSA); } @@ -6207,7 +6264,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { // predetermined, explicitly determined, and implicitly // determined data-sharing attributes (2.15.1.1). if (Symbol * found{currScope().FindSymbol(name.source)}) { - if (IsObjectWithDSA(*found)) { + if (symbol != found) { name.symbol = found; // adjust the symbol within region } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { context_.Say(name.source, @@ -6250,7 +6307,7 @@ void OmpAttributeVisitor::ResolveOmpObject( common::visitors{ [&](const parser::Designator &designator) { if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { - if (auto *symbol{ResolveOmp(*name, ompFlag)}) { + if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { AddToContextObjectWithDSA(*symbol, ompFlag); if (dataSharingAttributeFlags.test(ompFlag)) { CheckMultipleAppearances(*name, *symbol, ompFlag); @@ -6288,7 +6345,8 @@ void OmpAttributeVisitor::ResolveOmpObject( for (const Symbol &object : symbol->get().objects()) { Symbol &mutableObject{const_cast(object)}; - if (auto *resolvedObject{ResolveOmp(mutableObject, ompFlag)}) { + if (auto *resolvedObject{ + ResolveOmp(mutableObject, ompFlag, currScope())}) { AddToContextObjectWithDSA(*resolvedObject, ompFlag); } } @@ -6303,35 +6361,36 @@ void OmpAttributeVisitor::ResolveOmpObject( } Symbol *OmpAttributeVisitor::ResolveOmp( - const parser::Name &name, Symbol::Flag ompFlag) { + const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(name, ompFlag); + return DeclarePrivateAccessEntity(name, ompFlag, scope); } else { return DeclareOrMarkOtherAccessEntity(name, ompFlag); } } -Symbol *OmpAttributeVisitor::ResolveOmp(Symbol &symbol, Symbol::Flag ompFlag) { +Symbol *OmpAttributeVisitor::ResolveOmp( + Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(symbol, ompFlag); + return DeclarePrivateAccessEntity(symbol, ompFlag, scope); } else { return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); } } Symbol *OmpAttributeVisitor::DeclarePrivateAccessEntity( - const parser::Name &name, Symbol::Flag ompFlag) { + const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { if (!name.symbol) { return nullptr; // not resolved by Name Resolution step, do nothing } - name.symbol = DeclarePrivateAccessEntity(*name.symbol, ompFlag); + name.symbol = DeclarePrivateAccessEntity(*name.symbol, ompFlag, scope); return name.symbol; } Symbol *OmpAttributeVisitor::DeclarePrivateAccessEntity( - Symbol &object, Symbol::Flag ompFlag) { + Symbol &object, Symbol::Flag ompFlag, Scope &scope) { if (object.owner() != currScope()) { - auto &symbol{MakeAssocSymbol(object.name(), object)}; + auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; symbol.set(ompFlag); return &symbol; } else { @@ -6455,6 +6514,18 @@ void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) { } } +void ResolveNamesVisitor::ResolveOmpParts(const parser::ProgramUnit &node) { + OmpAttributeVisitor{context(), *this}.Walk(node); + if (!context().AnyFatalError()) { + // The data-sharing attribute of the loop iteration variable for a + // sequential loop (2.15.1.1) can only be determined when visiting + // the corresponding DoConstruct, a second walk is to adjust the + // symbols for all the data-refs of that loop iteration variable + // prior to the DoConstruct. + OmpAttributeVisitor{context(), *this}.Walk(node); + } +} + void ResolveNamesVisitor::Post(const parser::Program &) { // ensure that all temps were deallocated CHECK(!attrs_); diff --git a/test/semantics/omp-symbol01.f90 b/test/semantics/omp-symbol01.f90 index eca885565fba..bec8e0450dd5 100644 --- a/test/semantics/omp-symbol01.f90 +++ b/test/semantics/omp-symbol01.f90 @@ -45,7 +45,7 @@ program mm !DEF: /mm/c (Implicit) ObjectEntity REAL(4) c = 2.0 !$omp parallel do private(a,t,/c/) shared(c) - !DEF: /mm/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /mm/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,10 !DEF: /mm/Block1/a (OmpPrivate) HostAssoc REAL(4) !REF: /mm/b diff --git a/test/semantics/omp-symbol04.f90 b/test/semantics/omp-symbol04.f90 index 9daacfb49854..4824c78dc92b 100644 --- a/test/semantics/omp-symbol04.f90 +++ b/test/semantics/omp-symbol04.f90 @@ -12,7 +12,7 @@ !DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 2. !$omp do private(a) - !DEF: /MainProgram1/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /MainProgram1/Block1/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,10 !DEF: /MainProgram1/Block1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 1. diff --git a/test/semantics/omp-symbol06.f90 b/test/semantics/omp-symbol06.f90 index 3b82fc88e598..c1d7581db8be 100644 --- a/test/semantics/omp-symbol06.f90 +++ b/test/semantics/omp-symbol06.f90 @@ -8,7 +8,7 @@ !DEF: /MainProgram1/a (Implicit) ObjectEntity REAL(4) a = 1. !$omp parallel do firstprivate(a) lastprivate(a) - !DEF: /MainProgram1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /MainProgram1/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,10 !DEF: /MainProgram1/Block1/a (OmpFirstPrivate, OmpLastPrivate) HostAssoc REAL(4) a = 2. diff --git a/test/semantics/omp-symbol08.f90 b/test/semantics/omp-symbol08.f90 index eaf8ff2eaa9d..ac09e1690677 100644 --- a/test/semantics/omp-symbol08.f90 +++ b/test/semantics/omp-symbol08.f90 @@ -8,6 +8,9 @@ ! increment of the associated do-loop. ! c) The loop iteration variables in the associated do-loops of a simd ! construct with multiple associated do-loops are lastprivate. +! d) A loop iteration variable for a sequential loop in a parallel or task +! generating construct is private in the innermost such construct that +! encloses the loop. ! - TBD ! All the tests assume that the do-loops association for collapse/ordered @@ -28,16 +31,16 @@ subroutine test_do !REF: /test_do/i i = 99 !$omp do collapse(2) - !DEF: /test_do/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_do/Block1/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,5 - !DEF: /test_do/Block1/Block1/j (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_do/Block1/Block1/j (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do j=6,10 !REF: /test_do/a a(1,1,1) = 0. - !REF: /test_do/k + !DEF: /test_do/Block1/k (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do k=11,15 !REF: /test_do/a - !REF: /test_do/k + !REF: /test_do/Block1/k !REF: /test_do/Block1/Block1/j !REF: /test_do/Block1/Block1/i a(k,j,i) = 1. @@ -58,13 +61,13 @@ subroutine test_pardo !DEF: /test_pardo/k ObjectEntity INTEGER(4) integer i, j, k !$omp parallel do collapse(2) private(k) ordered(2) - !DEF: /test_pardo/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_pardo/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,5 - !DEF: /test_pardo/Block1/j (OmpPrivate) HostAssoc INTEGER(4) - do j=6,10 + !DEF: /test_pardo/Block1/j (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) + do j=6,10 !REF: /test_pardo/a a(1,1,1) = 0. - !DEF: /test_pardo/Block1/k (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_pardo/Block1/k (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do k=11,15 !REF: /test_pardo/a !REF: /test_pardo/Block1/k @@ -86,9 +89,9 @@ subroutine test_taskloop !DEF: /test_taskloop/j ObjectEntity INTEGER(4) integer i, j !$omp taskloop private(j) - !DEF: /test_taskloop/Block1/i (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_taskloop/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,5 - !DEF: /test_taskloop/Block1/j (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /test_taskloop/Block1/j (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) !REF: /test_taskloop/Block1/i do j=1,i !REF: /test_taskloop/a @@ -129,13 +132,13 @@ subroutine dotprod (b, c, n, block_size, num_teams, block_threads) !$omp target map(to:b,c) map(tofrom:sum) !$omp teams num_teams(num_teams) thread_limit(block_threads) reduction(+:sum) !$omp distribute - !DEF: /dotprod/Block1/Block1/Block1/i0 (OmpPrivate) HostAssoc INTEGER(4) + !DEF: /dotprod/Block1/Block1/Block1/i0 (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) !REF: /dotprod/n !REF: /dotprod/block_size do i0=1,n,block_size !$omp parallel do reduction(+:sum) - !DEF: /dotprod/Block1/Block1/Block1/Block1/i (OmpPrivate) HostAssoc INTEGER(4) - !REF: /dotprod/i0 + !DEF: /dotprod/Block1/Block1/Block1/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) + !REF: /dotprod/Block1/Block1/Block1/i0 !DEF: /dotprod/min INTRINSIC (Function) ProcEntity !REF: /dotprod/block_size !REF: /dotprod/n @@ -165,15 +168,15 @@ subroutine test_simd !DEF: /test_simd/k ObjectEntity INTEGER(4) integer i, j, k !$omp parallel do simd - !DEF: /test_simd/Block1/i (OmpLinear) HostAssoc INTEGER(4) + !DEF: /test_simd/Block1/i (OmpLinear, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,5 - !REF: /test_simd/j + !DEF: /test_simd/Block1/j (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do j=6,10 - !REF: /test_simd/k + !DEF: /test_simd/Block1/k (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do k=11,15 !REF: /test_simd/a - !REF: /test_simd/k - !REF: /test_simd/j + !REF: /test_simd/Block1/k + !REF: /test_simd/Block1/j !REF: /test_simd/Block1/i a(k,j,i) = 3.14 end do @@ -192,11 +195,11 @@ subroutine test_simd_multi !DEF: /test_simd_multi/k ObjectEntity INTEGER(4) integer i, j, k !$omp parallel do simd collapse(3) - !DEF: /test_simd_multi/Block1/i (OmpLastPrivate) HostAssoc INTEGER(4) + !DEF: /test_simd_multi/Block1/i (OmpLastPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do i=1,5 - !DEF: /test_simd_multi/Block1/j (OmpLastPrivate) HostAssoc INTEGER(4) + !DEF: /test_simd_multi/Block1/j (OmpLastPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do j=6,10 - !DEF: /test_simd_multi/Block1/k (OmpLastPrivate) HostAssoc INTEGER(4) + !DEF: /test_simd_multi/Block1/k (OmpLastPrivate, OmpPreDetermined) HostAssoc INTEGER(4) do k=11,15 !REF: /test_simd_multi/a !REF: /test_simd_multi/Block1/k @@ -207,3 +210,42 @@ subroutine test_simd_multi end do end do end subroutine test_simd_multi + +! Rule d) +!DEF: /test_seq_loop (Subroutine) Subprogram +subroutine test_seq_loop + implicit none + !DEF: /test_seq_loop/i ObjectEntity INTEGER(4) + !DEF: /test_seq_loop/j ObjectEntity INTEGER(4) + integer i, j + !REF: /test_seq_loop/i + i = -1 + !REF: /test_seq_loop/j + j = -1 + !$omp parallel + !REF: /test_seq_loop/i + !REF: /test_seq_loop/j + print *, i, j + !$omp parallel + !REF: /test_seq_loop/i + !DEF: /test_seq_loop/Block1/Block1/j (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) + print *, i, j + !$omp do + !DEF: /test_seq_loop/Block1/Block1/Block1/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4) + do i=1,10 + !REF: /test_seq_loop/Block1/Block1/j + do j=1,10 + end do + end do + !REF: /test_seq_loop/i + !REF: /test_seq_loop/Block1/Block1/j + print *, i, j + !$omp end parallel + !REF: /test_seq_loop/i + !REF: /test_seq_loop/j + print *, i, j + !$omp end parallel + !REF: /test_seq_loop/i + !REF: /test_seq_loop/j + print *, i, j +end subroutine test_seq_loop From 84752c492e910573e2f0ede1ed3c0417aac363b9 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 18 Feb 2020 17:14:24 -0800 Subject: [PATCH 033/345] Add FORALL checking to DoChecker FORALL statements and constructs require a lot of the same checking as DO CONCURRENT, so do the checks in DoChecker so that code can be shared where possible. This requires some reorganization there. Remove code from AssignmentChecker that did some of these checks. Change names that contain `DoVar` or `DoVariable` to `IndexVar` to reflect the fact that they may be DO or FORALL index variables. Distinguish between the two when necessary with enum `IndexVarKind`. Change some messages that referred to "concurrent-header" or "concurrent-control" to specifically say "DO CONCURRENT" or "FORALL". --- include/flang/evaluate/tools.h | 3 + include/flang/semantics/semantics.h | 27 ++-- lib/evaluate/tools.cpp | 4 + lib/semantics/assignment.cpp | 124 --------------- lib/semantics/assignment.h | 17 +-- lib/semantics/check-allocate.cpp | 2 +- lib/semantics/check-deallocate.cpp | 2 +- lib/semantics/check-do.cpp | 229 ++++++++++++++++++++-------- lib/semantics/check-do.h | 8 + lib/semantics/check-io.cpp | 4 +- lib/semantics/semantics.cpp | 71 ++++----- test/semantics/call11.f90 | 4 +- test/semantics/dosemantics02.f90 | 6 +- test/semantics/dosemantics04.f90 | 12 +- test/semantics/dosemantics05.f90 | 2 +- test/semantics/dosemantics09.f90 | 12 +- test/semantics/forall01.f90 | 52 +++++++ test/semantics/resolve35.f90 | 8 - 18 files changed, 300 insertions(+), 287 deletions(-) diff --git a/include/flang/evaluate/tools.h b/include/flang/evaluate/tools.h index 9a473619d5b0..d992e8eacff5 100644 --- a/include/flang/evaluate/tools.h +++ b/include/flang/evaluate/tools.h @@ -843,5 +843,8 @@ parser::Message *SayWithDeclaration( // of one to complain about, if any exist. std::optional FindImpureCall( const IntrinsicProcTable &, const Expr &); +std::optional FindImpureCall( + const IntrinsicProcTable &, const ProcedureRef &); + } #endif // FORTRAN_EVALUATE_TOOLS_H_ diff --git a/include/flang/semantics/semantics.h b/include/flang/semantics/semantics.h index e823f48dc397..b13f617108b4 100644 --- a/include/flang/semantics/semantics.h +++ b/include/flang/semantics/semantics.h @@ -150,19 +150,18 @@ class SemanticsContext { } void PopConstruct(); - // Check to see if a variable being redefined is a DO variable. If so, emit - // a message - void WarnDoVarRedefine(const parser::CharBlock &, const Symbol &); - void CheckDoVarRedefine(const parser::CharBlock &, const Symbol &); - void CheckDoVarRedefine(const parser::Variable &); - void CheckDoVarRedefine(const parser::Name &); - void ActivateDoVariable(const parser::Name &); - void DeactivateDoVariable(const parser::Name &); - bool IsActiveDoVariable(const Symbol &); + ENUM_CLASS(IndexVarKind, DO, FORALL) + // Check to see if a variable being redefined is a DO or FORALL index. + // If so, emit a message. + void WarnIndexVarRedefine(const parser::CharBlock &, const Symbol &); + void CheckIndexVarRedefine(const parser::CharBlock &, const Symbol &); + void CheckIndexVarRedefine(const parser::Variable &); + void CheckIndexVarRedefine(const parser::Name &); + void ActivateIndexVar(const parser::Name &, IndexVarKind); + void DeactivateIndexVar(const parser::Name &); private: - parser::CharBlock GetDoVariableLocation(const Symbol &); - void CheckDoVarRedefine( + void CheckIndexVarRedefine( const parser::CharBlock &, const Symbol &, parser::MessageFixedText &&); const common::IntrinsicTypeDefaultKinds &defaultKinds_; const common::LanguageFeatureControl languageFeatures_; @@ -180,7 +179,11 @@ class SemanticsContext { bool CheckError(bool); ConstructStack constructStack_; - std::map activeDoVariables_; + struct IndexVarInfo { + parser::CharBlock location; + IndexVarKind kind; + }; + std::map activeIndexVars_; }; class Semantics { diff --git a/lib/evaluate/tools.cpp b/lib/evaluate/tools.cpp index 8f9af2e1fa34..f082c496aee5 100644 --- a/lib/evaluate/tools.cpp +++ b/lib/evaluate/tools.cpp @@ -842,5 +842,9 @@ std::optional FindImpureCall( const IntrinsicProcTable &intrinsics, const Expr &expr) { return FindImpureCallHelper{intrinsics}(expr); } +std::optional FindImpureCall( + const IntrinsicProcTable &intrinsics, const ProcedureRef &proc) { + return FindImpureCallHelper{intrinsics}(proc); +} } diff --git a/lib/semantics/assignment.cpp b/lib/semantics/assignment.cpp index cb727efb2b66..aee651e42b19 100644 --- a/lib/semantics/assignment.cpp +++ b/lib/semantics/assignment.cpp @@ -46,20 +46,8 @@ struct Control { struct ForallContext { explicit ForallContext(const ForallContext *that) : outer{that} {} - std::optional GetActiveIntKind(const parser::CharBlock &name) const { - const auto iter{activeNames.find(name)}; - if (iter != activeNames.cend()) { - return {integerKind}; - } else if (outer) { - return outer->GetActiveIntKind(name); - } else { - return std::nullopt; - } - } - const ForallContext *outer{nullptr}; std::optional constructName; - int integerKind; std::vector control; std::optional maskExpr; std::set activeNames; @@ -89,10 +77,7 @@ class AssignmentContext { void Analyze(const parser::PointerAssignmentStmt &); void Analyze(const parser::WhereStmt &); void Analyze(const parser::WhereConstruct &); - void Analyze(const parser::ForallStmt &); void Analyze(const parser::ForallConstruct &); - void Analyze(const parser::ForallConstructStmt &); - void Analyze(const parser::ConcurrentHeader &); template void Analyze(const parser::UnlabeledStatement &stmt) { context_.set_location(stmt.source); @@ -120,9 +105,6 @@ class AssignmentContext { void Analyze(const parser::MaskedElsewhereStmt &); void Analyze(const parser::WhereConstruct::Elsewhere &); - int GetIntegerKind(const std::optional &); - void CheckForImpureCall(const SomeExpr &); - void CheckForImpureCall(const SomeExpr *); void CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, parser::CharBlock rhsSource, bool isPointerAssignment); @@ -142,8 +124,6 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { // Assignment statement analysis is in expression.cpp where user-defined // assignments can be recognized and replaced. if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) { - CheckForImpureCall(assignment->lhs); - CheckForImpureCall(assignment->rhs); if (forall_) { // TODO: Warn if some name in forall_->activeNames or its outer // contexts does not appear on LHS @@ -163,22 +143,6 @@ void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { } const SomeExpr &lhs{assignment->lhs}; const SomeExpr &rhs{assignment->rhs}; - CheckForImpureCall(lhs); - CheckForImpureCall(rhs); - std::visit( - common::visitors{[&](const evaluate::Assignment::BoundsSpec &bounds) { - for (const auto &bound : bounds) { - CheckForImpureCall(SomeExpr{bound}); - } - }, - [&](const evaluate::Assignment::BoundsRemapping &bounds) { - for (const auto &bound : bounds) { - CheckForImpureCall(SomeExpr{bound.first}); - CheckForImpureCall(SomeExpr{bound.second}); - } - }, - [](const auto &) { DIE("not valid for pointer assignment"); }}, - assignment->u); if (forall_) { // TODO: Warn if some name in forall_->activeNames or its outer // contexts does not appear on LHS @@ -216,32 +180,6 @@ void AssignmentContext::Analyze(const parser::WhereConstruct &construct) { std::get>(construct.t)); } -void AssignmentContext::Analyze(const parser::ForallStmt &stmt) { - CHECK(!where_); - ForallContext forall{forall_}; - AssignmentContext nested{*this, forall}; - nested.Analyze( - std::get>(stmt.t)); - nested.Analyze( - std::get>( - stmt.t)); -} - -// N.B. Construct name matching is checked during label resolution; -// index name distinction is checked during name resolution. -void AssignmentContext::Analyze(const parser::ForallConstruct &construct) { - CHECK(!where_); - ForallContext forall{forall_}; - AssignmentContext nested{*this, forall}; - nested.Analyze( - std::get>(construct.t)); - nested.Analyze(std::get>(construct.t)); -} - -void AssignmentContext::Analyze(const parser::ForallConstructStmt &stmt) { - Analyze(std::get>(stmt.t)); -} - void AssignmentContext::Analyze( const parser::WhereConstruct::MaskedElsewhere &elsewhere) { CHECK(where_); @@ -279,56 +217,6 @@ void AssignmentContext::Analyze( Analyze(std::get>(elsewhere.t)); } -void AssignmentContext::Analyze(const parser::ConcurrentHeader &header) { - DEREF(forall_).integerKind = GetIntegerKind( - std::get>(header.t)); - for (const auto &control : - std::get>(header.t)) { - const parser::Name &name{std::get(control.t)}; - bool inserted{forall_->activeNames.insert(name.source).second}; - CHECK(inserted || context_.HasError(name)); - CheckForImpureCall(GetExpr(std::get<1>(control.t))); - CheckForImpureCall(GetExpr(std::get<2>(control.t))); - if (const auto &stride{std::get<3>(control.t)}) { - CheckForImpureCall(GetExpr(*stride)); - } - } - if (const auto &mask{ - std::get>(header.t)}) { - CheckForImpureCall(GetExpr(*mask)); - } -} - -int AssignmentContext::GetIntegerKind( - const std::optional &spec) { - std::optional empty; - evaluate::Expr kind{AnalyzeKindSelector( - context_, TypeCategory::Integer, spec ? spec->v : empty)}; - if (auto value{evaluate::ToInt64(kind)}) { - return static_cast(*value); - } else { - context_.Say("Kind of INTEGER type must be a constant value"_err_en_US); - return context_.GetDefaultKind(TypeCategory::Integer); - } -} - -void AssignmentContext::CheckForImpureCall(const SomeExpr &expr) { - if (forall_) { - const auto &intrinsics{context_.foldingContext().intrinsics()}; - if (auto bad{FindImpureCall(intrinsics, expr)}) { - context_.Say( - "Impure procedure '%s' may not be referenced in a FORALL"_err_en_US, - *bad); - } - } -} - -void AssignmentContext::CheckForImpureCall(const SomeExpr *expr) { - if (expr) { - CheckForImpureCall(*expr); - } -} - // C1594 checks static bool IsPointerDummyOfPureFunction(const Symbol &x) { return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) && @@ -449,18 +337,12 @@ MaskExpr AssignmentContext::GetMask( const parser::LogicalExpr &logicalExpr, bool defaultValue) { MaskExpr mask{defaultValue}; if (const SomeExpr * expr{GetExpr(logicalExpr)}) { - CheckForImpureCall(*expr); auto *logical{std::get_if>(&expr->u)}; mask = evaluate::ConvertTo(mask, common::Clone(DEREF(logical))); } return mask; } -void AnalyzeConcurrentHeader( - SemanticsContext &context, const parser::ConcurrentHeader &header) { - AssignmentContext{context}.Analyze(header); -} - AssignmentChecker::~AssignmentChecker() {} AssignmentChecker::AssignmentChecker(SemanticsContext &context) @@ -477,12 +359,6 @@ void AssignmentChecker::Enter(const parser::WhereStmt &x) { void AssignmentChecker::Enter(const parser::WhereConstruct &x) { context_.value().Analyze(x); } -void AssignmentChecker::Enter(const parser::ForallStmt &x) { - context_.value().Analyze(x); -} -void AssignmentChecker::Enter(const parser::ForallConstruct &x) { - context_.value().Analyze(x); -} } template class Fortran::common::Indirection< diff --git a/lib/semantics/assignment.h b/lib/semantics/assignment.h index 4bce8cb16dd5..d86bd45b1823 100644 --- a/lib/semantics/assignment.h +++ b/lib/semantics/assignment.h @@ -12,24 +12,20 @@ #include "flang/common/indirection.h" #include "flang/evaluate/expression.h" #include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" -#include namespace Fortran::parser { -template struct Statement; +class ContextualMessages; struct AssignmentStmt; -struct ConcurrentHeader; -struct ForallStmt; struct PointerAssignmentStmt; -struct Program; struct WhereStmt; struct WhereConstruct; -struct ForallConstruct; } namespace Fortran::semantics { class AssignmentContext; +class Scope; +class Symbol; // Applies checks from C1594(1-2) on definitions in pure subprograms void CheckDefinabilityInPureScope(parser::ContextualMessages &, const Symbol &, @@ -46,18 +42,11 @@ class AssignmentChecker : public virtual BaseChecker { void Enter(const parser::PointerAssignmentStmt &); void Enter(const parser::WhereStmt &); void Enter(const parser::WhereConstruct &); - void Enter(const parser::ForallStmt &); - void Enter(const parser::ForallConstruct &); private: common::Indirection context_; }; -// R1125 concurrent-header is used in FORALL statements & constructs as -// well as in DO CONCURRENT loops. -void AnalyzeConcurrentHeader( - SemanticsContext &, const parser::ConcurrentHeader &); - } extern template class Fortran::common::Indirection< diff --git a/lib/semantics/check-allocate.cpp b/lib/semantics/check-allocate.cpp index 83f3ae9ebfc6..32ceb3805729 100644 --- a/lib/semantics/check-allocate.cpp +++ b/lib/semantics/check-allocate.cpp @@ -531,7 +531,7 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) { "Allocatable object declared here with rank %d"_en_US, rank_); return false; } - context.CheckDoVarRedefine(name_); + context.CheckIndexVarRedefine(name_); return RunCoarrayRelatedChecks(context); } diff --git a/lib/semantics/check-deallocate.cpp b/lib/semantics/check-deallocate.cpp index 3f48fb4cd03f..6a0ea45ebb43 100644 --- a/lib/semantics/check-deallocate.cpp +++ b/lib/semantics/check-deallocate.cpp @@ -30,7 +30,7 @@ void DeallocateChecker::Leave(const parser::DeallocateStmt &deallocateStmt) { context_.Say(name.source, "name in DEALLOCATE statement must have the ALLOCATABLE or POINTER attribute"_err_en_US); } else { - context_.CheckDoVarRedefine(name); + context_.CheckIndexVarRedefine(name); } }, [&](const parser::StructureComponent &structureComponent) { diff --git a/lib/semantics/check-do.cpp b/lib/semantics/check-do.cpp index 75acd1b887f6..10596c31cd21 100644 --- a/lib/semantics/check-do.cpp +++ b/lib/semantics/check-do.cpp @@ -34,13 +34,31 @@ namespace Fortran::semantics { using namespace parser::literals; using Bounds = parser::LoopControl::Bounds; +using IndexVarKind = SemanticsContext::IndexVarKind; -static const std::list &GetControls( +static const parser::ConcurrentHeader &GetConcurrentHeader( const parser::LoopControl &loopControl) { const auto &concurrent{ std::get(loopControl.u)}; - const auto &header{std::get(concurrent.t)}; - return std::get>(header.t); + return std::get(concurrent.t); +} +static const parser::ConcurrentHeader &GetConcurrentHeader( + const parser::ForallConstruct &construct) { + const auto &stmt{ + std::get>(construct.t)}; + return std::get>( + stmt.statement.t) + .value(); +} +static const parser::ConcurrentHeader &GetConcurrentHeader( + const parser::ForallStmt &stmt) { + return std::get>(stmt.t) + .value(); +} +template +static const std::list &GetControls(const T &x) { + return std::get>( + GetConcurrentHeader(x).t); } static const Bounds &GetBounds(const parser::DoConstruct &doConstruct) { @@ -366,10 +384,11 @@ class DoConcurrentVariableEnforce { const Scope &blockScope_; }; // class DoConcurrentVariableEnforce -// Find a DO statement and enforce semantics checks on its body +// Find a DO or FORALL and enforce semantics checks on its body class DoContext { public: - DoContext(SemanticsContext &context) : context_{context} {} + DoContext(SemanticsContext &context, IndexVarKind kind) + : context_{context}, kind_{kind} {} // Mark this DO construct as a point of definition for the DO variables // or index-names it contains. If they're already defined, emit an error @@ -378,13 +397,10 @@ class DoContext { // the DO construct and use its location in error messages. void DefineDoVariables(const parser::DoConstruct &doConstruct) { if (doConstruct.IsDoNormal()) { - context_.ActivateDoVariable(GetDoVariable(doConstruct)); + context_.ActivateIndexVar(GetDoVariable(doConstruct), IndexVarKind::DO); } else if (doConstruct.IsDoConcurrent()) { if (const auto &loopControl{doConstruct.GetLoopControl()}) { - const auto &controls{GetControls(*loopControl)}; - for (const parser::ConcurrentControl &control : controls) { - context_.ActivateDoVariable(std::get(control.t)); - } + ActivateIndexVars(GetControls(*loopControl)); } } } @@ -392,17 +408,26 @@ class DoContext { // Called at the end of a DO construct to deactivate the DO construct void ResetDoVariables(const parser::DoConstruct &doConstruct) { if (doConstruct.IsDoNormal()) { - context_.DeactivateDoVariable(GetDoVariable(doConstruct)); + context_.DeactivateIndexVar(GetDoVariable(doConstruct)); } else if (doConstruct.IsDoConcurrent()) { if (const auto &loopControl{doConstruct.GetLoopControl()}) { - const auto &controls{GetControls(*loopControl)}; - for (const parser::ConcurrentControl &control : controls) { - context_.DeactivateDoVariable(std::get(control.t)); - } + DeactivateIndexVars(GetControls(*loopControl)); } } } + void ActivateIndexVars(const std::list &controls) { + for (const auto &control : controls) { + context_.ActivateIndexVar(std::get(control.t), kind_); + } + } + void DeactivateIndexVars( + const std::list &controls) { + for (const auto &control : controls) { + context_.DeactivateIndexVar(std::get(control.t)); + } + } + void Check(const parser::DoConstruct &doConstruct) { if (doConstruct.IsDoConcurrent()) { CheckDoConcurrent(doConstruct); @@ -415,6 +440,46 @@ class DoContext { // TODO: handle the other cases } + void Check(const parser::ForallStmt &stmt) { + CheckConcurrentHeader(GetConcurrentHeader(stmt)); + } + void Check(const parser::ForallConstruct &construct) { + CheckConcurrentHeader(GetConcurrentHeader(construct)); + } + + void Check(const parser::ForallAssignmentStmt &stmt) { + const evaluate::Assignment *assignment{std::visit( + common::visitors{[&](const auto &x) { return GetAssignment(x); }}, + stmt.u)}; + if (assignment) { + CheckForImpureCall(assignment->lhs); + CheckForImpureCall(assignment->rhs); + if (const auto *proc{ + std::get_if(&assignment->u)}) { + CheckForImpureCall(*proc); + } + std::visit( + common::visitors{ + [](const evaluate::Assignment::Intrinsic &) {}, + [&](const evaluate::ProcedureRef &proc) { + CheckForImpureCall(proc); + }, + [&](const evaluate::Assignment::BoundsSpec &bounds) { + for (const auto &bound : bounds) { + CheckForImpureCall(SomeExpr{bound}); + } + }, + [&](const evaluate::Assignment::BoundsRemapping &bounds) { + for (const auto &bound : bounds) { + CheckForImpureCall(SomeExpr{bound.first}); + CheckForImpureCall(SomeExpr{bound.second}); + } + }, + }, + assignment->u); + } + } + private: void SayBadDoControl(parser::CharBlock sourceLocation) { context_.Say(sourceLocation, "DO controls should be INTEGER"_err_en_US); @@ -493,11 +558,9 @@ class DoContext { "DO CONCURRENT"}; parser::Walk(block, doConcurrentLabelEnforce); - const auto &loopControl{ - std::get>(doStmt.statement.t)}; - const auto &concurrent{ - std::get(loopControl->u)}; - CheckConcurrentLoopControl(concurrent, block); + const auto &loopControl{doConstruct.GetLoopControl()}; + CheckConcurrentLoopControl(*loopControl); + CheckLocalitySpecs(*loopControl, block); } // Return a set of symbols whose names are in a Local locality-spec. Look @@ -543,9 +606,9 @@ class DoContext { SymbolSet references{GatherSymbolsFromExpression(mask.thing.thing.value())}; for (const Symbol &ref : references) { if (IsProcedure(ref) && !IsPureProcedure(ref)) { - context_.SayWithDecl(ref, currentStatementSourcePosition_, - "Concurrent-header mask expression cannot reference an impure" - " procedure"_err_en_US); + context_.SayWithDecl(ref, parser::Unwrap(mask)->source, + "%s mask expression may not reference impure procedure '%s'"_err_en_US, + LoopKindName(), ref.name()); return; } } @@ -556,8 +619,8 @@ class DoContext { const parser::CharBlock &refPosition) const { for (const Symbol &ref : refs) { if (uses.find(ref) != uses.end()) { - context_.SayWithDecl( - ref, refPosition, std::move(errorMessage), ref.name()); + context_.SayWithDecl(ref, refPosition, std::move(errorMessage), + LoopKindName(), ref.name()); return; } } @@ -567,7 +630,7 @@ class DoContext { const SymbolSet &indexNames, const parser::ScalarIntExpr &expr) const { CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), indexNames, - "concurrent-control expression references index-name '%s'"_err_en_US, + "%s limit expression may not reference index variable '%s'"_err_en_US, expr.thing.thing.value().source); } @@ -576,7 +639,7 @@ class DoContext { const parser::ScalarLogicalExpr &mask, const SymbolSet &localVars) const { CheckNoCollisions(GatherSymbolsFromExpression(mask.thing.thing.value()), localVars, - "concurrent-header mask-expr references variable '%s'" + "%s mask expression references variable '%s'" " in LOCAL locality-spec"_err_en_US, mask.thing.thing.value().source); } @@ -587,7 +650,7 @@ class DoContext { const parser::ScalarIntExpr &expr, const SymbolSet &localVars) const { CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), localVars, - "concurrent-header expression references variable '%s'" + "%s expression references variable '%s'" " in LOCAL locality-spec"_err_en_US, expr.thing.thing.value().source); } @@ -618,40 +681,47 @@ class DoContext { // C1123, concurrent limit or step expressions can't reference index-names void CheckConcurrentHeader(const parser::ConcurrentHeader &header) const { + if (const auto &mask{ + std::get>(header.t)}) { + CheckMaskIsPure(*mask); + } auto &controls{std::get>(header.t)}; SymbolSet indexNames; - for (const auto &c : controls) { - const auto &indexName{std::get(c.t)}; + for (const parser::ConcurrentControl &control : controls) { + const auto &indexName{std::get(control.t)}; if (indexName.symbol) { indexNames.insert(*indexName.symbol); } } if (!indexNames.empty()) { - for (const auto &c : controls) { - HasNoReferences(indexNames, std::get<1>(c.t)); - HasNoReferences(indexNames, std::get<2>(c.t)); - if (const auto &expr{ - std::get>(c.t)}) { - HasNoReferences(indexNames, *expr); - if (IsZero(*expr)) { - context_.Say(expr->thing.thing.value().source, - "DO CONCURRENT step expression should not be zero"_err_en_US); + for (const parser::ConcurrentControl &control : controls) { + HasNoReferences(indexNames, std::get<1>(control.t)); + HasNoReferences(indexNames, std::get<2>(control.t)); + if (const auto &intExpr{ + std::get>(control.t)}) { + const parser::Expr &expr{intExpr->thing.thing.value()}; + CheckNoCollisions(GatherSymbolsFromExpression(expr), indexNames, + "%s step expression may not reference index variable '%s'"_err_en_US, + expr.source); + if (IsZero(expr)) { + context_.Say(expr.source, + "%s step expression may not be zero"_err_en_US, LoopKindName()); } } } } } - void CheckLocalitySpecs(const parser::LoopControl::Concurrent &concurrent, - const parser::Block &block) const { + void CheckLocalitySpecs( + const parser::LoopControl &control, const parser::Block &block) const { + const auto &concurrent{ + std::get(control.u)}; const auto &header{std::get(concurrent.t)}; - const auto &controls{ - std::get>(header.t)}; const auto &localitySpecs{ std::get>(concurrent.t)}; if (!localitySpecs.empty()) { const SymbolSet &localVars{GatherLocals(localitySpecs)}; - for (const auto &c : controls) { + for (const auto &c : GetControls(control)) { CheckExprDoesNotReferenceLocal(std::get<1>(c.t), localVars); CheckExprDoesNotReferenceLocal(std::get<2>(c.t), localVars); if (const auto &expr{ @@ -668,35 +738,66 @@ class DoContext { } // check constraints [C1121 .. C1130] - void CheckConcurrentLoopControl( - const parser::LoopControl::Concurrent &concurrent, - const parser::Block &block) const { + void CheckConcurrentLoopControl(const parser::LoopControl &control) const { + const auto &concurrent{ + std::get(control.u)}; + CheckConcurrentHeader(std::get(concurrent.t)); + } - const auto &header{std::get(concurrent.t)}; - const auto &mask{ - std::get>(header.t)}; - if (mask) { - CheckMaskIsPure(*mask); + template void CheckForImpureCall(const T &x) { + const auto &intrinsics{context_.foldingContext().intrinsics()}; + if (auto bad{FindImpureCall(intrinsics, x)}) { + context_.Say( + "Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad, + LoopKindName()); } - CheckConcurrentHeader(header); - CheckLocalitySpecs(concurrent, block); + } + + // For messages where the DO loop must be DO CONCURRENT, make that explicit. + const char *LoopKindName() const { + return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL"; } SemanticsContext &context_; + const IndexVarKind kind_; parser::CharBlock currentStatementSourcePosition_; }; // class DoContext void DoChecker::Enter(const parser::DoConstruct &doConstruct) { - DoContext doContext{context_}; + DoContext doContext{context_, IndexVarKind::DO}; doContext.DefineDoVariables(doConstruct); } void DoChecker::Leave(const parser::DoConstruct &doConstruct) { - DoContext doContext{context_}; + DoContext doContext{context_, IndexVarKind::DO}; doContext.Check(doConstruct); doContext.ResetDoVariables(doConstruct); } +void DoChecker::Enter(const parser::ForallConstruct &construct) { + DoContext doContext{context_, IndexVarKind::FORALL}; + doContext.ActivateIndexVars(GetControls(construct)); +} +void DoChecker::Leave(const parser::ForallConstruct &construct) { + DoContext doContext{context_, IndexVarKind::FORALL}; + doContext.Check(construct); + doContext.DeactivateIndexVars(GetControls(construct)); +} + +void DoChecker::Enter(const parser::ForallStmt &stmt) { + DoContext doContext{context_, IndexVarKind::FORALL}; + doContext.ActivateIndexVars(GetControls(stmt)); +} +void DoChecker::Leave(const parser::ForallStmt &stmt) { + DoContext doContext{context_, IndexVarKind::FORALL}; + doContext.Check(stmt); + doContext.DeactivateIndexVars(GetControls(stmt)); +} +void DoChecker::Leave(const parser::ForallAssignmentStmt &stmt) { + DoContext doContext{context_, IndexVarKind::FORALL}; + doContext.Check(stmt); +} + // Return the (possibly null) name of the ConstructNode static const parser::Name *MaybeGetNodeName(const ConstructNode &construct) { return std::visit( @@ -819,7 +920,7 @@ void DoChecker::Enter(const parser::ExitStmt &exitStmt) { void DoChecker::Leave(const parser::AssignmentStmt &stmt) { const auto &variable{std::get(stmt.t)}; - context_.CheckDoVarRedefine(variable); + context_.CheckIndexVarRedefine(variable); } static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, @@ -829,9 +930,9 @@ static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, if (const SomeExpr * argExpr{arg.UnwrapExpr()}) { if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { if (intent == common::Intent::Out) { - context.CheckDoVarRedefine(location, *var); + context.CheckIndexVarRedefine(location, *var); } else { - context.WarnDoVarRedefine(location, *var); // INTENT(INOUT) + context.WarnIndexVarRedefine(location, *var); // INTENT(INOUT) } } } @@ -873,7 +974,7 @@ void DoChecker::Leave(const parser::ConnectSpec &connectSpec) { const auto *newunit{ std::get_if(&connectSpec.u)}; if (newunit) { - context_.CheckDoVarRedefine(newunit->v.thing.thing); + context_.CheckIndexVarRedefine(newunit->v.thing.thing); } } @@ -909,25 +1010,25 @@ void DoChecker::Leave(const parser::InquireSpec &inquireSpec) { const auto *intVar{std::get_if(&inquireSpec.u)}; if (intVar) { const auto &scalar{std::get(intVar->t)}; - context_.CheckDoVarRedefine(scalar.thing.thing); + context_.CheckIndexVarRedefine(scalar.thing.thing); } } void DoChecker::Leave(const parser::IoControlSpec &ioControlSpec) { const auto *size{std::get_if(&ioControlSpec.u)}; if (size) { - context_.CheckDoVarRedefine(size->v.thing.thing); + context_.CheckIndexVarRedefine(size->v.thing.thing); } } void DoChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { const auto &control{std::get(outputImpliedDo.t)}; const parser::Name &name{control.name.thing.thing}; - context_.CheckDoVarRedefine(name.source, *name.symbol); + context_.CheckIndexVarRedefine(name.source, *name.symbol); } void DoChecker::Leave(const parser::StatVariable &statVariable) { - context_.CheckDoVarRedefine(statVariable.v.thing.thing); + context_.CheckIndexVarRedefine(statVariable.v.thing.thing); } } // namespace Fortran::semantics diff --git a/lib/semantics/check-do.h b/lib/semantics/check-do.h index 03d8c75212b1..fb3a4be9b082 100644 --- a/lib/semantics/check-do.h +++ b/lib/semantics/check-do.h @@ -20,6 +20,9 @@ struct CycleStmt; struct DoConstruct; struct ExitStmt; struct Expr; +struct ForallAssignmentStmt; +struct ForallConstruct; +struct ForallStmt; struct InquireSpec; struct IoControlSpec; struct OutputImpliedDo; @@ -40,6 +43,11 @@ class DoChecker : public virtual BaseChecker { void Enter(const parser::CycleStmt &); void Enter(const parser::DoConstruct &); void Leave(const parser::DoConstruct &); + void Enter(const parser::ForallConstruct &); + void Leave(const parser::ForallConstruct &); + void Enter(const parser::ForallStmt &); + void Leave(const parser::ForallStmt &); + void Leave(const parser::ForallAssignmentStmt &s); void Enter(const parser::ExitStmt &); void Leave(const parser::Expr &); void Leave(const parser::InquireSpec &); diff --git a/lib/semantics/check-io.cpp b/lib/semantics/check-io.cpp index 6f824a3164a1..abbf915b675f 100644 --- a/lib/semantics/check-io.cpp +++ b/lib/semantics/check-io.cpp @@ -509,7 +509,7 @@ static void CheckForDoVariableInNamelist(const Symbol &namelist, SemanticsContext &context, parser::CharBlock namelistLocation) { const auto &details{namelist.GetUltimate().get()}; for (const Symbol &object : details.objects()) { - context.CheckDoVarRedefine(namelistLocation, object); + context.CheckIndexVarRedefine(namelistLocation, object); } } @@ -532,7 +532,7 @@ static void CheckForDoVariable( for (const auto &item : items) { if (const parser::Variable * variable{std::get_if(&item.u)}) { - context.CheckDoVarRedefine(*variable); + context.CheckIndexVarRedefine(*variable); } } } diff --git a/lib/semantics/semantics.cpp b/lib/semantics/semantics.cpp index 8c5bece6f72e..c6353a7b0d44 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/semantics/semantics.cpp @@ -203,78 +203,63 @@ void SemanticsContext::PopConstruct() { constructStack_.pop_back(); } -void SemanticsContext::CheckDoVarRedefine(const parser::CharBlock &location, +void SemanticsContext::CheckIndexVarRedefine(const parser::CharBlock &location, const Symbol &variable, parser::MessageFixedText &&message) { if (const Symbol * root{GetAssociationRoot(variable)}) { - if (IsActiveDoVariable(*root)) { - parser::CharBlock doLoc{GetDoVariableLocation(*root)}; - CHECK(doLoc != parser::CharBlock{}); - Say(location, std::move(message), root->name()) - .Attach(doLoc, "Enclosing DO construct"_en_US); + auto it{activeIndexVars_.find(*root)}; + if (it != activeIndexVars_.end()) { + std::string kind{EnumToString(it->second.kind)}; + Say(location, std::move(message), kind, root->name()) + .Attach(it->second.location, "Enclosing %s construct"_en_US, kind); } } } -void SemanticsContext::WarnDoVarRedefine( +void SemanticsContext::WarnIndexVarRedefine( const parser::CharBlock &location, const Symbol &variable) { - CheckDoVarRedefine( - location, variable, "Possible redefinition of DO variable '%s'"_en_US); + CheckIndexVarRedefine( + location, variable, "Possible redefinition of %s variable '%s'"_en_US); } -void SemanticsContext::CheckDoVarRedefine( +void SemanticsContext::CheckIndexVarRedefine( const parser::CharBlock &location, const Symbol &variable) { - CheckDoVarRedefine( - location, variable, "Cannot redefine DO variable '%s'"_err_en_US); + CheckIndexVarRedefine( + location, variable, "Cannot redefine %s variable '%s'"_err_en_US); } -void SemanticsContext::CheckDoVarRedefine(const parser::Variable &variable) { +void SemanticsContext::CheckIndexVarRedefine(const parser::Variable &variable) { if (const Symbol * entity{GetLastName(variable).symbol}) { - const parser::CharBlock &sourceLocation{variable.GetSource()}; - CheckDoVarRedefine(sourceLocation, *entity); + CheckIndexVarRedefine(variable.GetSource(), *entity); } } -void SemanticsContext::CheckDoVarRedefine(const parser::Name &name) { - const parser::CharBlock &sourceLocation{name.source}; +void SemanticsContext::CheckIndexVarRedefine(const parser::Name &name) { if (const Symbol * entity{name.symbol}) { - CheckDoVarRedefine(sourceLocation, *entity); + CheckIndexVarRedefine(name.source, *entity); } } -void SemanticsContext::ActivateDoVariable(const parser::Name &name) { - CheckDoVarRedefine(name); - if (const Symbol * doVariable{name.symbol}) { - if (const Symbol * root{GetAssociationRoot(*doVariable)}) { - if (!IsActiveDoVariable(*root)) { - activeDoVariables_.emplace(*root, name.source); - } +void SemanticsContext::ActivateIndexVar( + const parser::Name &name, IndexVarKind kind) { + CheckIndexVarRedefine(name); + if (const Symbol * indexVar{name.symbol}) { + if (const Symbol * root{GetAssociationRoot(*indexVar)}) { + activeIndexVars_.emplace(*root, IndexVarInfo{name.source, kind}); } } } -void SemanticsContext::DeactivateDoVariable(const parser::Name &name) { - if (Symbol * doVariable{name.symbol}) { - if (const Symbol * root{GetAssociationRoot(*doVariable)}) { - if (name.source == GetDoVariableLocation(*root)) { - activeDoVariables_.erase(*root); +void SemanticsContext::DeactivateIndexVar(const parser::Name &name) { + if (Symbol * indexVar{name.symbol}) { + if (const Symbol * root{GetAssociationRoot(*indexVar)}) { + auto it{activeIndexVars_.find(*root)}; + if (it != activeIndexVars_.end() && it->second.location == name.source) { + activeIndexVars_.erase(it); } } } } -bool SemanticsContext::IsActiveDoVariable(const Symbol &variable) { - return activeDoVariables_.find(variable) != activeDoVariables_.end(); -} - -parser::CharBlock SemanticsContext::GetDoVariableLocation( - const Symbol &variable) { - if (IsActiveDoVariable(variable)) { - return activeDoVariables_[variable]; - } else { - return parser::CharBlock{}; - } -} - bool Semantics::Perform() { return ValidateLabels(context_, program_) && parser::CanonicalizeDo(program_) && // force line break diff --git a/test/semantics/call11.f90 b/test/semantics/call11.f90 index 061b73d2d374..254566fa38b8 100644 --- a/test/semantics/call11.f90 +++ b/test/semantics/call11.f90 @@ -36,7 +36,7 @@ subroutine test !ERROR: Impure procedure 'impure' may not be referenced in a FORALL a(j) = pure(impure(j)) ! C1037 end forall - !ERROR: Concurrent-header mask expression cannot reference an impure procedure + !ERROR: DO CONCURRENT mask expression may not reference impure procedure 'impure' do concurrent (j=1:1, impure(j) /= 0) ! C1121 !ERROR: Call to an impure procedure is not allowed in DO CONCURRENT a(j) = impure(j) ! C1139 @@ -58,7 +58,7 @@ subroutine test2 do concurrent (j=1:1, x%tbp_pure(j) /= 0) ! ok a(j) = x%tbp_pure(j) ! ok end do - !ERROR: Concurrent-header mask expression cannot reference an impure procedure + !ERROR: DO CONCURRENT mask expression may not reference impure procedure 'impure' do concurrent (j=1:1, x%tbp_impure(j) /= 0) ! C1121 !ERROR: Call to an impure procedure component is not allowed in DO CONCURRENT a(j) = x%tbp_impure(j) ! C1139 diff --git a/test/semantics/dosemantics02.f90 b/test/semantics/dosemantics02.f90 index 0e7c23c97bc0..0b3165a88270 100644 --- a/test/semantics/dosemantics02.f90 +++ b/test/semantics/dosemantics02.f90 @@ -6,7 +6,7 @@ SUBROUTINE do_concurrent_c1121(i,n) IMPLICIT NONE INTEGER :: i, n, flag -!ERROR: Concurrent-header mask expression cannot reference an impure procedure + !ERROR: DO CONCURRENT mask expression may not reference impure procedure 'random' DO CONCURRENT (i = 1:n, random() < 3) flag = 3 END DO @@ -30,12 +30,12 @@ SUBROUTINE s1() 20 CONTINUE ! Error, no compatibility requirement for DO CONCURRENT -!ERROR: DO CONCURRENT step expression should not be zero + !ERROR: DO CONCURRENT step expression may not be zero DO CONCURRENT (I = 1 : 10 : 0) END DO ! Error, this time with an integer constant -!ERROR: DO CONCURRENT step expression should not be zero + !ERROR: DO CONCURRENT step expression may not be zero DO CONCURRENT (I = 1 : 10 : constInt) END DO end subroutine s1 diff --git a/test/semantics/dosemantics04.f90 b/test/semantics/dosemantics04.f90 index 5b9b5f4b01a9..7c0743517f17 100644 --- a/test/semantics/dosemantics04.f90 +++ b/test/semantics/dosemantics04.f90 @@ -4,32 +4,32 @@ PROGRAM dosemantics04 IMPLICIT NONE INTEGER :: a, i, j, k, n -!ERROR: concurrent-header mask-expr references variable 'n' in LOCAL locality-spec + !ERROR: DO CONCURRENT mask expression references variable 'n' in LOCAL locality-spec DO CONCURRENT (INTEGER *2 :: i = 1:10, i < j + n) LOCAL(n) PRINT *, "hello" END DO -!ERROR: concurrent-header mask-expr references variable 'a' in LOCAL locality-spec + !ERROR: DO CONCURRENT mask expression references variable 'a' in LOCAL locality-spec DO 30 CONCURRENT (i = 1:n:1, j=1:n:2, k=1:n:3, a<3) LOCAL (a) PRINT *, "hello" 30 END DO ! Initial expression -!ERROR: concurrent-control expression references index-name 'j' + !ERROR: DO CONCURRENT limit expression may not reference index variable 'j' DO CONCURRENT (i = j:3, j=1:3) END DO ! Final expression -!ERROR: concurrent-control expression references index-name 'j' + !ERROR: DO CONCURRENT limit expression may not reference index variable 'j' DO CONCURRENT (i = 1:j, j=1:3) END DO ! Step expression -!ERROR: concurrent-control expression references index-name 'j' + !ERROR: DO CONCURRENT step expression may not reference index variable 'j' DO CONCURRENT (i = 1:3:j, j=1:3) END DO -!ERROR: concurrent-control expression references index-name 'i' + !ERROR: DO CONCURRENT limit expression may not reference index variable 'i' DO CONCURRENT (INTEGER*2 :: i = 1:3, j=i:3) END DO diff --git a/test/semantics/dosemantics05.f90 b/test/semantics/dosemantics05.f90 index 9f5f71ee478e..c7e27d53aec4 100644 --- a/test/semantics/dosemantics05.f90 +++ b/test/semantics/dosemantics05.f90 @@ -47,7 +47,7 @@ subroutine s1() end associate associate (avar => ivar) -!ERROR: DO CONCURRENT step expression should not be zero +!ERROR: DO CONCURRENT step expression may not be zero do concurrent (i = 1:2:0) default(none) shared(jvar) local(kvar) !ERROR: Variable 'ivar' from an enclosing scope referenced in DO CONCURRENT with DEFAULT(NONE) must appear in a locality-spec ivar = & diff --git a/test/semantics/dosemantics09.f90 b/test/semantics/dosemantics09.f90 index 8a4ef4741af0..425e71e3db54 100644 --- a/test/semantics/dosemantics09.f90 +++ b/test/semantics/dosemantics09.f90 @@ -18,7 +18,7 @@ subroutine s2() end subroutine s2 subroutine s4() -!ERROR: concurrent-header expression references variable 'i' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'i' in LOCAL locality-spec do concurrent (j=i:10) local(i) end do end subroutine s4 @@ -36,7 +36,7 @@ subroutine s6() end subroutine s6 subroutine s7() -!ERROR: concurrent-header expression references variable 'i' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'i' in LOCAL locality-spec do concurrent (j=1:i) local(i) end do end subroutine s7 @@ -54,7 +54,7 @@ subroutine s9() end subroutine s9 subroutine s10() -!ERROR: concurrent-header expression references variable 'i' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'i' in LOCAL locality-spec do concurrent (j=1:10:i) local(i) end do end subroutine s10 @@ -75,7 +75,7 @@ subroutine s13() ! Test construct-association, in this case, established by the "shared" integer :: ivar associate (avar => ivar) -!ERROR: concurrent-header expression references variable 'ivar' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'ivar' in LOCAL locality-spec do concurrent (j=1:10:avar) local(avar) end do end associate @@ -88,7 +88,7 @@ subroutine s14() ! Test use-association, in this case, established by the "shared" use m1 -!ERROR: concurrent-header expression references variable 'mvar' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'mvar' in LOCAL locality-spec do concurrent (k=mvar:10) local(mvar) end do end subroutine s14 @@ -98,7 +98,7 @@ subroutine s15() ! locality-spec ivar = 3 do concurrent (j=ivar:10) shared(ivar) -!ERROR: concurrent-header expression references variable 'ivar' in LOCAL locality-spec +!ERROR: DO CONCURRENT expression references variable 'ivar' in LOCAL locality-spec do concurrent (k=ivar:10) local(ivar) end do end do diff --git a/test/semantics/forall01.f90 b/test/semantics/forall01.f90 index aa509b0125d6..bd665e2a5283 100644 --- a/test/semantics/forall01.f90 +++ b/test/semantics/forall01.f90 @@ -1,14 +1,22 @@ subroutine forall1 real :: a(9) !ERROR: 'i' is already declared in this scoping unit + !ERROR: Cannot redefine FORALL variable 'i' forall (i=1:8, i=1:9) a(i) = i + !ERROR: 'i' is already declared in this scoping unit + !ERROR: Cannot redefine FORALL variable 'i' + forall (i=1:8, i=1:9) + a(i) = i + end forall forall (j=1:8) !ERROR: 'j' is already declared in this scoping unit + !ERROR: Cannot redefine FORALL variable 'j' forall (j=1:9) end forall end forall end + subroutine forall2 integer, pointer :: a(:) integer, target :: b(10,10) @@ -16,8 +24,52 @@ subroutine forall2 !ERROR: Impure procedure 'f_impure' may not be referenced in a FORALL a(f_impure(i):) => b(i,:) end forall + !ERROR: FORALL mask expression may not reference impure procedure 'f_impure' + forall (j=1:10, f_impure(1)>2) + end forall contains impure integer function f_impure(i) f_impure = i end end + +subroutine forall3 + real :: x + forall(i=1:10) + !ERROR: Cannot redefine FORALL variable 'i' + i = 1 + end forall + forall(i=1:10) + forall(j=1:10) + !ERROR: Cannot redefine FORALL variable 'i' + i = 1 + end forall + end forall + !ERROR: Cannot redefine FORALL variable 'i' + forall(i=1:10) i = 1 +end + +subroutine forall4 + integer, parameter :: zero = 0 + integer :: a(10) + + !ERROR: FORALL limit expression may not reference index variable 'i' + forall(i=1:i) + a(i) = i + end forall + !ERROR: FORALL step expression may not reference index variable 'i' + forall(i=1:10:i) + a(i) = i + end forall + !ERROR: FORALL step expression may not be zero + forall(i=1:10:zero) + a(i) = i + end forall + + !ERROR: FORALL limit expression may not reference index variable 'i' + forall(i=1:i) a(i) = i + !ERROR: FORALL step expression may not reference index variable 'i' + forall(i=1:10:i) a(i) = i + !ERROR: FORALL step expression may not be zero + forall(i=1:10:zero) a(i) = i +end diff --git a/test/semantics/resolve35.f90 b/test/semantics/resolve35.f90 index bb93ab98131b..2598d9ca82e8 100644 --- a/test/semantics/resolve35.f90 +++ b/test/semantics/resolve35.f90 @@ -50,14 +50,6 @@ subroutine s4 end forall end -subroutine s5 - real :: a(10), b(10) - !ERROR: 'i' is already declared in this scoping unit - forall(i=1:10, i=1:10) - a(i) = b(i) - end forall -end - subroutine s6 integer, parameter :: n = 4 real, dimension(n) :: x From bbbbe9c1aa2fd38dc2164a6440290dbbea1518e2 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 19 Feb 2020 05:19:08 -0800 Subject: [PATCH 034/345] Fix issues with -DBUILD_SHARED_LIBS=On This re-ordering allows building f18 with shared library using and LLVM build with static libraries. This reordering (that also made sens form an alphabetical point of view) works here to do such "shared+archive" compiling because the current dependency on LLVM is simple (only one f18 lib + an executable depends on LLVM). As soon as two f18 libraries will depend on LLVM, one will have to use an LLVM version built with -DBUILD_SHARED_LIBS=On if one wants to use this option to compile f18. --- tools/f18/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 79f5c52d6a6c..b2f8e129e83a 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -18,8 +18,8 @@ target_link_libraries(f18 FortranParser FortranEvaluate FortranSemantics - LLVMSupport FortranLower + LLVMSupport ) add_executable(f18-parse-demo From 4a703f2b5a6484208a059dc0b456363c138a661d Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Fri, 14 Feb 2020 15:53:11 -0800 Subject: [PATCH 035/345] Semantic checks for C709, C710, and C711 C709 An assumed-type entity shall be a dummy data object that does not have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE attribute and is not an explicit-shape array. C710 An assumed-type variable name shall not appear in a designator or expression except as an actual argument corresponding to a dummy argument that is assumed-type, or as the first argument to the intrinsic function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND, or the function C_LOC from the intrinsic module ISO_C_BINDING. C711 An assumed-type actual argument that corresponds to an assumed-rank dummy argument shall be assumed-shape or assumed-rank. For C709 I added code to check-declarations.cpp. For this, I had to distinguish between polymorphic types and assumed-type types to eliminate multiple messages on the same line. C710 was already checked, but I added a notation in the source. For C711 I added code to check-call.cpp and the test call15.f90. --- include/flang/semantics/type.h | 3 ++ lib/semantics/check-call.cpp | 20 +++++++++--- lib/semantics/check-declarations.cpp | 49 +++++++++++++++++++++++++++- lib/semantics/expression.cpp | 2 +- module/iso_c_binding.f90 | 2 +- test/semantics/CMakeLists.txt | 2 ++ test/semantics/call15.f90 | 17 ++++++++++ test/semantics/modfile12.f90 | 8 +++-- test/semantics/resolve72.f90 | 25 ++++++++++++++ 9 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 test/semantics/call15.f90 create mode 100644 test/semantics/resolve72.f90 diff --git a/include/flang/semantics/type.h b/include/flang/semantics/type.h index 19d02000c45c..bbd5f47f3a0d 100644 --- a/include/flang/semantics/type.h +++ b/include/flang/semantics/type.h @@ -327,6 +327,9 @@ class DeclTypeSpec { bool IsUnlimitedPolymorphic() const { return category_ == TypeStar || category_ == ClassStar; } + bool IsAssumedType() const { + return category_ == TypeStar; + } bool IsNumeric(TypeCategory) const; const NumericTypeSpec &numericTypeSpec() const; const LogicalTypeSpec &logicalTypeSpec() const; diff --git a/lib/semantics/check-call.cpp b/lib/semantics/check-call.cpp index 2afd00f4627e..f03f30e14d5e 100644 --- a/lib/semantics/check-call.cpp +++ b/lib/semantics/check-call.cpp @@ -610,11 +610,23 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg, messages.Say( "Actual argument is not a variable or typed expression"_err_en_US); } - } else if (!object.type.type().IsAssumedType()) { + } else { const Symbol &assumed{DEREF(arg.GetAssumedTypeDummy())}; - messages.Say( - "Assumed-type TYPE(*) '%s' may be associated only with an assumed-TYPE(*) %s"_err_en_US, - assumed.name(), dummyName); + if (!object.type.type().IsAssumedType()) { + messages.Say( + "Assumed-type TYPE(*) '%s' may be associated only with an" + " assumed-TYPE(*) %s"_err_en_US, + assumed.name(), dummyName); + } else if (const auto *details{ + assumed.detailsIf()}) { + if (!(details->IsAssumedShape() || details->IsAssumedRank())) { + messages.Say( // C711 + "Assumed-type TYPE(*) '%s' must be either assumed " + "shape or assumed rank to be associated with TYPE(*) " + "%s"_err_en_US, + assumed.name(), dummyName); + } + } } }, [&](const characteristics::DummyProcedure &proc) { diff --git a/lib/semantics/check-declarations.cpp b/lib/semantics/check-declarations.cpp index 57f4d788bcc7..7c27fcba91d1 100644 --- a/lib/semantics/check-declarations.cpp +++ b/lib/semantics/check-declarations.cpp @@ -59,6 +59,7 @@ class CheckHelper { void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &); void CheckArraySpec(const Symbol &, const ArraySpec &); void CheckProcEntity(const Symbol &, const ProcEntityDetails &); + void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); void CheckGeneric(const Symbol &, const GenericDetails &); std::optional> Characterize(const SymbolVector &); @@ -293,11 +294,56 @@ void CheckHelper::CheckValue( } } +void CheckHelper::CheckAssumedTypeEntity( // C709 + const Symbol &symbol, const ObjectEntityDetails &details) { + if (const DeclTypeSpec * type{symbol.GetType()}; + type && type->category() == DeclTypeSpec::TypeStar) { + if (!symbol.IsDummy()) { + messages_.Say( + "Assumed-type entity '%s' must be a dummy argument"_err_en_US, + symbol.name()); + } else { + if (symbol.attrs().test(Attr::ALLOCATABLE)) { + messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE" + " attribute"_err_en_US, + symbol.name()); + } + if (symbol.attrs().test(Attr::POINTER)) { + messages_.Say("Assumed-type argument '%s' cannot have the POINTER" + " attribute"_err_en_US, + symbol.name()); + } + if (symbol.attrs().test(Attr::VALUE)) { + messages_.Say("Assumed-type argument '%s' cannot have the VALUE" + " attribute"_err_en_US, + symbol.name()); + } + if (symbol.attrs().test(Attr::INTENT_OUT)) { + messages_.Say( + "Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US, + symbol.name()); + } + if (IsCoarray(symbol)) { + messages_.Say( + "Assumed-type argument '%s' cannot be a coarray"_err_en_US, + symbol.name()); + } + if (details.IsArray() && + !(details.IsAssumedShape() || details.IsAssumedSize())) { + messages_.Say("Assumed-type argument '%s' must be assumed shape" + " or assumed size array"_err_en_US, + symbol.name()); + } + } + } +} + void CheckHelper::CheckObjectEntity( const Symbol &symbol, const ObjectEntityDetails &details) { CheckArraySpec(symbol, details.shape()); Check(details.shape()); Check(details.coshape()); + CheckAssumedTypeEntity(symbol, details); if (!details.coshape().empty()) { if (IsAllocatable(symbol)) { if (!details.coshape().IsDeferredShape()) { // C827 @@ -373,7 +419,8 @@ void CheckHelper::CheckObjectEntity( } if (const DeclTypeSpec * type{details.type()}) { // C708 if (type->IsPolymorphic() && - !(IsAllocatableOrPointer(symbol) || symbol.IsDummy())) { + !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) || + symbol.IsDummy())) { messages_.Say("CLASS entity '%s' must be a dummy argument or have " "ALLOCATABLE or POINTER attribute"_err_en_US, symbol.name()); diff --git a/lib/semantics/expression.cpp b/lib/semantics/expression.cpp index 59593ae90e8f..32e6692bce95 100644 --- a/lib/semantics/expression.cpp +++ b/lib/semantics/expression.cpp @@ -2344,7 +2344,7 @@ MaybeExpr ExpressionAnalyzer::ExprOrVariable(const PARSED &x) { if (!x.typedExpr) { FixMisparsedFunctionReference(context_, x.u); MaybeExpr result; - if (AssumedTypeDummy(x)) { + if (AssumedTypeDummy(x)) { // C710 Say("TYPE(*) dummy argument may only be used as an actual argument"_err_en_US); } else { if constexpr (std::is_same_v) { diff --git a/module/iso_c_binding.f90 b/module/iso_c_binding.f90 index 7d7a5a29da9e..d1cb001c2a54 100644 --- a/module/iso_c_binding.f90 +++ b/module/iso_c_binding.f90 @@ -91,7 +91,7 @@ end function c_associated function c_loc(x) type(c_ptr) :: c_loc - type(*), intent(in) :: x + type(*), dimension(:), intent(in) :: x c_loc = c_ptr(loc(x)) end function c_loc diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index c29282442cd9..7d6ca5fe79e0 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -102,6 +102,7 @@ set(ERROR_TESTS resolve69.f90 resolve70.f90 resolve71.f90 + resolve72.f90 stop01.f90 structconst01.f90 structconst02.f90 @@ -195,6 +196,7 @@ set(ERROR_TESTS call12.f90 call13.f90 call14.f90 + call15.f90 forall01.f90 misc-declarations.f90 separate-module-procs.f90 diff --git a/test/semantics/call15.f90 b/test/semantics/call15.f90 new file mode 100644 index 000000000000..204a0e6c6237 --- /dev/null +++ b/test/semantics/call15.f90 @@ -0,0 +1,17 @@ +! C711 An assumed-type actual argument that corresponds to an assumed-rank +! dummy argument shall be assumed-shape or assumed-rank. +subroutine s(arg1, arg2, arg3) + type(*), dimension(..) :: arg1 ! assumed rank + type(*), dimension(:) :: arg2 ! assumed shape + type(*) :: arg3 + + call inner(arg1) ! OK, assumed rank + call inner(arg2) ! OK, assumed shape + !ERROR: Assumed-type TYPE(*) 'arg3' must be either assumed shape or assumed rank to be associated with TYPE(*) dummy argument 'dummy=' + call inner(arg3) + + contains + subroutine inner(dummy) + type(*), dimension(..) :: dummy + end subroutine inner +end subroutine s diff --git a/test/semantics/modfile12.f90 b/test/semantics/modfile12.f90 index e6b00cc7c3f7..89f43ad350eb 100644 --- a/test/semantics/modfile12.f90 +++ b/test/semantics/modfile12.f90 @@ -9,7 +9,6 @@ module m end type type(t(a+3,:)), allocatable :: z class(t(a+4,:)), allocatable :: z2 - type(*), allocatable :: z3 class(*), allocatable :: z4 real*2 :: f complex*32 :: g @@ -25,6 +24,9 @@ subroutine foo(x) subroutine bar(x) real :: x(..) end + subroutine baz(x) + type(*) :: x + end end !Expect: m.mod @@ -42,7 +44,6 @@ subroutine bar(x) ! end type ! type(t(c=4_4,d=:)),allocatable::z ! class(t(c=5_4,d=:)),allocatable::z2 -! type(*),allocatable::z3 ! class(*),allocatable::z4 ! real(2)::f ! complex(16)::g @@ -58,4 +59,7 @@ subroutine bar(x) ! subroutine bar(x) ! real(4)::x(..) ! end +! subroutine baz(x) +! type(*)::x +! end !end diff --git a/test/semantics/resolve72.f90 b/test/semantics/resolve72.f90 new file mode 100644 index 000000000000..fdead88c8fe8 --- /dev/null +++ b/test/semantics/resolve72.f90 @@ -0,0 +1,25 @@ +! C709 An assumed-type entity shall be a dummy data object that does not have +! the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE attribute and +! is not an explicit-shape array. +subroutine s() + !ERROR: Assumed-type entity 'starvar' must be a dummy argument + type(*) :: starVar + + contains + subroutine inner1(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + type(*) :: arg1 ! OK + type(*), dimension(*) :: arg2 ! OK + !ERROR: Assumed-type argument 'arg3' cannot have the ALLOCATABLE attribute + type(*), allocatable :: arg3 + !ERROR: Assumed-type argument 'arg4' cannot be a coarray + type(*), codimension[*] :: arg4 + !ERROR: Assumed-type argument 'arg5' cannot be INTENT(OUT) + type(*), intent(out) :: arg5 + !ERROR: Assumed-type argument 'arg6' cannot have the POINTER attribute + type(*), pointer :: arg6 + !ERROR: Assumed-type argument 'arg7' cannot have the VALUE attribute + type(*), value :: arg7 + !ERROR: Assumed-type argument 'arg8' must be assumed shape or assumed size array + type(*), dimension(3) :: arg8 + end subroutine inner1 +end subroutine s From 8ecb6a279f53ff4cc9493506370664fb1923470a Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Wed, 19 Feb 2020 13:28:19 -0800 Subject: [PATCH 036/345] Rename DoChecker to DoForallChecker This reflects the fact that it now performs checks on both DO and FORALL constructs. Rename the source files as well. --- lib/semantics/CMakeLists.txt | 2 +- .../{check-do.cpp => check-do-forall.cpp} | 48 +++++++++---------- .../{check-do.h => check-do-forall.h} | 13 ++--- lib/semantics/semantics.cpp | 11 +++-- 4 files changed, 38 insertions(+), 36 deletions(-) rename lib/semantics/{check-do.cpp => check-do-forall.cpp} (96%) rename lib/semantics/{check-do.h => check-do-forall.h} (84%) diff --git a/lib/semantics/CMakeLists.txt b/lib/semantics/CMakeLists.txt index 14e636e177d5..d06c8a2b5779 100644 --- a/lib/semantics/CMakeLists.txt +++ b/lib/semantics/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(FortranSemantics check-coarray.cpp check-deallocate.cpp check-declarations.cpp - check-do.cpp + check-do-forall.cpp check-if-stmt.cpp check-io.cpp check-nullify.cpp diff --git a/lib/semantics/check-do.cpp b/lib/semantics/check-do-forall.cpp similarity index 96% rename from lib/semantics/check-do.cpp rename to lib/semantics/check-do-forall.cpp index 10596c31cd21..1b1abfd10a84 100644 --- a/lib/semantics/check-do.cpp +++ b/lib/semantics/check-do-forall.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-do.cpp ----------------------------------------===// +//===-- lib/semantics/check-do-forall.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "check-do.h" +#include "check-do-forall.h" #include "flang/common/template.h" #include "flang/evaluate/call.h" #include "flang/evaluate/expression.h" @@ -763,37 +763,37 @@ class DoContext { parser::CharBlock currentStatementSourcePosition_; }; // class DoContext -void DoChecker::Enter(const parser::DoConstruct &doConstruct) { +void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) { DoContext doContext{context_, IndexVarKind::DO}; doContext.DefineDoVariables(doConstruct); } -void DoChecker::Leave(const parser::DoConstruct &doConstruct) { +void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) { DoContext doContext{context_, IndexVarKind::DO}; doContext.Check(doConstruct); doContext.ResetDoVariables(doConstruct); } -void DoChecker::Enter(const parser::ForallConstruct &construct) { +void DoForallChecker::Enter(const parser::ForallConstruct &construct) { DoContext doContext{context_, IndexVarKind::FORALL}; doContext.ActivateIndexVars(GetControls(construct)); } -void DoChecker::Leave(const parser::ForallConstruct &construct) { +void DoForallChecker::Leave(const parser::ForallConstruct &construct) { DoContext doContext{context_, IndexVarKind::FORALL}; doContext.Check(construct); doContext.DeactivateIndexVars(GetControls(construct)); } -void DoChecker::Enter(const parser::ForallStmt &stmt) { +void DoForallChecker::Enter(const parser::ForallStmt &stmt) { DoContext doContext{context_, IndexVarKind::FORALL}; doContext.ActivateIndexVars(GetControls(stmt)); } -void DoChecker::Leave(const parser::ForallStmt &stmt) { +void DoForallChecker::Leave(const parser::ForallStmt &stmt) { DoContext doContext{context_, IndexVarKind::FORALL}; doContext.Check(stmt); doContext.DeactivateIndexVars(GetControls(stmt)); } -void DoChecker::Leave(const parser::ForallAssignmentStmt &stmt) { +void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) { DoContext doContext{context_, IndexVarKind::FORALL}; doContext.Check(stmt); } @@ -813,8 +813,8 @@ static parser::CharBlock GetNodePosition(const ConstructNode &construct) { [&](const auto &x) { return GetConstructPosition(*x); }, construct); } -void DoChecker::SayBadLeave(StmtType stmtType, const char *enclosingStmtName, - const ConstructNode &construct) const { +void DoForallChecker::SayBadLeave(StmtType stmtType, + const char *enclosingStmtName, const ConstructNode &construct) const { context_ .Say("%s must not leave a %s statement"_err_en_US, EnumToString(stmtType), enclosingStmtName) @@ -838,7 +838,7 @@ static bool ConstructIsDoConcurrent(const ConstructNode &construct) { // Check that CYCLE and EXIT statements do not cause flow of control to // leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs. -void DoChecker::CheckForBadLeave( +void DoForallChecker::CheckForBadLeave( StmtType stmtType, const ConstructNode &construct) const { std::visit( common::visitors{ @@ -876,7 +876,7 @@ static bool StmtMatchesConstruct(const parser::Name *stmtName, } // C1167 Can't EXIT from a DO CONCURRENT -void DoChecker::CheckDoConcurrentExit( +void DoForallChecker::CheckDoConcurrentExit( StmtType stmtType, const ConstructNode &construct) const { if (stmtType == StmtType::EXIT && ConstructIsDoConcurrent(construct)) { SayBadLeave(StmtType::EXIT, "DO CONCURRENT", construct); @@ -887,7 +887,7 @@ void DoChecker::CheckDoConcurrentExit( // nesting levels looking for a construct that matches the CYCLE or EXIT // statment. At every construct, check for a violation. If we find a match // without finding a violation, the check is complete. -void DoChecker::CheckNesting( +void DoForallChecker::CheckNesting( StmtType stmtType, const parser::Name *stmtName) const { const ConstructStack &stack{context_.constructStack()}; for (auto iter{stack.cend()}; iter-- != stack.cbegin();) { @@ -909,16 +909,16 @@ void DoChecker::CheckNesting( } // C1135 -- Nesting for CYCLE statements -void DoChecker::Enter(const parser::CycleStmt &cycleStmt) { +void DoForallChecker::Enter(const parser::CycleStmt &cycleStmt) { CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v)); } // C1167 and C1168 -- Nesting for EXIT statements -void DoChecker::Enter(const parser::ExitStmt &exitStmt) { +void DoForallChecker::Enter(const parser::ExitStmt &exitStmt) { CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v)); } -void DoChecker::Leave(const parser::AssignmentStmt &stmt) { +void DoForallChecker::Leave(const parser::AssignmentStmt &stmt) { const auto &variable{std::get(stmt.t)}; context_.CheckIndexVarRedefine(variable); } @@ -947,7 +947,7 @@ static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, // the same time, we need to iterate over the parser::Expr versions of the // actual arguments to get their source locations of the arguments for the // messages. -void DoChecker::Leave(const parser::CallStmt &callStmt) { +void DoForallChecker::Leave(const parser::CallStmt &callStmt) { if (const auto &typedCall{callStmt.typedCall}) { const auto &parsedArgs{ std::get>(callStmt.v.t)}; @@ -970,7 +970,7 @@ void DoChecker::Leave(const parser::CallStmt &callStmt) { } } -void DoChecker::Leave(const parser::ConnectSpec &connectSpec) { +void DoForallChecker::Leave(const parser::ConnectSpec &connectSpec) { const auto *newunit{ std::get_if(&connectSpec.u)}; if (newunit) { @@ -997,7 +997,7 @@ template ActualArgumentSet CollectActualArguments(const A &x) { template ActualArgumentSet CollectActualArguments(const SomeExpr &); -void DoChecker::Leave(const parser::Expr &parsedExpr) { +void DoForallChecker::Leave(const parser::Expr &parsedExpr) { if (const SomeExpr * expr{GetExpr(parsedExpr)}) { ActualArgumentSet argSet{CollectActualArguments(*expr)}; for (const evaluate::ActualArgumentRef &argRef : argSet) { @@ -1006,7 +1006,7 @@ void DoChecker::Leave(const parser::Expr &parsedExpr) { } } -void DoChecker::Leave(const parser::InquireSpec &inquireSpec) { +void DoForallChecker::Leave(const parser::InquireSpec &inquireSpec) { const auto *intVar{std::get_if(&inquireSpec.u)}; if (intVar) { const auto &scalar{std::get(intVar->t)}; @@ -1014,20 +1014,20 @@ void DoChecker::Leave(const parser::InquireSpec &inquireSpec) { } } -void DoChecker::Leave(const parser::IoControlSpec &ioControlSpec) { +void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) { const auto *size{std::get_if(&ioControlSpec.u)}; if (size) { context_.CheckIndexVarRedefine(size->v.thing.thing); } } -void DoChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { +void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { const auto &control{std::get(outputImpliedDo.t)}; const parser::Name &name{control.name.thing.thing}; context_.CheckIndexVarRedefine(name.source, *name.symbol); } -void DoChecker::Leave(const parser::StatVariable &statVariable) { +void DoForallChecker::Leave(const parser::StatVariable &statVariable) { context_.CheckIndexVarRedefine(statVariable.v.thing.thing); } diff --git a/lib/semantics/check-do.h b/lib/semantics/check-do-forall.h similarity index 84% rename from lib/semantics/check-do.h rename to lib/semantics/check-do-forall.h index fb3a4be9b082..1ba9b6b81060 100644 --- a/lib/semantics/check-do.h +++ b/lib/semantics/check-do-forall.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-do.h --------------------------------*- C++ -*-===// +//===-- lib/semantics/check-do-forall.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_SEMANTICS_CHECK_DO_H_ -#define FORTRAN_SEMANTICS_CHECK_DO_H_ +#ifndef FORTRAN_SEMANTICS_CHECK_DO_FORALL_H_ +#define FORTRAN_SEMANTICS_CHECK_DO_FORALL_H_ #include "flang/common/idioms.h" #include "flang/semantics/semantics.h" @@ -34,9 +34,10 @@ namespace Fortran::semantics { // To specify different statement types used in semantic checking. ENUM_CLASS(StmtType, CYCLE, EXIT) -class DoChecker : public virtual BaseChecker { +// Perform semantic checks on DO and FORALL constructs and statements. +class DoForallChecker : public virtual BaseChecker { public: - explicit DoChecker(SemanticsContext &context) : context_{context} {} + explicit DoForallChecker(SemanticsContext &context) : context_{context} {} void Leave(const parser::AssignmentStmt &); void Leave(const parser::CallStmt &); void Leave(const parser::ConnectSpec &); @@ -65,4 +66,4 @@ class DoChecker : public virtual BaseChecker { void CheckNesting(StmtType, const parser::Name *) const; }; } -#endif // FORTRAN_SEMANTICS_CHECK_DO_H_ +#endif diff --git a/lib/semantics/semantics.cpp b/lib/semantics/semantics.cpp index c6353a7b0d44..dcb119813a0e 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/semantics/semantics.cpp @@ -15,7 +15,7 @@ #include "check-coarray.h" #include "check-deallocate.h" #include "check-declarations.h" -#include "check-do.h" +#include "check-do-forall.h" #include "check-if-stmt.h" #include "check-io.h" #include "check-nullify.h" @@ -110,10 +110,11 @@ template class SemanticsVisitor : public virtual C... { }; using StatementSemanticsPass1 = ExprChecker; -using StatementSemanticsPass2 = SemanticsVisitor; +using StatementSemanticsPass2 = SemanticsVisitor< // + AllocateChecker, ArithmeticIfStmtChecker, AssignmentChecker, CoarrayChecker, + DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, + NullifyChecker, OmpStructureChecker, PurityChecker, ReturnStmtChecker, + StopChecker>; static bool PerformStatementSemantics( SemanticsContext &context, parser::Program &program) { From 2c1fc63758eac43eb4d2fd4fdf1e7b0e63d52f70 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Thu, 20 Feb 2020 13:33:43 -0600 Subject: [PATCH 037/345] Disable 80-bit extended precision if on MSVC (#1003) --- include/flang/decimal/decimal.h | 4 ++-- lib/decimal/binary-to-decimal.cpp | 2 +- lib/decimal/decimal-to-binary.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/flang/decimal/decimal.h b/include/flang/decimal/decimal.h index c9aad161f4dd..05ed0068d95c 100644 --- a/include/flang/decimal/decimal.h +++ b/include/flang/decimal/decimal.h @@ -130,7 +130,7 @@ struct NS(ConversionToDecimalResult) struct NS(ConversionToDecimalResult) ConvertDoubleToDecimal(char *, size_t, enum NS(DecimalConversionFlags), int digits, enum NS(FortranRounding), double); -#if __x86_64__ +#if __x86_64__ && !defined(_MSC_VER) struct NS(ConversionToDecimalResult) ConvertLongDoubleToDecimal(char *, size_t, enum NS(DecimalConversionFlags), int digits, enum NS(FortranRounding), long double); @@ -140,7 +140,7 @@ enum NS(ConversionResultFlags) ConvertDecimalToFloat(const char **, float *, enum NS(FortranRounding)); enum NS(ConversionResultFlags) ConvertDecimalToDouble(const char **, double *, enum NS(FortranRounding)); -#if __x86_64__ +#if __x86_64__ && !defined(_MSC_VER) enum NS(ConversionResultFlags) ConvertDecimalToLongDouble( const char **, long double *, enum NS(FortranRounding)); #endif diff --git a/lib/decimal/binary-to-decimal.cpp b/lib/decimal/binary-to-decimal.cpp index d15aab5ff638..f4b644045648 100644 --- a/lib/decimal/binary-to-decimal.cpp +++ b/lib/decimal/binary-to-decimal.cpp @@ -389,7 +389,7 @@ ConversionToDecimalResult ConvertDoubleToDecimal(char *buffer, std::size_t size, rounding, Fortran::decimal::BinaryFloatingPointNumber<53>(x)); } -#if __x86_64__ +#if __x86_64__ && !defined(_MSC_VER) ConversionToDecimalResult ConvertLongDoubleToDecimal(char *buffer, std::size_t size, enum DecimalConversionFlags flags, int digits, enum FortranRounding rounding, long double x) { diff --git a/lib/decimal/decimal-to-binary.cpp b/lib/decimal/decimal-to-binary.cpp index a07cd570d8c3..f71ca941e8a7 100644 --- a/lib/decimal/decimal-to-binary.cpp +++ b/lib/decimal/decimal-to-binary.cpp @@ -417,7 +417,7 @@ enum ConversionResultFlags ConvertDecimalToDouble( reinterpret_cast(&result.binary), sizeof *d); return result.flags; } -#if __x86_64__ +#if __x86_64__ && !defined(_MSC_VER) enum ConversionResultFlags ConvertDecimalToLongDouble( const char **p, long double *ld, enum FortranRounding rounding) { auto result{Fortran::decimal::ConvertToBinary<64>(*p, rounding)}; From de801a0db7614c93abc8239d02a453d60c9ea8a1 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Thu, 20 Feb 2020 15:12:49 -0600 Subject: [PATCH 038/345] Add missing array include (#1004) --- include/flang/semantics/symbol.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/flang/semantics/symbol.h b/include/flang/semantics/symbol.h index 5c45a8c5ad06..d6ac63f79d25 100644 --- a/include/flang/semantics/symbol.h +++ b/include/flang/semantics/symbol.h @@ -13,6 +13,7 @@ #include "flang/common/Fortran.h" #include "flang/common/enum-set.h" #include "flang/common/reference.h" +#include #include #include #include From 822129736b6b7a96b6ff3ffe810d842ce42e3672 Mon Sep 17 00:00:00 2001 From: Anchu Rajendran S <59249359+anchu-rajendran@users.noreply.github.com> Date: Fri, 21 Feb 2020 11:49:14 +0530 Subject: [PATCH 039/345] Issue #992 : Implementing Semantic checks for DATA Statement (C874-C887) (#992) This commit covers Semantic Constraints C882 - C887 C882 : It was partially Implemented. Finished the implementation and added test case C884 : Implemented and added test case C883 : Implementation was there already. Added test case C885, C886, C887 : Implementation was there already. Added test case for data-repeat. --- include/flang/semantics/expression.h | 2 +- lib/semantics/CMakeLists.txt | 1 + lib/semantics/check-data.cpp | 50 ++++++++++++++++++++++++++++ lib/semantics/check-data.h | 28 ++++++++++++++++ lib/semantics/expression.cpp | 2 +- lib/semantics/resolve-names.cpp | 4 +-- lib/semantics/semantics.cpp | 5 +-- test/semantics/CMakeLists.txt | 1 + test/semantics/data01.f90 | 48 ++++++++++++++++++++++++++ 9 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 lib/semantics/check-data.cpp create mode 100644 lib/semantics/check-data.h create mode 100644 test/semantics/data01.f90 diff --git a/include/flang/semantics/expression.h b/include/flang/semantics/expression.h index 110a0bdb0853..79360fc0bed0 100644 --- a/include/flang/semantics/expression.h +++ b/include/flang/semantics/expression.h @@ -186,7 +186,7 @@ class ExpressionAnalyzer { auto result{Analyze(x.thing)}; if (result) { *result = Fold(std::move(*result)); - if (!IsConstantExpr(*result)) { + if (!IsConstantExpr(*result)) { //C886,C887 SayAt(x, "Must be a constant value"_err_en_US); ResetExpr(x); return std::nullopt; diff --git a/lib/semantics/CMakeLists.txt b/lib/semantics/CMakeLists.txt index d06c8a2b5779..5f2e3c686fc4 100644 --- a/lib/semantics/CMakeLists.txt +++ b/lib/semantics/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(FortranSemantics check-arithmeticif.cpp check-call.cpp check-coarray.cpp + check-data.cpp check-deallocate.cpp check-declarations.cpp check-do-forall.cpp diff --git a/lib/semantics/check-data.cpp b/lib/semantics/check-data.cpp new file mode 100644 index 000000000000..e831bf7e3d53 --- /dev/null +++ b/lib/semantics/check-data.cpp @@ -0,0 +1,50 @@ +//===-- lib/semantics/check-data.cpp --------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "check-data.h" + +namespace Fortran::semantics { + +void DataChecker::Leave(const parser::DataStmtConstant &dataConst) { + if (auto *structure{ + std::get_if(&dataConst.u)}) { + for (const auto &component : + std::get>(structure->t)) { + const parser::Expr &parsedExpr{ + std::get(component.t).v.value()}; + if (const auto *expr{GetExpr(parsedExpr)}) { + if (!evaluate::IsConstantExpr(*expr)) { // C884 + context_.Say(parsedExpr.source, + "Structure constructor in data value must be a constant expression"_err_en_US); + } + } + } + } + // TODO: C886 and C887 for data-stmt-constant +} + +// TODO: C874-C881 + +void DataChecker::Leave(const parser::DataStmtRepeat &dataRepeat) { + if (const auto *designator{parser::Unwrap(dataRepeat)}) { + if (auto *dataRef{std::get_if(&designator->u)}) { + evaluate::ExpressionAnalyzer exprAnalyzer{context_}; + if (MaybeExpr checked{exprAnalyzer.Analyze(*dataRef)}) { + auto expr{ + evaluate::Fold(context_.foldingContext(), std::move(checked))}; + if (auto i64{ToInt64(expr)}) { + if (*i64 < 0) { // C882 + context_.Say(designator->source, + "Repeat count for data value must not be negative"_err_en_US); + } + } + } + } + } +} +} diff --git a/lib/semantics/check-data.h b/lib/semantics/check-data.h new file mode 100644 index 000000000000..b2e96519a346 --- /dev/null +++ b/lib/semantics/check-data.h @@ -0,0 +1,28 @@ +//===-------lib/semantics/check-data.h ------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_CHECK_DATA_H_ +#define FORTRAN_SEMANTICS_CHECK_DATA_H_ + +#include "flang/parser/parse-tree.h" +#include "flang/parser/tools.h" +#include "flang/semantics/semantics.h" +#include "flang/semantics/tools.h" + +namespace Fortran::semantics { +class DataChecker : public virtual BaseChecker { +public: + DataChecker(SemanticsContext &context) : context_{context} {} + void Leave(const parser::DataStmtRepeat &); + void Leave(const parser::DataStmtConstant &); + +private: + SemanticsContext &context_; +}; +} +#endif // FORTRAN_SEMANTICS_CHECK_DATA_H_ diff --git a/lib/semantics/expression.cpp b/lib/semantics/expression.cpp index 054b4d1fdcb5..65e9f7ecdeb3 100644 --- a/lib/semantics/expression.cpp +++ b/lib/semantics/expression.cpp @@ -2471,7 +2471,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); diff --git a/lib/semantics/resolve-names.cpp b/lib/semantics/resolve-names.cpp index b98b7bc14a8d..3c322b694014 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/semantics/resolve-names.cpp @@ -2957,7 +2957,7 @@ void DeclarationVisitor::Post(const parser::EntityDecl &x) { if (ConvertToObjectEntity(symbol)) { Initialization(name, *init, false); } - } else if (attrs.test(Attr::PARAMETER)) { + } else if (attrs.test(Attr::PARAMETER)) { // C882, C883 Say(name, "Missing initialization for parameter '%s'"_err_en_US); } } @@ -4408,7 +4408,7 @@ std::optional DeclarationVisitor::ResolveDerivedType( DerivedTypeDetails details; details.set_isForwardReferenced(); symbol->set_details(std::move(details)); - } else { + } else { // C883 Say(name, "Derived type '%s' not found"_err_en_US); return std::nullopt; } diff --git a/lib/semantics/semantics.cpp b/lib/semantics/semantics.cpp index dcb119813a0e..d64ba6375f33 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/semantics/semantics.cpp @@ -13,6 +13,7 @@ #include "check-allocate.h" #include "check-arithmeticif.h" #include "check-coarray.h" +#include "check-data.h" #include "check-deallocate.h" #include "check-declarations.h" #include "check-do-forall.h" @@ -110,9 +111,9 @@ template class SemanticsVisitor : public virtual C... { }; using StatementSemanticsPass1 = ExprChecker; -using StatementSemanticsPass2 = SemanticsVisitor< // +using StatementSemanticsPass2 = SemanticsVisitor< AllocateChecker, ArithmeticIfStmtChecker, AssignmentChecker, CoarrayChecker, - DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, + DataChecker, DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, NullifyChecker, OmpStructureChecker, PurityChecker, ReturnStmtChecker, StopChecker>; diff --git a/test/semantics/CMakeLists.txt b/test/semantics/CMakeLists.txt index 7d6ca5fe79e0..ff5aa3f0e4de 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/semantics/CMakeLists.txt @@ -207,6 +207,7 @@ set(ERROR_TESTS critical02.f90 critical03.f90 block-data01.f90 + data01.f90 ) # These test files have expected symbols in the source diff --git a/test/semantics/data01.f90 b/test/semantics/data01.f90 new file mode 100644 index 000000000000..02c4674d241d --- /dev/null +++ b/test/semantics/data01.f90 @@ -0,0 +1,48 @@ +!Test for checking data constraints, C882-C887 +module m1 + type person + integer :: age + character(len=25) :: name + end type + integer, parameter::digits(5) = ( /-11,-22,-33,44,55/ ) + integer ::notConstDigits(5) = ( /-11,-22,-33,44,55/ ) + real, parameter::numbers(5) = ( /-11.11,-22.22,-33.33,44.44,55.55/ ) + integer, parameter :: repeat = -1 + integer :: myAge = 2 + type(person) myName +end + +subroutine CheckRepeat + use m1 + !C882 + !ERROR: Missing initialization for parameter 'uninitialized' + integer, parameter :: uninitialized + !C882 + !ERROR: Repeat count for data value must not be negative + DATA myName%age / repeat * 35 / + !C882 + !ERROR: Repeat count for data value must not be negative + DATA myName%age / digits(1) * 35 / + !C882 + !ERROR: Must be a constant value + DATA myName%age / repet * 35 / + !C885 + !ERROR: Must have INTEGER type, but is REAL(4) + DATA myName%age / numbers(1) * 35 / + !C886 + !ERROR: Must be a constant value + DATA myName%age / notConstDigits(1) * 35 / + !C887 + !ERROR: Must be a constant value + DATA myName%age / digits(myAge) * 35 / +end + +subroutine CheckValue + use m1 + !C883 + !ERROR: Derived type 'persn' not found + DATA myname / persn(2, 'Abcd Efgh') / + !C884 + !ERROR: Structure constructor in data value must be a constant expression + DATA myname / person(myAge, 'Abcd Ijkl') / +end From 04a76b272675d47ec7752420b15976c69a907dab Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Fri, 21 Feb 2020 15:31:12 -0800 Subject: [PATCH 040/345] Fix parsing bug on DATA statement This DATA statement was getting a parsing error: `data x /a(i)%b/` The parser was expecting the ending '/' where the '%' was. The problem was parsing `a(i)` as a structure constructor. Instead, move the constant subobject case before structure constructor, but match it only if not followed by '('. That is because in `data x /a(1)(2)/`, `a(1)` is a valid structure constructor. Also, remove the NamedConstant alternative from DataStmtRepeat. A named constant is always parsed as a ConstantSubobject so it can never occur. --- include/flang/parser/parse-tree.h | 4 +--- lib/parser/Fortran-parsers.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/flang/parser/parse-tree.h b/include/flang/parser/parse-tree.h index 57093e3903cb..f1e02e0a45d5 100644 --- a/include/flang/parser/parse-tree.h +++ b/include/flang/parser/parse-tree.h @@ -1412,9 +1412,7 @@ struct DataStmtConstant { // (only literal-constant -> int-literal-constant applies) struct DataStmtRepeat { UNION_CLASS_BOILERPLATE(DataStmtRepeat); - std::variant>, - Scalar>> - u; + std::variant>> u; }; // R843 data-stmt-value -> [data-stmt-repeat *] data-stmt-constant diff --git a/lib/parser/Fortran-parsers.cpp b/lib/parser/Fortran-parsers.cpp index f895189aa99a..d15901a74ea7 100644 --- a/lib/parser/Fortran-parsers.cpp +++ b/lib/parser/Fortran-parsers.cpp @@ -818,12 +818,10 @@ constexpr auto constantSubobject{constant(indirect(designator))}; // R844 data-stmt-repeat -> scalar-int-constant | scalar-int-constant-subobject // R607 int-constant -> constant -// Factored into: -// constant -> literal-constant -> int-literal-constant and -// constant -> named-constant +// Factored into: constant -> literal-constant -> int-literal-constant +// The named-constant alternative of constant is subsumed by constant-subobject TYPE_PARSER(construct(intLiteralConstant) || - construct(scalar(integer(constantSubobject))) || - construct(scalar(integer(namedConstant)))) + construct(scalar(integer(constantSubobject)))) // R845 data-stmt-constant -> // scalar-constant | scalar-constant-subobject | @@ -833,8 +831,8 @@ TYPE_PARSER(construct(intLiteralConstant) || // references into constant subobjects. TYPE_PARSER(first(construct(scalar(Parser{})), construct(nullInit), + construct(scalar(constantSubobject)) / !"("_tok, construct(Parser{}), - construct(scalar(constantSubobject)), construct(signedRealLiteralConstant), construct(signedIntLiteralConstant), extension( From 8b04dbebcf5621cfd571a8c45878cebcd1a1bfb0 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 20 Feb 2020 14:54:46 -0800 Subject: [PATCH 041/345] Add more checks on WHERE and FORALL Check that masks and LHS of assignments in WHERE statements and constructs have consistent shapes. They must all have the same rank and any extents that are compile-time constants must match. Emit a warning for assignments in FORALL statements and constructs where the LHS does not reference each of the index variables. --- include/flang/semantics/semantics.h | 5 +- lib/semantics/assignment.cpp | 251 +++++++++------------------- lib/semantics/assignment.h | 10 +- lib/semantics/check-do-forall.cpp | 33 ++++ lib/semantics/semantics.cpp | 13 +- test/semantics/assign01.f90 | 65 +++++-- test/semantics/forall01.f90 | 32 +++- 7 files changed, 220 insertions(+), 189 deletions(-) diff --git a/include/flang/semantics/semantics.h b/include/flang/semantics/semantics.h index b13f617108b4..0e64e42f742b 100644 --- a/include/flang/semantics/semantics.h +++ b/include/flang/semantics/semantics.h @@ -159,10 +159,13 @@ class SemanticsContext { void CheckIndexVarRedefine(const parser::Name &); void ActivateIndexVar(const parser::Name &, IndexVarKind); void DeactivateIndexVar(const parser::Name &); + SymbolVector GetIndexVars(IndexVarKind); private: void CheckIndexVarRedefine( const parser::CharBlock &, const Symbol &, parser::MessageFixedText &&); + bool CheckError(bool); + const common::IntrinsicTypeDefaultKinds &defaultKinds_; const common::LanguageFeatureControl languageFeatures_; parser::AllSources &allSources_; @@ -176,8 +179,6 @@ class SemanticsContext { Scope globalScope_; parser::Messages messages_; evaluate::FoldingContext foldingContext_; - - bool CheckError(bool); ConstructStack constructStack_; struct IndexVarInfo { parser::CharBlock location; diff --git a/lib/semantics/assignment.cpp b/lib/semantics/assignment.cpp index aee651e42b19..b286f6578168 100644 --- a/lib/semantics/assignment.cpp +++ b/lib/semantics/assignment.cpp @@ -29,194 +29,62 @@ using namespace Fortran::parser::literals; namespace Fortran::semantics { -using ControlExpr = evaluate::Expr; -using MaskExpr = evaluate::Expr; - -// The context tracks some number of active FORALL statements/constructs -// and some number of active WHERE statements/constructs. WHERE can nest -// in FORALL but not vice versa. Pointer assignments are allowed in -// FORALL but not in WHERE. These constraints are manifest in the grammar -// and don't need to be rechecked here, since errors cannot appear in the -// parse tree. -struct Control { - Symbol *name; - ControlExpr lower, upper, step; -}; - -struct ForallContext { - explicit ForallContext(const ForallContext *that) : outer{that} {} - - const ForallContext *outer{nullptr}; - std::optional constructName; - std::vector control; - std::optional maskExpr; - std::set activeNames; -}; - -struct WhereContext { - WhereContext(MaskExpr &&x, const WhereContext *o, const ForallContext *f) - : outer{o}, forall{f}, thisMaskExpr{std::move(x)} {} - const WhereContext *outer{nullptr}; - const ForallContext *forall{nullptr}; // innermost enclosing FORALL - std::optional constructName; - MaskExpr thisMaskExpr; // independent of outer WHERE, if any - MaskExpr cumulativeMaskExpr{thisMaskExpr}; -}; - class AssignmentContext { public: - explicit AssignmentContext(SemanticsContext &c) : context_{c} {} - AssignmentContext(const AssignmentContext &c, WhereContext &w) - : context_{c.context_}, where_{&w} {} - AssignmentContext(const AssignmentContext &c, ForallContext &f) - : context_{c.context_}, forall_{&f} {} - + explicit AssignmentContext(SemanticsContext &context) : context_{context} {} + AssignmentContext(AssignmentContext &&) = default; + AssignmentContext(const AssignmentContext &) = delete; bool operator==(const AssignmentContext &x) const { return this == &x; } + template void PushWhereContext(const A &); + void PopWhereContext(); void Analyze(const parser::AssignmentStmt &); void Analyze(const parser::PointerAssignmentStmt &); - void Analyze(const parser::WhereStmt &); - void Analyze(const parser::WhereConstruct &); - void Analyze(const parser::ForallConstruct &); - - template void Analyze(const parser::UnlabeledStatement &stmt) { - context_.set_location(stmt.source); - Analyze(stmt.statement); - } - template void Analyze(const common::Indirection &x) { - Analyze(x.value()); - } - template std::enable_if_t> Analyze(const A &x) { - std::visit([&](const auto &y) { Analyze(y); }, x.u); - } - template void Analyze(const std::list &list) { - for (const auto &elem : list) { - Analyze(elem); - } - } - template void Analyze(const std::optional &x) { - if (x) { - Analyze(*x); - } - } + void Analyze(const parser::ConcurrentControl &); private: - void Analyze(const parser::WhereConstruct::MaskedElsewhere &); - void Analyze(const parser::MaskedElsewhereStmt &); - void Analyze(const parser::WhereConstruct::Elsewhere &); - void CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, parser::CharBlock rhsSource, bool isPointerAssignment); - - MaskExpr GetMask(const parser::LogicalExpr &, bool defaultValue = true); - + void CheckShape(parser::CharBlock, const SomeExpr *); template parser::Message *Say(parser::CharBlock at, A &&... args) { return &context_.Say(at, std::forward(args)...); } + evaluate::FoldingContext &foldingContext() { + return context_.foldingContext(); + } SemanticsContext &context_; - WhereContext *where_{nullptr}; - ForallContext *forall_{nullptr}; + int whereDepth_{0}; // number of WHEREs currently nested in + // shape of masks in LHS of assignments in current WHERE: + std::vector> whereExtents_; }; void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { - // Assignment statement analysis is in expression.cpp where user-defined - // assignments can be recognized and replaced. if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) { - if (forall_) { - // TODO: Warn if some name in forall_->activeNames or its outer - // contexts does not appear on LHS + const SomeExpr &lhs{assignment->lhs}; + const SomeExpr &rhs{assignment->rhs}; + auto lhsLoc{std::get(stmt.t).GetSource()}; + auto rhsLoc{std::get(stmt.t).source}; + if (whereDepth_ > 0) { + CheckShape(lhsLoc, &lhs); } - CheckForPureContext(assignment->lhs, assignment->rhs, - std::get(stmt.t).source, false /* not => */); + CheckForPureContext(lhs, rhs, rhsLoc, false); } - // TODO: Fortran 2003 ALLOCATABLE assignment semantics (automatic - // (re)allocation of LHS array when unallocated or nonconformable) } void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { - CHECK(!where_); - const evaluate::Assignment *assignment{GetAssignment(stmt)}; - if (!assignment) { - return; - } - const SomeExpr &lhs{assignment->lhs}; - const SomeExpr &rhs{assignment->rhs}; - if (forall_) { - // TODO: Warn if some name in forall_->activeNames or its outer - // contexts does not appear on LHS - } - CheckForPureContext(lhs, rhs, std::get(stmt.t).source, - true /* isPointerAssignment */); - auto restorer{context_.foldingContext().messages().SetLocation( - context_.location().value())}; - CheckPointerAssignment(context_.foldingContext(), *assignment); -} - -void AssignmentContext::Analyze(const parser::WhereStmt &stmt) { - WhereContext where{ - GetMask(std::get(stmt.t)), where_, forall_}; - AssignmentContext nested{*this, where}; - nested.Analyze(std::get(stmt.t)); -} - -// N.B. Construct name matching is checked during label resolution. -void AssignmentContext::Analyze(const parser::WhereConstruct &construct) { - const auto &whereStmt{ - std::get>(construct.t)}; - WhereContext where{ - GetMask(std::get(whereStmt.statement.t)), where_, - forall_}; - if (const auto &name{ - std::get>(whereStmt.statement.t)}) { - where.constructName = name->source; - } - AssignmentContext nested{*this, where}; - nested.Analyze(std::get>(construct.t)); - nested.Analyze(std::get>( - construct.t)); - nested.Analyze( - std::get>(construct.t)); -} - -void AssignmentContext::Analyze( - const parser::WhereConstruct::MaskedElsewhere &elsewhere) { - CHECK(where_); - Analyze( - std::get>(elsewhere.t)); - Analyze(std::get>(elsewhere.t)); -} - -void AssignmentContext::Analyze(const parser::MaskedElsewhereStmt &elsewhere) { - MaskExpr mask{GetMask(std::get(elsewhere.t))}; - MaskExpr copyCumulative{where_->cumulativeMaskExpr}; - MaskExpr notOldMask{evaluate::LogicalNegation(std::move(copyCumulative))}; - if (!evaluate::AreConformable(notOldMask, mask)) { - context_.Say("mask of ELSEWHERE statement is not conformable with " - "the prior mask(s) in its WHERE construct"_err_en_US); - } - MaskExpr copyMask{mask}; - where_->cumulativeMaskExpr = - evaluate::BinaryLogicalOperation(evaluate::LogicalOperator::Or, - std::move(where_->cumulativeMaskExpr), std::move(copyMask)); - where_->thisMaskExpr = evaluate::BinaryLogicalOperation( - evaluate::LogicalOperator::And, std::move(notOldMask), std::move(mask)); - if (where_->outer && - !evaluate::AreConformable( - where_->outer->thisMaskExpr, where_->thisMaskExpr)) { - context_.Say("effective mask of ELSEWHERE statement is not conformable " - "with the mask of the surrounding WHERE construct"_err_en_US); + CHECK(whereDepth_ == 0); + if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) { + const SomeExpr &lhs{assignment->lhs}; + const SomeExpr &rhs{assignment->rhs}; + CheckForPureContext(lhs, rhs, std::get(stmt.t).source, true); + auto restorer{ + foldingContext().messages().SetLocation(context_.location().value())}; + CheckPointerAssignment(foldingContext(), *assignment); } } -void AssignmentContext::Analyze( - const parser::WhereConstruct::Elsewhere &elsewhere) { - MaskExpr copyCumulative{DEREF(where_).cumulativeMaskExpr}; - where_->thisMaskExpr = evaluate::LogicalNegation(std::move(copyCumulative)); - Analyze(std::get>(elsewhere.t)); -} - // C1594 checks static bool IsPointerDummyOfPureFunction(const Symbol &x) { return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) && @@ -333,14 +201,45 @@ void AssignmentContext::CheckForPureContext(const SomeExpr &lhs, } } -MaskExpr AssignmentContext::GetMask( - const parser::LogicalExpr &logicalExpr, bool defaultValue) { - MaskExpr mask{defaultValue}; - if (const SomeExpr * expr{GetExpr(logicalExpr)}) { - auto *logical{std::get_if>(&expr->u)}; - mask = evaluate::ConvertTo(mask, common::Clone(DEREF(logical))); +// 10.2.3.1(2) The masks and LHS of assignments must all have the same shape +void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) { + if (auto shape{evaluate::GetShape(foldingContext(), expr)}) { + std::size_t size{shape->size()}; + if (whereDepth_ == 0) { + whereExtents_.resize(size); + } else if (whereExtents_.size() != size) { + Say(at, + "Must have rank %zd to match prior mask or assignment of" + " WHERE construct"_err_en_US, + whereExtents_.size()); + return; + } + for (std::size_t i{0}; i < size; ++i) { + if (std::optional extent{evaluate::ToInt64((*shape)[i])}) { + if (!whereExtents_[i]) { + whereExtents_[i] = *extent; + } else if (*whereExtents_[i] != *extent) { + Say(at, + "Dimension %d must have extent %jd to match prior mask or" + " assignment of WHERE construct"_err_en_US, + i + 1, static_cast(*whereExtents_[i])); + } + } + } + } +} + +template void AssignmentContext::PushWhereContext(const A &x) { + const auto &expr{std::get(x.t)}; + CheckShape(expr.thing.value().source, GetExpr(expr)); + ++whereDepth_; +} + +void AssignmentContext::PopWhereContext() { + --whereDepth_; + if (whereDepth_ == 0) { + whereExtents_.clear(); } - return mask; } AssignmentChecker::~AssignmentChecker() {} @@ -354,10 +253,22 @@ void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) { context_.value().Analyze(x); } void AssignmentChecker::Enter(const parser::WhereStmt &x) { - context_.value().Analyze(x); + context_.value().PushWhereContext(x); } -void AssignmentChecker::Enter(const parser::WhereConstruct &x) { - context_.value().Analyze(x); +void AssignmentChecker::Leave(const parser::WhereStmt &) { + context_.value().PopWhereContext(); +} +void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) { + context_.value().PushWhereContext(x); +} +void AssignmentChecker::Leave(const parser::EndWhereStmt &) { + context_.value().PopWhereContext(); +} +void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) { + context_.value().PushWhereContext(x); +} +void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) { + context_.value().PopWhereContext(); } } diff --git a/lib/semantics/assignment.h b/lib/semantics/assignment.h index d86bd45b1823..51b7c1736635 100644 --- a/lib/semantics/assignment.h +++ b/lib/semantics/assignment.h @@ -16,9 +16,11 @@ namespace Fortran::parser { class ContextualMessages; struct AssignmentStmt; +struct EndWhereStmt; +struct MaskedElsewhereStmt; struct PointerAssignmentStmt; +struct WhereConstructStmt; struct WhereStmt; -struct WhereConstruct; } namespace Fortran::semantics { @@ -41,7 +43,11 @@ class AssignmentChecker : public virtual BaseChecker { void Enter(const parser::AssignmentStmt &); void Enter(const parser::PointerAssignmentStmt &); void Enter(const parser::WhereStmt &); - void Enter(const parser::WhereConstruct &); + void Leave(const parser::WhereStmt &); + void Enter(const parser::WhereConstructStmt &); + void Leave(const parser::EndWhereStmt &); + void Enter(const parser::MaskedElsewhereStmt &); + void Leave(const parser::MaskedElsewhereStmt &); private: common::Indirection context_; diff --git a/lib/semantics/check-do-forall.cpp b/lib/semantics/check-do-forall.cpp index 1b1abfd10a84..071a873d72d2 100644 --- a/lib/semantics/check-do-forall.cpp +++ b/lib/semantics/check-do-forall.cpp @@ -452,6 +452,7 @@ class DoContext { common::visitors{[&](const auto &x) { return GetAssignment(x); }}, stmt.u)}; if (assignment) { + CheckForallIndexesUsed(*assignment); CheckForImpureCall(assignment->lhs); CheckForImpureCall(assignment->rhs); if (const auto *proc{ @@ -753,6 +754,38 @@ class DoContext { } } + // Each index should be used on the LHS of each assignment in a FORALL + void CheckForallIndexesUsed(const evaluate::Assignment &assignment) { + SymbolVector indexVars{context_.GetIndexVars(IndexVarKind::FORALL)}; + if (!indexVars.empty()) { + SymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)}; + std::visit( + common::visitors{ + [&](const evaluate::Assignment::BoundsSpec &spec) { + for (const auto &bound : spec) { + symbols.merge(evaluate::CollectSymbols(bound)); + } + }, + [&](const evaluate::Assignment::BoundsRemapping &remapping) { + for (const auto &bounds : remapping) { + symbols.merge(evaluate::CollectSymbols(bounds.first)); + symbols.merge(evaluate::CollectSymbols(bounds.second)); + } + }, + [](const auto &) {}, + }, + assignment.u); + for (const Symbol &index : indexVars) { + if (symbols.count(index) == 0) { + context_.Say( + "Warning: FORALL index variable '%s' not used on left-hand side" + " of assignment"_en_US, + index.name()); + } + } + } + } + // For messages where the DO loop must be DO CONCURRENT, make that explicit. const char *LoopKindName() const { return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL"; diff --git a/lib/semantics/semantics.cpp b/lib/semantics/semantics.cpp index dcb119813a0e..038209160b57 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/semantics/semantics.cpp @@ -122,7 +122,8 @@ static bool PerformStatementSemantics( RewriteParseTree(context, program); CheckDeclarations(context); StatementSemanticsPass1{context}.Walk(program); - return StatementSemanticsPass2{context}.Walk(program); + StatementSemanticsPass2{context}.Walk(program); + return !context.AnyFatalError(); } SemanticsContext::SemanticsContext( @@ -261,6 +262,16 @@ void SemanticsContext::DeactivateIndexVar(const parser::Name &name) { } } +SymbolVector SemanticsContext::GetIndexVars(IndexVarKind kind) { + SymbolVector result; + for (const auto &[symbol, info] : activeIndexVars_) { + if (info.kind == kind) { + result.push_back(symbol); + } + } + return result; +} + bool Semantics::Perform() { return ValidateLabels(context_, program_) && parser::CanonicalizeDo(program_) && // force line break diff --git a/test/semantics/assign01.f90 b/test/semantics/assign01.f90 index c2ab99ca0f0f..b125da87ad22 100644 --- a/test/semantics/assign01.f90 +++ b/test/semantics/assign01.f90 @@ -1,14 +1,53 @@ -integer :: a1(10), a2(10) -logical :: m1(10), m2(5,5) -m1 = .true. -m2 = .false. -a1 = [((i),i=1,10)] -where (m1) - a2 = 1 -!ERROR: mask of ELSEWHERE statement is not conformable with the prior mask(s) in its WHERE construct -elsewhere (m2) - a2 = 2 -elsewhere - a2 = 3 -end where +! 10.2.3.1(2) All masks and LHS of assignments in a WHERE must conform + +subroutine s1 + integer :: a1(10), a2(10) + logical :: m1(10), m2(5,5) + m1 = .true. + m2 = .false. + a1 = [((i),i=1,10)] + where (m1) + a2 = 1 + !ERROR: Must have rank 1 to match prior mask or assignment of WHERE construct + elsewhere (m2) + a2 = 2 + elsewhere + a2 = 3 + end where +end + +subroutine s2 + logical, allocatable :: m1(:), m4(:,:) + logical :: m2(2), m3(3) + where(m1) + where(m2) + end where + !ERROR: Dimension 1 must have extent 2 to match prior mask or assignment of WHERE construct + where(m3) + end where + !ERROR: Must have rank 1 to match prior mask or assignment of WHERE construct + where(m4) + end where + endwhere + where(m1) + where(m3) + end where + !ERROR: Dimension 1 must have extent 3 to match prior mask or assignment of WHERE construct + elsewhere(m2) + end where +end + +subroutine s3 + logical, allocatable :: m1(:,:) + logical :: m2(4,2) + real :: x(4,4), y(4,4) + real :: a(4,5), b(4,5) + where(m1) + x = y + !ERROR: Dimension 2 must have extent 4 to match prior mask or assignment of WHERE construct + a = b + !ERROR: Dimension 2 must have extent 4 to match prior mask or assignment of WHERE construct + where(m2) + end where + end where end diff --git a/test/semantics/forall01.f90 b/test/semantics/forall01.f90 index bd665e2a5283..e90a17f62978 100644 --- a/test/semantics/forall01.f90 +++ b/test/semantics/forall01.f90 @@ -16,7 +16,6 @@ subroutine forall1 end forall end - subroutine forall2 integer, pointer :: a(:) integer, target :: b(10,10) @@ -73,3 +72,34 @@ subroutine forall4 !ERROR: FORALL step expression may not be zero forall(i=1:10:zero) a(i) = i end + +! Note: this gets warnings but not errors +subroutine forall5 + real, target :: x(10), y(10) + forall(i=1:10) + x(i) = y(i) + end forall + forall(i=1:10) + x = y ! warning: i not used on LHS + forall(j=1:10) + x(i) = y(i) ! warning: j not used on LHS + x(j) = y(j) ! warning: i not used on LHS + endforall + endforall + do concurrent(i=1:10) + x = y + forall(i=1:10) x = y + end do +end + +subroutine forall6 + type t + real, pointer :: p + end type + type(t) :: a(10) + real, target :: b(10) + forall(i=1:10) + a(i)%p => b(i) + a(1)%p => b(i) ! warning: i not used on LHS + end forall +end From 72aa278f0341f345a12fcd76a6ad1f2045ecc233 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Fri, 21 Feb 2020 16:25:10 -0800 Subject: [PATCH 042/345] Temporarily disable part of data01 test `data x /a(1)/` is ambiguous. The data value may be an array element or a structure constructor. We need to parse it as one of these and then fix up the parse tree when it should have been the other one. My PR 1012 changed the parser to identify this as an array element. That makes this test invalid until we have the right parse tree fixup, so I am disabling it for now. --- test/semantics/data01.f90 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/semantics/data01.f90 b/test/semantics/data01.f90 index 02c4674d241d..db978331d541 100644 --- a/test/semantics/data01.f90 +++ b/test/semantics/data01.f90 @@ -37,12 +37,12 @@ subroutine CheckRepeat DATA myName%age / digits(myAge) * 35 / end -subroutine CheckValue - use m1 - !C883 - !ERROR: Derived type 'persn' not found - DATA myname / persn(2, 'Abcd Efgh') / - !C884 - !ERROR: Structure constructor in data value must be a constant expression - DATA myname / person(myAge, 'Abcd Ijkl') / -end +!subroutine CheckValue +! use m1 +! !C883 +! !ERROR: Derived type 'persn' not found +! DATA myname / persn(2, 'Abcd Efgh') / +! !C884 +! !ERROR: Structure constructor in data value must be a constant expression +! DATA myname / person(myAge, 'Abcd Ijkl') / +!end From d2eb7a1c443d1539ef12b6f027074a0eb15b1ea0 Mon Sep 17 00:00:00 2001 From: CarolineConcatto <51754594+CarolineConcatto@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:11:52 +0000 Subject: [PATCH 043/345] [LLVMify F18] Compiler module folders should have capitalised names (#980) This patch renames the modules in f18 to use a capital letter in the module name Signed-off-by: Caroline Concatto --- documentation/C++style.md | 2 +- documentation/ImplementingASemanticCheck.md | 46 +++++++++--------- .../{common => Common}/Fortran-features.h | 8 +-- include/flang/{common => Common}/Fortran.h | 2 +- .../{common => Common}/bit-population-count.h | 2 +- .../{common => Common}/constexpr-bitset.h | 2 +- .../flang/{common => Common}/default-kinds.h | 4 +- include/flang/{common => Common}/enum-set.h | 2 +- include/flang/{common => Common}/format.h | 4 +- include/flang/{common => Common}/idioms.h | 2 +- .../flang/{common => Common}/indirection.h | 2 +- include/flang/{common => Common}/interval.h | 2 +- .../leading-zero-bit-count.h | 2 +- include/flang/{common => Common}/real.h | 2 +- .../{common => Common}/reference-counted.h | 2 +- include/flang/{common => Common}/reference.h | 2 +- include/flang/{common => Common}/restorer.h | 2 +- include/flang/{common => Common}/template.h | 4 +- include/flang/{common => Common}/uint128.h | 2 +- .../unsigned-const-division.h | 2 +- include/flang/{common => Common}/unwrap.h | 2 +- .../binary-floating-point.h | 6 +-- include/flang/{decimal => Decimal}/decimal.h | 2 +- include/flang/{evaluate => Evaluate}/call.h | 10 ++-- .../{evaluate => Evaluate}/characteristics.h | 14 +++--- .../{evaluate => Evaluate}/check-expression.h | 2 +- include/flang/{evaluate => Evaluate}/common.h | 18 +++---- .../flang/{evaluate => Evaluate}/complex.h | 2 +- .../flang/{evaluate => Evaluate}/constant.h | 6 +-- .../flang/{evaluate => Evaluate}/expression.h | 12 ++--- include/flang/{evaluate => Evaluate}/fold.h | 2 +- .../flang/{evaluate => Evaluate}/formatting.h | 6 +-- .../flang/{evaluate => Evaluate}/integer.h | 8 +-- .../intrinsics-library.h | 2 +- .../flang/{evaluate => Evaluate}/intrinsics.h | 8 +-- .../flang/{evaluate => Evaluate}/logical.h | 2 +- include/flang/{evaluate => Evaluate}/real.h | 6 +-- .../{evaluate => Evaluate}/rounding-bits.h | 2 +- include/flang/{evaluate => Evaluate}/shape.h | 8 +-- .../{evaluate => Evaluate}/static-data.h | 4 +- include/flang/{evaluate => Evaluate}/tools.h | 18 +++---- .../flang/{evaluate => Evaluate}/traverse.h | 6 +-- include/flang/{evaluate => Evaluate}/type.h | 8 +-- .../flang/{evaluate => Evaluate}/variable.h | 10 ++-- include/flang/{lower => Lower}/.clang-format | 0 include/flang/{lower => Lower}/PFTBuilder.h | 6 +-- .../{optimizer => Optimizer}/.clang-format | 0 include/flang/{parser => Parser}/char-block.h | 4 +- .../flang/{parser => Parser}/char-buffer.h | 2 +- include/flang/{parser => Parser}/char-set.h | 2 +- include/flang/{parser => Parser}/characters.h | 2 +- .../{parser => Parser}/dump-parse-tree.h | 6 +-- .../{parser => Parser}/format-specification.h | 2 +- .../{parser => Parser}/instrumented-parser.h | 6 +-- include/flang/{parser => Parser}/message.h | 8 +-- .../flang/{parser => Parser}/parse-state.h | 12 ++--- .../{parser => Parser}/parse-tree-visitor.h | 2 +- include/flang/{parser => Parser}/parse-tree.h | 8 +-- include/flang/{parser => Parser}/parsing.h | 4 +- include/flang/{parser => Parser}/provenance.h | 6 +-- include/flang/{parser => Parser}/source.h | 2 +- include/flang/{parser => Parser}/tools.h | 2 +- include/flang/{parser => Parser}/unparse.h | 2 +- include/flang/{parser => Parser}/user-state.h | 10 ++-- include/flang/{semantics => Semantics}/attr.h | 6 +-- .../{semantics => Semantics}/expression.h | 24 ++++----- .../flang/{semantics => Semantics}/scope.h | 12 ++--- .../{semantics => Semantics}/semantics.h | 10 ++-- .../flang/{semantics => Semantics}/symbol.h | 10 ++-- .../flang/{semantics => Semantics}/tools.h | 20 ++++---- include/flang/{semantics => Semantics}/type.h | 10 ++-- .../unparse-with-symbols.h | 4 +- lib/CMakeLists.txt | 12 ++--- lib/{common => Common}/CMakeLists.txt | 2 +- lib/{common => Common}/Fortran-features.cpp | 8 +-- lib/{common => Common}/Fortran.cpp | 4 +- lib/{common => Common}/default-kinds.cpp | 6 +-- lib/{common => Common}/idioms.cpp | 4 +- lib/{decimal => Decimal}/CMakeLists.txt | 2 +- .../big-radix-floating-point.h | 14 +++--- .../binary-to-decimal.cpp | 4 +- .../decimal-to-binary.cpp | 10 ++-- lib/{evaluate => Evaluate}/CMakeLists.txt | 2 +- lib/{evaluate => Evaluate}/call.cpp | 14 +++--- lib/{evaluate => Evaluate}/character.h | 4 +- .../characteristics.cpp | 22 ++++----- .../check-expression.cpp | 12 ++--- lib/{evaluate => Evaluate}/common.cpp | 6 +-- lib/{evaluate => Evaluate}/complex.cpp | 4 +- lib/{evaluate => Evaluate}/constant.cpp | 10 ++-- lib/{evaluate => Evaluate}/expression.cpp | 14 +++--- lib/{evaluate => Evaluate}/fold-character.cpp | 2 +- lib/{evaluate => Evaluate}/fold-complex.cpp | 2 +- .../fold-implementation.h | 36 +++++++------- lib/{evaluate => Evaluate}/fold-integer.cpp | 2 +- lib/{evaluate => Evaluate}/fold-logical.cpp | 4 +- lib/{evaluate => Evaluate}/fold-real.cpp | 2 +- lib/{evaluate => Evaluate}/fold.cpp | 4 +- lib/{evaluate => Evaluate}/formatting.cpp | 18 +++---- lib/{evaluate => Evaluate}/host.cpp | 4 +- lib/{evaluate => Evaluate}/host.h | 4 +- lib/{evaluate => Evaluate}/int-power.h | 4 +- lib/{evaluate => Evaluate}/integer.cpp | 4 +- .../intrinsics-library-templates.h | 8 +-- .../intrinsics-library.cpp | 2 +- lib/{evaluate => Evaluate}/intrinsics.cpp | 22 ++++----- lib/{evaluate => Evaluate}/logical.cpp | 4 +- lib/{evaluate => Evaluate}/real.cpp | 10 ++-- lib/{evaluate => Evaluate}/shape.cpp | 22 ++++----- lib/{evaluate => Evaluate}/static-data.cpp | 6 +-- lib/{evaluate => Evaluate}/tools.cpp | 12 ++--- lib/{evaluate => Evaluate}/type.cpp | 22 ++++----- lib/{evaluate => Evaluate}/variable.cpp | 18 +++---- lib/{fir => Fir}/.clang-format | 0 lib/{lower => Lower}/.clang-format | 0 lib/{lower => Lower}/CMakeLists.txt | 0 lib/{lower => Lower}/PFTBuilder.cpp | 8 +-- lib/{optimizer => Optimizer}/.clang-format | 0 lib/{parser => Parser}/CMakeLists.txt | 2 +- lib/{parser => Parser}/Fortran-parsers.cpp | 6 +-- lib/{parser => Parser}/basic-parsers.h | 18 +++---- lib/{parser => Parser}/char-block.cpp | 4 +- lib/{parser => Parser}/char-buffer.cpp | 6 +-- lib/{parser => Parser}/char-set.cpp | 4 +- lib/{parser => Parser}/characters.cpp | 6 +-- lib/{parser => Parser}/debug-parser.cpp | 4 +- lib/{parser => Parser}/debug-parser.h | 4 +- lib/{parser => Parser}/executable-parsers.cpp | 6 +-- lib/{parser => Parser}/expr-parsers.cpp | 6 +-- lib/{parser => Parser}/expr-parsers.h | 4 +- .../instrumented-parser.cpp | 8 +-- lib/{parser => Parser}/io-parsers.cpp | 6 +-- lib/{parser => Parser}/message.cpp | 8 +-- lib/{parser => Parser}/misc-parsers.h | 6 +-- lib/{parser => Parser}/openmp-parsers.cpp | 4 +- lib/{parser => Parser}/parse-tree.cpp | 10 ++-- lib/{parser => Parser}/parsing.cpp | 10 ++-- lib/{parser => Parser}/preprocessor.cpp | 8 +-- lib/{parser => Parser}/preprocessor.h | 6 +-- lib/{parser => Parser}/prescan.cpp | 10 ++-- lib/{parser => Parser}/prescan.h | 10 ++-- lib/{parser => Parser}/program-parsers.cpp | 6 +-- lib/{parser => Parser}/provenance.cpp | 6 +-- lib/{parser => Parser}/source.cpp | 8 +-- lib/{parser => Parser}/stmt-parser.h | 2 +- lib/{parser => Parser}/token-parsers.h | 12 ++--- lib/{parser => Parser}/token-sequence.cpp | 4 +- lib/{parser => Parser}/token-sequence.h | 6 +-- lib/{parser => Parser}/tools.cpp | 4 +- .../type-parser-implementation.h | 2 +- lib/{parser => Parser}/type-parsers.h | 6 +-- lib/{parser => Parser}/unparse.cpp | 16 +++--- lib/{parser => Parser}/user-state.cpp | 6 +-- lib/{semantics => Semantics}/CMakeLists.txt | 2 +- lib/{semantics => Semantics}/assignment.cpp | 26 +++++----- lib/{semantics => Semantics}/assignment.h | 8 +-- lib/{semantics => Semantics}/attr.cpp | 6 +-- .../canonicalize-do.cpp | 4 +- .../canonicalize-do.h | 2 +- .../canonicalize-omp.cpp | 4 +- .../canonicalize-omp.h | 2 +- .../check-allocate.cpp | 18 +++---- lib/{semantics => Semantics}/check-allocate.h | 4 +- .../check-arithmeticif.cpp | 8 +-- .../check-arithmeticif.h | 4 +- lib/{semantics => Semantics}/check-call.cpp | 18 +++---- lib/{semantics => Semantics}/check-call.h | 4 +- .../check-coarray.cpp | 16 +++--- lib/{semantics => Semantics}/check-coarray.h | 4 +- lib/{semantics => Semantics}/check-data.cpp | 2 +- lib/{semantics => Semantics}/check-data.h | 10 ++-- .../check-deallocate.cpp | 10 ++-- .../check-deallocate.h | 4 +- .../check-declarations.cpp | 18 +++---- .../check-declarations.h | 2 +- .../check-do-forall.cpp | 28 +++++------ .../check-do-forall.h | 6 +-- .../check-if-stmt.cpp | 8 +-- lib/{semantics => Semantics}/check-if-stmt.h | 4 +- lib/{semantics => Semantics}/check-io.cpp | 10 ++-- lib/{semantics => Semantics}/check-io.h | 10 ++-- .../check-nullify.cpp | 12 ++--- lib/{semantics => Semantics}/check-nullify.h | 4 +- .../check-omp-structure.cpp | 6 +-- .../check-omp-structure.h | 8 +-- lib/{semantics => Semantics}/check-purity.cpp | 6 +-- lib/{semantics => Semantics}/check-purity.h | 4 +- lib/{semantics => Semantics}/check-return.cpp | 12 ++--- lib/{semantics => Semantics}/check-return.h | 4 +- lib/{semantics => Semantics}/check-stop.cpp | 12 ++--- lib/{semantics => Semantics}/check-stop.h | 4 +- lib/{semantics => Semantics}/expression.cpp | 30 ++++++------ lib/{semantics => Semantics}/mod-file.cpp | 16 +++--- lib/{semantics => Semantics}/mod-file.h | 4 +- .../pointer-assignment.cpp | 26 +++++----- .../pointer-assignment.h | 8 +-- lib/{semantics => Semantics}/program-tree.cpp | 8 +-- lib/{semantics => Semantics}/program-tree.h | 6 +-- .../resolve-labels.cpp | 10 ++-- lib/{semantics => Semantics}/resolve-labels.h | 2 +- .../resolve-names-utils.cpp | 24 ++++----- .../resolve-names-utils.h | 10 ++-- .../resolve-names.cpp | 42 ++++++++-------- lib/{semantics => Semantics}/resolve-names.h | 2 +- .../rewrite-parse-tree.cpp | 18 +++---- .../rewrite-parse-tree.h | 2 +- lib/{semantics => Semantics}/scope.cpp | 10 ++-- lib/{semantics => Semantics}/semantics.cpp | 16 +++--- lib/{semantics => Semantics}/symbol.cpp | 14 +++--- lib/{semantics => Semantics}/tools.cpp | 24 ++++----- lib/{semantics => Semantics}/type.cpp | 14 +++--- .../unparse-with-symbols.cpp | 12 ++--- runtime/descriptor.cpp | 2 +- runtime/environment.h | 2 +- runtime/format-implementation.h | 4 +- runtime/format.h | 4 +- runtime/numeric-output.cpp | 2 +- runtime/numeric-output.h | 2 +- runtime/transformational.cpp | 4 +- runtime/type-code.h | 2 +- test-lit/{driver => Driver}/version_test.f90 | 0 test-lit/{lower => Lower}/pre-fir-tree01.f90 | 0 test-lit/{lower => Lower}/pre-fir-tree02.f90 | 0 test-lit/{lower => Lower}/pre-fir-tree03.f90 | 0 test-lit/{lower => Lower}/pre-fir-tree04.f90 | 0 test/CMakeLists.txt | 8 +-- test/{decimal => Decimal}/CMakeLists.txt | 2 +- .../quick-sanity-test.cpp | 2 +- test/{decimal => Decimal}/thorough-test.cpp | 2 +- test/{evaluate => Evaluate}/CMakeLists.txt | 4 +- .../ISO-Fortran-binding.cpp | 0 .../bit-population-count.cpp | 2 +- test/{evaluate => Evaluate}/expression.cpp | 10 ++-- test/{evaluate => Evaluate}/folding.cpp | 14 +++--- test/{evaluate => Evaluate}/folding01.f90 | 0 test/{evaluate => Evaluate}/folding02.f90 | 0 test/{evaluate => Evaluate}/folding03.f90 | 0 test/{evaluate => Evaluate}/folding04.f90 | 0 test/{evaluate => Evaluate}/folding05.f90 | Bin test/{evaluate => Evaluate}/folding06.f90 | 0 test/{evaluate => Evaluate}/folding07.f90 | 0 test/{evaluate => Evaluate}/folding08.f90 | 0 test/{evaluate => Evaluate}/folding09.f90 | 0 test/{evaluate => Evaluate}/fp-testing.cpp | 0 test/{evaluate => Evaluate}/fp-testing.h | 2 +- test/{evaluate => Evaluate}/integer.cpp | 2 +- test/{evaluate => Evaluate}/intrinsics.cpp | 10 ++-- .../leading-zero-bit-count.cpp | 2 +- test/{evaluate => Evaluate}/logical.cpp | 2 +- test/{evaluate => Evaluate}/real.cpp | 2 +- test/{evaluate => Evaluate}/reshape.cpp | 0 test/{evaluate => Evaluate}/test_folding.sh | 0 test/{evaluate => Evaluate}/testing.cpp | 0 test/{evaluate => Evaluate}/testing.h | 0 test/{evaluate => Evaluate}/uint128.cpp | 2 +- test/{preprocessing => Preprocessing}/pp001.F | 0 test/{preprocessing => Preprocessing}/pp002.F | 0 test/{preprocessing => Preprocessing}/pp003.F | 0 test/{preprocessing => Preprocessing}/pp004.F | 0 test/{preprocessing => Preprocessing}/pp005.F | 0 test/{preprocessing => Preprocessing}/pp006.F | 0 test/{preprocessing => Preprocessing}/pp007.F | 0 test/{preprocessing => Preprocessing}/pp008.F | 0 test/{preprocessing => Preprocessing}/pp009.F | 0 test/{preprocessing => Preprocessing}/pp010.F | 0 test/{preprocessing => Preprocessing}/pp011.F | 0 test/{preprocessing => Preprocessing}/pp012.F | 0 test/{preprocessing => Preprocessing}/pp013.F | 0 test/{preprocessing => Preprocessing}/pp014.F | 0 test/{preprocessing => Preprocessing}/pp015.F | 0 test/{preprocessing => Preprocessing}/pp016.F | 0 test/{preprocessing => Preprocessing}/pp017.F | 0 test/{preprocessing => Preprocessing}/pp018.F | 0 test/{preprocessing => Preprocessing}/pp019.F | 0 test/{preprocessing => Preprocessing}/pp020.F | 0 test/{preprocessing => Preprocessing}/pp021.F | 0 test/{preprocessing => Preprocessing}/pp022.F | 0 test/{preprocessing => Preprocessing}/pp023.F | 0 test/{preprocessing => Preprocessing}/pp024.F | 0 test/{preprocessing => Preprocessing}/pp025.F | 0 test/{preprocessing => Preprocessing}/pp026.F | 0 test/{preprocessing => Preprocessing}/pp027.F | 0 test/{preprocessing => Preprocessing}/pp028.F | 0 test/{preprocessing => Preprocessing}/pp029.F | 0 test/{preprocessing => Preprocessing}/pp030.F | 0 test/{preprocessing => Preprocessing}/pp031.F | 0 test/{preprocessing => Preprocessing}/pp032.F | 0 test/{preprocessing => Preprocessing}/pp033.F | 0 test/{preprocessing => Preprocessing}/pp034.F | 0 test/{preprocessing => Preprocessing}/pp035.F | 0 test/{preprocessing => Preprocessing}/pp036.F | 0 test/{preprocessing => Preprocessing}/pp037.F | 0 test/{preprocessing => Preprocessing}/pp038.F | 0 test/{preprocessing => Preprocessing}/pp039.F | 0 test/{preprocessing => Preprocessing}/pp040.F | 0 test/{preprocessing => Preprocessing}/pp041.F | 0 test/{preprocessing => Preprocessing}/pp042.F | 0 test/{preprocessing => Preprocessing}/pp043.F | 0 test/{preprocessing => Preprocessing}/pp044.F | 0 .../pp101.F90 | 0 .../pp102.F90 | 0 .../pp103.F90 | 0 .../pp104.F90 | 0 .../pp105.F90 | 0 .../pp106.F90 | 0 .../pp107.F90 | 0 .../pp108.F90 | 0 .../pp109.F90 | 0 .../pp110.F90 | 0 .../pp111.F90 | 0 .../pp112.F90 | 0 .../pp113.F90 | 0 .../pp114.F90 | 0 .../pp115.F90 | 0 .../pp116.F90 | 0 .../pp117.F90 | 0 .../pp118.F90 | 0 .../pp119.F90 | 0 .../pp120.F90 | 0 .../pp121.F90 | 0 .../pp122.F90 | 0 .../pp123.F90 | 0 .../pp124.F90 | 0 .../pp125.F90 | 0 .../pp126.F90 | 0 .../pp127.F90 | 0 .../pp128.F90 | 0 .../pp129.F90 | 0 .../pp130.F90 | 0 test/{runtime => Runtime}/CMakeLists.txt | 2 +- test/{runtime => Runtime}/external-hello.cpp | 0 test/{runtime => Runtime}/format.cpp | 0 test/{runtime => Runtime}/hello.cpp | 0 test/{semantics => Semantics}/CMakeLists.txt | 2 +- test/{semantics => Semantics}/allocate01.f90 | 0 test/{semantics => Semantics}/allocate02.f90 | 0 test/{semantics => Semantics}/allocate03.f90 | 0 test/{semantics => Semantics}/allocate04.f90 | 0 test/{semantics => Semantics}/allocate05.f90 | 0 test/{semantics => Semantics}/allocate06.f90 | 0 test/{semantics => Semantics}/allocate07.f90 | 0 test/{semantics => Semantics}/allocate08.f90 | 0 test/{semantics => Semantics}/allocate09.f90 | 0 test/{semantics => Semantics}/allocate10.f90 | 0 test/{semantics => Semantics}/allocate11.f90 | 0 test/{semantics => Semantics}/allocate12.f90 | 0 test/{semantics => Semantics}/allocate13.f90 | 0 test/{semantics => Semantics}/altreturn01.f90 | 0 test/{semantics => Semantics}/altreturn02.f90 | 0 test/{semantics => Semantics}/altreturn03.f90 | 0 test/{semantics => Semantics}/altreturn04.f90 | 0 test/{semantics => Semantics}/altreturn05.f90 | 0 test/{semantics => Semantics}/assign01.f90 | 0 test/{semantics => Semantics}/assign02.f90 | 0 test/{semantics => Semantics}/assign03.f90 | 0 .../bad-forward-type.f90 | 0 test/{semantics => Semantics}/bindings01.f90 | 0 .../{semantics => Semantics}/block-data01.f90 | 0 .../blockconstruct01.f90 | 0 .../blockconstruct02.f90 | 0 .../blockconstruct03.f90 | 0 test/{semantics => Semantics}/c_f_pointer.f90 | 0 test/{semantics => Semantics}/call01.f90 | 0 test/{semantics => Semantics}/call02.f90 | 0 test/{semantics => Semantics}/call03.f90 | 0 test/{semantics => Semantics}/call04.f90 | 0 test/{semantics => Semantics}/call05.f90 | 0 test/{semantics => Semantics}/call06.f90 | 0 test/{semantics => Semantics}/call07.f90 | 0 test/{semantics => Semantics}/call08.f90 | 0 test/{semantics => Semantics}/call09.f90 | 0 test/{semantics => Semantics}/call10.f90 | 0 test/{semantics => Semantics}/call11.f90 | 0 test/{semantics => Semantics}/call12.f90 | 0 test/{semantics => Semantics}/call13.f90 | 0 test/{semantics => Semantics}/call14.f90 | 0 test/{semantics => Semantics}/call15.f90 | 0 test/{semantics => Semantics}/canondo01.f90 | 0 test/{semantics => Semantics}/canondo02.f90 | 0 test/{semantics => Semantics}/canondo03.f90 | 0 test/{semantics => Semantics}/canondo04.f90 | 0 test/{semantics => Semantics}/canondo05.f90 | 0 test/{semantics => Semantics}/canondo06.f90 | 0 test/{semantics => Semantics}/canondo07.f90 | 0 test/{semantics => Semantics}/canondo08.f90 | 0 test/{semantics => Semantics}/canondo09.f90 | 0 test/{semantics => Semantics}/canondo10.f90 | 0 test/{semantics => Semantics}/canondo11.f90 | 0 test/{semantics => Semantics}/canondo12.f90 | 0 test/{semantics => Semantics}/canondo13.f90 | 0 test/{semantics => Semantics}/canondo14.f90 | 0 test/{semantics => Semantics}/canondo15.f90 | 0 test/{semantics => Semantics}/canondo16.f90 | 0 test/{semantics => Semantics}/canondo17.f90 | 0 test/{semantics => Semantics}/canondo18.f90 | 0 test/{semantics => Semantics}/canondo19.f90 | 0 test/{semantics => Semantics}/coarrays01.f90 | 0 test/{semantics => Semantics}/common.sh | 0 .../computed-goto01.f90 | 0 .../computed-goto02.f90 | 0 test/{semantics => Semantics}/critical01.f90 | 0 test/{semantics => Semantics}/critical02.f90 | 0 test/{semantics => Semantics}/critical03.f90 | 0 test/{semantics => Semantics}/critical04.f90 | 0 test/{semantics => Semantics}/data01.f90 | 0 .../{semantics => Semantics}/deallocate01.f90 | 0 .../{semantics => Semantics}/deallocate04.f90 | 0 .../{semantics => Semantics}/deallocate05.f90 | 0 .../doconcurrent01.f90 | 0 .../doconcurrent02.f90 | 0 .../doconcurrent03.f90 | 0 .../doconcurrent04.f90 | 0 .../doconcurrent05.f90 | 0 .../doconcurrent06.f90 | 0 .../doconcurrent07.f90 | 0 .../doconcurrent08.f90 | 2 +- .../dosemantics01.f90 | 0 .../dosemantics02.f90 | 0 .../dosemantics03.f90 | 0 .../dosemantics04.f90 | 0 .../dosemantics05.f90 | 0 .../dosemantics06.f90 | 0 .../dosemantics07.f90 | 0 .../dosemantics08.f90 | 0 .../dosemantics09.f90 | 0 .../dosemantics10.f90 | 0 .../dosemantics11.f90 | 0 .../dosemantics12.f90 | 0 .../equivalence01.f90 | 0 .../expr-errors01.f90 | 0 .../expr-errors02.f90 | 0 test/{semantics => Semantics}/forall01.f90 | 0 .../getdefinition01.f90 | 0 .../getdefinition02.f | 0 .../getdefinition03-a.f90 | 0 .../getdefinition03-b.f90 | 0 .../getdefinition04.f90 | 0 .../getdefinition05.f90 | 0 .../{semantics => Semantics}/getsymbols01.f90 | 0 .../getsymbols02-a.f90 | 0 .../getsymbols02-b.f90 | 0 .../getsymbols02-c.f90 | 0 .../getsymbols03-a.f90 | 0 .../getsymbols03-b.f90 | 0 .../{semantics => Semantics}/getsymbols04.f90 | 0 .../{semantics => Semantics}/getsymbols05.f90 | 0 test/{semantics => Semantics}/if_arith01.f90 | 0 test/{semantics => Semantics}/if_arith02.f90 | 0 test/{semantics => Semantics}/if_arith03.f90 | 0 test/{semantics => Semantics}/if_arith04.f90 | 0 .../if_construct01.f90 | 0 .../if_construct02.f90 | 0 test/{semantics => Semantics}/if_stmt01.f90 | 0 test/{semantics => Semantics}/if_stmt02.f90 | 0 test/{semantics => Semantics}/if_stmt03.f90 | 0 test/{semantics => Semantics}/implicit01.f90 | 0 test/{semantics => Semantics}/implicit02.f90 | 0 test/{semantics => Semantics}/implicit03.f90 | 0 test/{semantics => Semantics}/implicit04.f90 | 0 test/{semantics => Semantics}/implicit05.f90 | 0 test/{semantics => Semantics}/implicit06.f90 | 0 test/{semantics => Semantics}/implicit07.f90 | 0 test/{semantics => Semantics}/implicit08.f90 | 0 test/{semantics => Semantics}/init01.f90 | 0 .../{semantics => Semantics}/int-literals.f90 | 0 test/{semantics => Semantics}/io01.f90 | 0 test/{semantics => Semantics}/io02.f90 | 0 test/{semantics => Semantics}/io03.f90 | 0 test/{semantics => Semantics}/io04.f90 | 0 test/{semantics => Semantics}/io05.f90 | 0 test/{semantics => Semantics}/io06.f90 | 0 test/{semantics => Semantics}/io07.f90 | 0 test/{semantics => Semantics}/io08.f90 | 0 test/{semantics => Semantics}/io09.f90 | 0 test/{semantics => Semantics}/io10.f90 | 0 test/{semantics => Semantics}/kinds01.f90 | 0 test/{semantics => Semantics}/kinds02.f90 | 0 test/{semantics => Semantics}/kinds03.f90 | 0 test/{semantics => Semantics}/label01.F90 | 0 test/{semantics => Semantics}/label02.f90 | 0 test/{semantics => Semantics}/label03.f90 | 0 test/{semantics => Semantics}/label04.f90 | 0 test/{semantics => Semantics}/label05.f90 | 0 test/{semantics => Semantics}/label06.f90 | 0 test/{semantics => Semantics}/label07.f90 | 0 test/{semantics => Semantics}/label08.f90 | 0 test/{semantics => Semantics}/label09.f90 | 0 test/{semantics => Semantics}/label10.f90 | 0 test/{semantics => Semantics}/label11.f90 | 0 test/{semantics => Semantics}/label12.f90 | 0 test/{semantics => Semantics}/label13.f90 | 0 test/{semantics => Semantics}/label14.f90 | 0 .../misc-declarations.f90 | 0 test/{semantics => Semantics}/modfile01.f90 | 0 test/{semantics => Semantics}/modfile02.f90 | 0 test/{semantics => Semantics}/modfile03.f90 | 0 test/{semantics => Semantics}/modfile04.f90 | 0 test/{semantics => Semantics}/modfile05.f90 | 0 test/{semantics => Semantics}/modfile06.f90 | 0 test/{semantics => Semantics}/modfile07.f90 | 0 test/{semantics => Semantics}/modfile08.f90 | 0 test/{semantics => Semantics}/modfile09-a.f90 | 0 test/{semantics => Semantics}/modfile09-b.f90 | 0 test/{semantics => Semantics}/modfile09-c.f90 | 0 test/{semantics => Semantics}/modfile09-d.f90 | 0 test/{semantics => Semantics}/modfile10.f90 | 0 test/{semantics => Semantics}/modfile11.f90 | 0 test/{semantics => Semantics}/modfile12.f90 | 0 test/{semantics => Semantics}/modfile13.f90 | 0 test/{semantics => Semantics}/modfile14.f90 | 0 test/{semantics => Semantics}/modfile15.f90 | 0 test/{semantics => Semantics}/modfile16.f90 | 0 test/{semantics => Semantics}/modfile17.f90 | 0 test/{semantics => Semantics}/modfile18.f90 | 0 test/{semantics => Semantics}/modfile19.f90 | 0 test/{semantics => Semantics}/modfile20.f90 | 0 test/{semantics => Semantics}/modfile21.f90 | 0 test/{semantics => Semantics}/modfile22.f90 | 0 test/{semantics => Semantics}/modfile23.f90 | 0 test/{semantics => Semantics}/modfile24.f90 | 0 test/{semantics => Semantics}/modfile25.f90 | 0 test/{semantics => Semantics}/modfile26.f90 | 0 test/{semantics => Semantics}/modfile27.f90 | 0 test/{semantics => Semantics}/modfile28.f90 | 0 test/{semantics => Semantics}/modfile29.f90 | 0 test/{semantics => Semantics}/modfile30.f90 | 0 test/{semantics => Semantics}/modfile31.f90 | 0 test/{semantics => Semantics}/modfile32.f90 | 0 test/{semantics => Semantics}/modfile33.f90 | 0 test/{semantics => Semantics}/modfile34.f90 | 0 test/{semantics => Semantics}/modfile35.f90 | 0 test/{semantics => Semantics}/null01.f90 | 0 test/{semantics => Semantics}/nullify01.f90 | 0 test/{semantics => Semantics}/nullify02.f90 | 0 test/{semantics => Semantics}/omp-atomic.f90 | 0 .../omp-clause-validity01.f90 | 0 .../omp-declarative-directive.f90 | 0 .../omp-device-constructs.f90 | 0 .../omp-loop-association.f90 | 0 .../{semantics => Semantics}/omp-nested01.f90 | 0 .../omp-resolve01.f90 | 0 .../omp-resolve02.f90 | 0 .../omp-resolve03.f90 | 0 .../omp-resolve04.f90 | 0 .../omp-resolve05.f90 | 0 .../{semantics => Semantics}/omp-symbol01.f90 | 0 .../{semantics => Semantics}/omp-symbol02.f90 | 0 .../{semantics => Semantics}/omp-symbol03.f90 | 0 .../{semantics => Semantics}/omp-symbol04.f90 | 0 .../{semantics => Semantics}/omp-symbol05.f90 | 0 .../{semantics => Semantics}/omp-symbol06.f90 | 0 .../{semantics => Semantics}/omp-symbol07.f90 | 0 .../{semantics => Semantics}/omp-symbol08.f90 | 0 .../procinterface01.f90 | 0 test/{semantics => Semantics}/resolve01.f90 | 0 test/{semantics => Semantics}/resolve02.f90 | 0 test/{semantics => Semantics}/resolve03.f90 | 0 test/{semantics => Semantics}/resolve04.f90 | 0 test/{semantics => Semantics}/resolve05.f90 | 0 test/{semantics => Semantics}/resolve06.f90 | 0 test/{semantics => Semantics}/resolve07.f90 | 0 test/{semantics => Semantics}/resolve08.f90 | 0 test/{semantics => Semantics}/resolve09.f90 | 0 test/{semantics => Semantics}/resolve10.f90 | 0 test/{semantics => Semantics}/resolve11.f90 | 0 test/{semantics => Semantics}/resolve12.f90 | 0 test/{semantics => Semantics}/resolve13.f90 | 0 test/{semantics => Semantics}/resolve14.f90 | 0 test/{semantics => Semantics}/resolve15.f90 | 0 test/{semantics => Semantics}/resolve16.f90 | 0 test/{semantics => Semantics}/resolve17.f90 | 0 test/{semantics => Semantics}/resolve18.f90 | 0 test/{semantics => Semantics}/resolve19.f90 | 0 test/{semantics => Semantics}/resolve20.f90 | 0 test/{semantics => Semantics}/resolve21.f90 | 0 test/{semantics => Semantics}/resolve22.f90 | 0 test/{semantics => Semantics}/resolve23.f90 | 0 test/{semantics => Semantics}/resolve24.f90 | 0 test/{semantics => Semantics}/resolve25.f90 | 0 test/{semantics => Semantics}/resolve26.f90 | 0 test/{semantics => Semantics}/resolve27.f90 | 0 test/{semantics => Semantics}/resolve28.f90 | 0 test/{semantics => Semantics}/resolve29.f90 | 0 test/{semantics => Semantics}/resolve30.f90 | 0 test/{semantics => Semantics}/resolve31.f90 | 0 test/{semantics => Semantics}/resolve32.f90 | 0 test/{semantics => Semantics}/resolve33.f90 | 0 test/{semantics => Semantics}/resolve34.f90 | 0 test/{semantics => Semantics}/resolve35.f90 | 0 test/{semantics => Semantics}/resolve36.f90 | 0 test/{semantics => Semantics}/resolve37.f90 | 0 test/{semantics => Semantics}/resolve38.f90 | 0 test/{semantics => Semantics}/resolve39.f90 | 0 test/{semantics => Semantics}/resolve40.f90 | 0 test/{semantics => Semantics}/resolve41.f90 | 0 test/{semantics => Semantics}/resolve42.f90 | 0 test/{semantics => Semantics}/resolve43.f90 | 0 test/{semantics => Semantics}/resolve44.f90 | 0 test/{semantics => Semantics}/resolve45.f90 | 0 test/{semantics => Semantics}/resolve46.f90 | 0 test/{semantics => Semantics}/resolve47.f90 | 0 test/{semantics => Semantics}/resolve48.f90 | 0 test/{semantics => Semantics}/resolve49.f90 | 0 test/{semantics => Semantics}/resolve50.f90 | 0 test/{semantics => Semantics}/resolve51.f90 | 0 test/{semantics => Semantics}/resolve52.f90 | 0 test/{semantics => Semantics}/resolve53.f90 | 0 test/{semantics => Semantics}/resolve54.f90 | 0 test/{semantics => Semantics}/resolve55.f90 | 0 test/{semantics => Semantics}/resolve56.f90 | 0 test/{semantics => Semantics}/resolve57.f90 | 0 test/{semantics => Semantics}/resolve58.f90 | 0 test/{semantics => Semantics}/resolve59.f90 | 0 test/{semantics => Semantics}/resolve60.f90 | 0 test/{semantics => Semantics}/resolve61.f90 | 0 test/{semantics => Semantics}/resolve62.f90 | 0 test/{semantics => Semantics}/resolve63.f90 | 0 test/{semantics => Semantics}/resolve64.f90 | 0 test/{semantics => Semantics}/resolve65.f90 | 0 test/{semantics => Semantics}/resolve66.f90 | 0 test/{semantics => Semantics}/resolve67.f90 | 0 test/{semantics => Semantics}/resolve68.f90 | 0 test/{semantics => Semantics}/resolve69.f90 | 0 test/{semantics => Semantics}/resolve70.f90 | 0 test/{semantics => Semantics}/resolve71.f90 | 0 test/{semantics => Semantics}/resolve72.f90 | 0 .../separate-module-procs.f90 | 0 test/{semantics => Semantics}/stop01.f90 | 0 .../structconst01.f90 | 0 .../structconst02.f90 | 0 .../structconst03.f90 | 2 +- .../structconst04.f90 | 0 test/{semantics => Semantics}/symbol01.f90 | 0 test/{semantics => Semantics}/symbol02.f90 | 0 test/{semantics => Semantics}/symbol03.f90 | 0 test/{semantics => Semantics}/symbol05.f90 | 0 test/{semantics => Semantics}/symbol06.f90 | 0 test/{semantics => Semantics}/symbol07.f90 | 0 test/{semantics => Semantics}/symbol08.f90 | 0 test/{semantics => Semantics}/symbol09.f90 | 0 test/{semantics => Semantics}/symbol10.f90 | 0 test/{semantics => Semantics}/symbol11.f90 | 0 test/{semantics => Semantics}/symbol12.f90 | 0 test/{semantics => Semantics}/symbol13.f90 | 0 test/{semantics => Semantics}/symbol14.f90 | 0 test/{semantics => Semantics}/symbol15.f90 | 0 test/{semantics => Semantics}/symbol16.f90 | 0 test/{semantics => Semantics}/symbol17.f90 | 0 test/{semantics => Semantics}/test_any.sh | 0 test/{semantics => Semantics}/test_errors.sh | 0 test/{semantics => Semantics}/test_modfile.sh | 0 test/{semantics => Semantics}/test_symbols.sh | 0 tools/f18/f18-parse-demo.cpp | 20 ++++---- tools/f18/f18.cpp | 30 ++++++------ tools/f18/stub-evaluate.cpp | 2 +- 655 files changed, 905 insertions(+), 905 deletions(-) rename include/flang/{common => Common}/Fortran-features.h (94%) rename include/flang/{common => Common}/Fortran.h (97%) rename include/flang/{common => Common}/bit-population-count.h (98%) rename include/flang/{common => Common}/constexpr-bitset.h (98%) rename include/flang/{common => Common}/default-kinds.h (96%) rename include/flang/{common => Common}/enum-set.h (99%) rename include/flang/{common => Common}/format.h (99%) rename include/flang/{common => Common}/idioms.h (98%) rename include/flang/{common => Common}/indirection.h (98%) rename include/flang/{common => Common}/interval.h (98%) rename include/flang/{common => Common}/leading-zero-bit-count.h (98%) rename include/flang/{common => Common}/real.h (98%) rename include/flang/{common => Common}/reference-counted.h (96%) rename include/flang/{common => Common}/reference.h (96%) rename include/flang/{common => Common}/restorer.h (95%) rename include/flang/{common => Common}/template.h (99%) rename include/flang/{common => Common}/uint128.h (99%) rename include/flang/{common => Common}/unsigned-const-division.h (97%) rename include/flang/{common => Common}/unwrap.h (98%) rename include/flang/{decimal => Decimal}/binary-floating-point.h (96%) rename include/flang/{decimal => Decimal}/decimal.h (98%) rename include/flang/{evaluate => Evaluate}/call.h (97%) rename include/flang/{evaluate => Evaluate}/characteristics.h (97%) rename include/flang/{evaluate => Evaluate}/check-expression.h (97%) rename include/flang/{evaluate => Evaluate}/common.h (96%) rename include/flang/{evaluate => Evaluate}/complex.h (98%) rename include/flang/{evaluate => Evaluate}/constant.h (98%) rename include/flang/{evaluate => Evaluate}/expression.h (99%) rename include/flang/{evaluate => Evaluate}/fold.h (97%) rename include/flang/{evaluate => Evaluate}/formatting.h (90%) rename include/flang/{evaluate => Evaluate}/integer.h (99%) rename include/flang/{evaluate => Evaluate}/intrinsics-library.h (98%) rename include/flang/{evaluate => Evaluate}/intrinsics.h (93%) rename include/flang/{evaluate => Evaluate}/logical.h (97%) rename include/flang/{evaluate => Evaluate}/real.h (98%) rename include/flang/{evaluate => Evaluate}/rounding-bits.h (97%) rename include/flang/{evaluate => Evaluate}/shape.h (97%) rename include/flang/{evaluate => Evaluate}/static-data.h (95%) rename include/flang/{evaluate => Evaluate}/tools.h (98%) rename include/flang/{evaluate => Evaluate}/traverse.h (98%) rename include/flang/{evaluate => Evaluate}/type.h (99%) rename include/flang/{evaluate => Evaluate}/variable.h (98%) rename include/flang/{lower => Lower}/.clang-format (100%) rename include/flang/{lower => Lower}/PFTBuilder.h (99%) rename include/flang/{optimizer => Optimizer}/.clang-format (100%) rename include/flang/{parser => Parser}/char-block.h (98%) rename include/flang/{parser => Parser}/char-buffer.h (97%) rename include/flang/{parser => Parser}/char-set.h (97%) rename include/flang/{parser => Parser}/characters.h (99%) rename include/flang/{parser => Parser}/dump-parse-tree.h (99%) rename include/flang/{parser => Parser}/format-specification.h (98%) rename include/flang/{parser => Parser}/instrumented-parser.h (94%) rename include/flang/{parser => Parser}/message.h (98%) rename include/flang/{parser => Parser}/parse-state.h (96%) rename include/flang/{parser => Parser}/parse-tree-visitor.h (99%) rename include/flang/{parser => Parser}/parse-tree.h (99%) rename include/flang/{parser => Parser}/parsing.h (95%) rename include/flang/{parser => Parser}/provenance.h (98%) rename include/flang/{parser => Parser}/source.h (97%) rename include/flang/{parser => Parser}/tools.h (97%) rename include/flang/{parser => Parser}/unparse.h (95%) rename include/flang/{parser => Parser}/user-state.h (94%) rename include/flang/{semantics => Semantics}/attr.h (92%) rename include/flang/{semantics => Semantics}/expression.h (97%) rename include/flang/{semantics => Semantics}/scope.h (97%) rename include/flang/{semantics => Semantics}/semantics.h (97%) rename include/flang/{semantics => Semantics}/symbol.h (99%) rename include/flang/{semantics => Semantics}/tools.h (98%) rename include/flang/{semantics => Semantics}/type.h (98%) rename include/flang/{semantics => Semantics}/unparse-with-symbols.h (87%) rename lib/{common => Common}/CMakeLists.txt (88%) rename lib/{common => Common}/Fortran-features.cpp (90%) rename lib/{common => Common}/Fortran.cpp (93%) rename lib/{common => Common}/default-kinds.cpp (93%) rename lib/{common => Common}/idioms.cpp (92%) rename lib/{decimal => Decimal}/CMakeLists.txt (88%) rename lib/{decimal => Decimal}/big-radix-floating-point.h (96%) rename lib/{decimal => Decimal}/binary-to-decimal.cpp (99%) rename lib/{decimal => Decimal}/decimal-to-binary.cpp (98%) rename lib/{evaluate => Evaluate}/CMakeLists.txt (95%) rename lib/{evaluate => Evaluate}/call.cpp (95%) rename lib/{evaluate => Evaluate}/character.h (97%) rename lib/{evaluate => Evaluate}/characteristics.cpp (98%) rename lib/{evaluate => Evaluate}/check-expression.cpp (98%) rename lib/{evaluate => Evaluate}/common.cpp (92%) rename lib/{evaluate => Evaluate}/complex.cpp (97%) rename lib/{evaluate => Evaluate}/constant.cpp (97%) rename lib/{evaluate => Evaluate}/expression.cpp (96%) rename lib/{evaluate => Evaluate}/fold-character.cpp (98%) rename lib/{evaluate => Evaluate}/fold-complex.cpp (98%) rename lib/{evaluate => Evaluate}/fold-implementation.h (98%) rename lib/{evaluate => Evaluate}/fold-integer.cpp (99%) rename lib/{evaluate => Evaluate}/fold-logical.cpp (98%) rename lib/{evaluate => Evaluate}/fold-real.cpp (99%) rename lib/{evaluate => Evaluate}/fold.cpp (98%) rename lib/{evaluate => Evaluate}/formatting.cpp (98%) rename lib/{evaluate => Evaluate}/host.cpp (97%) rename lib/{evaluate => Evaluate}/host.h (98%) rename lib/{evaluate => Evaluate}/int-power.h (95%) rename lib/{evaluate => Evaluate}/integer.cpp (92%) rename lib/{evaluate => Evaluate}/intrinsics-library-templates.h (97%) rename lib/{evaluate => Evaluate}/intrinsics-library.cpp (99%) rename lib/{evaluate => Evaluate}/intrinsics.cpp (99%) rename lib/{evaluate => Evaluate}/logical.cpp (82%) rename lib/{evaluate => Evaluate}/real.cpp (98%) rename lib/{evaluate => Evaluate}/shape.cpp (98%) rename lib/{evaluate => Evaluate}/static-data.cpp (95%) rename lib/{evaluate => Evaluate}/tools.cpp (99%) rename lib/{evaluate => Evaluate}/type.cpp (97%) rename lib/{evaluate => Evaluate}/variable.cpp (98%) rename lib/{fir => Fir}/.clang-format (100%) rename lib/{lower => Lower}/.clang-format (100%) rename lib/{lower => Lower}/CMakeLists.txt (100%) rename lib/{lower => Lower}/PFTBuilder.cpp (99%) rename lib/{optimizer => Optimizer}/.clang-format (100%) rename lib/{parser => Parser}/CMakeLists.txt (93%) rename lib/{parser => Parser}/Fortran-parsers.cpp (99%) rename lib/{parser => Parser}/basic-parsers.h (98%) rename lib/{parser => Parser}/char-block.cpp (81%) rename lib/{parser => Parser}/char-buffer.cpp (94%) rename lib/{parser => Parser}/char-set.cpp (85%) rename lib/{parser => Parser}/characters.cpp (98%) rename lib/{parser => Parser}/debug-parser.cpp (88%) rename lib/{parser => Parser}/debug-parser.h (91%) rename lib/{parser => Parser}/executable-parsers.cpp (99%) rename lib/{parser => Parser}/expr-parsers.cpp (99%) rename lib/{parser => Parser}/expr-parsers.h (97%) rename lib/{parser => Parser}/instrumented-parser.cpp (92%) rename lib/{parser => Parser}/io-parsers.cpp (99%) rename lib/{parser => Parser}/message.cpp (98%) rename lib/{parser => Parser}/misc-parsers.h (92%) rename lib/{parser => Parser}/openmp-parsers.cpp (99%) rename lib/{parser => Parser}/parse-tree.cpp (97%) rename lib/{parser => Parser}/parsing.cpp (95%) rename lib/{parser => Parser}/preprocessor.cpp (99%) rename lib/{parser => Parser}/preprocessor.h (95%) rename lib/{parser => Parser}/prescan.cpp (99%) rename lib/{parser => Parser}/prescan.h (97%) rename lib/{parser => Parser}/program-parsers.cpp (99%) rename lib/{parser => Parser}/provenance.cpp (99%) rename lib/{parser => Parser}/source.cpp (97%) rename lib/{parser => Parser}/stmt-parser.h (98%) rename lib/{parser => Parser}/token-parsers.h (98%) rename lib/{parser => Parser}/token-sequence.cpp (98%) rename lib/{parser => Parser}/token-sequence.h (96%) rename lib/{parser => Parser}/tools.cpp (96%) rename lib/{parser => Parser}/type-parser-implementation.h (94%) rename lib/{parser => Parser}/type-parsers.h (97%) rename lib/{parser => Parser}/unparse.cpp (99%) rename lib/{parser => Parser}/user-state.cpp (94%) rename lib/{semantics => Semantics}/CMakeLists.txt (94%) rename lib/{semantics => Semantics}/assignment.cpp (95%) rename lib/{semantics => Semantics}/assignment.h (90%) rename lib/{semantics => Semantics}/attr.cpp (90%) rename lib/{semantics => Semantics}/canonicalize-do.cpp (98%) rename lib/{semantics => Semantics}/canonicalize-do.h (91%) rename lib/{semantics => Semantics}/canonicalize-omp.cpp (97%) rename lib/{semantics => Semantics}/canonicalize-omp.h (90%) rename lib/{semantics => Semantics}/check-allocate.cpp (98%) rename lib/{semantics => Semantics}/check-allocate.h (87%) rename lib/{semantics => Semantics}/check-arithmeticif.cpp (90%) rename lib/{semantics => Semantics}/check-arithmeticif.h (88%) rename lib/{semantics => Semantics}/check-call.cpp (98%) rename lib/{semantics => Semantics}/check-call.h (94%) rename lib/{semantics => Semantics}/check-coarray.cpp (93%) rename lib/{semantics => Semantics}/check-coarray.h (92%) rename lib/{semantics => Semantics}/check-data.cpp (96%) rename lib/{semantics => Semantics}/check-data.h (77%) rename lib/{semantics => Semantics}/check-deallocate.cpp (92%) rename lib/{semantics => Semantics}/check-deallocate.h (88%) rename lib/{semantics => Semantics}/check-declarations.cpp (99%) rename lib/{semantics => Semantics}/check-declarations.h (89%) rename lib/{semantics => Semantics}/check-do-forall.cpp (98%) rename lib/{semantics => Semantics}/check-do-forall.h (94%) rename lib/{semantics => Semantics}/check-if-stmt.cpp (82%) rename lib/{semantics => Semantics}/check-if-stmt.h (87%) rename lib/{semantics => Semantics}/check-io.cpp (99%) rename lib/{semantics => Semantics}/check-io.h (96%) rename lib/{semantics => Semantics}/check-nullify.cpp (91%) rename lib/{semantics => Semantics}/check-nullify.h (87%) rename lib/{semantics => Semantics}/check-omp-structure.cpp (99%) rename lib/{semantics => Semantics}/check-omp-structure.h (98%) rename lib/{semantics => Semantics}/check-purity.cpp (94%) rename lib/{semantics => Semantics}/check-purity.h (92%) rename lib/{semantics => Semantics}/check-return.cpp (86%) rename lib/{semantics => Semantics}/check-return.h (87%) rename lib/{semantics => Semantics}/check-stop.cpp (84%) rename lib/{semantics => Semantics}/check-stop.h (88%) rename lib/{semantics => Semantics}/expression.cpp (99%) rename lib/{semantics => Semantics}/mod-file.cpp (99%) rename lib/{semantics => Semantics}/mod-file.h (95%) rename lib/{semantics => Semantics}/pointer-assignment.cpp (97%) rename lib/{semantics => Semantics}/pointer-assignment.h (85%) rename lib/{semantics => Semantics}/program-tree.cpp (97%) rename lib/{semantics => Semantics}/program-tree.h (96%) rename lib/{semantics => Semantics}/resolve-labels.cpp (99%) rename lib/{semantics => Semantics}/resolve-labels.h (92%) rename lib/{semantics => Semantics}/resolve-names-utils.cpp (98%) rename lib/{semantics => Semantics}/resolve-names-utils.h (95%) rename lib/{semantics => Semantics}/resolve-names.cpp (99%) rename lib/{semantics => Semantics}/resolve-names.h (91%) rename lib/{semantics => Semantics}/rewrite-parse-tree.cpp (93%) rename lib/{semantics => Semantics}/rewrite-parse-tree.h (91%) rename lib/{semantics => Semantics}/scope.cpp (97%) rename lib/{semantics => Semantics}/semantics.cpp (97%) rename lib/{semantics => Semantics}/symbol.cpp (98%) rename lib/{semantics => Semantics}/tools.cpp (98%) rename lib/{semantics => Semantics}/type.cpp (98%) rename lib/{semantics => Semantics}/unparse-with-symbols.cpp (92%) rename test-lit/{driver => Driver}/version_test.f90 (100%) rename test-lit/{lower => Lower}/pre-fir-tree01.f90 (100%) rename test-lit/{lower => Lower}/pre-fir-tree02.f90 (100%) rename test-lit/{lower => Lower}/pre-fir-tree03.f90 (100%) rename test-lit/{lower => Lower}/pre-fir-tree04.f90 (100%) rename test/{decimal => Decimal}/CMakeLists.txt (89%) rename test/{decimal => Decimal}/quick-sanity-test.cpp (99%) rename test/{decimal => Decimal}/thorough-test.cpp (98%) rename test/{evaluate => Evaluate}/CMakeLists.txt (96%) rename test/{evaluate => Evaluate}/ISO-Fortran-binding.cpp (100%) rename test/{evaluate => Evaluate}/bit-population-count.cpp (98%) rename test/{evaluate => Evaluate}/expression.cpp (85%) rename test/{evaluate => Evaluate}/folding.cpp (93%) rename test/{evaluate => Evaluate}/folding01.f90 (100%) rename test/{evaluate => Evaluate}/folding02.f90 (100%) rename test/{evaluate => Evaluate}/folding03.f90 (100%) rename test/{evaluate => Evaluate}/folding04.f90 (100%) rename test/{evaluate => Evaluate}/folding05.f90 (100%) rename test/{evaluate => Evaluate}/folding06.f90 (100%) rename test/{evaluate => Evaluate}/folding07.f90 (100%) rename test/{evaluate => Evaluate}/folding08.f90 (100%) rename test/{evaluate => Evaluate}/folding09.f90 (100%) rename test/{evaluate => Evaluate}/fp-testing.cpp (100%) rename test/{evaluate => Evaluate}/fp-testing.h (94%) rename test/{evaluate => Evaluate}/integer.cpp (99%) rename test/{evaluate => Evaluate}/intrinsics.cpp (97%) rename test/{evaluate => Evaluate}/leading-zero-bit-count.cpp (95%) rename test/{evaluate => Evaluate}/logical.cpp (97%) rename test/{evaluate => Evaluate}/real.cpp (99%) rename test/{evaluate => Evaluate}/reshape.cpp (100%) rename test/{evaluate => Evaluate}/test_folding.sh (100%) rename test/{evaluate => Evaluate}/testing.cpp (100%) rename test/{evaluate => Evaluate}/testing.h (100%) rename test/{evaluate => Evaluate}/uint128.cpp (99%) rename test/{preprocessing => Preprocessing}/pp001.F (100%) rename test/{preprocessing => Preprocessing}/pp002.F (100%) rename test/{preprocessing => Preprocessing}/pp003.F (100%) rename test/{preprocessing => Preprocessing}/pp004.F (100%) rename test/{preprocessing => Preprocessing}/pp005.F (100%) rename test/{preprocessing => Preprocessing}/pp006.F (100%) rename test/{preprocessing => Preprocessing}/pp007.F (100%) rename test/{preprocessing => Preprocessing}/pp008.F (100%) rename test/{preprocessing => Preprocessing}/pp009.F (100%) rename test/{preprocessing => Preprocessing}/pp010.F (100%) rename test/{preprocessing => Preprocessing}/pp011.F (100%) rename test/{preprocessing => Preprocessing}/pp012.F (100%) rename test/{preprocessing => Preprocessing}/pp013.F (100%) rename test/{preprocessing => Preprocessing}/pp014.F (100%) rename test/{preprocessing => Preprocessing}/pp015.F (100%) rename test/{preprocessing => Preprocessing}/pp016.F (100%) rename test/{preprocessing => Preprocessing}/pp017.F (100%) rename test/{preprocessing => Preprocessing}/pp018.F (100%) rename test/{preprocessing => Preprocessing}/pp019.F (100%) rename test/{preprocessing => Preprocessing}/pp020.F (100%) rename test/{preprocessing => Preprocessing}/pp021.F (100%) rename test/{preprocessing => Preprocessing}/pp022.F (100%) rename test/{preprocessing => Preprocessing}/pp023.F (100%) rename test/{preprocessing => Preprocessing}/pp024.F (100%) rename test/{preprocessing => Preprocessing}/pp025.F (100%) rename test/{preprocessing => Preprocessing}/pp026.F (100%) rename test/{preprocessing => Preprocessing}/pp027.F (100%) rename test/{preprocessing => Preprocessing}/pp028.F (100%) rename test/{preprocessing => Preprocessing}/pp029.F (100%) rename test/{preprocessing => Preprocessing}/pp030.F (100%) rename test/{preprocessing => Preprocessing}/pp031.F (100%) rename test/{preprocessing => Preprocessing}/pp032.F (100%) rename test/{preprocessing => Preprocessing}/pp033.F (100%) rename test/{preprocessing => Preprocessing}/pp034.F (100%) rename test/{preprocessing => Preprocessing}/pp035.F (100%) rename test/{preprocessing => Preprocessing}/pp036.F (100%) rename test/{preprocessing => Preprocessing}/pp037.F (100%) rename test/{preprocessing => Preprocessing}/pp038.F (100%) rename test/{preprocessing => Preprocessing}/pp039.F (100%) rename test/{preprocessing => Preprocessing}/pp040.F (100%) rename test/{preprocessing => Preprocessing}/pp041.F (100%) rename test/{preprocessing => Preprocessing}/pp042.F (100%) rename test/{preprocessing => Preprocessing}/pp043.F (100%) rename test/{preprocessing => Preprocessing}/pp044.F (100%) rename test/{preprocessing => Preprocessing}/pp101.F90 (100%) rename test/{preprocessing => Preprocessing}/pp102.F90 (100%) rename test/{preprocessing => Preprocessing}/pp103.F90 (100%) rename test/{preprocessing => Preprocessing}/pp104.F90 (100%) rename test/{preprocessing => Preprocessing}/pp105.F90 (100%) rename test/{preprocessing => Preprocessing}/pp106.F90 (100%) rename test/{preprocessing => Preprocessing}/pp107.F90 (100%) rename test/{preprocessing => Preprocessing}/pp108.F90 (100%) rename test/{preprocessing => Preprocessing}/pp109.F90 (100%) rename test/{preprocessing => Preprocessing}/pp110.F90 (100%) rename test/{preprocessing => Preprocessing}/pp111.F90 (100%) rename test/{preprocessing => Preprocessing}/pp112.F90 (100%) rename test/{preprocessing => Preprocessing}/pp113.F90 (100%) rename test/{preprocessing => Preprocessing}/pp114.F90 (100%) rename test/{preprocessing => Preprocessing}/pp115.F90 (100%) rename test/{preprocessing => Preprocessing}/pp116.F90 (100%) rename test/{preprocessing => Preprocessing}/pp117.F90 (100%) rename test/{preprocessing => Preprocessing}/pp118.F90 (100%) rename test/{preprocessing => Preprocessing}/pp119.F90 (100%) rename test/{preprocessing => Preprocessing}/pp120.F90 (100%) rename test/{preprocessing => Preprocessing}/pp121.F90 (100%) rename test/{preprocessing => Preprocessing}/pp122.F90 (100%) rename test/{preprocessing => Preprocessing}/pp123.F90 (100%) rename test/{preprocessing => Preprocessing}/pp124.F90 (100%) rename test/{preprocessing => Preprocessing}/pp125.F90 (100%) rename test/{preprocessing => Preprocessing}/pp126.F90 (100%) rename test/{preprocessing => Preprocessing}/pp127.F90 (100%) rename test/{preprocessing => Preprocessing}/pp128.F90 (100%) rename test/{preprocessing => Preprocessing}/pp129.F90 (100%) rename test/{preprocessing => Preprocessing}/pp130.F90 (100%) rename test/{runtime => Runtime}/CMakeLists.txt (92%) rename test/{runtime => Runtime}/external-hello.cpp (100%) rename test/{runtime => Runtime}/format.cpp (100%) rename test/{runtime => Runtime}/hello.cpp (100%) rename test/{semantics => Semantics}/CMakeLists.txt (98%) rename test/{semantics => Semantics}/allocate01.f90 (100%) rename test/{semantics => Semantics}/allocate02.f90 (100%) rename test/{semantics => Semantics}/allocate03.f90 (100%) rename test/{semantics => Semantics}/allocate04.f90 (100%) rename test/{semantics => Semantics}/allocate05.f90 (100%) rename test/{semantics => Semantics}/allocate06.f90 (100%) rename test/{semantics => Semantics}/allocate07.f90 (100%) rename test/{semantics => Semantics}/allocate08.f90 (100%) rename test/{semantics => Semantics}/allocate09.f90 (100%) rename test/{semantics => Semantics}/allocate10.f90 (100%) rename test/{semantics => Semantics}/allocate11.f90 (100%) rename test/{semantics => Semantics}/allocate12.f90 (100%) rename test/{semantics => Semantics}/allocate13.f90 (100%) rename test/{semantics => Semantics}/altreturn01.f90 (100%) rename test/{semantics => Semantics}/altreturn02.f90 (100%) rename test/{semantics => Semantics}/altreturn03.f90 (100%) rename test/{semantics => Semantics}/altreturn04.f90 (100%) rename test/{semantics => Semantics}/altreturn05.f90 (100%) rename test/{semantics => Semantics}/assign01.f90 (100%) rename test/{semantics => Semantics}/assign02.f90 (100%) rename test/{semantics => Semantics}/assign03.f90 (100%) rename test/{semantics => Semantics}/bad-forward-type.f90 (100%) rename test/{semantics => Semantics}/bindings01.f90 (100%) rename test/{semantics => Semantics}/block-data01.f90 (100%) rename test/{semantics => Semantics}/blockconstruct01.f90 (100%) rename test/{semantics => Semantics}/blockconstruct02.f90 (100%) rename test/{semantics => Semantics}/blockconstruct03.f90 (100%) rename test/{semantics => Semantics}/c_f_pointer.f90 (100%) rename test/{semantics => Semantics}/call01.f90 (100%) rename test/{semantics => Semantics}/call02.f90 (100%) rename test/{semantics => Semantics}/call03.f90 (100%) rename test/{semantics => Semantics}/call04.f90 (100%) rename test/{semantics => Semantics}/call05.f90 (100%) rename test/{semantics => Semantics}/call06.f90 (100%) rename test/{semantics => Semantics}/call07.f90 (100%) rename test/{semantics => Semantics}/call08.f90 (100%) rename test/{semantics => Semantics}/call09.f90 (100%) rename test/{semantics => Semantics}/call10.f90 (100%) rename test/{semantics => Semantics}/call11.f90 (100%) rename test/{semantics => Semantics}/call12.f90 (100%) rename test/{semantics => Semantics}/call13.f90 (100%) rename test/{semantics => Semantics}/call14.f90 (100%) rename test/{semantics => Semantics}/call15.f90 (100%) rename test/{semantics => Semantics}/canondo01.f90 (100%) rename test/{semantics => Semantics}/canondo02.f90 (100%) rename test/{semantics => Semantics}/canondo03.f90 (100%) rename test/{semantics => Semantics}/canondo04.f90 (100%) rename test/{semantics => Semantics}/canondo05.f90 (100%) rename test/{semantics => Semantics}/canondo06.f90 (100%) rename test/{semantics => Semantics}/canondo07.f90 (100%) rename test/{semantics => Semantics}/canondo08.f90 (100%) rename test/{semantics => Semantics}/canondo09.f90 (100%) rename test/{semantics => Semantics}/canondo10.f90 (100%) rename test/{semantics => Semantics}/canondo11.f90 (100%) rename test/{semantics => Semantics}/canondo12.f90 (100%) rename test/{semantics => Semantics}/canondo13.f90 (100%) rename test/{semantics => Semantics}/canondo14.f90 (100%) rename test/{semantics => Semantics}/canondo15.f90 (100%) rename test/{semantics => Semantics}/canondo16.f90 (100%) rename test/{semantics => Semantics}/canondo17.f90 (100%) rename test/{semantics => Semantics}/canondo18.f90 (100%) rename test/{semantics => Semantics}/canondo19.f90 (100%) rename test/{semantics => Semantics}/coarrays01.f90 (100%) rename test/{semantics => Semantics}/common.sh (100%) rename test/{semantics => Semantics}/computed-goto01.f90 (100%) rename test/{semantics => Semantics}/computed-goto02.f90 (100%) rename test/{semantics => Semantics}/critical01.f90 (100%) rename test/{semantics => Semantics}/critical02.f90 (100%) rename test/{semantics => Semantics}/critical03.f90 (100%) rename test/{semantics => Semantics}/critical04.f90 (100%) rename test/{semantics => Semantics}/data01.f90 (100%) rename test/{semantics => Semantics}/deallocate01.f90 (100%) rename test/{semantics => Semantics}/deallocate04.f90 (100%) rename test/{semantics => Semantics}/deallocate05.f90 (100%) rename test/{semantics => Semantics}/doconcurrent01.f90 (100%) rename test/{semantics => Semantics}/doconcurrent02.f90 (100%) rename test/{semantics => Semantics}/doconcurrent03.f90 (100%) rename test/{semantics => Semantics}/doconcurrent04.f90 (100%) rename test/{semantics => Semantics}/doconcurrent05.f90 (100%) rename test/{semantics => Semantics}/doconcurrent06.f90 (100%) rename test/{semantics => Semantics}/doconcurrent07.f90 (100%) rename test/{semantics => Semantics}/doconcurrent08.f90 (99%) rename test/{semantics => Semantics}/dosemantics01.f90 (100%) rename test/{semantics => Semantics}/dosemantics02.f90 (100%) rename test/{semantics => Semantics}/dosemantics03.f90 (100%) rename test/{semantics => Semantics}/dosemantics04.f90 (100%) rename test/{semantics => Semantics}/dosemantics05.f90 (100%) rename test/{semantics => Semantics}/dosemantics06.f90 (100%) rename test/{semantics => Semantics}/dosemantics07.f90 (100%) rename test/{semantics => Semantics}/dosemantics08.f90 (100%) rename test/{semantics => Semantics}/dosemantics09.f90 (100%) rename test/{semantics => Semantics}/dosemantics10.f90 (100%) rename test/{semantics => Semantics}/dosemantics11.f90 (100%) rename test/{semantics => Semantics}/dosemantics12.f90 (100%) rename test/{semantics => Semantics}/equivalence01.f90 (100%) rename test/{semantics => Semantics}/expr-errors01.f90 (100%) rename test/{semantics => Semantics}/expr-errors02.f90 (100%) rename test/{semantics => Semantics}/forall01.f90 (100%) rename test/{semantics => Semantics}/getdefinition01.f90 (100%) rename test/{semantics => Semantics}/getdefinition02.f (100%) rename test/{semantics => Semantics}/getdefinition03-a.f90 (100%) rename test/{semantics => Semantics}/getdefinition03-b.f90 (100%) rename test/{semantics => Semantics}/getdefinition04.f90 (100%) rename test/{semantics => Semantics}/getdefinition05.f90 (100%) rename test/{semantics => Semantics}/getsymbols01.f90 (100%) rename test/{semantics => Semantics}/getsymbols02-a.f90 (100%) rename test/{semantics => Semantics}/getsymbols02-b.f90 (100%) rename test/{semantics => Semantics}/getsymbols02-c.f90 (100%) rename test/{semantics => Semantics}/getsymbols03-a.f90 (100%) rename test/{semantics => Semantics}/getsymbols03-b.f90 (100%) rename test/{semantics => Semantics}/getsymbols04.f90 (100%) rename test/{semantics => Semantics}/getsymbols05.f90 (100%) rename test/{semantics => Semantics}/if_arith01.f90 (100%) rename test/{semantics => Semantics}/if_arith02.f90 (100%) rename test/{semantics => Semantics}/if_arith03.f90 (100%) rename test/{semantics => Semantics}/if_arith04.f90 (100%) rename test/{semantics => Semantics}/if_construct01.f90 (100%) rename test/{semantics => Semantics}/if_construct02.f90 (100%) rename test/{semantics => Semantics}/if_stmt01.f90 (100%) rename test/{semantics => Semantics}/if_stmt02.f90 (100%) rename test/{semantics => Semantics}/if_stmt03.f90 (100%) rename test/{semantics => Semantics}/implicit01.f90 (100%) rename test/{semantics => Semantics}/implicit02.f90 (100%) rename test/{semantics => Semantics}/implicit03.f90 (100%) rename test/{semantics => Semantics}/implicit04.f90 (100%) rename test/{semantics => Semantics}/implicit05.f90 (100%) rename test/{semantics => Semantics}/implicit06.f90 (100%) rename test/{semantics => Semantics}/implicit07.f90 (100%) rename test/{semantics => Semantics}/implicit08.f90 (100%) rename test/{semantics => Semantics}/init01.f90 (100%) rename test/{semantics => Semantics}/int-literals.f90 (100%) rename test/{semantics => Semantics}/io01.f90 (100%) rename test/{semantics => Semantics}/io02.f90 (100%) rename test/{semantics => Semantics}/io03.f90 (100%) rename test/{semantics => Semantics}/io04.f90 (100%) rename test/{semantics => Semantics}/io05.f90 (100%) rename test/{semantics => Semantics}/io06.f90 (100%) rename test/{semantics => Semantics}/io07.f90 (100%) rename test/{semantics => Semantics}/io08.f90 (100%) rename test/{semantics => Semantics}/io09.f90 (100%) rename test/{semantics => Semantics}/io10.f90 (100%) rename test/{semantics => Semantics}/kinds01.f90 (100%) rename test/{semantics => Semantics}/kinds02.f90 (100%) rename test/{semantics => Semantics}/kinds03.f90 (100%) rename test/{semantics => Semantics}/label01.F90 (100%) rename test/{semantics => Semantics}/label02.f90 (100%) rename test/{semantics => Semantics}/label03.f90 (100%) rename test/{semantics => Semantics}/label04.f90 (100%) rename test/{semantics => Semantics}/label05.f90 (100%) rename test/{semantics => Semantics}/label06.f90 (100%) rename test/{semantics => Semantics}/label07.f90 (100%) rename test/{semantics => Semantics}/label08.f90 (100%) rename test/{semantics => Semantics}/label09.f90 (100%) rename test/{semantics => Semantics}/label10.f90 (100%) rename test/{semantics => Semantics}/label11.f90 (100%) rename test/{semantics => Semantics}/label12.f90 (100%) rename test/{semantics => Semantics}/label13.f90 (100%) rename test/{semantics => Semantics}/label14.f90 (100%) rename test/{semantics => Semantics}/misc-declarations.f90 (100%) rename test/{semantics => Semantics}/modfile01.f90 (100%) rename test/{semantics => Semantics}/modfile02.f90 (100%) rename test/{semantics => Semantics}/modfile03.f90 (100%) rename test/{semantics => Semantics}/modfile04.f90 (100%) rename test/{semantics => Semantics}/modfile05.f90 (100%) rename test/{semantics => Semantics}/modfile06.f90 (100%) rename test/{semantics => Semantics}/modfile07.f90 (100%) rename test/{semantics => Semantics}/modfile08.f90 (100%) rename test/{semantics => Semantics}/modfile09-a.f90 (100%) rename test/{semantics => Semantics}/modfile09-b.f90 (100%) rename test/{semantics => Semantics}/modfile09-c.f90 (100%) rename test/{semantics => Semantics}/modfile09-d.f90 (100%) rename test/{semantics => Semantics}/modfile10.f90 (100%) rename test/{semantics => Semantics}/modfile11.f90 (100%) rename test/{semantics => Semantics}/modfile12.f90 (100%) rename test/{semantics => Semantics}/modfile13.f90 (100%) rename test/{semantics => Semantics}/modfile14.f90 (100%) rename test/{semantics => Semantics}/modfile15.f90 (100%) rename test/{semantics => Semantics}/modfile16.f90 (100%) rename test/{semantics => Semantics}/modfile17.f90 (100%) rename test/{semantics => Semantics}/modfile18.f90 (100%) rename test/{semantics => Semantics}/modfile19.f90 (100%) rename test/{semantics => Semantics}/modfile20.f90 (100%) rename test/{semantics => Semantics}/modfile21.f90 (100%) rename test/{semantics => Semantics}/modfile22.f90 (100%) rename test/{semantics => Semantics}/modfile23.f90 (100%) rename test/{semantics => Semantics}/modfile24.f90 (100%) rename test/{semantics => Semantics}/modfile25.f90 (100%) rename test/{semantics => Semantics}/modfile26.f90 (100%) rename test/{semantics => Semantics}/modfile27.f90 (100%) rename test/{semantics => Semantics}/modfile28.f90 (100%) rename test/{semantics => Semantics}/modfile29.f90 (100%) rename test/{semantics => Semantics}/modfile30.f90 (100%) rename test/{semantics => Semantics}/modfile31.f90 (100%) rename test/{semantics => Semantics}/modfile32.f90 (100%) rename test/{semantics => Semantics}/modfile33.f90 (100%) rename test/{semantics => Semantics}/modfile34.f90 (100%) rename test/{semantics => Semantics}/modfile35.f90 (100%) rename test/{semantics => Semantics}/null01.f90 (100%) rename test/{semantics => Semantics}/nullify01.f90 (100%) rename test/{semantics => Semantics}/nullify02.f90 (100%) rename test/{semantics => Semantics}/omp-atomic.f90 (100%) rename test/{semantics => Semantics}/omp-clause-validity01.f90 (100%) rename test/{semantics => Semantics}/omp-declarative-directive.f90 (100%) rename test/{semantics => Semantics}/omp-device-constructs.f90 (100%) rename test/{semantics => Semantics}/omp-loop-association.f90 (100%) rename test/{semantics => Semantics}/omp-nested01.f90 (100%) rename test/{semantics => Semantics}/omp-resolve01.f90 (100%) rename test/{semantics => Semantics}/omp-resolve02.f90 (100%) rename test/{semantics => Semantics}/omp-resolve03.f90 (100%) rename test/{semantics => Semantics}/omp-resolve04.f90 (100%) rename test/{semantics => Semantics}/omp-resolve05.f90 (100%) rename test/{semantics => Semantics}/omp-symbol01.f90 (100%) rename test/{semantics => Semantics}/omp-symbol02.f90 (100%) rename test/{semantics => Semantics}/omp-symbol03.f90 (100%) rename test/{semantics => Semantics}/omp-symbol04.f90 (100%) rename test/{semantics => Semantics}/omp-symbol05.f90 (100%) rename test/{semantics => Semantics}/omp-symbol06.f90 (100%) rename test/{semantics => Semantics}/omp-symbol07.f90 (100%) rename test/{semantics => Semantics}/omp-symbol08.f90 (100%) rename test/{semantics => Semantics}/procinterface01.f90 (100%) rename test/{semantics => Semantics}/resolve01.f90 (100%) rename test/{semantics => Semantics}/resolve02.f90 (100%) rename test/{semantics => Semantics}/resolve03.f90 (100%) rename test/{semantics => Semantics}/resolve04.f90 (100%) rename test/{semantics => Semantics}/resolve05.f90 (100%) rename test/{semantics => Semantics}/resolve06.f90 (100%) rename test/{semantics => Semantics}/resolve07.f90 (100%) rename test/{semantics => Semantics}/resolve08.f90 (100%) rename test/{semantics => Semantics}/resolve09.f90 (100%) rename test/{semantics => Semantics}/resolve10.f90 (100%) rename test/{semantics => Semantics}/resolve11.f90 (100%) rename test/{semantics => Semantics}/resolve12.f90 (100%) rename test/{semantics => Semantics}/resolve13.f90 (100%) rename test/{semantics => Semantics}/resolve14.f90 (100%) rename test/{semantics => Semantics}/resolve15.f90 (100%) rename test/{semantics => Semantics}/resolve16.f90 (100%) rename test/{semantics => Semantics}/resolve17.f90 (100%) rename test/{semantics => Semantics}/resolve18.f90 (100%) rename test/{semantics => Semantics}/resolve19.f90 (100%) rename test/{semantics => Semantics}/resolve20.f90 (100%) rename test/{semantics => Semantics}/resolve21.f90 (100%) rename test/{semantics => Semantics}/resolve22.f90 (100%) rename test/{semantics => Semantics}/resolve23.f90 (100%) rename test/{semantics => Semantics}/resolve24.f90 (100%) rename test/{semantics => Semantics}/resolve25.f90 (100%) rename test/{semantics => Semantics}/resolve26.f90 (100%) rename test/{semantics => Semantics}/resolve27.f90 (100%) rename test/{semantics => Semantics}/resolve28.f90 (100%) rename test/{semantics => Semantics}/resolve29.f90 (100%) rename test/{semantics => Semantics}/resolve30.f90 (100%) rename test/{semantics => Semantics}/resolve31.f90 (100%) rename test/{semantics => Semantics}/resolve32.f90 (100%) rename test/{semantics => Semantics}/resolve33.f90 (100%) rename test/{semantics => Semantics}/resolve34.f90 (100%) rename test/{semantics => Semantics}/resolve35.f90 (100%) rename test/{semantics => Semantics}/resolve36.f90 (100%) rename test/{semantics => Semantics}/resolve37.f90 (100%) rename test/{semantics => Semantics}/resolve38.f90 (100%) rename test/{semantics => Semantics}/resolve39.f90 (100%) rename test/{semantics => Semantics}/resolve40.f90 (100%) rename test/{semantics => Semantics}/resolve41.f90 (100%) rename test/{semantics => Semantics}/resolve42.f90 (100%) rename test/{semantics => Semantics}/resolve43.f90 (100%) rename test/{semantics => Semantics}/resolve44.f90 (100%) rename test/{semantics => Semantics}/resolve45.f90 (100%) rename test/{semantics => Semantics}/resolve46.f90 (100%) rename test/{semantics => Semantics}/resolve47.f90 (100%) rename test/{semantics => Semantics}/resolve48.f90 (100%) rename test/{semantics => Semantics}/resolve49.f90 (100%) rename test/{semantics => Semantics}/resolve50.f90 (100%) rename test/{semantics => Semantics}/resolve51.f90 (100%) rename test/{semantics => Semantics}/resolve52.f90 (100%) rename test/{semantics => Semantics}/resolve53.f90 (100%) rename test/{semantics => Semantics}/resolve54.f90 (100%) rename test/{semantics => Semantics}/resolve55.f90 (100%) rename test/{semantics => Semantics}/resolve56.f90 (100%) rename test/{semantics => Semantics}/resolve57.f90 (100%) rename test/{semantics => Semantics}/resolve58.f90 (100%) rename test/{semantics => Semantics}/resolve59.f90 (100%) rename test/{semantics => Semantics}/resolve60.f90 (100%) rename test/{semantics => Semantics}/resolve61.f90 (100%) rename test/{semantics => Semantics}/resolve62.f90 (100%) rename test/{semantics => Semantics}/resolve63.f90 (100%) rename test/{semantics => Semantics}/resolve64.f90 (100%) rename test/{semantics => Semantics}/resolve65.f90 (100%) rename test/{semantics => Semantics}/resolve66.f90 (100%) rename test/{semantics => Semantics}/resolve67.f90 (100%) rename test/{semantics => Semantics}/resolve68.f90 (100%) rename test/{semantics => Semantics}/resolve69.f90 (100%) rename test/{semantics => Semantics}/resolve70.f90 (100%) rename test/{semantics => Semantics}/resolve71.f90 (100%) rename test/{semantics => Semantics}/resolve72.f90 (100%) rename test/{semantics => Semantics}/separate-module-procs.f90 (100%) rename test/{semantics => Semantics}/stop01.f90 (100%) rename test/{semantics => Semantics}/structconst01.f90 (100%) rename test/{semantics => Semantics}/structconst02.f90 (100%) rename test/{semantics => Semantics}/structconst03.f90 (98%) rename test/{semantics => Semantics}/structconst04.f90 (100%) rename test/{semantics => Semantics}/symbol01.f90 (100%) rename test/{semantics => Semantics}/symbol02.f90 (100%) rename test/{semantics => Semantics}/symbol03.f90 (100%) rename test/{semantics => Semantics}/symbol05.f90 (100%) rename test/{semantics => Semantics}/symbol06.f90 (100%) rename test/{semantics => Semantics}/symbol07.f90 (100%) rename test/{semantics => Semantics}/symbol08.f90 (100%) rename test/{semantics => Semantics}/symbol09.f90 (100%) rename test/{semantics => Semantics}/symbol10.f90 (100%) rename test/{semantics => Semantics}/symbol11.f90 (100%) rename test/{semantics => Semantics}/symbol12.f90 (100%) rename test/{semantics => Semantics}/symbol13.f90 (100%) rename test/{semantics => Semantics}/symbol14.f90 (100%) rename test/{semantics => Semantics}/symbol15.f90 (100%) rename test/{semantics => Semantics}/symbol16.f90 (100%) rename test/{semantics => Semantics}/symbol17.f90 (100%) rename test/{semantics => Semantics}/test_any.sh (100%) rename test/{semantics => Semantics}/test_errors.sh (100%) rename test/{semantics => Semantics}/test_modfile.sh (100%) rename test/{semantics => Semantics}/test_symbols.sh (100%) diff --git a/documentation/C++style.md b/documentation/C++style.md index 9136a3f21e6a..f8e58968f330 100644 --- a/documentation/C++style.md +++ b/documentation/C++style.md @@ -219,7 +219,7 @@ or assignments should exist for a class, explicitly `=delete` all of them. There are many -- perhaps too many -- means of indirect addressing data in this project. Some of these are standard C++ language and library features, -while others are local inventions in `lib/common`: +while others are local inventions in `lib/Common`: * Bare pointers (`Foo *p`): these are obviously nullable, non-owning, undefined when uninitialized, shallowly copyable, reassignable, and often not the right abstraction to use in this project. diff --git a/documentation/ImplementingASemanticCheck.md b/documentation/ImplementingASemanticCheck.md index 736202abaa1f..fc2e4f14061e 100644 --- a/documentation/ImplementingASemanticCheck.md +++ b/documentation/ImplementingASemanticCheck.md @@ -111,9 +111,9 @@ checking had already taken place. Most semantic checks for statements are implemented by walking the parse tree and performing analysis on the nodes they visit. My plan was to use this method. The infrastructure for walking the parse tree for statement semantic -checking is implemented in the files `lib/semantics/semantics.cpp`. +checking is implemented in the files `lib/Semantics/semantics.cpp`. Here's a fragment of the declaration of the framework's parse tree visitor from -`lib/semantics/semantics.cpp`: +`lib/Semantics/semantics.cpp`: ```C++ // A parse tree visitor that calls Enter/Leave functions from each checker @@ -136,7 +136,7 @@ Here's a fragment of the declaration of the framework's parse tree visitor from Since FUNCTION calls are a kind of expression, I was planning to base my implementation on the contents of `parser::Expr` nodes. I would need to define either an `Enter()` or `Leave()` function whose parameter was a `parser::Expr` -node. Here's the declaration I put into `lib/semantics/check-do.h`: +node. Here's the declaration I put into `lib/Semantics/check-do.h`: ```C++ void Leave(const parser::Expr &); @@ -148,12 +148,12 @@ arbitrarily chose to implement the `Leave()` function to visit the parse tree node. Since my semantic check was focused on DO CONCURRENT statements, I added it to -the file `lib/semantics/check-do.cpp` where most of the semantic checking for +the file `lib/Semantics/check-do.cpp` where most of the semantic checking for DO statements already lived. ## Taking advantage of prior work When implementing a similar check for SUBROUTINE calls, I created a utility -functions in `lib/semantics/semantics.cpp` to emit messages if +functions in `lib/Semantics/semantics.cpp` to emit messages if a symbol corresponding to an active DO variable was being potentially modified: ```C++ @@ -176,7 +176,7 @@ functions. The second is needed to determine whether to call them. ## Finding the source location The source code location information that I'd need for the error message must come from the parse tree. I looked in the file -`include/flang/parser/parse-tree.h` and determined that a `struct Expr` +`include/flang/Parser/parse-tree.h` and determined that a `struct Expr` contained source location information since it had the field `CharBlock source`. Thus, if I visited a `parser::Expr` node, I could get the source location information for the associated expression. @@ -184,7 +184,7 @@ location information for the associated expression. ## Determining the `INTENT` I knew that I could find the `INTENT` of the dummy argument associated with the actual argument from the function called `dummyIntent()` in the class -`evaluate::ActualArgument` in the file `include/flang/evaluate/call.h`. So +`evaluate::ActualArgument` in the file `include/flang/Evaluate/call.h`. So if I could find an `evaluate::ActualArgument` in an expression, I could determine the `INTENT` of the associated dummy argument. I knew that it was valid to call `dummyIntent()` because the data on which `dummyIntent()` @@ -216,12 +216,12 @@ find all of the `evaluate::ActualArgument` nodes. Note that the compiler has multiple types called `Expr`. One is in the `parser` namespace. `parser::Expr` is defined in the file -`include/flang/parser/parse-tree.h`. It represents a parsed expression that +`include/flang/Parser/parse-tree.h`. It represents a parsed expression that maps directly to the source code and has fields that specify any operators in the expression, the operands, and the source position of the expression. Additionally, in the namespace `evaluate`, there are `evaluate::Expr` -template classes defined in the file `include/flang/evaluate/expression.h`. +template classes defined in the file `include/flang/Evaluate/expression.h`. These are parameterized over the various types of Fortran and constitute a suite of strongly-typed representations of valid Fortran expressions of type `T` that have been fully elaborated with conversion operations and subjected to @@ -231,7 +231,7 @@ owns an instance of `evaluate::Expr`, the most general representation of an analyzed expression. All of the declarations associated with both FUNCTION and SUBROUTINE calls are -in `include/flang/evaluate/call.h`. An `evaluate::FunctionRef` inherits from +in `include/flang/Evaluate/call.h`. An `evaluate::FunctionRef` inherits from an `evaluate::ProcedureRef` which contains the list of `evaluate::ActualArgument` nodes. But the relationship between an `evaluate::FunctionRef` node and its associated arguments is not relevant. I @@ -269,16 +269,16 @@ argument was an active DO variable. ## Adding a parse tree visitor I started my implementation by adding a visitor for `parser::Expr` nodes. Since this analysis is part of DO construct checking, I did this in -`lib/semantics/check-do.cpp`. I added a print statement to the visitor to +`lib/Semantics/check-do.cpp`. I added a print statement to the visitor to verify that my new code was actually getting executed. -In `lib/semantics/check-do.h`, I added the declaration for the visitor: +In `lib/Semantics/check-do.h`, I added the declaration for the visitor: ```C++ void Leave(const parser::Expr &); ``` -In `lib/semantics/check-do.cpp`, I added an (almost empty) implementation: +In `lib/Semantics/check-do.cpp`, I added an (almost empty) implementation: ```C++ void DoChecker::Leave(const parser::Expr &) { @@ -316,7 +316,7 @@ framework to walk the `evaluate::Expr` to gather all of the `evaluate::ActualArgument` nodes. The code that I planned to model it on was the existing infrastructure that collected all of the `semantics::Symbol` nodes from an `evaluate::Expr`. I found this implementation in -`lib/evaluate/tools.cpp`: +`lib/Evaluate/tools.cpp`: ```C++ struct CollectSymbolsHelper @@ -334,7 +334,7 @@ was the existing infrastructure that collected all of the `semantics::Symbol` no ``` Note that the `CollectSymbols()` function returns a `semantics::Symbolset`, -which is declared in `include/flang/semantics/symbol.h`: +which is declared in `include/flang/Semantics/symbol.h`: ```C++ using SymbolSet = std::set; @@ -356,11 +356,11 @@ full `semantics::Symbol` objects into the set. Ideally, we would be able to cre `std::set` (a set of C++ references to symbols). But C++ doesn't support sets that contain references. This limitation is part of the rationale for the f18 implementation of type `common::Reference`, which is defined in - `include/flang/common/reference.h`. + `include/flang/Common/reference.h`. `SymbolRef`, the specialization of the template `common::Reference` for `semantics::Symbol`, is declared in the file -`include/flang/semantics/symbol.h`: +`include/flang/Semantics/symbol.h`: ```C++ using SymbolRef = common::Reference; @@ -370,7 +370,7 @@ So to implement something that would collect `evaluate::ActualArgument` nodes from an `evaluate::Expr`, I first defined the required types `ActualArgumentRef` and `ActualArgumentSet`. Since these are being used exclusively for DO construct semantic checking (currently), I put their -definitions into `lib/semantics/check-do.cpp`: +definitions into `lib/Semantics/check-do.cpp`: ```C++ @@ -386,7 +386,7 @@ Since `ActualArgument` is in the namespace `evaluate`, I put the definition for `ActualArgumentRef` in that namespace, too. I then modeled the code to create an `ActualArgumentSet` after the code to -collect a `SymbolSet` and put it into `lib/semantics/check-do.cpp`: +collect a `SymbolSet` and put it into `lib/Semantics/check-do.cpp`: ```C++ @@ -525,8 +525,8 @@ symbol table node (`semantics::Symbol`) for the variable. My starting point was `evaluate::ActualArgument` node. I was unsure of how to do this, so I browsed through existing code to look for -how it treated `evaluate::ActualArgument` objects. Since most of the code that deals with the `evaluate` namespace is in the lib/evaluate directory, I looked there. I ran `grep` on all of the `.cpp` files looking for -uses of `ActualArgument`. One of the first hits I got was in `lib/evaluate/call.cpp` in the definition of `ActualArgument::GetType()`: +how it treated `evaluate::ActualArgument` objects. Since most of the code that deals with the `evaluate` namespace is in the lib/Evaluate directory, I looked there. I ran `grep` on all of the `.cpp` files looking for +uses of `ActualArgument`. One of the first hits I got was in `lib/Evaluate/call.cpp` in the definition of `ActualArgument::GetType()`: ```C++ std::optional ActualArgument::GetType() const { @@ -544,7 +544,7 @@ I noted the call to `UnwrapExpr()` that yielded a value of `Expr`. So I guessed that I could use this member function to get an `evaluate::Expr` on which I could perform further analysis. -I also knew that the header file `include/flang/evaluate/tools.h` held many +I also knew that the header file `include/flang/Evaluate/tools.h` held many utility functions for dealing with `evaluate::Expr` objects. I was hoping to find something that would determine if an `evaluate::Expr` was a variable. So I searched for `IsVariable` and got a hit immediately. @@ -560,7 +560,7 @@ I searched for `IsVariable` and got a hit immediately. But I actually needed more than just the knowledge that an `evaluate::Expr` was a variable. I needed the `semantics::Symbol` associated with the variable. So -I searched in `include/flang/evaluate/tools.h` for functions that returned a +I searched in `include/flang/Evaluate/tools.h` for functions that returned a `semantics::Symbol`. I found the following: ```C++ diff --git a/include/flang/common/Fortran-features.h b/include/flang/Common/Fortran-features.h similarity index 94% rename from include/flang/common/Fortran-features.h rename to include/flang/Common/Fortran-features.h index 0b318e90e3af..dfce05c99cd5 100644 --- a/include/flang/common/Fortran-features.h +++ b/include/flang/Common/Fortran-features.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/Fortran-features.h -----------------*- C++ -*-===// +//===-- include/flang/Common/Fortran-features.h -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,9 +9,9 @@ #ifndef FORTRAN_COMMON_FORTRAN_FEATURES_H_ #define FORTRAN_COMMON_FORTRAN_FEATURES_H_ -#include "flang/common/Fortran.h" -#include "flang/common/enum-set.h" -#include "flang/common/idioms.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/idioms.h" namespace Fortran::common { diff --git a/include/flang/common/Fortran.h b/include/flang/Common/Fortran.h similarity index 97% rename from include/flang/common/Fortran.h rename to include/flang/Common/Fortran.h index 9c73c32dcebb..20a507aed415 100644 --- a/include/flang/common/Fortran.h +++ b/include/flang/Common/Fortran.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/Fortran.h --------------------------*- C++ -*-===// +//===-- include/flang/Common/Fortran.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/bit-population-count.h b/include/flang/Common/bit-population-count.h similarity index 98% rename from include/flang/common/bit-population-count.h rename to include/flang/Common/bit-population-count.h index 0a95643eb71d..af3a1f22fd30 100644 --- a/include/flang/common/bit-population-count.h +++ b/include/flang/Common/bit-population-count.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/bit-population-count.h -------------*- C++ -*-===// +//===-- include/flang/Common/bit-population-count.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/constexpr-bitset.h b/include/flang/Common/constexpr-bitset.h similarity index 98% rename from include/flang/common/constexpr-bitset.h rename to include/flang/Common/constexpr-bitset.h index 1125655adbab..eae11f57d1e1 100644 --- a/include/flang/common/constexpr-bitset.h +++ b/include/flang/Common/constexpr-bitset.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/constexpr-bitset.h -----------------*- C++ -*-===// +//===-- include/flang/Common/constexpr-bitset.h -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/default-kinds.h b/include/flang/Common/default-kinds.h similarity index 96% rename from include/flang/common/default-kinds.h rename to include/flang/Common/default-kinds.h index e9532ad8ddcb..c3610b23745f 100644 --- a/include/flang/common/default-kinds.h +++ b/include/flang/Common/default-kinds.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/default-kinds.h --------------------*- C++ -*-===// +//===-- include/flang/Common/default-kinds.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_COMMON_DEFAULT_KINDS_H_ #define FORTRAN_COMMON_DEFAULT_KINDS_H_ -#include "flang/common/Fortran.h" +#include "flang/Common/Fortran.h" #include namespace Fortran::common { diff --git a/include/flang/common/enum-set.h b/include/flang/Common/enum-set.h similarity index 99% rename from include/flang/common/enum-set.h rename to include/flang/Common/enum-set.h index 04141808a5b8..4b255c38cc5a 100644 --- a/include/flang/common/enum-set.h +++ b/include/flang/Common/enum-set.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/enum-set.h -------------------------*- C++ -*-===// +//===-- include/flang/Common/enum-set.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/format.h b/include/flang/Common/format.h similarity index 99% rename from include/flang/common/format.h rename to include/flang/Common/format.h index 92cd0e94fad9..4cb274c81cd5 100644 --- a/include/flang/common/format.h +++ b/include/flang/Common/format.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/format.h ---------------------------*- C++ -*-===// +//===-- include/flang/Common/format.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,7 +10,7 @@ #define FORTRAN_COMMON_FORMAT_H_ #include "enum-set.h" -#include "flang/common/Fortran.h" +#include "flang/Common/Fortran.h" #include // Define a FormatValidator class template to validate a format expression diff --git a/include/flang/common/idioms.h b/include/flang/Common/idioms.h similarity index 98% rename from include/flang/common/idioms.h rename to include/flang/Common/idioms.h index 8debe21acb66..84862f4976da 100644 --- a/include/flang/common/idioms.h +++ b/include/flang/Common/idioms.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/idioms.h ---------------------------*- C++ -*-===// +//===-- include/flang/Common/idioms.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/indirection.h b/include/flang/Common/indirection.h similarity index 98% rename from include/flang/common/indirection.h rename to include/flang/Common/indirection.h index fcf05549ae7e..a79fc19a388f 100644 --- a/include/flang/common/indirection.h +++ b/include/flang/Common/indirection.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/indirection.h ----------------------*- C++ -*-===// +//===-- include/flang/Common/indirection.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/interval.h b/include/flang/Common/interval.h similarity index 98% rename from include/flang/common/interval.h rename to include/flang/Common/interval.h index 144f719de7b3..c1d1cfb890c8 100644 --- a/include/flang/common/interval.h +++ b/include/flang/Common/interval.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/interval.h -------------------------*- C++ -*-===// +//===-- include/flang/Common/interval.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/leading-zero-bit-count.h b/include/flang/Common/leading-zero-bit-count.h similarity index 98% rename from include/flang/common/leading-zero-bit-count.h rename to include/flang/Common/leading-zero-bit-count.h index fe7bf00378e0..48fc3f129059 100644 --- a/include/flang/common/leading-zero-bit-count.h +++ b/include/flang/Common/leading-zero-bit-count.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/leading-zero-bit-count.h -----------*- C++ -*-===// +//===-- include/flang/Common/leading-zero-bit-count.h -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/real.h b/include/flang/Common/real.h similarity index 98% rename from include/flang/common/real.h rename to include/flang/Common/real.h index d15de663a92b..158482e6c5c1 100644 --- a/include/flang/common/real.h +++ b/include/flang/Common/real.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/real.h -----------------------------*- C++ -*-===// +//===-- include/flang/Common/real.h -----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/reference-counted.h b/include/flang/Common/reference-counted.h similarity index 96% rename from include/flang/common/reference-counted.h rename to include/flang/Common/reference-counted.h index d7dc68c76492..2a96741bb8f7 100644 --- a/include/flang/common/reference-counted.h +++ b/include/flang/Common/reference-counted.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/reference-counted.h ----------------*- C++ -*-===// +//===-- include/flang/Common/reference-counted.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/reference.h b/include/flang/Common/reference.h similarity index 96% rename from include/flang/common/reference.h rename to include/flang/Common/reference.h index 8f01b6587c23..37e7ab9ca523 100644 --- a/include/flang/common/reference.h +++ b/include/flang/Common/reference.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/reference.h ------------------------*- C++ -*-===// +//===-- include/flang/Common/reference.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/restorer.h b/include/flang/Common/restorer.h similarity index 95% rename from include/flang/common/restorer.h rename to include/flang/Common/restorer.h index 95b730b83513..26ddcfca0820 100644 --- a/include/flang/common/restorer.h +++ b/include/flang/Common/restorer.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/restorer.h -------------------------*- C++ -*-===// +//===-- include/flang/Common/restorer.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/template.h b/include/flang/Common/template.h similarity index 99% rename from include/flang/common/template.h rename to include/flang/Common/template.h index 460f1a8bdaed..2f726fe6cc39 100644 --- a/include/flang/common/template.h +++ b/include/flang/Common/template.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/template.h -------------------------*- C++ -*-===// +//===-- include/flang/Common/template.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_COMMON_TEMPLATE_H_ #define FORTRAN_COMMON_TEMPLATE_H_ -#include "flang/common/idioms.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/include/flang/common/uint128.h b/include/flang/Common/uint128.h similarity index 99% rename from include/flang/common/uint128.h rename to include/flang/Common/uint128.h index 0129101a1ce9..4d4fed94a002 100644 --- a/include/flang/common/uint128.h +++ b/include/flang/Common/uint128.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/uint128.h --------------------------*- C++ -*-===// +//===-- include/flang/Common/uint128.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/unsigned-const-division.h b/include/flang/Common/unsigned-const-division.h similarity index 97% rename from include/flang/common/unsigned-const-division.h rename to include/flang/Common/unsigned-const-division.h index 749983e8464c..752bbd94e7fa 100644 --- a/include/flang/common/unsigned-const-division.h +++ b/include/flang/Common/unsigned-const-division.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/unsigned-const-division.h ----------*- C++ -*-===// +//===-- include/flang/Common/unsigned-const-division.h ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/common/unwrap.h b/include/flang/Common/unwrap.h similarity index 98% rename from include/flang/common/unwrap.h rename to include/flang/Common/unwrap.h index 1370b1425202..8a14bf68fb27 100644 --- a/include/flang/common/unwrap.h +++ b/include/flang/Common/unwrap.h @@ -1,4 +1,4 @@ -//===-- include/flang/common/unwrap.h ---------------------------*- C++ -*-===// +//===-- include/flang/Common/unwrap.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/decimal/binary-floating-point.h b/include/flang/Decimal/binary-floating-point.h similarity index 96% rename from include/flang/decimal/binary-floating-point.h rename to include/flang/Decimal/binary-floating-point.h index bf467c5cbb70..ad3eb3e47ad1 100644 --- a/include/flang/decimal/binary-floating-point.h +++ b/include/flang/Decimal/binary-floating-point.h @@ -1,4 +1,4 @@ -//===-- include/flang/decimal/binary-floating-point.h -----------*- C++ -*-===// +//===-- include/flang/Decimal/binary-floating-point.h -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,8 +12,8 @@ // Access and manipulate the fields of an IEEE-754 binary // floating-point value via a generalized template. -#include "flang/common/real.h" -#include "flang/common/uint128.h" +#include "flang/Common/real.h" +#include "flang/Common/uint128.h" #include #include #include diff --git a/include/flang/decimal/decimal.h b/include/flang/Decimal/decimal.h similarity index 98% rename from include/flang/decimal/decimal.h rename to include/flang/Decimal/decimal.h index 05ed0068d95c..1e792d78cc1f 100644 --- a/include/flang/decimal/decimal.h +++ b/include/flang/Decimal/decimal.h @@ -1,4 +1,4 @@ -/*===-- include/flang/decimal/decimal.h ---------------------------*- C++ -*-=== +/*===-- include/flang/Decimal/decimal.h ---------------------------*- C++ -*-=== * * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. * See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/call.h b/include/flang/Evaluate/call.h similarity index 97% rename from include/flang/evaluate/call.h rename to include/flang/Evaluate/call.h index 97d1ea1b04f9..a792ff4188ec 100644 --- a/include/flang/evaluate/call.h +++ b/include/flang/Evaluate/call.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/call.h ---------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/call.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,10 +13,10 @@ #include "constant.h" #include "formatting.h" #include "type.h" -#include "flang/common/indirection.h" -#include "flang/common/reference.h" -#include "flang/parser/char-block.h" -#include "flang/semantics/attr.h" +#include "flang/Common/indirection.h" +#include "flang/Common/reference.h" +#include "flang/Parser/char-block.h" +#include "flang/Semantics/attr.h" #include #include #include diff --git a/include/flang/evaluate/characteristics.h b/include/flang/Evaluate/characteristics.h similarity index 97% rename from include/flang/evaluate/characteristics.h rename to include/flang/Evaluate/characteristics.h index 9e61602579e7..52ac7384815b 100644 --- a/include/flang/evaluate/characteristics.h +++ b/include/flang/Evaluate/characteristics.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/characteristics.h ----------------*- C++ -*-===// +//===-- include/flang/Evaluate/characteristics.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,12 +17,12 @@ #include "expression.h" #include "shape.h" #include "type.h" -#include "flang/common/Fortran.h" -#include "flang/common/enum-set.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/parser/char-block.h" -#include "flang/semantics/symbol.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/char-block.h" +#include "flang/Semantics/symbol.h" #include #include #include diff --git a/include/flang/evaluate/check-expression.h b/include/flang/Evaluate/check-expression.h similarity index 97% rename from include/flang/evaluate/check-expression.h rename to include/flang/Evaluate/check-expression.h index 31a79fb19585..afd730924baf 100644 --- a/include/flang/evaluate/check-expression.h +++ b/include/flang/Evaluate/check-expression.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/check-expression.h ---------------*- C++ -*-===// +//===-- include/flang/Evaluate/check-expression.h ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/common.h b/include/flang/Evaluate/common.h similarity index 96% rename from include/flang/evaluate/common.h rename to include/flang/Evaluate/common.h index b7ea530e712f..d76126b1436b 100644 --- a/include/flang/evaluate/common.h +++ b/include/flang/Evaluate/common.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/common.h -------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/common.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,14 +10,14 @@ #define FORTRAN_EVALUATE_COMMON_H_ #include "intrinsics-library.h" -#include "flang/common/Fortran.h" -#include "flang/common/default-kinds.h" -#include "flang/common/enum-set.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/common/restorer.h" -#include "flang/parser/char-block.h" -#include "flang/parser/message.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/default-kinds.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Common/restorer.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/message.h" #include #include diff --git a/include/flang/evaluate/complex.h b/include/flang/Evaluate/complex.h similarity index 98% rename from include/flang/evaluate/complex.h rename to include/flang/Evaluate/complex.h index 16559e9f0962..417d4cfecbbd 100644 --- a/include/flang/evaluate/complex.h +++ b/include/flang/Evaluate/complex.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/complex.h ------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/complex.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/constant.h b/include/flang/Evaluate/constant.h similarity index 98% rename from include/flang/evaluate/constant.h rename to include/flang/Evaluate/constant.h index 833702a03c91..2a4a8109283f 100644 --- a/include/flang/evaluate/constant.h +++ b/include/flang/Evaluate/constant.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/constant.h -----------------------*- C++ -*-===// +//===-- include/flang/Evaluate/constant.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,8 +11,8 @@ #include "formatting.h" #include "type.h" -#include "flang/common/default-kinds.h" -#include "flang/common/reference.h" +#include "flang/Common/default-kinds.h" +#include "flang/Common/reference.h" #include #include #include diff --git a/include/flang/evaluate/expression.h b/include/flang/Evaluate/expression.h similarity index 99% rename from include/flang/evaluate/expression.h rename to include/flang/Evaluate/expression.h index 019858a6e3da..52c17a957b9b 100644 --- a/include/flang/evaluate/expression.h +++ b/include/flang/Evaluate/expression.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/expression.h ---------------------*- C++ -*-===// +//===-- include/flang/Evaluate/expression.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -21,11 +21,11 @@ #include "formatting.h" #include "type.h" #include "variable.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/common/template.h" -#include "flang/parser/char-block.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Common/template.h" +#include "flang/Parser/char-block.h" #include #include #include diff --git a/include/flang/evaluate/fold.h b/include/flang/Evaluate/fold.h similarity index 97% rename from include/flang/evaluate/fold.h rename to include/flang/Evaluate/fold.h index 5f33d69a2fe6..85e497464508 100644 --- a/include/flang/evaluate/fold.h +++ b/include/flang/Evaluate/fold.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/fold.h ---------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/fold.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/formatting.h b/include/flang/Evaluate/formatting.h similarity index 90% rename from include/flang/evaluate/formatting.h rename to include/flang/Evaluate/formatting.h index a2b8458d042f..0a1f9517d2d0 100644 --- a/include/flang/evaluate/formatting.h +++ b/include/flang/Evaluate/formatting.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/formatting.h ---------------------*- C++ -*-===// +//===-- include/flang/Evaluate/formatting.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,7 +11,7 @@ // It is inconvenient in C++ to have std::ostream::operator<<() as a direct // friend function of a class template with many instantiations, so the -// various representational class templates in lib/evaluate format themselves +// various representational class templates in lib/Evaluate format themselves // via AsFortran(std::ostream &) member functions, which the operator<<() // overload below will call. Others have AsFortran() member functions that // return strings. @@ -19,7 +19,7 @@ // This header is meant to be included by the headers that define the several // representational class templates that need it, not by external clients. -#include "flang/common/indirection.h" +#include "flang/Common/indirection.h" #include #include #include diff --git a/include/flang/evaluate/integer.h b/include/flang/Evaluate/integer.h similarity index 99% rename from include/flang/evaluate/integer.h rename to include/flang/Evaluate/integer.h index 46478f7e6106..6f997c967e69 100644 --- a/include/flang/evaluate/integer.h +++ b/include/flang/Evaluate/integer.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/integer.h ------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/integer.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,9 +17,9 @@ // (*"Signed" here means two's-complement, just to be clear. Ones'-complement // and signed-magnitude encodings appear to be extinct in 2018.) -#include "flang/common/bit-population-count.h" -#include "flang/common/leading-zero-bit-count.h" -#include "flang/evaluate/common.h" +#include "flang/Common/bit-population-count.h" +#include "flang/Common/leading-zero-bit-count.h" +#include "flang/Evaluate/common.h" #include #include #include diff --git a/include/flang/evaluate/intrinsics-library.h b/include/flang/Evaluate/intrinsics-library.h similarity index 98% rename from include/flang/evaluate/intrinsics-library.h rename to include/flang/Evaluate/intrinsics-library.h index a7a1959a22bb..3e1bf5471c2c 100644 --- a/include/flang/evaluate/intrinsics-library.h +++ b/include/flang/Evaluate/intrinsics-library.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/intrinsics-library.h -------------*- C++ -*-===// +//===-- include/flang/Evaluate/intrinsics-library.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/intrinsics.h b/include/flang/Evaluate/intrinsics.h similarity index 93% rename from include/flang/evaluate/intrinsics.h rename to include/flang/Evaluate/intrinsics.h index 525e0907a159..dce5162cb944 100644 --- a/include/flang/evaluate/intrinsics.h +++ b/include/flang/Evaluate/intrinsics.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/intrinsics.h ---------------------*- C++ -*-===// +//===-- include/flang/Evaluate/intrinsics.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,9 +12,9 @@ #include "call.h" #include "characteristics.h" #include "type.h" -#include "flang/common/default-kinds.h" -#include "flang/parser/char-block.h" -#include "flang/parser/message.h" +#include "flang/Common/default-kinds.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/message.h" #include #include #include diff --git a/include/flang/evaluate/logical.h b/include/flang/Evaluate/logical.h similarity index 97% rename from include/flang/evaluate/logical.h rename to include/flang/Evaluate/logical.h index d76abf854d6a..a7813ecfdd70 100644 --- a/include/flang/evaluate/logical.h +++ b/include/flang/Evaluate/logical.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/logical.h ------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/logical.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/real.h b/include/flang/Evaluate/real.h similarity index 98% rename from include/flang/evaluate/real.h rename to include/flang/Evaluate/real.h index bcc73cb54b73..0624abd3ee93 100644 --- a/include/flang/evaluate/real.h +++ b/include/flang/Evaluate/real.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/real.h ---------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/real.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,8 +12,8 @@ #include "formatting.h" #include "integer.h" #include "rounding-bits.h" -#include "flang/common/real.h" -#include "flang/evaluate/common.h" +#include "flang/Common/real.h" +#include "flang/Evaluate/common.h" #include #include #include diff --git a/include/flang/evaluate/rounding-bits.h b/include/flang/Evaluate/rounding-bits.h similarity index 97% rename from include/flang/evaluate/rounding-bits.h rename to include/flang/Evaluate/rounding-bits.h index bc5771659595..271d780b923f 100644 --- a/include/flang/evaluate/rounding-bits.h +++ b/include/flang/Evaluate/rounding-bits.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/rounding-bits.h ------------------*- C++ -*-===// +//===-- include/flang/Evaluate/rounding-bits.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/evaluate/shape.h b/include/flang/Evaluate/shape.h similarity index 97% rename from include/flang/evaluate/shape.h rename to include/flang/Evaluate/shape.h index ca9700b02583..c7f453c90f0b 100644 --- a/include/flang/evaluate/shape.h +++ b/include/flang/Evaluate/shape.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/shape.h --------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/shape.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,9 +15,9 @@ #include "expression.h" #include "traverse.h" #include "variable.h" -#include "flang/common/indirection.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" #include #include diff --git a/include/flang/evaluate/static-data.h b/include/flang/Evaluate/static-data.h similarity index 95% rename from include/flang/evaluate/static-data.h rename to include/flang/Evaluate/static-data.h index ce66351ba140..5a708aa25390 100644 --- a/include/flang/evaluate/static-data.h +++ b/include/flang/Evaluate/static-data.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/static-data.h --------------------*- C++ -*-===// +//===-- include/flang/Evaluate/static-data.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,7 +13,7 @@ #include "formatting.h" #include "type.h" -#include "flang/common/idioms.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/include/flang/evaluate/tools.h b/include/flang/Evaluate/tools.h similarity index 98% rename from include/flang/evaluate/tools.h rename to include/flang/Evaluate/tools.h index d992e8eacff5..7f867813b2bc 100644 --- a/include/flang/evaluate/tools.h +++ b/include/flang/Evaluate/tools.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/tools.h --------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/tools.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,14 +10,14 @@ #define FORTRAN_EVALUATE_TOOLS_H_ #include "traverse.h" -#include "flang/common/idioms.h" -#include "flang/common/template.h" -#include "flang/common/unwrap.h" -#include "flang/evaluate/constant.h" -#include "flang/evaluate/expression.h" -#include "flang/parser/message.h" -#include "flang/semantics/attr.h" -#include "flang/semantics/symbol.h" +#include "flang/Common/idioms.h" +#include "flang/Common/template.h" +#include "flang/Common/unwrap.h" +#include "flang/Evaluate/constant.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/attr.h" +#include "flang/Semantics/symbol.h" #include #include #include diff --git a/include/flang/evaluate/traverse.h b/include/flang/Evaluate/traverse.h similarity index 98% rename from include/flang/evaluate/traverse.h rename to include/flang/Evaluate/traverse.h index d85afac239e0..23b3d4cf682c 100644 --- a/include/flang/evaluate/traverse.h +++ b/include/flang/Evaluate/traverse.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/traverse.h -----------------------*- C++ -*-===// +//===-- include/flang/Evaluate/traverse.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -35,8 +35,8 @@ // - Overloads of operator() in each visitor handle the cases of interest. #include "expression.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/type.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/type.h" #include #include diff --git a/include/flang/evaluate/type.h b/include/flang/Evaluate/type.h similarity index 99% rename from include/flang/evaluate/type.h rename to include/flang/Evaluate/type.h index a558928d4893..137cf66e9cd9 100644 --- a/include/flang/evaluate/type.h +++ b/include/flang/Evaluate/type.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/type.h ---------------------------*- C++ -*-===// +//===-- include/flang/Evaluate/type.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -22,9 +22,9 @@ #include "integer.h" #include "logical.h" #include "real.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/common/template.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Common/template.h" #include #include #include diff --git a/include/flang/evaluate/variable.h b/include/flang/Evaluate/variable.h similarity index 98% rename from include/flang/evaluate/variable.h rename to include/flang/Evaluate/variable.h index bbbdd7c74903..b39011aeab64 100644 --- a/include/flang/evaluate/variable.h +++ b/include/flang/Evaluate/variable.h @@ -1,4 +1,4 @@ -//===-- include/flang/evaluate/variable.h -----------------------*- C++ -*-===// +//===-- include/flang/Evaluate/variable.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,10 +20,10 @@ #include "formatting.h" #include "static-data.h" #include "type.h" -#include "flang/common/idioms.h" -#include "flang/common/reference.h" -#include "flang/common/template.h" -#include "flang/parser/char-block.h" +#include "flang/Common/idioms.h" +#include "flang/Common/reference.h" +#include "flang/Common/template.h" +#include "flang/Parser/char-block.h" #include #include #include diff --git a/include/flang/lower/.clang-format b/include/flang/Lower/.clang-format similarity index 100% rename from include/flang/lower/.clang-format rename to include/flang/Lower/.clang-format diff --git a/include/flang/lower/PFTBuilder.h b/include/flang/Lower/PFTBuilder.h similarity index 99% rename from include/flang/lower/PFTBuilder.h rename to include/flang/Lower/PFTBuilder.h index 0b1345ef8bee..bef98b519038 100644 --- a/include/flang/lower/PFTBuilder.h +++ b/include/flang/Lower/PFTBuilder.h @@ -1,4 +1,4 @@ -//===-- include/flang/lower/PFTBuilder.h ------------------------*- C++ -*-===// +//===-- include/flang/Lower/PFTBuilder.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_LOWER_PFT_BUILDER_H_ #define FORTRAN_LOWER_PFT_BUILDER_H_ -#include "flang/common/template.h" -#include "flang/parser/parse-tree.h" +#include "flang/Common/template.h" +#include "flang/Parser/parse-tree.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/include/flang/optimizer/.clang-format b/include/flang/Optimizer/.clang-format similarity index 100% rename from include/flang/optimizer/.clang-format rename to include/flang/Optimizer/.clang-format diff --git a/include/flang/parser/char-block.h b/include/flang/Parser/char-block.h similarity index 98% rename from include/flang/parser/char-block.h rename to include/flang/Parser/char-block.h index f05211fa8845..421bff9dc0d7 100644 --- a/include/flang/parser/char-block.h +++ b/include/flang/Parser/char-block.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/char-block.h -----------------------*- C++ -*-===// +//===-- include/flang/Parser/char-block.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,7 +11,7 @@ // Describes a contiguous block of characters; does not own their storage. -#include "flang/common/interval.h" +#include "flang/Common/interval.h" #include #include #include diff --git a/include/flang/parser/char-buffer.h b/include/flang/Parser/char-buffer.h similarity index 97% rename from include/flang/parser/char-buffer.h rename to include/flang/Parser/char-buffer.h index b9f66c6f3dde..a62659b7c69d 100644 --- a/include/flang/parser/char-buffer.h +++ b/include/flang/Parser/char-buffer.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/char-buffer.h ----------------------*- C++ -*-===// +//===-- include/flang/Parser/char-buffer.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/char-set.h b/include/flang/Parser/char-set.h similarity index 97% rename from include/flang/parser/char-set.h rename to include/flang/Parser/char-set.h index 9bac7ea01180..64ff6692b1d1 100644 --- a/include/flang/parser/char-set.h +++ b/include/flang/Parser/char-set.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/char-set.h -------------------------*- C++ -*-===// +//===-- include/flang/Parser/char-set.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/characters.h b/include/flang/Parser/characters.h similarity index 99% rename from include/flang/parser/characters.h rename to include/flang/Parser/characters.h index 102d886d3a0b..02b92ba6f047 100644 --- a/include/flang/parser/characters.h +++ b/include/flang/Parser/characters.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/characters.h -----------------------*- C++ -*-===// +//===-- include/flang/Parser/characters.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/dump-parse-tree.h b/include/flang/Parser/dump-parse-tree.h similarity index 99% rename from include/flang/parser/dump-parse-tree.h rename to include/flang/Parser/dump-parse-tree.h index a6181834c94f..d132e93eeab5 100644 --- a/include/flang/parser/dump-parse-tree.h +++ b/include/flang/Parser/dump-parse-tree.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/dump-parse-tree.h ------------------*- C++ -*-===// +//===-- include/flang/Parser/dump-parse-tree.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,8 +13,8 @@ #include "parse-tree-visitor.h" #include "parse-tree.h" #include "unparse.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" #include #include #include diff --git a/include/flang/parser/format-specification.h b/include/flang/Parser/format-specification.h similarity index 98% rename from include/flang/parser/format-specification.h rename to include/flang/Parser/format-specification.h index 6f1de183a30e..872d025b5e61 100644 --- a/include/flang/parser/format-specification.h +++ b/include/flang/Parser/format-specification.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/format-specification.h -------------*- C++ -*-===// +//===-- include/flang/Parser/format-specification.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/instrumented-parser.h b/include/flang/Parser/instrumented-parser.h similarity index 94% rename from include/flang/parser/instrumented-parser.h rename to include/flang/Parser/instrumented-parser.h index ec760a610a08..0369a5f363fb 100644 --- a/include/flang/parser/instrumented-parser.h +++ b/include/flang/Parser/instrumented-parser.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/instrumented-parser.h --------------*- C++ -*-===// +//===-- include/flang/Parser/instrumented-parser.h --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,8 +11,8 @@ #include "parse-state.h" #include "user-state.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/include/flang/parser/message.h b/include/flang/Parser/message.h similarity index 98% rename from include/flang/parser/message.h rename to include/flang/Parser/message.h index fd62f4671f55..19b94bb70388 100644 --- a/include/flang/parser/message.h +++ b/include/flang/Parser/message.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/message.h --------------------------*- C++ -*-===// +//===-- include/flang/Parser/message.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,9 +15,9 @@ #include "char-block.h" #include "char-set.h" #include "provenance.h" -#include "flang/common/idioms.h" -#include "flang/common/reference-counted.h" -#include "flang/common/restorer.h" +#include "flang/Common/idioms.h" +#include "flang/Common/reference-counted.h" +#include "flang/Common/restorer.h" #include #include #include diff --git a/include/flang/parser/parse-state.h b/include/flang/Parser/parse-state.h similarity index 96% rename from include/flang/parser/parse-state.h rename to include/flang/Parser/parse-state.h index afd4516403a3..d0b304f3c73b 100644 --- a/include/flang/parser/parse-state.h +++ b/include/flang/Parser/parse-state.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/parse-state.h ----------------------*- C++ -*-===// +//===-- include/flang/Parser/parse-state.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,11 +16,11 @@ // and recovery during parsing! #include "user-state.h" -#include "flang/common/Fortran-features.h" -#include "flang/common/idioms.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/include/flang/parser/parse-tree-visitor.h b/include/flang/Parser/parse-tree-visitor.h similarity index 99% rename from include/flang/parser/parse-tree-visitor.h rename to include/flang/Parser/parse-tree-visitor.h index b2c49fe48034..de8e36ba7fd6 100644 --- a/include/flang/parser/parse-tree-visitor.h +++ b/include/flang/Parser/parse-tree-visitor.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/parse-tree-visitor.h ---------------*- C++ -*-===// +//===-- include/flang/Parser/parse-tree-visitor.h ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/parse-tree.h b/include/flang/Parser/parse-tree.h similarity index 99% rename from include/flang/parser/parse-tree.h rename to include/flang/Parser/parse-tree.h index f1e02e0a45d5..d7bb1f5686aa 100644 --- a/include/flang/parser/parse-tree.h +++ b/include/flang/Parser/parse-tree.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/parse-tree.h -----------------------*- C++ -*-===// +//===-- include/flang/Parser/parse-tree.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -22,9 +22,9 @@ #include "format-specification.h" #include "message.h" #include "provenance.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" #include #include #include diff --git a/include/flang/parser/parsing.h b/include/flang/Parser/parsing.h similarity index 95% rename from include/flang/parser/parsing.h rename to include/flang/Parser/parsing.h index a163a77e5856..cff2f57b8185 100644 --- a/include/flang/parser/parsing.h +++ b/include/flang/Parser/parsing.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/parsing.h --------------------------*- C++ -*-===// +//===-- include/flang/Parser/parsing.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,7 +14,7 @@ #include "message.h" #include "parse-tree.h" #include "provenance.h" -#include "flang/common/Fortran-features.h" +#include "flang/Common/Fortran-features.h" #include #include #include diff --git a/include/flang/parser/provenance.h b/include/flang/Parser/provenance.h similarity index 98% rename from include/flang/parser/provenance.h rename to include/flang/Parser/provenance.h index 08cdf51345d3..f1f48c07715d 100644 --- a/include/flang/parser/provenance.h +++ b/include/flang/Parser/provenance.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/provenance.h -----------------------*- C++ -*-===// +//===-- include/flang/Parser/provenance.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,8 +13,8 @@ #include "char-buffer.h" #include "characters.h" #include "source.h" -#include "flang/common/idioms.h" -#include "flang/common/interval.h" +#include "flang/Common/idioms.h" +#include "flang/Common/interval.h" #include #include #include diff --git a/include/flang/parser/source.h b/include/flang/Parser/source.h similarity index 97% rename from include/flang/parser/source.h rename to include/flang/Parser/source.h index 5eb000da2163..08ce1514c07c 100644 --- a/include/flang/parser/source.h +++ b/include/flang/Parser/source.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/source.h ---------------------------*- C++ -*-===// +//===-- include/flang/Parser/source.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/tools.h b/include/flang/Parser/tools.h similarity index 97% rename from include/flang/parser/tools.h rename to include/flang/Parser/tools.h index 447d08c1f496..dcb503b31b79 100644 --- a/include/flang/parser/tools.h +++ b/include/flang/Parser/tools.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/tools.h ----------------------------*- C++ -*-===// +//===-- include/flang/Parser/tools.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/unparse.h b/include/flang/Parser/unparse.h similarity index 95% rename from include/flang/parser/unparse.h rename to include/flang/Parser/unparse.h index 0055ce30a701..d6bca8d2133c 100644 --- a/include/flang/parser/unparse.h +++ b/include/flang/Parser/unparse.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/unparse.h --------------------------*- C++ -*-===// +//===-- include/flang/Parser/unparse.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/include/flang/parser/user-state.h b/include/flang/Parser/user-state.h similarity index 94% rename from include/flang/parser/user-state.h rename to include/flang/Parser/user-state.h index aaf86720d749..60a85d1fed6b 100644 --- a/include/flang/parser/user-state.h +++ b/include/flang/Parser/user-state.h @@ -1,4 +1,4 @@ -//===-- include/flang/parser/user-state.h -----------------------*- C++ -*-===// +//===-- include/flang/Parser/user-state.h -----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,10 +14,10 @@ // parse tree construction so as to avoid any need for representing // state in static data. -#include "flang/common/Fortran-features.h" -#include "flang/common/idioms.h" -#include "flang/parser/char-block.h" -#include "flang/parser/parse-tree.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/parse-tree.h" #include #include #include diff --git a/include/flang/semantics/attr.h b/include/flang/Semantics/attr.h similarity index 92% rename from include/flang/semantics/attr.h rename to include/flang/Semantics/attr.h index 475d6891d528..48fec0441cd3 100644 --- a/include/flang/semantics/attr.h +++ b/include/flang/Semantics/attr.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/attr.h --------------------------*- C++ -*-===// +//===-- include/flang/Semantics/attr.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_ATTR_H_ #define FORTRAN_SEMANTICS_ATTR_H_ -#include "flang/common/enum-set.h" -#include "flang/common/idioms.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/idioms.h" #include #include diff --git a/include/flang/semantics/expression.h b/include/flang/Semantics/expression.h similarity index 97% rename from include/flang/semantics/expression.h rename to include/flang/Semantics/expression.h index 79360fc0bed0..2e135b0885d0 100644 --- a/include/flang/semantics/expression.h +++ b/include/flang/Semantics/expression.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/expression.h --------------------*- C++ -*-===// +//===-- include/flang/Semantics/expression.h --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,17 +10,17 @@ #define FORTRAN_SEMANTICS_EXPRESSION_H_ #include "semantics.h" -#include "flang/common/Fortran.h" -#include "flang/common/indirection.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/check-expression.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" -#include "flang/parser/char-block.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/check-expression.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" #include #include #include diff --git a/include/flang/semantics/scope.h b/include/flang/Semantics/scope.h similarity index 97% rename from include/flang/semantics/scope.h rename to include/flang/Semantics/scope.h index 1de11bb53171..6f67ecbabf31 100644 --- a/include/flang/semantics/scope.h +++ b/include/flang/Semantics/scope.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/scope.h -------------------------*- C++ -*-===// +//===-- include/flang/Semantics/scope.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,11 +11,11 @@ #include "attr.h" #include "symbol.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/common/reference.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Common/reference.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/include/flang/semantics/semantics.h b/include/flang/Semantics/semantics.h similarity index 97% rename from include/flang/semantics/semantics.h rename to include/flang/Semantics/semantics.h index 0e64e42f742b..dd384583e549 100644 --- a/include/flang/semantics/semantics.h +++ b/include/flang/Semantics/semantics.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/semantics.h ---------------------*- C++ -*-===// +//===-- include/flang/Semantics/semantics.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,10 +11,10 @@ #include "scope.h" #include "symbol.h" -#include "flang/common/Fortran-features.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/parser/message.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Parser/message.h" #include #include #include diff --git a/include/flang/semantics/symbol.h b/include/flang/Semantics/symbol.h similarity index 99% rename from include/flang/semantics/symbol.h rename to include/flang/Semantics/symbol.h index d6ac63f79d25..80f702a98307 100644 --- a/include/flang/semantics/symbol.h +++ b/include/flang/Semantics/symbol.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/symbol.h ------------------------*- C++ -*-===// +//===-- include/flang/Semantics/symbol.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,9 +10,9 @@ #define FORTRAN_SEMANTICS_SYMBOL_H_ #include "type.h" -#include "flang/common/Fortran.h" -#include "flang/common/enum-set.h" -#include "flang/common/reference.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/reference.h" #include #include #include @@ -702,7 +702,7 @@ template class Symbols { }; // Define a few member functions here in the header so that they -// can be used by lib/evaluate without inducing a dependence cycle +// can be used by lib/Evaluate without inducing a dependence cycle // between the two shared libraries. inline bool ProcEntityDetails::HasExplicitInterface() const { diff --git a/include/flang/semantics/tools.h b/include/flang/Semantics/tools.h similarity index 98% rename from include/flang/semantics/tools.h rename to include/flang/Semantics/tools.h index 59e41700c211..69ca0e35dfe9 100644 --- a/include/flang/semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/tools.h -------------------------*- C++ -*-===// +//===-- include/flang/Semantics/tools.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,15 +12,15 @@ // Simple predicates and look-up functions that are best defined // canonically for use in semantic checking. -#include "flang/common/Fortran.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/type.h" -#include "flang/evaluate/variable.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/attr.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/semantics.h" +#include "flang/Common/Fortran.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/type.h" +#include "flang/Evaluate/variable.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/attr.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/semantics.h" #include namespace Fortran::semantics { diff --git a/include/flang/semantics/type.h b/include/flang/Semantics/type.h similarity index 98% rename from include/flang/semantics/type.h rename to include/flang/Semantics/type.h index bbd5f47f3a0d..935c8dbf7949 100644 --- a/include/flang/semantics/type.h +++ b/include/flang/Semantics/type.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/type.h --------------------------*- C++ -*-===// +//===-- include/flang/Semantics/type.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_TYPE_H_ #define FORTRAN_SEMANTICS_TYPE_H_ -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/expression.h" -#include "flang/parser/char-block.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/char-block.h" #include #include #include diff --git a/include/flang/semantics/unparse-with-symbols.h b/include/flang/Semantics/unparse-with-symbols.h similarity index 87% rename from include/flang/semantics/unparse-with-symbols.h rename to include/flang/Semantics/unparse-with-symbols.h index 8a6760f0094b..6553b7b34de0 100644 --- a/include/flang/semantics/unparse-with-symbols.h +++ b/include/flang/Semantics/unparse-with-symbols.h @@ -1,4 +1,4 @@ -//===-- include/flang/semantics/unparse-with-symbols.h ----------*- C++ -*-===// +//===-- include/flang/Semantics/unparse-with-symbols.h ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_ #define FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_ -#include "flang/parser/characters.h" +#include "flang/Parser/characters.h" #include namespace Fortran::parser { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 35c3e139b1bf..fae2eed92fa8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,9 +6,9 @@ # #===------------------------------------------------------------------------===# -add_subdirectory(common) -add_subdirectory(evaluate) -add_subdirectory(decimal) -add_subdirectory(lower) -add_subdirectory(parser) -add_subdirectory(semantics) +add_subdirectory(Common) +add_subdirectory(Evaluate) +add_subdirectory(Decimal) +add_subdirectory(Lower) +add_subdirectory(Parser) +add_subdirectory(Semantics) diff --git a/lib/common/CMakeLists.txt b/lib/Common/CMakeLists.txt similarity index 88% rename from lib/common/CMakeLists.txt rename to lib/Common/CMakeLists.txt index bfa8bf7953e0..acbe9d125b99 100644 --- a/lib/common/CMakeLists.txt +++ b/lib/Common/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- lib/common/CMakeLists.txt -------------------------------------------===# +#===-- lib/Common/CMakeLists.txt -------------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/common/Fortran-features.cpp b/lib/Common/Fortran-features.cpp similarity index 90% rename from lib/common/Fortran-features.cpp rename to lib/Common/Fortran-features.cpp index 62c3e505ea17..a9e03395cab2 100644 --- a/lib/common/Fortran-features.cpp +++ b/lib/Common/Fortran-features.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran-features.cpp -----------------------------------===// +//===-- lib/Common/Fortran-features.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "flang/common/Fortran-features.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" namespace Fortran::common { diff --git a/lib/common/Fortran.cpp b/lib/Common/Fortran.cpp similarity index 93% rename from lib/common/Fortran.cpp rename to lib/Common/Fortran.cpp index 61ff0ee2f6e9..d65a17e6a7b7 100644 --- a/lib/common/Fortran.cpp +++ b/lib/Common/Fortran.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/Fortran.cpp --------------------------------------------===// +//===-- lib/Common/Fortran.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/common/Fortran.h" +#include "flang/Common/Fortran.h" namespace Fortran::common { diff --git a/lib/common/default-kinds.cpp b/lib/Common/default-kinds.cpp similarity index 93% rename from lib/common/default-kinds.cpp rename to lib/Common/default-kinds.cpp index 490f4dc1f156..8470cfc6d137 100644 --- a/lib/common/default-kinds.cpp +++ b/lib/Common/default-kinds.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/default-kinds.cpp --------------------------------------===// +//===-- lib/Common/default-kinds.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/common/default-kinds.h" -#include "flang/common/idioms.h" +#include "flang/Common/default-kinds.h" +#include "flang/Common/idioms.h" namespace Fortran::common { diff --git a/lib/common/idioms.cpp b/lib/Common/idioms.cpp similarity index 92% rename from lib/common/idioms.cpp rename to lib/Common/idioms.cpp index d28d76fe83b8..229e1c062d8c 100644 --- a/lib/common/idioms.cpp +++ b/lib/Common/idioms.cpp @@ -1,4 +1,4 @@ -//===-- lib/common/idioms.cpp ---------------------------------------------===// +//===-- lib/Common/idioms.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/common/idioms.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/lib/decimal/CMakeLists.txt b/lib/Decimal/CMakeLists.txt similarity index 88% rename from lib/decimal/CMakeLists.txt rename to lib/Decimal/CMakeLists.txt index 28c9c828ac3a..54542187aea4 100644 --- a/lib/decimal/CMakeLists.txt +++ b/lib/Decimal/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- lib/decimal/CMakeLists.txt ------------------------------------------===# +#===-- lib/Decimal/CMakeLists.txt ------------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/decimal/big-radix-floating-point.h b/lib/Decimal/big-radix-floating-point.h similarity index 96% rename from lib/decimal/big-radix-floating-point.h rename to lib/Decimal/big-radix-floating-point.h index 35f0a2e8c31f..4af217a52b42 100644 --- a/lib/decimal/big-radix-floating-point.h +++ b/lib/Decimal/big-radix-floating-point.h @@ -1,4 +1,4 @@ -//===-- lib/decimal/big-radix-floating-point.h ------------------*- C++ -*-===// +//===-- lib/Decimal/big-radix-floating-point.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -21,12 +21,12 @@ // for conversions between binary and decimal representations; it is not // a general-purpose facility. -#include "flang/common/bit-population-count.h" -#include "flang/common/leading-zero-bit-count.h" -#include "flang/common/uint128.h" -#include "flang/common/unsigned-const-division.h" -#include "flang/decimal/binary-floating-point.h" -#include "flang/decimal/decimal.h" +#include "flang/Common/bit-population-count.h" +#include "flang/Common/leading-zero-bit-count.h" +#include "flang/Common/uint128.h" +#include "flang/Common/unsigned-const-division.h" +#include "flang/Decimal/binary-floating-point.h" +#include "flang/Decimal/decimal.h" #include #include #include diff --git a/lib/decimal/binary-to-decimal.cpp b/lib/Decimal/binary-to-decimal.cpp similarity index 99% rename from lib/decimal/binary-to-decimal.cpp rename to lib/Decimal/binary-to-decimal.cpp index f4b644045648..416d85097dda 100644 --- a/lib/decimal/binary-to-decimal.cpp +++ b/lib/Decimal/binary-to-decimal.cpp @@ -1,4 +1,4 @@ -//===-- lib/decimal/binary-to-decimal.cpp ---------------------------------===// +//===-- lib/Decimal/binary-to-decimal.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "big-radix-floating-point.h" -#include "flang/decimal/decimal.h" +#include "flang/Decimal/decimal.h" namespace Fortran::decimal { diff --git a/lib/decimal/decimal-to-binary.cpp b/lib/Decimal/decimal-to-binary.cpp similarity index 98% rename from lib/decimal/decimal-to-binary.cpp rename to lib/Decimal/decimal-to-binary.cpp index f71ca941e8a7..7deec9f101b2 100644 --- a/lib/decimal/decimal-to-binary.cpp +++ b/lib/Decimal/decimal-to-binary.cpp @@ -1,4 +1,4 @@ -//===-- lib/decimal/decimal-to-binary.cpp ---------------------------------===// +//===-- lib/Decimal/decimal-to-binary.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "big-radix-floating-point.h" -#include "flang/common/bit-population-count.h" -#include "flang/common/leading-zero-bit-count.h" -#include "flang/decimal/binary-floating-point.h" -#include "flang/decimal/decimal.h" +#include "flang/Common/bit-population-count.h" +#include "flang/Common/leading-zero-bit-count.h" +#include "flang/Decimal/binary-floating-point.h" +#include "flang/Decimal/decimal.h" #include #include #include diff --git a/lib/evaluate/CMakeLists.txt b/lib/Evaluate/CMakeLists.txt similarity index 95% rename from lib/evaluate/CMakeLists.txt rename to lib/Evaluate/CMakeLists.txt index 8714819c49a8..8da75eddff69 100644 --- a/lib/evaluate/CMakeLists.txt +++ b/lib/Evaluate/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- lib/evaluate/CMakeLists.txt -----------------------------------------===# +#===-- lib/Evaluate/CMakeLists.txt -----------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/call.cpp b/lib/Evaluate/call.cpp similarity index 95% rename from lib/evaluate/call.cpp rename to lib/Evaluate/call.cpp index d1272af63484..52c16f2e3f3b 100644 --- a/lib/evaluate/call.cpp +++ b/lib/Evaluate/call.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/call.cpp ---------------------------------------------===// +//===-- lib/Evaluate/call.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/call.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/tools.h" -#include "flang/semantics/symbol.h" +#include "flang/Evaluate/call.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/tools.h" +#include "flang/Semantics/symbol.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/character.h b/lib/Evaluate/character.h similarity index 97% rename from lib/evaluate/character.h rename to lib/Evaluate/character.h index be1303eacca8..ef43a2b3afbb 100644 --- a/lib/evaluate/character.h +++ b/lib/Evaluate/character.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/character.h --------------------------------*- C++ -*-===// +//===-- lib/Evaluate/character.h --------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_EVALUATE_CHARACTER_H_ #define FORTRAN_EVALUATE_CHARACTER_H_ -#include "flang/evaluate/type.h" +#include "flang/Evaluate/type.h" #include // Provides implementations of intrinsic functions operating on character diff --git a/lib/evaluate/characteristics.cpp b/lib/Evaluate/characteristics.cpp similarity index 98% rename from lib/evaluate/characteristics.cpp rename to lib/Evaluate/characteristics.cpp index 3197c46863fa..dffcf3232936 100644 --- a/lib/evaluate/characteristics.cpp +++ b/lib/Evaluate/characteristics.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/characteristics.cpp ----------------------------------===// +//===-- lib/Evaluate/characteristics.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/characteristics.h" -#include "flang/common/indirection.h" -#include "flang/evaluate/check-expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" -#include "flang/parser/message.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/check-expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" #include #include diff --git a/lib/evaluate/check-expression.cpp b/lib/Evaluate/check-expression.cpp similarity index 98% rename from lib/evaluate/check-expression.cpp rename to lib/Evaluate/check-expression.cpp index b809211dbdb4..fede3aec6e9a 100644 --- a/lib/evaluate/check-expression.cpp +++ b/lib/Evaluate/check-expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/check-expression.cpp ---------------------------------===// +//===-- lib/Evaluate/check-expression.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/check-expression.h" -#include "flang/evaluate/traverse.h" -#include "flang/evaluate/type.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Evaluate/check-expression.h" +#include "flang/Evaluate/traverse.h" +#include "flang/Evaluate/type.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/common.cpp b/lib/Evaluate/common.cpp similarity index 92% rename from lib/evaluate/common.cpp rename to lib/Evaluate/common.cpp index 9c45e668767d..92f7403918ee 100644 --- a/lib/evaluate/common.cpp +++ b/lib/Evaluate/common.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/common.cpp -------------------------------------------===// +//===-- lib/Evaluate/common.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/common.h" -#include "flang/common/idioms.h" +#include "flang/Evaluate/common.h" +#include "flang/Common/idioms.h" using namespace Fortran::parser::literals; diff --git a/lib/evaluate/complex.cpp b/lib/Evaluate/complex.cpp similarity index 97% rename from lib/evaluate/complex.cpp rename to lib/Evaluate/complex.cpp index a2dca42e4e0b..8f03304b6b05 100644 --- a/lib/evaluate/complex.cpp +++ b/lib/Evaluate/complex.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/complex.cpp ------------------------------------------===// +//===-- lib/Evaluate/complex.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/complex.h" +#include "flang/Evaluate/complex.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/constant.cpp b/lib/Evaluate/constant.cpp similarity index 97% rename from lib/evaluate/constant.cpp rename to lib/Evaluate/constant.cpp index 65da1378f10f..0d6a780b9879 100644 --- a/lib/evaluate/constant.cpp +++ b/lib/Evaluate/constant.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/constant.cpp -----------------------------------------===// +//===-- lib/Evaluate/constant.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/constant.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/shape.h" -#include "flang/evaluate/type.h" +#include "flang/Evaluate/constant.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/shape.h" +#include "flang/Evaluate/type.h" #include namespace Fortran::evaluate { diff --git a/lib/evaluate/expression.cpp b/lib/Evaluate/expression.cpp similarity index 96% rename from lib/evaluate/expression.cpp rename to lib/Evaluate/expression.cpp index c80599670f9e..a390c9e4d1b2 100644 --- a/lib/evaluate/expression.cpp +++ b/lib/Evaluate/expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/expression.cpp ---------------------------------------===// +//===-- lib/Evaluate/expression.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/expression.h" +#include "flang/Evaluate/expression.h" #include "int-power.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/variable.h" -#include "flang/parser/message.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/variable.h" +#include "flang/Parser/message.h" #include #include diff --git a/lib/evaluate/fold-character.cpp b/lib/Evaluate/fold-character.cpp similarity index 98% rename from lib/evaluate/fold-character.cpp rename to lib/Evaluate/fold-character.cpp index 0c99b96df71b..7398a1b56d0f 100644 --- a/lib/evaluate/fold-character.cpp +++ b/lib/Evaluate/fold-character.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-character.cpp -----------------------------------===// +//===-- lib/Evaluate/fold-character.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-complex.cpp b/lib/Evaluate/fold-complex.cpp similarity index 98% rename from lib/evaluate/fold-complex.cpp rename to lib/Evaluate/fold-complex.cpp index d4c5f00873a4..f2006daf3b56 100644 --- a/lib/evaluate/fold-complex.cpp +++ b/lib/Evaluate/fold-complex.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-complex.cpp -------------------------------------===// +//===-- lib/Evaluate/fold-complex.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-implementation.h b/lib/Evaluate/fold-implementation.h similarity index 98% rename from lib/evaluate/fold-implementation.h rename to lib/Evaluate/fold-implementation.h index fdb85dfb0a7f..67e541e1204a 100644 --- a/lib/evaluate/fold-implementation.h +++ b/lib/Evaluate/fold-implementation.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-implementation.h --------------------------------===// +//===-- lib/Evaluate/fold-implementation.h --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,23 +13,23 @@ #include "host.h" #include "int-power.h" #include "intrinsics-library-templates.h" -#include "flang/common/indirection.h" -#include "flang/common/template.h" -#include "flang/common/unwrap.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/constant.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/formatting.h" -#include "flang/evaluate/shape.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/traverse.h" -#include "flang/evaluate/type.h" -#include "flang/parser/message.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Common/indirection.h" +#include "flang/Common/template.h" +#include "flang/Common/unwrap.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/constant.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/formatting.h" +#include "flang/Evaluate/shape.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/traverse.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include #include diff --git a/lib/evaluate/fold-integer.cpp b/lib/Evaluate/fold-integer.cpp similarity index 99% rename from lib/evaluate/fold-integer.cpp rename to lib/Evaluate/fold-integer.cpp index cda0a39e616f..1e1e06a28681 100644 --- a/lib/evaluate/fold-integer.cpp +++ b/lib/Evaluate/fold-integer.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-integer.cpp -------------------------------------===// +//===-- lib/Evaluate/fold-integer.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold-logical.cpp b/lib/Evaluate/fold-logical.cpp similarity index 98% rename from lib/evaluate/fold-logical.cpp rename to lib/Evaluate/fold-logical.cpp index cf1bb9a1374c..ebffbb20c6f3 100644 --- a/lib/evaluate/fold-logical.cpp +++ b/lib/Evaluate/fold-logical.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-logical.cpp -------------------------------------===// +//===-- lib/Evaluate/fold-logical.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "fold-implementation.h" -#include "flang/evaluate/check-expression.h" +#include "flang/Evaluate/check-expression.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/fold-real.cpp b/lib/Evaluate/fold-real.cpp similarity index 99% rename from lib/evaluate/fold-real.cpp rename to lib/Evaluate/fold-real.cpp index b1d3ed33ca44..1a0d1d2cfd45 100644 --- a/lib/evaluate/fold-real.cpp +++ b/lib/Evaluate/fold-real.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold-real.cpp ----------------------------------------===// +//===-- lib/Evaluate/fold-real.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/fold.cpp b/lib/Evaluate/fold.cpp similarity index 98% rename from lib/evaluate/fold.cpp rename to lib/Evaluate/fold.cpp index 4a7263f1ffce..3ec4a2ed213d 100644 --- a/lib/evaluate/fold.cpp +++ b/lib/Evaluate/fold.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/fold.cpp ---------------------------------------------===// +//===-- lib/Evaluate/fold.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/fold.h" +#include "flang/Evaluate/fold.h" #include "fold-implementation.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/formatting.cpp b/lib/Evaluate/formatting.cpp similarity index 98% rename from lib/evaluate/formatting.cpp rename to lib/Evaluate/formatting.cpp index 5bc627748675..0b397c92590b 100644 --- a/lib/evaluate/formatting.cpp +++ b/lib/Evaluate/formatting.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/formatting.cpp ---------------------------------------===// +//===-- lib/Evaluate/formatting.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,14 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/formatting.h" -#include "flang/evaluate/call.h" -#include "flang/evaluate/constant.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/characters.h" -#include "flang/semantics/symbol.h" +#include "flang/Evaluate/formatting.h" +#include "flang/Evaluate/call.h" +#include "flang/Evaluate/constant.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/characters.h" +#include "flang/Semantics/symbol.h" #include namespace Fortran::evaluate { diff --git a/lib/evaluate/host.cpp b/lib/Evaluate/host.cpp similarity index 97% rename from lib/evaluate/host.cpp rename to lib/Evaluate/host.cpp index 47685e08b762..c9f48789e218 100644 --- a/lib/evaluate/host.cpp +++ b/lib/Evaluate/host.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/host.cpp ---------------------------------------------===// +//===-- lib/Evaluate/host.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,7 +8,7 @@ #include "host.h" -#include "flang/common/idioms.h" +#include "flang/Common/idioms.h" #include #include diff --git a/lib/evaluate/host.h b/lib/Evaluate/host.h similarity index 98% rename from lib/evaluate/host.h rename to lib/Evaluate/host.h index f2d8c1dc37b4..cb7d5806f0cf 100644 --- a/lib/evaluate/host.h +++ b/lib/Evaluate/host.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/host.h -------------------------------------*- C++ -*-===// +//===-- lib/Evaluate/host.h -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,7 +17,7 @@ // hardware type maps to Fortran intrinsic type T. Then HostType can be used // to safely refer to this hardware type. -#include "flang/evaluate/type.h" +#include "flang/Evaluate/type.h" #include #include #include diff --git a/lib/evaluate/int-power.h b/lib/Evaluate/int-power.h similarity index 95% rename from lib/evaluate/int-power.h rename to lib/Evaluate/int-power.h index 6a6fe831a7c4..5ab16cd6d209 100644 --- a/lib/evaluate/int-power.h +++ b/lib/Evaluate/int-power.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/int-power.h --------------------------------*- C++ -*-===// +//===-- lib/Evaluate/int-power.h --------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,7 +11,7 @@ // Computes an integer power of a real or complex value. -#include "flang/evaluate/common.h" +#include "flang/Evaluate/common.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/integer.cpp b/lib/Evaluate/integer.cpp similarity index 92% rename from lib/evaluate/integer.cpp rename to lib/Evaluate/integer.cpp index 06503e6f42b8..074bee7b9fb8 100644 --- a/lib/evaluate/integer.cpp +++ b/lib/Evaluate/integer.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/integer.cpp ------------------------------------------===// +//===-- lib/Evaluate/integer.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/integer.h" +#include "flang/Evaluate/integer.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/intrinsics-library-templates.h b/lib/Evaluate/intrinsics-library-templates.h similarity index 97% rename from lib/evaluate/intrinsics-library-templates.h rename to lib/Evaluate/intrinsics-library-templates.h index 65d3de7e05f9..268a50718992 100644 --- a/lib/evaluate/intrinsics-library-templates.h +++ b/lib/Evaluate/intrinsics-library-templates.h @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics-library-templates.h -------------*- C++ -*-===// +//===-- lib/Evaluate/intrinsics-library-templates.h -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,9 +17,9 @@ // which version should be instantiated in a generic way. #include "host.h" -#include "flang/common/template.h" -#include "flang/evaluate/intrinsics-library.h" -#include "flang/evaluate/type.h" +#include "flang/Common/template.h" +#include "flang/Evaluate/intrinsics-library.h" +#include "flang/Evaluate/type.h" #include #include diff --git a/lib/evaluate/intrinsics-library.cpp b/lib/Evaluate/intrinsics-library.cpp similarity index 99% rename from lib/evaluate/intrinsics-library.cpp rename to lib/Evaluate/intrinsics-library.cpp index bfddcb366b8f..9636179d01e4 100644 --- a/lib/evaluate/intrinsics-library.cpp +++ b/lib/Evaluate/intrinsics-library.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics-library.cpp -------------------------------===// +//===-- lib/Evaluate/intrinsics-library.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/evaluate/intrinsics.cpp b/lib/Evaluate/intrinsics.cpp similarity index 99% rename from lib/evaluate/intrinsics.cpp rename to lib/Evaluate/intrinsics.cpp index 93b2f864ae41..b5eacb9415ff 100644 --- a/lib/evaluate/intrinsics.cpp +++ b/lib/Evaluate/intrinsics.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/intrinsics.cpp ---------------------------------------===// +//===-- lib/Evaluate/intrinsics.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/intrinsics.h" -#include "flang/common/Fortran.h" -#include "flang/common/enum-set.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/shape.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/shape.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" #include #include #include diff --git a/lib/evaluate/logical.cpp b/lib/Evaluate/logical.cpp similarity index 82% rename from lib/evaluate/logical.cpp rename to lib/Evaluate/logical.cpp index 8bd3a3b7524a..c29ed1df33d3 100644 --- a/lib/evaluate/logical.cpp +++ b/lib/Evaluate/logical.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/logical.cpp ------------------------------------------===// +//===-- lib/Evaluate/logical.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/logical.h" +#include "flang/Evaluate/logical.h" namespace Fortran::evaluate::value { diff --git a/lib/evaluate/real.cpp b/lib/Evaluate/real.cpp similarity index 98% rename from lib/evaluate/real.cpp rename to lib/Evaluate/real.cpp index 29ad1e0aa5a3..6f9a17c6262e 100644 --- a/lib/evaluate/real.cpp +++ b/lib/Evaluate/real.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/real.cpp ---------------------------------------------===// +//===-- lib/Evaluate/real.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/real.h" +#include "flang/Evaluate/real.h" #include "int-power.h" -#include "flang/common/idioms.h" -#include "flang/decimal/decimal.h" -#include "flang/parser/characters.h" +#include "flang/Common/idioms.h" +#include "flang/Decimal/decimal.h" +#include "flang/Parser/characters.h" #include namespace Fortran::evaluate::value { diff --git a/lib/evaluate/shape.cpp b/lib/Evaluate/shape.cpp similarity index 98% rename from lib/evaluate/shape.cpp rename to lib/Evaluate/shape.cpp index ea14c4b244ad..c523f304f84b 100644 --- a/lib/evaluate/shape.cpp +++ b/lib/Evaluate/shape.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/shape.cpp --------------------------------------------===// +//===-- lib/Evaluate/shape.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/shape.h" -#include "flang/common/idioms.h" -#include "flang/common/template.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" -#include "flang/parser/message.h" -#include "flang/semantics/symbol.h" +#include "flang/Evaluate/shape.h" +#include "flang/Common/idioms.h" +#include "flang/Common/template.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/symbol.h" #include using namespace std::placeholders; // _1, _2, &c. for std::bind() diff --git a/lib/evaluate/static-data.cpp b/lib/Evaluate/static-data.cpp similarity index 95% rename from lib/evaluate/static-data.cpp rename to lib/Evaluate/static-data.cpp index 668fcb47191f..8de4f3119684 100644 --- a/lib/evaluate/static-data.cpp +++ b/lib/Evaluate/static-data.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/static-data.cpp --------------------------------------===// +//===-- lib/Evaluate/static-data.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/static-data.h" -#include "flang/parser/characters.h" +#include "flang/Evaluate/static-data.h" +#include "flang/Parser/characters.h" namespace Fortran::evaluate { diff --git a/lib/evaluate/tools.cpp b/lib/Evaluate/tools.cpp similarity index 99% rename from lib/evaluate/tools.cpp rename to lib/Evaluate/tools.cpp index f082c496aee5..cfa675c1bf81 100644 --- a/lib/evaluate/tools.cpp +++ b/lib/Evaluate/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/tools.cpp --------------------------------------------===// +//===-- lib/Evaluate/tools.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/tools.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/traverse.h" -#include "flang/parser/message.h" +#include "flang/Evaluate/tools.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/traverse.h" +#include "flang/Parser/message.h" #include #include diff --git a/lib/evaluate/type.cpp b/lib/Evaluate/type.cpp similarity index 97% rename from lib/evaluate/type.cpp rename to lib/Evaluate/type.cpp index 11f800349ca6..79f40d0481aa 100644 --- a/lib/evaluate/type.cpp +++ b/lib/Evaluate/type.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/type.cpp ---------------------------------------------===// +//===-- lib/Evaluate/type.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/type.h" -#include "flang/common/idioms.h" -#include "flang/common/template.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/parser/characters.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Evaluate/type.h" +#include "flang/Common/idioms.h" +#include "flang/Common/template.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Parser/characters.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" #include #include #include diff --git a/lib/evaluate/variable.cpp b/lib/Evaluate/variable.cpp similarity index 98% rename from lib/evaluate/variable.cpp rename to lib/Evaluate/variable.cpp index f10b0a6ee2f2..2ed759057820 100644 --- a/lib/evaluate/variable.cpp +++ b/lib/Evaluate/variable.cpp @@ -1,4 +1,4 @@ -//===-- lib/evaluate/variable.cpp -----------------------------------------===// +//===-- lib/Evaluate/variable.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,14 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "flang/evaluate/variable.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/char-block.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" -#include "flang/semantics/symbol.h" +#include "flang/Evaluate/variable.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/symbol.h" #include #include diff --git a/lib/fir/.clang-format b/lib/Fir/.clang-format similarity index 100% rename from lib/fir/.clang-format rename to lib/Fir/.clang-format diff --git a/lib/lower/.clang-format b/lib/Lower/.clang-format similarity index 100% rename from lib/lower/.clang-format rename to lib/Lower/.clang-format diff --git a/lib/lower/CMakeLists.txt b/lib/Lower/CMakeLists.txt similarity index 100% rename from lib/lower/CMakeLists.txt rename to lib/Lower/CMakeLists.txt diff --git a/lib/lower/PFTBuilder.cpp b/lib/Lower/PFTBuilder.cpp similarity index 99% rename from lib/lower/PFTBuilder.cpp rename to lib/Lower/PFTBuilder.cpp index d7998e259f78..5941b570b216 100644 --- a/lib/lower/PFTBuilder.cpp +++ b/lib/Lower/PFTBuilder.cpp @@ -1,4 +1,4 @@ -//===-- lib/lower/PFTBuilder.cc -------------------------------------------===// +//===-- lib/Lower/PFTBuilder.cc -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "flang/lower/PFTBuilder.h" -#include "flang/parser/dump-parse-tree.h" -#include "flang/parser/parse-tree-visitor.h" +#include "flang/Lower/PFTBuilder.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/parse-tree-visitor.h" #include "llvm/ADT/DenseMap.h" #include #include diff --git a/lib/optimizer/.clang-format b/lib/Optimizer/.clang-format similarity index 100% rename from lib/optimizer/.clang-format rename to lib/Optimizer/.clang-format diff --git a/lib/parser/CMakeLists.txt b/lib/Parser/CMakeLists.txt similarity index 93% rename from lib/parser/CMakeLists.txt rename to lib/Parser/CMakeLists.txt index 72ced8cc9e45..5f7ba14b0cc8 100644 --- a/lib/parser/CMakeLists.txt +++ b/lib/Parser/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- lib/parser/CMakeLists.txt -------------------------------------------===# +#===-- lib/Parser/CMakeLists.txt -------------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/Fortran-parsers.cpp b/lib/Parser/Fortran-parsers.cpp similarity index 99% rename from lib/parser/Fortran-parsers.cpp rename to lib/Parser/Fortran-parsers.cpp index d15901a74ea7..1bf5393b9b42 100644 --- a/lib/parser/Fortran-parsers.cpp +++ b/lib/Parser/Fortran-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/Fortran-parsers.cpp ------------------------------------===// +//===-- lib/Parser/Fortran-parsers.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -36,8 +36,8 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/user-state.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/user-state.h" namespace Fortran::parser { diff --git a/lib/parser/basic-parsers.h b/lib/Parser/basic-parsers.h similarity index 98% rename from lib/parser/basic-parsers.h rename to lib/Parser/basic-parsers.h index 31491a238363..10df4a71bcf9 100644 --- a/lib/parser/basic-parsers.h +++ b/lib/Parser/basic-parsers.h @@ -1,4 +1,4 @@ -//===-- lib/parser/basic-parsers.h ------------------------------*- C++ -*-===// +//===-- lib/Parser/basic-parsers.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -22,14 +22,14 @@ // This header defines the fundamental parser class templates and helper // template functions. See parser-combinators.txt for documentation. -#include "flang/common/Fortran-features.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/parser/char-block.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-state.h" -#include "flang/parser/provenance.h" -#include "flang/parser/user-state.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-state.h" +#include "flang/Parser/provenance.h" +#include "flang/Parser/user-state.h" #include #include #include diff --git a/lib/parser/char-block.cpp b/lib/Parser/char-block.cpp similarity index 81% rename from lib/parser/char-block.cpp rename to lib/Parser/char-block.cpp index a3cb60a9b13f..b68be8a146ef 100644 --- a/lib/parser/char-block.cpp +++ b/lib/Parser/char-block.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-block.cpp -------------------------------*- C++ -*-===// +//===-- lib/Parser/char-block.cpp -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //----------------------------------------------------------------------------// -#include "flang/parser/char-block.h" +#include "flang/Parser/char-block.h" #include namespace Fortran::parser { diff --git a/lib/parser/char-buffer.cpp b/lib/Parser/char-buffer.cpp similarity index 94% rename from lib/parser/char-buffer.cpp rename to lib/Parser/char-buffer.cpp index 3de83ec87b5a..655dd3173d7f 100644 --- a/lib/parser/char-buffer.cpp +++ b/lib/Parser/char-buffer.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-buffer.cpp ----------------------------------------===// +//===-- lib/Parser/char-buffer.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/char-buffer.h" -#include "flang/common/idioms.h" +#include "flang/Parser/char-buffer.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/lib/parser/char-set.cpp b/lib/Parser/char-set.cpp similarity index 85% rename from lib/parser/char-set.cpp rename to lib/Parser/char-set.cpp index 1390f5ca35f6..382cc4f430d5 100644 --- a/lib/parser/char-set.cpp +++ b/lib/Parser/char-set.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/char-set.cpp -------------------------------------------===// +//===-- lib/Parser/char-set.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/char-set.h" +#include "flang/Parser/char-set.h" namespace Fortran::parser { diff --git a/lib/parser/characters.cpp b/lib/Parser/characters.cpp similarity index 98% rename from lib/parser/characters.cpp rename to lib/Parser/characters.cpp index f4703565a7d4..62df4013332c 100644 --- a/lib/parser/characters.cpp +++ b/lib/Parser/characters.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/characters.cpp -----------------------------------------===// +//===-- lib/Parser/characters.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/characters.h" -#include "flang/common/idioms.h" +#include "flang/Parser/characters.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/lib/parser/debug-parser.cpp b/lib/Parser/debug-parser.cpp similarity index 88% rename from lib/parser/debug-parser.cpp rename to lib/Parser/debug-parser.cpp index 4957ce1dcbd5..b0db22ff143c 100644 --- a/lib/parser/debug-parser.cpp +++ b/lib/Parser/debug-parser.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/debug-parser.cpp ---------------------------------------===// +//===-- lib/Parser/debug-parser.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "debug-parser.h" -#include "flang/parser/user-state.h" +#include "flang/Parser/user-state.h" #include #include diff --git a/lib/parser/debug-parser.h b/lib/Parser/debug-parser.h similarity index 91% rename from lib/parser/debug-parser.h rename to lib/Parser/debug-parser.h index dbb812c9cec7..fb9f208cd2fd 100644 --- a/lib/parser/debug-parser.h +++ b/lib/Parser/debug-parser.h @@ -1,4 +1,4 @@ -//===-- lib/parser/debug-parser.h -------------------------------*- C++ -*-===// +//===-- lib/Parser/debug-parser.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,7 +14,7 @@ // flow of the parsers. Not to be used in production. #include "basic-parsers.h" -#include "flang/parser/parse-state.h" +#include "flang/Parser/parse-state.h" #include #include diff --git a/lib/parser/executable-parsers.cpp b/lib/Parser/executable-parsers.cpp similarity index 99% rename from lib/parser/executable-parsers.cpp rename to lib/Parser/executable-parsers.cpp index 72408f1da2f5..df1e7ba59d44 100644 --- a/lib/parser/executable-parsers.cpp +++ b/lib/Parser/executable-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/executable-parsers.cpp ---------------------------------===// +//===-- lib/Parser/executable-parsers.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,8 +15,8 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/characters.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/expr-parsers.cpp b/lib/Parser/expr-parsers.cpp similarity index 99% rename from lib/parser/expr-parsers.cpp rename to lib/Parser/expr-parsers.cpp index 11e94fc0da2d..79370881759c 100644 --- a/lib/parser/expr-parsers.cpp +++ b/lib/Parser/expr-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/expr-parsers.cpp ---------------------------------------===// +//===-- lib/Parser/expr-parsers.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,8 +15,8 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/characters.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/expr-parsers.h b/lib/Parser/expr-parsers.h similarity index 97% rename from lib/parser/expr-parsers.h rename to lib/Parser/expr-parsers.h index fcaeedad3994..7106352943f9 100644 --- a/lib/parser/expr-parsers.h +++ b/lib/Parser/expr-parsers.h @@ -1,4 +1,4 @@ -//===-- lib/parser/expr-parsers.h -------------------------------*- C++ -*-===// +//===-- lib/Parser/expr-parsers.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,7 +12,7 @@ #include "basic-parsers.h" #include "token-parsers.h" #include "type-parsers.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/instrumented-parser.cpp b/lib/Parser/instrumented-parser.cpp similarity index 92% rename from lib/parser/instrumented-parser.cpp rename to lib/Parser/instrumented-parser.cpp index fc5e14867032..47ff042b168d 100644 --- a/lib/parser/instrumented-parser.cpp +++ b/lib/Parser/instrumented-parser.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/instrumented-parser.cpp --------------------------------===// +//===-- lib/Parser/instrumented-parser.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/instrumented-parser.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" +#include "flang/Parser/instrumented-parser.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" #include #include diff --git a/lib/parser/io-parsers.cpp b/lib/Parser/io-parsers.cpp similarity index 99% rename from lib/parser/io-parsers.cpp rename to lib/Parser/io-parsers.cpp index a9ccbb75566d..48b383bfd80a 100644 --- a/lib/parser/io-parsers.cpp +++ b/lib/Parser/io-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/io-parsers.cpp -----------------------------------------===// +//===-- lib/Parser/io-parsers.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,8 +15,8 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/characters.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { // R1201 io-unit -> file-unit-number | * | internal-file-variable diff --git a/lib/parser/message.cpp b/lib/Parser/message.cpp similarity index 98% rename from lib/parser/message.cpp rename to lib/Parser/message.cpp index 2f5655eb1e48..feb93a76c53e 100644 --- a/lib/parser/message.cpp +++ b/lib/Parser/message.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/message.cpp --------------------------------------------===// +//===-- lib/Parser/message.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/message.h" -#include "flang/common/idioms.h" -#include "flang/parser/char-set.h" +#include "flang/Parser/message.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/char-set.h" #include #include #include diff --git a/lib/parser/misc-parsers.h b/lib/Parser/misc-parsers.h similarity index 92% rename from lib/parser/misc-parsers.h rename to lib/Parser/misc-parsers.h index 1a7c641557a5..ca98e691ad82 100644 --- a/lib/parser/misc-parsers.h +++ b/lib/Parser/misc-parsers.h @@ -1,4 +1,4 @@ -//===-- lib/parser/misc-parsers.h -------------------------------*- C++ -*-===// +//===-- lib/Parser/misc-parsers.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,8 +15,8 @@ #include "basic-parsers.h" #include "token-parsers.h" #include "type-parsers.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/openmp-parsers.cpp b/lib/Parser/openmp-parsers.cpp similarity index 99% rename from lib/parser/openmp-parsers.cpp rename to lib/Parser/openmp-parsers.cpp index fd1b96191b24..407b5125e2b5 100644 --- a/lib/parser/openmp-parsers.cpp +++ b/lib/Parser/openmp-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/openmp-parsers.cpp -------------------------------------===// +//===-- lib/Parser/openmp-parsers.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,7 +15,7 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/parse-tree.h" // OpenMP Directives and Clauses namespace Fortran::parser { diff --git a/lib/parser/parse-tree.cpp b/lib/Parser/parse-tree.cpp similarity index 97% rename from lib/parser/parse-tree.cpp rename to lib/Parser/parse-tree.cpp index c412214b7432..d0722aaa571c 100644 --- a/lib/parser/parse-tree.cpp +++ b/lib/Parser/parse-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/parse-tree.cpp -----------------------------------------===// +//===-- lib/Parser/parse-tree.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/parse-tree.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/parser/user-state.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/user-state.h" #include // So "delete Expr;" calls an external destructor for its typedExpr. diff --git a/lib/parser/parsing.cpp b/lib/Parser/parsing.cpp similarity index 95% rename from lib/parser/parsing.cpp rename to lib/Parser/parsing.cpp index ab39c2b1f25c..9b77b9f0ada5 100644 --- a/lib/parser/parsing.cpp +++ b/lib/Parser/parsing.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/parsing.cpp --------------------------------------------===// +//===-- lib/Parser/parsing.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/parsing.h" +#include "flang/Parser/parsing.h" #include "preprocessor.h" #include "prescan.h" #include "type-parsers.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" -#include "flang/parser/source.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" +#include "flang/Parser/source.h" #include namespace Fortran::parser { diff --git a/lib/parser/preprocessor.cpp b/lib/Parser/preprocessor.cpp similarity index 99% rename from lib/parser/preprocessor.cpp rename to lib/Parser/preprocessor.cpp index cd5cee714af8..f825f561e32b 100644 --- a/lib/parser/preprocessor.cpp +++ b/lib/Parser/preprocessor.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/preprocessor.cpp ---------------------------------------===// +//===-- lib/Parser/preprocessor.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,9 +8,9 @@ #include "preprocessor.h" #include "prescan.h" -#include "flang/common/idioms.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" #include #include #include diff --git a/lib/parser/preprocessor.h b/lib/Parser/preprocessor.h similarity index 95% rename from lib/parser/preprocessor.h rename to lib/Parser/preprocessor.h index 523c880316c2..9b1b019b8c29 100644 --- a/lib/parser/preprocessor.h +++ b/lib/Parser/preprocessor.h @@ -1,4 +1,4 @@ -//===-- lib/parser/preprocessor.h -------------------------------*- C++ -*-===// +//===-- lib/Parser/preprocessor.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,8 +16,8 @@ // extensions for preprocessing will not be necessary. #include "token-sequence.h" -#include "flang/parser/char-block.h" -#include "flang/parser/provenance.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/lib/parser/prescan.cpp b/lib/Parser/prescan.cpp similarity index 99% rename from lib/parser/prescan.cpp rename to lib/Parser/prescan.cpp index 679f136305cb..c28fc1535e38 100644 --- a/lib/parser/prescan.cpp +++ b/lib/Parser/prescan.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/prescan.cpp --------------------------------------------===// +//===-- lib/Parser/prescan.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #include "prescan.h" #include "preprocessor.h" #include "token-sequence.h" -#include "flang/common/idioms.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" -#include "flang/parser/source.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Parser/source.h" #include #include #include diff --git a/lib/parser/prescan.h b/lib/Parser/prescan.h similarity index 97% rename from lib/parser/prescan.h rename to lib/Parser/prescan.h index 1b13cb7feac5..94098048f23e 100644 --- a/lib/parser/prescan.h +++ b/lib/Parser/prescan.h @@ -1,4 +1,4 @@ -//===-- lib/parser/prescan.h ------------------------------------*- C++ -*-===// +//===-- lib/Parser/prescan.h ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -17,10 +17,10 @@ // inclusion, and driving the Fortran source preprocessor. #include "token-sequence.h" -#include "flang/common/Fortran-features.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" -#include "flang/parser/provenance.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/lib/parser/program-parsers.cpp b/lib/Parser/program-parsers.cpp similarity index 99% rename from lib/parser/program-parsers.cpp rename to lib/Parser/program-parsers.cpp index f0e4e69498b1..36a9d39d4512 100644 --- a/lib/parser/program-parsers.cpp +++ b/lib/Parser/program-parsers.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/program-parsers.cpp ------------------------------------===// +//===-- lib/Parser/program-parsers.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,8 +15,8 @@ #include "stmt-parser.h" #include "token-parsers.h" #include "type-parser-implementation.h" -#include "flang/parser/characters.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/parse-tree.h" namespace Fortran::parser { diff --git a/lib/parser/provenance.cpp b/lib/Parser/provenance.cpp similarity index 99% rename from lib/parser/provenance.cpp rename to lib/Parser/provenance.cpp index 391e650e1b30..db61e4e44f2e 100644 --- a/lib/parser/provenance.cpp +++ b/lib/Parser/provenance.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/provenance.cpp -----------------------------------------===// +//===-- lib/Parser/provenance.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/provenance.h" -#include "flang/common/idioms.h" +#include "flang/Parser/provenance.h" +#include "flang/Common/idioms.h" #include #include diff --git a/lib/parser/source.cpp b/lib/Parser/source.cpp similarity index 97% rename from lib/parser/source.cpp rename to lib/Parser/source.cpp index e6635e9f10d5..0fbb8e3df1b1 100644 --- a/lib/parser/source.cpp +++ b/lib/Parser/source.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/source.cpp ---------------------------------------------===// +//===-- lib/Parser/source.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/source.h" -#include "flang/common/idioms.h" -#include "flang/parser/char-buffer.h" +#include "flang/Parser/source.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/char-buffer.h" #include #include #include diff --git a/lib/parser/stmt-parser.h b/lib/Parser/stmt-parser.h similarity index 98% rename from lib/parser/stmt-parser.h rename to lib/Parser/stmt-parser.h index 1a290fdecd0a..ee26e50db119 100644 --- a/lib/parser/stmt-parser.h +++ b/lib/Parser/stmt-parser.h @@ -1,4 +1,4 @@ -//===-- lib/parser/stmt-parser.h --------------------------------*- C++ -*-===// +//===-- lib/Parser/stmt-parser.h --------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/token-parsers.h b/lib/Parser/token-parsers.h similarity index 98% rename from lib/parser/token-parsers.h rename to lib/Parser/token-parsers.h index d36d78906954..c34b9785b3fd 100644 --- a/lib/parser/token-parsers.h +++ b/lib/Parser/token-parsers.h @@ -1,4 +1,4 @@ -//===-- lib/parser/token-parsers.h ------------------------------*- C++ -*-===// +//===-- lib/Parser/token-parsers.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,11 +14,11 @@ #include "basic-parsers.h" #include "type-parsers.h" -#include "flang/common/idioms.h" -#include "flang/parser/char-set.h" -#include "flang/parser/characters.h" -#include "flang/parser/instrumented-parser.h" -#include "flang/parser/provenance.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/char-set.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/instrumented-parser.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/lib/parser/token-sequence.cpp b/lib/Parser/token-sequence.cpp similarity index 98% rename from lib/parser/token-sequence.cpp rename to lib/Parser/token-sequence.cpp index 3f984e1f3838..23aa450ceb41 100644 --- a/lib/parser/token-sequence.cpp +++ b/lib/Parser/token-sequence.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/token-sequence.cpp -------------------------------------===// +//===-- lib/Parser/token-sequence.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "token-sequence.h" -#include "flang/parser/characters.h" +#include "flang/Parser/characters.h" namespace Fortran::parser { diff --git a/lib/parser/token-sequence.h b/lib/Parser/token-sequence.h similarity index 96% rename from lib/parser/token-sequence.h rename to lib/Parser/token-sequence.h index e4b7dce8b6ad..d0ef0750e2fe 100644 --- a/lib/parser/token-sequence.h +++ b/lib/Parser/token-sequence.h @@ -1,4 +1,4 @@ -//===-- lib/parser/token-sequence.h -----------------------------*- C++ -*-===// +//===-- lib/Parser/token-sequence.h -----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,8 +13,8 @@ // and a partitioning thereof into preprocessing tokens, along with their // associated provenances. -#include "flang/parser/char-block.h" -#include "flang/parser/provenance.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/lib/parser/tools.cpp b/lib/Parser/tools.cpp similarity index 96% rename from lib/parser/tools.cpp rename to lib/Parser/tools.cpp index 522bd3afc642..7fb73791b32a 100644 --- a/lib/parser/tools.cpp +++ b/lib/Parser/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/tools.cpp ----------------------------------------------===// +//===-- lib/Parser/tools.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/tools.h" +#include "flang/Parser/tools.h" namespace Fortran::parser { diff --git a/lib/parser/type-parser-implementation.h b/lib/Parser/type-parser-implementation.h similarity index 94% rename from lib/parser/type-parser-implementation.h rename to lib/Parser/type-parser-implementation.h index 995c36d95b1b..1bea618f81b0 100644 --- a/lib/parser/type-parser-implementation.h +++ b/lib/Parser/type-parser-implementation.h @@ -1,4 +1,4 @@ -//===-- lib/parser/type-parser-implementation.h -----------------*- C++ -*-===// +//===-- lib/Parser/type-parser-implementation.h -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/parser/type-parsers.h b/lib/Parser/type-parsers.h similarity index 97% rename from lib/parser/type-parsers.h rename to lib/Parser/type-parsers.h index 82273a374274..40e36a8c5a0c 100644 --- a/lib/parser/type-parsers.h +++ b/lib/Parser/type-parsers.h @@ -1,4 +1,4 @@ -//===-- lib/parser/type-parsers.h -------------------------------*- C++ -*-===// +//===-- lib/Parser/type-parsers.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_PARSER_TYPE_PARSERS_H_ #define FORTRAN_PARSER_TYPE_PARSERS_H_ -#include "flang/parser/instrumented-parser.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/instrumented-parser.h" +#include "flang/Parser/parse-tree.h" #include namespace Fortran::parser { diff --git a/lib/parser/unparse.cpp b/lib/Parser/unparse.cpp similarity index 99% rename from lib/parser/unparse.cpp rename to lib/Parser/unparse.cpp index 82d52960f5f2..45bb54fd58fa 100644 --- a/lib/parser/unparse.cpp +++ b/lib/Parser/unparse.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/unparse.cpp --------------------------------------------===// +//===-- lib/Parser/unparse.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,13 +9,13 @@ // Generates Fortran from the content of a parse tree, using the // traversal templates in parse-tree-visitor.h. -#include "flang/parser/unparse.h" -#include "flang/common/Fortran.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/parser/characters.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" +#include "flang/Parser/unparse.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" #include #include #include diff --git a/lib/parser/user-state.cpp b/lib/Parser/user-state.cpp similarity index 94% rename from lib/parser/user-state.cpp rename to lib/Parser/user-state.cpp index bd84463d9ab0..1140d40214eb 100644 --- a/lib/parser/user-state.cpp +++ b/lib/Parser/user-state.cpp @@ -1,4 +1,4 @@ -//===-- lib/parser/user-state.cpp -----------------------------------------===// +//===-- lib/Parser/user-state.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/user-state.h" +#include "flang/Parser/user-state.h" #include "stmt-parser.h" #include "type-parsers.h" -#include "flang/parser/parse-state.h" +#include "flang/Parser/parse-state.h" #include namespace Fortran::parser { diff --git a/lib/semantics/CMakeLists.txt b/lib/Semantics/CMakeLists.txt similarity index 94% rename from lib/semantics/CMakeLists.txt rename to lib/Semantics/CMakeLists.txt index 5f2e3c686fc4..7900eeff9da1 100644 --- a/lib/semantics/CMakeLists.txt +++ b/lib/Semantics/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- lib/semantics/CMakeLists.txt ----------------------------------------===# +#===-- lib/Semantics/CMakeLists.txt ----------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/assignment.cpp b/lib/Semantics/assignment.cpp similarity index 95% rename from lib/semantics/assignment.cpp rename to lib/Semantics/assignment.cpp index b286f6578168..dfa11e800804 100644 --- a/lib/semantics/assignment.cpp +++ b/lib/Semantics/assignment.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/assignment.cpp --------------------------------------===// +//===-- lib/Semantics/assignment.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,18 +8,18 @@ #include "assignment.h" #include "pointer-assignment.h" -#include "flang/common/idioms.h" -#include "flang/common/restorer.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Common/idioms.h" +#include "flang/Common/restorer.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include #include diff --git a/lib/semantics/assignment.h b/lib/Semantics/assignment.h similarity index 90% rename from lib/semantics/assignment.h rename to lib/Semantics/assignment.h index 51b7c1736635..ad18577ac883 100644 --- a/lib/semantics/assignment.h +++ b/lib/Semantics/assignment.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/assignment.h ------------------------------*- C++ -*-===// +//===-- lib/Semantics/assignment.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,9 +9,9 @@ #ifndef FORTRAN_SEMANTICS_ASSIGNMENT_H_ #define FORTRAN_SEMANTICS_ASSIGNMENT_H_ -#include "flang/common/indirection.h" -#include "flang/evaluate/expression.h" -#include "flang/semantics/semantics.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/expression.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { class ContextualMessages; diff --git a/lib/semantics/attr.cpp b/lib/Semantics/attr.cpp similarity index 90% rename from lib/semantics/attr.cpp rename to lib/Semantics/attr.cpp index 65623442a2d6..341fe5e67f89 100644 --- a/lib/semantics/attr.cpp +++ b/lib/Semantics/attr.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/attr.cpp --------------------------------------------===// +//===-- lib/Semantics/attr.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/attr.h" -#include "flang/common/idioms.h" +#include "flang/Semantics/attr.h" +#include "flang/Common/idioms.h" #include #include diff --git a/lib/semantics/canonicalize-do.cpp b/lib/Semantics/canonicalize-do.cpp similarity index 98% rename from lib/semantics/canonicalize-do.cpp rename to lib/Semantics/canonicalize-do.cpp index 45353c97c1f8..b50c0ff974af 100644 --- a/lib/semantics/canonicalize-do.cpp +++ b/lib/Semantics/canonicalize-do.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-do.cpp ---------------------------------===// +//===-- lib/Semantics/canonicalize-do.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "canonicalize-do.h" -#include "flang/parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree-visitor.h" namespace Fortran::parser { diff --git a/lib/semantics/canonicalize-do.h b/lib/Semantics/canonicalize-do.h similarity index 91% rename from lib/semantics/canonicalize-do.h rename to lib/Semantics/canonicalize-do.h index 4b7c18a5a789..9de5b52c5ed8 100644 --- a/lib/semantics/canonicalize-do.h +++ b/lib/Semantics/canonicalize-do.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-do.h -------------------------*- C++ -*-===// +//===-- lib/Semantics/canonicalize-do.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/canonicalize-omp.cpp b/lib/Semantics/canonicalize-omp.cpp similarity index 97% rename from lib/semantics/canonicalize-omp.cpp rename to lib/Semantics/canonicalize-omp.cpp index 1af2f3be6277..9588b037aef0 100644 --- a/lib/semantics/canonicalize-omp.cpp +++ b/lib/Semantics/canonicalize-omp.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-omp.cpp --------------------------------===// +//===-- lib/Semantics/canonicalize-omp.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "canonicalize-omp.h" -#include "flang/parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree-visitor.h" // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP // Constructs more structured which provide explicit scopes for later diff --git a/lib/semantics/canonicalize-omp.h b/lib/Semantics/canonicalize-omp.h similarity index 90% rename from lib/semantics/canonicalize-omp.h rename to lib/Semantics/canonicalize-omp.h index f1f2cfeb9101..6531851a6675 100644 --- a/lib/semantics/canonicalize-omp.h +++ b/lib/Semantics/canonicalize-omp.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/canonicalize-omp.h ------------------------*- C++ -*-===// +//===-- lib/Semantics/canonicalize-omp.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-allocate.cpp b/lib/Semantics/check-allocate.cpp similarity index 98% rename from lib/semantics/check-allocate.cpp rename to lib/Semantics/check-allocate.cpp index 32ceb3805729..fdba6151d343 100644 --- a/lib/semantics/check-allocate.cpp +++ b/lib/Semantics/check-allocate.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-allocate.cpp ----------------------------------===// +//===-- lib/Semantics/check-allocate.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,14 +8,14 @@ #include "check-allocate.h" #include "assignment.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/type.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/tools.h" -#include "flang/semantics/attr.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/attr.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-allocate.h b/lib/Semantics/check-allocate.h similarity index 87% rename from lib/semantics/check-allocate.h rename to lib/Semantics/check-allocate.h index 2d495b13746c..2d7405d3e21d 100644 --- a/lib/semantics/check-allocate.h +++ b/lib/Semantics/check-allocate.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-allocate.h --------------------------*- C++ -*-===// +//===-- lib/Semantics/check-allocate.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_ALLOCATE_H_ #define FORTRAN_SEMANTICS_CHECK_ALLOCATE_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct AllocateStmt; diff --git a/lib/semantics/check-arithmeticif.cpp b/lib/Semantics/check-arithmeticif.cpp similarity index 90% rename from lib/semantics/check-arithmeticif.cpp rename to lib/Semantics/check-arithmeticif.cpp index d0b08a3e062a..efa711319333 100644 --- a/lib/semantics/check-arithmeticif.cpp +++ b/lib/Semantics/check-arithmeticif.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-arithmeticif.cpp ------------------------------===// +//===-- lib/Semantics/check-arithmeticif.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "check-arithmeticif.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-arithmeticif.h b/lib/Semantics/check-arithmeticif.h similarity index 88% rename from lib/semantics/check-arithmeticif.h rename to lib/Semantics/check-arithmeticif.h index 32e2b354cf22..f1b50e10d7d4 100644 --- a/lib/semantics/check-arithmeticif.h +++ b/lib/Semantics/check-arithmeticif.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-arithmeticif.h ----------------------*- C++ -*-===// +//===-- lib/Semantics/check-arithmeticif.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_ARITHMETICIF_STMT_H_ #define FORTRAN_SEMANTICS_CHECK_ARITHMETICIF_STMT_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct ArithmeticIfStmt; diff --git a/lib/semantics/check-call.cpp b/lib/Semantics/check-call.cpp similarity index 98% rename from lib/semantics/check-call.cpp rename to lib/Semantics/check-call.cpp index f03f30e14d5e..bca65e9909b7 100644 --- a/lib/semantics/check-call.cpp +++ b/lib/Semantics/check-call.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-call.cpp --------------------------------------===// +//===-- lib/Semantics/check-call.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,14 +8,14 @@ #include "check-call.h" #include "pointer-assignment.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/check-expression.h" -#include "flang/evaluate/shape.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/characters.h" -#include "flang/parser/message.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/tools.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/check-expression.h" +#include "flang/Evaluate/shape.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/tools.h" #include #include diff --git a/lib/semantics/check-call.h b/lib/Semantics/check-call.h similarity index 94% rename from lib/semantics/check-call.h rename to lib/Semantics/check-call.h index 68c5b53b078a..08bed15cb7ec 100644 --- a/lib/semantics/check-call.h +++ b/lib/Semantics/check-call.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-call.h ------------------------------*- C++ -*-===// +//===-- lib/Semantics/check-call.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,7 +11,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_CALL_H_ #define FORTRAN_SEMANTICS_CHECK_CALL_H_ -#include "flang/evaluate/call.h" +#include "flang/Evaluate/call.h" namespace Fortran::parser { class Messages; diff --git a/lib/semantics/check-coarray.cpp b/lib/Semantics/check-coarray.cpp similarity index 93% rename from lib/semantics/check-coarray.cpp rename to lib/Semantics/check-coarray.cpp index 0e43314de40d..f070ebc61695 100644 --- a/lib/semantics/check-coarray.cpp +++ b/lib/Semantics/check-coarray.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-coarray.cpp -----------------------------------===// +//===-- lib/Semantics/check-coarray.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,13 +7,13 @@ //===----------------------------------------------------------------------===// #include "check-coarray.h" -#include "flang/common/indirection.h" -#include "flang/evaluate/expression.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/tools.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/tools.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-coarray.h b/lib/Semantics/check-coarray.h similarity index 92% rename from lib/semantics/check-coarray.h rename to lib/Semantics/check-coarray.h index fe4176c1d943..dd34f0ae3641 100644 --- a/lib/semantics/check-coarray.h +++ b/lib/Semantics/check-coarray.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-coarray.h ---------------------------*- C++ -*-===// +//===-- lib/Semantics/check-coarray.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_COARRAY_H_ #define FORTRAN_SEMANTICS_CHECK_COARRAY_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" #include namespace Fortran::parser { diff --git a/lib/semantics/check-data.cpp b/lib/Semantics/check-data.cpp similarity index 96% rename from lib/semantics/check-data.cpp rename to lib/Semantics/check-data.cpp index e831bf7e3d53..e7d599eeebe5 100644 --- a/lib/semantics/check-data.cpp +++ b/lib/Semantics/check-data.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-data.cpp --------------------------------------===// +//===-- lib/Semantics/check-data.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-data.h b/lib/Semantics/check-data.h similarity index 77% rename from lib/semantics/check-data.h rename to lib/Semantics/check-data.h index b2e96519a346..80b8edc7f713 100644 --- a/lib/semantics/check-data.h +++ b/lib/Semantics/check-data.h @@ -1,4 +1,4 @@ -//===-------lib/semantics/check-data.h ------------------------------------===// +//===-------lib/Semantics/check-data.h ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_CHECK_DATA_H_ #define FORTRAN_SEMANTICS_CHECK_DATA_H_ -#include "flang/parser/parse-tree.h" -#include "flang/parser/tools.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { class DataChecker : public virtual BaseChecker { diff --git a/lib/semantics/check-deallocate.cpp b/lib/Semantics/check-deallocate.cpp similarity index 92% rename from lib/semantics/check-deallocate.cpp rename to lib/Semantics/check-deallocate.cpp index 6a0ea45ebb43..646251427db8 100644 --- a/lib/semantics/check-deallocate.cpp +++ b/lib/Semantics/check-deallocate.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-deallocate.cpp --------------------------------===// +//===-- lib/Semantics/check-deallocate.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "check-deallocate.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-deallocate.h b/lib/Semantics/check-deallocate.h similarity index 88% rename from lib/semantics/check-deallocate.h rename to lib/Semantics/check-deallocate.h index 6855055bd65d..f47283a22c29 100644 --- a/lib/semantics/check-deallocate.h +++ b/lib/Semantics/check-deallocate.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-deallocate.h ------------------------*- C++ -*-===// +//===-- lib/Semantics/check-deallocate.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_DEALLOCATE_H_ #define FORTRAN_SEMANTICS_CHECK_DEALLOCATE_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct DeallocateStmt; diff --git a/lib/semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp similarity index 99% rename from lib/semantics/check-declarations.cpp rename to lib/Semantics/check-declarations.cpp index 7c27fcba91d1..4e463319accc 100644 --- a/lib/semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-declarations.cpp ------------------------------===// +//===-- lib/Semantics/check-declarations.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,14 +9,14 @@ // Static declaration checking #include "check-declarations.h" -#include "flang/evaluate/check-expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Evaluate/check-expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-declarations.h b/lib/Semantics/check-declarations.h similarity index 89% rename from lib/semantics/check-declarations.h rename to lib/Semantics/check-declarations.h index 19d3072d2c5f..98cea3249d4a 100644 --- a/lib/semantics/check-declarations.h +++ b/lib/Semantics/check-declarations.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-declarations.h ----------------------*- C++ -*-===// +//===-- lib/Semantics/check-declarations.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/check-do-forall.cpp b/lib/Semantics/check-do-forall.cpp similarity index 98% rename from lib/semantics/check-do-forall.cpp rename to lib/Semantics/check-do-forall.cpp index 071a873d72d2..615e1005e36b 100644 --- a/lib/semantics/check-do-forall.cpp +++ b/lib/Semantics/check-do-forall.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-do-forall.cpp ---------------------------------===// +//===-- lib/Semantics/check-do-forall.cpp ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,19 +7,19 @@ //===----------------------------------------------------------------------===// #include "check-do-forall.h" -#include "flang/common/template.h" -#include "flang/evaluate/call.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/tools.h" -#include "flang/semantics/attr.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Common/template.h" +#include "flang/Evaluate/call.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/attr.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" namespace Fortran::evaluate { using ActualArgumentRef = common::Reference; diff --git a/lib/semantics/check-do-forall.h b/lib/Semantics/check-do-forall.h similarity index 94% rename from lib/semantics/check-do-forall.h rename to lib/Semantics/check-do-forall.h index 1ba9b6b81060..f820926d9199 100644 --- a/lib/semantics/check-do-forall.h +++ b/lib/Semantics/check-do-forall.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-do-forall.h -------------------------*- C++ -*-===// +//===-- lib/Semantics/check-do-forall.h -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_CHECK_DO_FORALL_H_ #define FORTRAN_SEMANTICS_CHECK_DO_FORALL_H_ -#include "flang/common/idioms.h" -#include "flang/semantics/semantics.h" +#include "flang/Common/idioms.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct AssignmentStmt; diff --git a/lib/semantics/check-if-stmt.cpp b/lib/Semantics/check-if-stmt.cpp similarity index 82% rename from lib/semantics/check-if-stmt.cpp rename to lib/Semantics/check-if-stmt.cpp index 589caf2abb7e..b9693389f5d9 100644 --- a/lib/semantics/check-if-stmt.cpp +++ b/lib/Semantics/check-if-stmt.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-if-stmt.cpp -----------------------------------===// +//===-- lib/Semantics/check-if-stmt.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "check-if-stmt.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-if-stmt.h b/lib/Semantics/check-if-stmt.h similarity index 87% rename from lib/semantics/check-if-stmt.h rename to lib/Semantics/check-if-stmt.h index 01aac0ec5cf1..21ae269d2049 100644 --- a/lib/semantics/check-if-stmt.h +++ b/lib/Semantics/check-if-stmt.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-if-stmt.h ---------------------------*- C++ -*-===// +//===-- lib/Semantics/check-if-stmt.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_IF_STMT_H_ #define FORTRAN_SEMANTICS_CHECK_IF_STMT_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct IfStmt; diff --git a/lib/semantics/check-io.cpp b/lib/Semantics/check-io.cpp similarity index 99% rename from lib/semantics/check-io.cpp rename to lib/Semantics/check-io.cpp index abbf915b675f..a3de44ef8dd0 100644 --- a/lib/semantics/check-io.cpp +++ b/lib/Semantics/check-io.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-io.cpp ----------------------------------------===// +//===-- lib/Semantics/check-io.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "check-io.h" -#include "flang/common/format.h" -#include "flang/parser/tools.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/tools.h" +#include "flang/Common/format.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-io.h b/lib/Semantics/check-io.h similarity index 96% rename from lib/semantics/check-io.h rename to lib/Semantics/check-io.h index 96315d5d865e..c43f62b1ae03 100644 --- a/lib/semantics/check-io.h +++ b/lib/Semantics/check-io.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-io.h --------------------------------*- C++ -*-===// +//===-- lib/Semantics/check-io.h --------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,10 @@ #ifndef FORTRAN_SEMANTICS_CHECK_IO_H_ #define FORTRAN_SEMANTICS_CHECK_IO_H_ -#include "flang/common/enum-set.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Common/enum-set.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-nullify.cpp b/lib/Semantics/check-nullify.cpp similarity index 91% rename from lib/semantics/check-nullify.cpp rename to lib/Semantics/check-nullify.cpp index 06a551ffe656..c7595f476d39 100644 --- a/lib/semantics/check-nullify.cpp +++ b/lib/Semantics/check-nullify.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-nullify.cpp -----------------------------------===// +//===-- lib/Semantics/check-nullify.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,11 +8,11 @@ #include "check-nullify.h" #include "assignment.h" -#include "flang/evaluate/expression.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/tools.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-nullify.h b/lib/Semantics/check-nullify.h similarity index 87% rename from lib/semantics/check-nullify.h rename to lib/Semantics/check-nullify.h index f06fc662c8d0..508631a9649b 100644 --- a/lib/semantics/check-nullify.h +++ b/lib/Semantics/check-nullify.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-nullify.h ---------------------------*- C++ -*-===// +//===-- lib/Semantics/check-nullify.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_NULLIFY_H_ #define FORTRAN_SEMANTICS_CHECK_NULLIFY_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct NullifyStmt; diff --git a/lib/semantics/check-omp-structure.cpp b/lib/Semantics/check-omp-structure.cpp similarity index 99% rename from lib/semantics/check-omp-structure.cpp rename to lib/Semantics/check-omp-structure.cpp index c8493c05b22c..0e122856985f 100644 --- a/lib/semantics/check-omp-structure.cpp +++ b/lib/Semantics/check-omp-structure.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-omp-structure.cpp -----------------------------===// +//===-- lib/Semantics/check-omp-structure.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "check-omp-structure.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-omp-structure.h b/lib/Semantics/check-omp-structure.h similarity index 98% rename from lib/semantics/check-omp-structure.h rename to lib/Semantics/check-omp-structure.h index e265b6bda909..67709e38b5a8 100644 --- a/lib/semantics/check-omp-structure.h +++ b/lib/Semantics/check-omp-structure.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-omp-structure.h ---------------------*- C++ -*-===// +//===-- lib/Semantics/check-omp-structure.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,9 +14,9 @@ #ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ #define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ -#include "flang/common/enum-set.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/semantics.h" +#include "flang/Common/enum-set.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-purity.cpp b/lib/Semantics/check-purity.cpp similarity index 94% rename from lib/semantics/check-purity.cpp rename to lib/Semantics/check-purity.cpp index 986a22ad0080..9ca56fc04fa1 100644 --- a/lib/semantics/check-purity.cpp +++ b/lib/Semantics/check-purity.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-purity.cpp ------------------------------------===// +//===-- lib/Semantics/check-purity.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "check-purity.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/tools.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { void PurityChecker::Enter(const parser::ExecutableConstruct &exec) { diff --git a/lib/semantics/check-purity.h b/lib/Semantics/check-purity.h similarity index 92% rename from lib/semantics/check-purity.h rename to lib/Semantics/check-purity.h index 189f72ca0396..b4c8272de634 100644 --- a/lib/semantics/check-purity.h +++ b/lib/Semantics/check-purity.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-purity.h ----------------------------*- C++ -*-===// +//===-- lib/Semantics/check-purity.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,7 +8,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_PURITY_H_ #define FORTRAN_SEMANTICS_CHECK_PURITY_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" #include namespace Fortran::parser { struct ExecutableConstruct; diff --git a/lib/semantics/check-return.cpp b/lib/Semantics/check-return.cpp similarity index 86% rename from lib/semantics/check-return.cpp rename to lib/Semantics/check-return.cpp index 0fb3b6159aa8..0e0dd2c3e66f 100644 --- a/lib/semantics/check-return.cpp +++ b/lib/Semantics/check-return.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-return.cpp ------------------------------------===// +//===-- lib/Semantics/check-return.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "check-return.h" -#include "flang/common/Fortran-features.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" namespace Fortran::semantics { diff --git a/lib/semantics/check-return.h b/lib/Semantics/check-return.h similarity index 87% rename from lib/semantics/check-return.h rename to lib/Semantics/check-return.h index b2f4f0655e82..edaf90b4f641 100644 --- a/lib/semantics/check-return.h +++ b/lib/Semantics/check-return.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-return.h ----------------------------*- C++ -*-===// +//===-- lib/Semantics/check-return.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_RETURN_H_ #define FORTRAN_SEMANTICS_CHECK_RETURN_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct ReturnStmt; diff --git a/lib/semantics/check-stop.cpp b/lib/Semantics/check-stop.cpp similarity index 84% rename from lib/semantics/check-stop.cpp rename to lib/Semantics/check-stop.cpp index 105f0df27b4d..08487a5c07af 100644 --- a/lib/semantics/check-stop.cpp +++ b/lib/Semantics/check-stop.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-stop.cpp --------------------------------------===// +//===-- lib/Semantics/check-stop.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "check-stop.h" -#include "flang/common/Fortran.h" -#include "flang/evaluate/expression.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Common/Fortran.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/check-stop.h b/lib/Semantics/check-stop.h similarity index 88% rename from lib/semantics/check-stop.h rename to lib/Semantics/check-stop.h index 3daf7da12110..ed50830edc4a 100644 --- a/lib/semantics/check-stop.h +++ b/lib/Semantics/check-stop.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/check-stop.h ------------------------------*- C++ -*-===// +//===-- lib/Semantics/check-stop.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_CHECK_STOP_H_ #define FORTRAN_SEMANTICS_CHECK_STOP_H_ -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" namespace Fortran::parser { struct StopStmt; diff --git a/lib/semantics/expression.cpp b/lib/Semantics/expression.cpp similarity index 99% rename from lib/semantics/expression.cpp rename to lib/Semantics/expression.cpp index 65e9f7ecdeb3..a41e754d5ade 100644 --- a/lib/semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/expression.cpp --------------------------------------===// +//===-- lib/Semantics/expression.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,21 +6,21 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/expression.h" +#include "flang/Semantics/expression.h" #include "check-call.h" #include "pointer-assignment.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/characters.h" -#include "flang/parser/dump-parse-tree.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include #include @@ -32,7 +32,7 @@ using MaybeExpr = std::optional>; // Much of the code that implements semantic analysis of expressions is -// tightly coupled with their typed representations in lib/evaluate, +// tightly coupled with their typed representations in lib/Evaluate, // and appears here in namespace Fortran::evaluate for convenience. namespace Fortran::evaluate { diff --git a/lib/semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp similarity index 99% rename from lib/semantics/mod-file.cpp rename to lib/Semantics/mod-file.cpp index 1f89c56e9870..53f72b4a99a0 100644 --- a/lib/semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/mod-file.cpp ----------------------------------------===// +//===-- lib/Semantics/mod-file.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,13 +8,13 @@ #include "mod-file.h" #include "resolve-names.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/message.h" -#include "flang/parser/parsing.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parsing.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include #include diff --git a/lib/semantics/mod-file.h b/lib/Semantics/mod-file.h similarity index 95% rename from lib/semantics/mod-file.h rename to lib/Semantics/mod-file.h index ba6bae0d014d..1bc356ad1bc1 100644 --- a/lib/semantics/mod-file.h +++ b/lib/Semantics/mod-file.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/mod-file.h --------------------------------*- C++ -*-===// +//===-- lib/Semantics/mod-file.h --------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,7 +9,7 @@ #ifndef FORTRAN_SEMANTICS_MOD_FILE_H_ #define FORTRAN_SEMANTICS_MOD_FILE_H_ -#include "flang/semantics/attr.h" +#include "flang/Semantics/attr.h" #include #include diff --git a/lib/semantics/pointer-assignment.cpp b/lib/Semantics/pointer-assignment.cpp similarity index 97% rename from lib/semantics/pointer-assignment.cpp rename to lib/Semantics/pointer-assignment.cpp index d3b3ec7f76ad..b59dc8169aff 100644 --- a/lib/semantics/pointer-assignment.cpp +++ b/lib/Semantics/pointer-assignment.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/pointer-assignment.cpp ------------------------------===// +//===-- lib/Semantics/pointer-assignment.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,18 +7,18 @@ //===----------------------------------------------------------------------===// #include "pointer-assignment.h" -#include "flang/common/idioms.h" -#include "flang/common/restorer.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Common/idioms.h" +#include "flang/Common/restorer.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include #include diff --git a/lib/semantics/pointer-assignment.h b/lib/Semantics/pointer-assignment.h similarity index 85% rename from lib/semantics/pointer-assignment.h rename to lib/Semantics/pointer-assignment.h index a9efc59994ac..31131a356e4a 100644 --- a/lib/semantics/pointer-assignment.h +++ b/lib/Semantics/pointer-assignment.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/pointer-assignment.h --------------------------------===// +//===-- lib/Semantics/pointer-assignment.h --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,9 +9,9 @@ #ifndef FORTRAN_SEMANTICS_POINTER_ASSIGNMENT_H_ #define FORTRAN_SEMANTICS_POINTER_ASSIGNMENT_H_ -#include "flang/evaluate/expression.h" -#include "flang/parser/char-block.h" -#include "flang/semantics/type.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/char-block.h" +#include "flang/Semantics/type.h" #include namespace Fortran::evaluate::characteristics { diff --git a/lib/semantics/program-tree.cpp b/lib/Semantics/program-tree.cpp similarity index 97% rename from lib/semantics/program-tree.cpp rename to lib/Semantics/program-tree.cpp index 74381e18cf42..9325079655d2 100644 --- a/lib/semantics/program-tree.cpp +++ b/lib/Semantics/program-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/program-tree.cpp ------------------------------------===// +//===-- lib/Semantics/program-tree.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "program-tree.h" -#include "flang/common/idioms.h" -#include "flang/parser/char-block.h" -#include "flang/semantics/scope.h" +#include "flang/Common/idioms.h" +#include "flang/Parser/char-block.h" +#include "flang/Semantics/scope.h" namespace Fortran::semantics { diff --git a/lib/semantics/program-tree.h b/lib/Semantics/program-tree.h similarity index 96% rename from lib/semantics/program-tree.h rename to lib/Semantics/program-tree.h index 88a274999361..84e33ba7738d 100644 --- a/lib/semantics/program-tree.h +++ b/lib/Semantics/program-tree.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/program-tree.h ----------------------------*- C++ -*-===// +//===-- lib/Semantics/program-tree.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +9,8 @@ #ifndef FORTRAN_SEMANTICS_PROGRAM_TREE_H_ #define FORTRAN_SEMANTICS_PROGRAM_TREE_H_ -#include "flang/parser/parse-tree.h" -#include "flang/semantics/symbol.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/symbol.h" #include // A ProgramTree represents a tree of program units and their contained diff --git a/lib/semantics/resolve-labels.cpp b/lib/Semantics/resolve-labels.cpp similarity index 99% rename from lib/semantics/resolve-labels.cpp rename to lib/Semantics/resolve-labels.cpp index 723762706a85..d17352b9a440 100644 --- a/lib/semantics/resolve-labels.cpp +++ b/lib/Semantics/resolve-labels.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-labels.cpp ----------------------------------===// +//===-- lib/Semantics/resolve-labels.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "resolve-labels.h" -#include "flang/common/enum-set.h" -#include "flang/common/template.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/semantics/semantics.h" +#include "flang/Common/enum-set.h" +#include "flang/Common/template.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Semantics/semantics.h" #include #include #include diff --git a/lib/semantics/resolve-labels.h b/lib/Semantics/resolve-labels.h similarity index 92% rename from lib/semantics/resolve-labels.h rename to lib/Semantics/resolve-labels.h index 9cb8a9a51a8c..6538a00cf18d 100644 --- a/lib/semantics/resolve-labels.h +++ b/lib/Semantics/resolve-labels.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-labels.h --------------------------*- C++ -*-===// +//===-- lib/Semantics/resolve-labels.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/resolve-names-utils.cpp b/lib/Semantics/resolve-names-utils.cpp similarity index 98% rename from lib/semantics/resolve-names-utils.cpp rename to lib/Semantics/resolve-names-utils.cpp index 3b1a0b6cd824..afc0aac5a315 100644 --- a/lib/semantics/resolve-names-utils.cpp +++ b/lib/Semantics/resolve-names-utils.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names-utils.cpp -----------------------------===// +//===-- lib/Semantics/resolve-names-utils.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,17 +7,17 @@ //===----------------------------------------------------------------------===// #include "resolve-names-utils.h" -#include "flang/common/Fortran-features.h" -#include "flang/common/idioms.h" -#include "flang/common/indirection.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" -#include "flang/parser/char-block.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/idioms.h" +#include "flang/Common/indirection.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/char-block.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" #include #include #include diff --git a/lib/semantics/resolve-names-utils.h b/lib/Semantics/resolve-names-utils.h similarity index 95% rename from lib/semantics/resolve-names-utils.h rename to lib/Semantics/resolve-names-utils.h index b0748c263016..055e47db1253 100644 --- a/lib/semantics/resolve-names-utils.h +++ b/lib/Semantics/resolve-names-utils.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names-utils.h ---------------------*- C++ -*-===// +//===-- lib/Semantics/resolve-names-utils.h ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,10 +11,10 @@ // Utility functions and class for use in resolve-names.cpp. -#include "flang/parser/message.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/type.h" +#include "flang/Parser/message.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/type.h" #include namespace Fortran::parser { diff --git a/lib/semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp similarity index 99% rename from lib/semantics/resolve-names.cpp rename to lib/Semantics/resolve-names.cpp index 3c322b694014..6b9402e7ae8b 100644 --- a/lib/semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names.cpp -----------------------------------===// +//===-- lib/Semantics/resolve-names.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -13,26 +13,26 @@ #include "program-tree.h" #include "resolve-names-utils.h" #include "rewrite-parse-tree.h" -#include "flang/common/Fortran.h" -#include "flang/common/default-kinds.h" -#include "flang/common/indirection.h" -#include "flang/common/restorer.h" -#include "flang/evaluate/characteristics.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/evaluate/tools.h" -#include "flang/evaluate/type.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/tools.h" -#include "flang/semantics/attr.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/default-kinds.h" +#include "flang/Common/indirection.h" +#include "flang/Common/restorer.h" +#include "flang/Evaluate/characteristics.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Evaluate/tools.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/attr.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" #include #include #include diff --git a/lib/semantics/resolve-names.h b/lib/Semantics/resolve-names.h similarity index 91% rename from lib/semantics/resolve-names.h rename to lib/Semantics/resolve-names.h index da4907ebd434..8f233adc5ec3 100644 --- a/lib/semantics/resolve-names.h +++ b/lib/Semantics/resolve-names.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/resolve-names.h ---------------------------*- C++ -*-===// +//===-- lib/Semantics/resolve-names.h ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/rewrite-parse-tree.cpp b/lib/Semantics/rewrite-parse-tree.cpp similarity index 93% rename from lib/semantics/rewrite-parse-tree.cpp rename to lib/Semantics/rewrite-parse-tree.cpp index 37c55376d1a7..761621cf58f0 100644 --- a/lib/semantics/rewrite-parse-tree.cpp +++ b/lib/Semantics/rewrite-parse-tree.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/rewrite-parse-tree.cpp ------------------------------===// +//===-- lib/Semantics/rewrite-parse-tree.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,14 +7,14 @@ //===----------------------------------------------------------------------===// #include "rewrite-parse-tree.h" -#include "flang/common/indirection.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/tools.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include namespace Fortran::semantics { diff --git a/lib/semantics/rewrite-parse-tree.h b/lib/Semantics/rewrite-parse-tree.h similarity index 91% rename from lib/semantics/rewrite-parse-tree.h rename to lib/Semantics/rewrite-parse-tree.h index e5ef961e8242..7978fd60edda 100644 --- a/lib/semantics/rewrite-parse-tree.h +++ b/lib/Semantics/rewrite-parse-tree.h @@ -1,4 +1,4 @@ -//===-- lib/semantics/rewrite-parse-tree.h ----------------------*- C++ -*-===// +//===-- lib/Semantics/rewrite-parse-tree.h ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/semantics/scope.cpp b/lib/Semantics/scope.cpp similarity index 97% rename from lib/semantics/scope.cpp rename to lib/Semantics/scope.cpp index 9db599560650..16ee107a4612 100644 --- a/lib/semantics/scope.cpp +++ b/lib/Semantics/scope.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/scope.cpp -------------------------------------------===// +//===-- lib/Semantics/scope.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/scope.h" -#include "flang/parser/characters.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/type.h" +#include "flang/Semantics/scope.h" +#include "flang/Parser/characters.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/type.h" #include #include #include diff --git a/lib/semantics/semantics.cpp b/lib/Semantics/semantics.cpp similarity index 97% rename from lib/semantics/semantics.cpp rename to lib/Semantics/semantics.cpp index 16d2ebade63b..058bf227a40a 100644 --- a/lib/semantics/semantics.cpp +++ b/lib/Semantics/semantics.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/semantics.cpp ---------------------------------------===// +//===-- lib/Semantics/semantics.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/semantics.h" +#include "flang/Semantics/semantics.h" #include "assignment.h" #include "canonicalize-do.h" #include "canonicalize-omp.h" @@ -28,12 +28,12 @@ #include "resolve-labels.h" #include "resolve-names.h" #include "rewrite-parse-tree.h" -#include "flang/common/default-kinds.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/tools.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" +#include "flang/Common/default-kinds.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" namespace Fortran::semantics { diff --git a/lib/semantics/symbol.cpp b/lib/Semantics/symbol.cpp similarity index 98% rename from lib/semantics/symbol.cpp rename to lib/Semantics/symbol.cpp index d513962b021c..f69748ab9eb9 100644 --- a/lib/semantics/symbol.cpp +++ b/lib/Semantics/symbol.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/symbol.cpp ------------------------------------------===// +//===-- lib/Semantics/symbol.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/symbol.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/expression.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/tools.h" +#include "flang/Semantics/symbol.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/expression.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" #include #include diff --git a/lib/semantics/tools.cpp b/lib/Semantics/tools.cpp similarity index 98% rename from lib/semantics/tools.cpp rename to lib/Semantics/tools.cpp index 8e31a81a0573..b4a2a281ee7d 100644 --- a/lib/semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/tools.cpp -------------------------------------------===// +//===-- lib/Semantics/tools.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,17 +6,17 @@ // //===----------------------------------------------------------------------===// -#include "flang/parser/tools.h" -#include "flang/common/Fortran.h" -#include "flang/common/indirection.h" -#include "flang/parser/dump-parse-tree.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" -#include "flang/semantics/type.h" +#include "flang/Parser/tools.h" +#include "flang/Common/Fortran.h" +#include "flang/Common/indirection.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" #include #include #include diff --git a/lib/semantics/type.cpp b/lib/Semantics/type.cpp similarity index 98% rename from lib/semantics/type.cpp rename to lib/Semantics/type.cpp index b216261f8e4a..47158c7af4ab 100644 --- a/lib/semantics/type.cpp +++ b/lib/Semantics/type.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/type.cpp --------------------------------------------===// +//===-- lib/Semantics/type.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/type.h" -#include "flang/evaluate/fold.h" -#include "flang/parser/characters.h" -#include "flang/semantics/scope.h" -#include "flang/semantics/symbol.h" -#include "flang/semantics/tools.h" +#include "flang/Semantics/type.h" +#include "flang/Evaluate/fold.h" +#include "flang/Parser/characters.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include #include diff --git a/lib/semantics/unparse-with-symbols.cpp b/lib/Semantics/unparse-with-symbols.cpp similarity index 92% rename from lib/semantics/unparse-with-symbols.cpp rename to lib/Semantics/unparse-with-symbols.cpp index 1af5c2a5638f..b4f89198b347 100644 --- a/lib/semantics/unparse-with-symbols.cpp +++ b/lib/Semantics/unparse-with-symbols.cpp @@ -1,4 +1,4 @@ -//===-- lib/semantics/unparse-with-symbols.cpp ----------------------------===// +//===-- lib/Semantics/unparse-with-symbols.cpp ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "flang/semantics/unparse-with-symbols.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/unparse.h" -#include "flang/semantics/symbol.h" +#include "flang/Semantics/unparse-with-symbols.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/unparse.h" +#include "flang/Semantics/symbol.h" #include #include #include diff --git a/runtime/descriptor.cpp b/runtime/descriptor.cpp index ca065246d522..9e91c1b80199 100644 --- a/runtime/descriptor.cpp +++ b/runtime/descriptor.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "descriptor.h" -#include "flang/common/idioms.h" +#include "flang/Common/idioms.h" #include #include #include diff --git a/runtime/environment.h b/runtime/environment.h index 056a13829b2a..93730c180673 100644 --- a/runtime/environment.h +++ b/runtime/environment.h @@ -9,7 +9,7 @@ #ifndef FORTRAN_RUNTIME_ENVIRONMENT_H_ #define FORTRAN_RUNTIME_ENVIRONMENT_H_ -#include "flang/decimal/decimal.h" +#include "flang/Decimal/decimal.h" namespace Fortran::runtime { struct ExecutionEnvironment { diff --git a/runtime/format-implementation.h b/runtime/format-implementation.h index cb5fc2dfd8b5..e066ba04debc 100644 --- a/runtime/format-implementation.h +++ b/runtime/format-implementation.h @@ -14,8 +14,8 @@ #include "format.h" #include "io-stmt.h" #include "main.h" -#include "flang/common/format.h" -#include "flang/decimal/decimal.h" +#include "flang/Common/format.h" +#include "flang/Decimal/decimal.h" #include namespace Fortran::runtime::io { diff --git a/runtime/format.h b/runtime/format.h index c072b3b9805d..a4899f829ea5 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -14,8 +14,8 @@ #include "environment.h" #include "io-error.h" #include "terminator.h" -#include "flang/common/Fortran.h" -#include "flang/decimal/decimal.h" +#include "flang/Common/Fortran.h" +#include "flang/Decimal/decimal.h" #include #include diff --git a/runtime/numeric-output.cpp b/runtime/numeric-output.cpp index daef7aba879a..c0c617b8c206 100644 --- a/runtime/numeric-output.cpp +++ b/runtime/numeric-output.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "numeric-output.h" -#include "flang/common/unsigned-const-division.h" +#include "flang/Common/unsigned-const-division.h" namespace Fortran::runtime::io { diff --git a/runtime/numeric-output.h b/runtime/numeric-output.h index f8c5437ca31b..c3826ffdf563 100644 --- a/runtime/numeric-output.h +++ b/runtime/numeric-output.h @@ -20,7 +20,7 @@ #include "format.h" #include "io-stmt.h" -#include "flang/decimal/decimal.h" +#include "flang/Decimal/decimal.h" namespace Fortran::runtime::io { diff --git a/runtime/transformational.cpp b/runtime/transformational.cpp index 42a05e627962..f2abb37c4c43 100644 --- a/runtime/transformational.cpp +++ b/runtime/transformational.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "transformational.h" -#include "flang/common/idioms.h" -#include "flang/evaluate/integer.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/integer.h" #include #include #include diff --git a/runtime/type-code.h b/runtime/type-code.h index b04d45388371..5136bb8d32ff 100644 --- a/runtime/type-code.h +++ b/runtime/type-code.h @@ -10,7 +10,7 @@ #define FORTRAN_RUNTIME_TYPE_CODE_H_ #include "flang/ISO_Fortran_binding.h" -#include "flang/common/Fortran.h" +#include "flang/Common/Fortran.h" namespace Fortran::runtime { diff --git a/test-lit/driver/version_test.f90 b/test-lit/Driver/version_test.f90 similarity index 100% rename from test-lit/driver/version_test.f90 rename to test-lit/Driver/version_test.f90 diff --git a/test-lit/lower/pre-fir-tree01.f90 b/test-lit/Lower/pre-fir-tree01.f90 similarity index 100% rename from test-lit/lower/pre-fir-tree01.f90 rename to test-lit/Lower/pre-fir-tree01.f90 diff --git a/test-lit/lower/pre-fir-tree02.f90 b/test-lit/Lower/pre-fir-tree02.f90 similarity index 100% rename from test-lit/lower/pre-fir-tree02.f90 rename to test-lit/Lower/pre-fir-tree02.f90 diff --git a/test-lit/lower/pre-fir-tree03.f90 b/test-lit/Lower/pre-fir-tree03.f90 similarity index 100% rename from test-lit/lower/pre-fir-tree03.f90 rename to test-lit/Lower/pre-fir-tree03.f90 diff --git a/test-lit/lower/pre-fir-tree04.f90 b/test-lit/Lower/pre-fir-tree04.f90 similarity index 100% rename from test-lit/lower/pre-fir-tree04.f90 rename to test-lit/Lower/pre-fir-tree04.f90 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9013757f3cc8..e83f63c8e030 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,7 +6,7 @@ # #===------------------------------------------------------------------------===# -add_subdirectory(decimal) -add_subdirectory(evaluate) -add_subdirectory(runtime) -add_subdirectory(semantics) +add_subdirectory(Decimal) +add_subdirectory(Evaluate) +add_subdirectory(Runtime) +add_subdirectory(Semantics) diff --git a/test/decimal/CMakeLists.txt b/test/Decimal/CMakeLists.txt similarity index 89% rename from test/decimal/CMakeLists.txt rename to test/Decimal/CMakeLists.txt index d1210ba1f4ac..46a3d1fd44bf 100644 --- a/test/decimal/CMakeLists.txt +++ b/test/Decimal/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- test/decimal/CMakeLists.txt -----------------------------------------===# +#===-- test/Decimal/CMakeLists.txt -----------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/test/decimal/quick-sanity-test.cpp b/test/Decimal/quick-sanity-test.cpp similarity index 99% rename from test/decimal/quick-sanity-test.cpp rename to test/Decimal/quick-sanity-test.cpp index d9ebf8d3a0dd..47089499d4b5 100644 --- a/test/decimal/quick-sanity-test.cpp +++ b/test/Decimal/quick-sanity-test.cpp @@ -1,4 +1,4 @@ -#include "flang/decimal/decimal.h" +#include "flang/Decimal/decimal.h" #include #include #include diff --git a/test/decimal/thorough-test.cpp b/test/Decimal/thorough-test.cpp similarity index 98% rename from test/decimal/thorough-test.cpp rename to test/Decimal/thorough-test.cpp index f5e3274d3208..f10467d1300a 100644 --- a/test/decimal/thorough-test.cpp +++ b/test/Decimal/thorough-test.cpp @@ -1,4 +1,4 @@ -#include "flang/decimal/decimal.h" +#include "flang/Decimal/decimal.h" #include #include #include diff --git a/test/evaluate/CMakeLists.txt b/test/Evaluate/CMakeLists.txt similarity index 96% rename from test/evaluate/CMakeLists.txt rename to test/Evaluate/CMakeLists.txt index 088280f9277b..f1bbc1dfd7c6 100644 --- a/test/evaluate/CMakeLists.txt +++ b/test/Evaluate/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- test/evaluate/CMakeLists.txt ----------------------------------------===# +#===-- test/Evaluate/CMakeLists.txt ----------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. @@ -35,7 +35,7 @@ target_link_libraries(uint128-test FortranEvaluateTesting ) -# These routines live in lib/common but we test them here. +# These routines live in lib/Common but we test them here. add_test(UINT128 uint128-test) add_test(Leadz leading-zero-bit-count-test) add_test(PopPar bit-population-count-test) diff --git a/test/evaluate/ISO-Fortran-binding.cpp b/test/Evaluate/ISO-Fortran-binding.cpp similarity index 100% rename from test/evaluate/ISO-Fortran-binding.cpp rename to test/Evaluate/ISO-Fortran-binding.cpp diff --git a/test/evaluate/bit-population-count.cpp b/test/Evaluate/bit-population-count.cpp similarity index 98% rename from test/evaluate/bit-population-count.cpp rename to test/Evaluate/bit-population-count.cpp index 0b98f644c215..24e721c14f94 100644 --- a/test/evaluate/bit-population-count.cpp +++ b/test/Evaluate/bit-population-count.cpp @@ -1,4 +1,4 @@ -#include "flang/common/bit-population-count.h" +#include "flang/Common/bit-population-count.h" #include "testing.h" using Fortran::common::BitPopulationCount; diff --git a/test/evaluate/expression.cpp b/test/Evaluate/expression.cpp similarity index 85% rename from test/evaluate/expression.cpp rename to test/Evaluate/expression.cpp index ced868d5a5e2..47419e410f58 100644 --- a/test/evaluate/expression.cpp +++ b/test/Evaluate/expression.cpp @@ -1,9 +1,9 @@ -#include "flang/evaluate/expression.h" +#include "flang/Evaluate/expression.h" #include "testing.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/message.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/message.h" #include #include #include diff --git a/test/evaluate/folding.cpp b/test/Evaluate/folding.cpp similarity index 93% rename from test/evaluate/folding.cpp rename to test/Evaluate/folding.cpp index bf68a74e0976..450f87ac2622 100644 --- a/test/evaluate/folding.cpp +++ b/test/Evaluate/folding.cpp @@ -1,11 +1,11 @@ #include "testing.h" -#include "../../lib/evaluate/host.h" -#include "../../lib/evaluate/intrinsics-library-templates.h" -#include "flang/evaluate/call.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/fold.h" -#include "flang/evaluate/intrinsics.h" -#include "flang/evaluate/tools.h" +#include "../../lib/Evaluate/host.h" +#include "../../lib/Evaluate/intrinsics-library-templates.h" +#include "flang/Evaluate/call.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/intrinsics.h" +#include "flang/Evaluate/tools.h" #include using namespace Fortran::evaluate; diff --git a/test/evaluate/folding01.f90 b/test/Evaluate/folding01.f90 similarity index 100% rename from test/evaluate/folding01.f90 rename to test/Evaluate/folding01.f90 diff --git a/test/evaluate/folding02.f90 b/test/Evaluate/folding02.f90 similarity index 100% rename from test/evaluate/folding02.f90 rename to test/Evaluate/folding02.f90 diff --git a/test/evaluate/folding03.f90 b/test/Evaluate/folding03.f90 similarity index 100% rename from test/evaluate/folding03.f90 rename to test/Evaluate/folding03.f90 diff --git a/test/evaluate/folding04.f90 b/test/Evaluate/folding04.f90 similarity index 100% rename from test/evaluate/folding04.f90 rename to test/Evaluate/folding04.f90 diff --git a/test/evaluate/folding05.f90 b/test/Evaluate/folding05.f90 similarity index 100% rename from test/evaluate/folding05.f90 rename to test/Evaluate/folding05.f90 diff --git a/test/evaluate/folding06.f90 b/test/Evaluate/folding06.f90 similarity index 100% rename from test/evaluate/folding06.f90 rename to test/Evaluate/folding06.f90 diff --git a/test/evaluate/folding07.f90 b/test/Evaluate/folding07.f90 similarity index 100% rename from test/evaluate/folding07.f90 rename to test/Evaluate/folding07.f90 diff --git a/test/evaluate/folding08.f90 b/test/Evaluate/folding08.f90 similarity index 100% rename from test/evaluate/folding08.f90 rename to test/Evaluate/folding08.f90 diff --git a/test/evaluate/folding09.f90 b/test/Evaluate/folding09.f90 similarity index 100% rename from test/evaluate/folding09.f90 rename to test/Evaluate/folding09.f90 diff --git a/test/evaluate/fp-testing.cpp b/test/Evaluate/fp-testing.cpp similarity index 100% rename from test/evaluate/fp-testing.cpp rename to test/Evaluate/fp-testing.cpp diff --git a/test/evaluate/fp-testing.h b/test/Evaluate/fp-testing.h similarity index 94% rename from test/evaluate/fp-testing.h rename to test/Evaluate/fp-testing.h index c86fbbaa2700..9e10561fd9e4 100644 --- a/test/evaluate/fp-testing.h +++ b/test/Evaluate/fp-testing.h @@ -1,7 +1,7 @@ #ifndef FORTRAN_TEST_EVALUATE_FP_TESTING_H_ #define FORTRAN_TEST_EVALUATE_FP_TESTING_H_ -#include "flang/evaluate/common.h" +#include "flang/Evaluate/common.h" #include using Fortran::common::RoundingMode; diff --git a/test/evaluate/integer.cpp b/test/Evaluate/integer.cpp similarity index 99% rename from test/evaluate/integer.cpp rename to test/Evaluate/integer.cpp index e31f230cb88a..a6111848ab9b 100644 --- a/test/evaluate/integer.cpp +++ b/test/Evaluate/integer.cpp @@ -1,4 +1,4 @@ -#include "flang/evaluate/integer.h" +#include "flang/Evaluate/integer.h" #include "testing.h" #include #include diff --git a/test/evaluate/intrinsics.cpp b/test/Evaluate/intrinsics.cpp similarity index 97% rename from test/evaluate/intrinsics.cpp rename to test/Evaluate/intrinsics.cpp index 1826a4691b9d..7a4ca4753b4f 100644 --- a/test/evaluate/intrinsics.cpp +++ b/test/Evaluate/intrinsics.cpp @@ -1,9 +1,9 @@ -#include "flang/evaluate/intrinsics.h" +#include "flang/Evaluate/intrinsics.h" #include "testing.h" -#include "flang/evaluate/common.h" -#include "flang/evaluate/expression.h" -#include "flang/evaluate/tools.h" -#include "flang/parser/provenance.h" +#include "flang/Evaluate/common.h" +#include "flang/Evaluate/expression.h" +#include "flang/Evaluate/tools.h" +#include "flang/Parser/provenance.h" #include #include #include diff --git a/test/evaluate/leading-zero-bit-count.cpp b/test/Evaluate/leading-zero-bit-count.cpp similarity index 95% rename from test/evaluate/leading-zero-bit-count.cpp rename to test/Evaluate/leading-zero-bit-count.cpp index 1abd0f7b6c11..968946b69f27 100644 --- a/test/evaluate/leading-zero-bit-count.cpp +++ b/test/Evaluate/leading-zero-bit-count.cpp @@ -1,4 +1,4 @@ -#include "flang/common/leading-zero-bit-count.h" +#include "flang/Common/leading-zero-bit-count.h" #include "testing.h" using Fortran::common::LeadingZeroBitCount; diff --git a/test/evaluate/logical.cpp b/test/Evaluate/logical.cpp similarity index 97% rename from test/evaluate/logical.cpp rename to test/Evaluate/logical.cpp index 3edbd6afd142..f2d171718093 100644 --- a/test/evaluate/logical.cpp +++ b/test/Evaluate/logical.cpp @@ -1,5 +1,5 @@ #include "testing.h" -#include "flang/evaluate/type.h" +#include "flang/Evaluate/type.h" #include template void testKind() { diff --git a/test/evaluate/real.cpp b/test/Evaluate/real.cpp similarity index 99% rename from test/evaluate/real.cpp rename to test/Evaluate/real.cpp index 85101e513726..732a2de1bec2 100644 --- a/test/evaluate/real.cpp +++ b/test/Evaluate/real.cpp @@ -1,6 +1,6 @@ #include "fp-testing.h" #include "testing.h" -#include "flang/evaluate/type.h" +#include "flang/Evaluate/type.h" #include #include #include diff --git a/test/evaluate/reshape.cpp b/test/Evaluate/reshape.cpp similarity index 100% rename from test/evaluate/reshape.cpp rename to test/Evaluate/reshape.cpp diff --git a/test/evaluate/test_folding.sh b/test/Evaluate/test_folding.sh similarity index 100% rename from test/evaluate/test_folding.sh rename to test/Evaluate/test_folding.sh diff --git a/test/evaluate/testing.cpp b/test/Evaluate/testing.cpp similarity index 100% rename from test/evaluate/testing.cpp rename to test/Evaluate/testing.cpp diff --git a/test/evaluate/testing.h b/test/Evaluate/testing.h similarity index 100% rename from test/evaluate/testing.h rename to test/Evaluate/testing.h diff --git a/test/evaluate/uint128.cpp b/test/Evaluate/uint128.cpp similarity index 99% rename from test/evaluate/uint128.cpp rename to test/Evaluate/uint128.cpp index 6a20d32de92a..07efc31c5a39 100644 --- a/test/evaluate/uint128.cpp +++ b/test/Evaluate/uint128.cpp @@ -1,5 +1,5 @@ #define AVOID_NATIVE_UINT128_T 1 -#include "flang/common/uint128.h" +#include "flang/Common/uint128.h" #include "testing.h" #include #include diff --git a/test/preprocessing/pp001.F b/test/Preprocessing/pp001.F similarity index 100% rename from test/preprocessing/pp001.F rename to test/Preprocessing/pp001.F diff --git a/test/preprocessing/pp002.F b/test/Preprocessing/pp002.F similarity index 100% rename from test/preprocessing/pp002.F rename to test/Preprocessing/pp002.F diff --git a/test/preprocessing/pp003.F b/test/Preprocessing/pp003.F similarity index 100% rename from test/preprocessing/pp003.F rename to test/Preprocessing/pp003.F diff --git a/test/preprocessing/pp004.F b/test/Preprocessing/pp004.F similarity index 100% rename from test/preprocessing/pp004.F rename to test/Preprocessing/pp004.F diff --git a/test/preprocessing/pp005.F b/test/Preprocessing/pp005.F similarity index 100% rename from test/preprocessing/pp005.F rename to test/Preprocessing/pp005.F diff --git a/test/preprocessing/pp006.F b/test/Preprocessing/pp006.F similarity index 100% rename from test/preprocessing/pp006.F rename to test/Preprocessing/pp006.F diff --git a/test/preprocessing/pp007.F b/test/Preprocessing/pp007.F similarity index 100% rename from test/preprocessing/pp007.F rename to test/Preprocessing/pp007.F diff --git a/test/preprocessing/pp008.F b/test/Preprocessing/pp008.F similarity index 100% rename from test/preprocessing/pp008.F rename to test/Preprocessing/pp008.F diff --git a/test/preprocessing/pp009.F b/test/Preprocessing/pp009.F similarity index 100% rename from test/preprocessing/pp009.F rename to test/Preprocessing/pp009.F diff --git a/test/preprocessing/pp010.F b/test/Preprocessing/pp010.F similarity index 100% rename from test/preprocessing/pp010.F rename to test/Preprocessing/pp010.F diff --git a/test/preprocessing/pp011.F b/test/Preprocessing/pp011.F similarity index 100% rename from test/preprocessing/pp011.F rename to test/Preprocessing/pp011.F diff --git a/test/preprocessing/pp012.F b/test/Preprocessing/pp012.F similarity index 100% rename from test/preprocessing/pp012.F rename to test/Preprocessing/pp012.F diff --git a/test/preprocessing/pp013.F b/test/Preprocessing/pp013.F similarity index 100% rename from test/preprocessing/pp013.F rename to test/Preprocessing/pp013.F diff --git a/test/preprocessing/pp014.F b/test/Preprocessing/pp014.F similarity index 100% rename from test/preprocessing/pp014.F rename to test/Preprocessing/pp014.F diff --git a/test/preprocessing/pp015.F b/test/Preprocessing/pp015.F similarity index 100% rename from test/preprocessing/pp015.F rename to test/Preprocessing/pp015.F diff --git a/test/preprocessing/pp016.F b/test/Preprocessing/pp016.F similarity index 100% rename from test/preprocessing/pp016.F rename to test/Preprocessing/pp016.F diff --git a/test/preprocessing/pp017.F b/test/Preprocessing/pp017.F similarity index 100% rename from test/preprocessing/pp017.F rename to test/Preprocessing/pp017.F diff --git a/test/preprocessing/pp018.F b/test/Preprocessing/pp018.F similarity index 100% rename from test/preprocessing/pp018.F rename to test/Preprocessing/pp018.F diff --git a/test/preprocessing/pp019.F b/test/Preprocessing/pp019.F similarity index 100% rename from test/preprocessing/pp019.F rename to test/Preprocessing/pp019.F diff --git a/test/preprocessing/pp020.F b/test/Preprocessing/pp020.F similarity index 100% rename from test/preprocessing/pp020.F rename to test/Preprocessing/pp020.F diff --git a/test/preprocessing/pp021.F b/test/Preprocessing/pp021.F similarity index 100% rename from test/preprocessing/pp021.F rename to test/Preprocessing/pp021.F diff --git a/test/preprocessing/pp022.F b/test/Preprocessing/pp022.F similarity index 100% rename from test/preprocessing/pp022.F rename to test/Preprocessing/pp022.F diff --git a/test/preprocessing/pp023.F b/test/Preprocessing/pp023.F similarity index 100% rename from test/preprocessing/pp023.F rename to test/Preprocessing/pp023.F diff --git a/test/preprocessing/pp024.F b/test/Preprocessing/pp024.F similarity index 100% rename from test/preprocessing/pp024.F rename to test/Preprocessing/pp024.F diff --git a/test/preprocessing/pp025.F b/test/Preprocessing/pp025.F similarity index 100% rename from test/preprocessing/pp025.F rename to test/Preprocessing/pp025.F diff --git a/test/preprocessing/pp026.F b/test/Preprocessing/pp026.F similarity index 100% rename from test/preprocessing/pp026.F rename to test/Preprocessing/pp026.F diff --git a/test/preprocessing/pp027.F b/test/Preprocessing/pp027.F similarity index 100% rename from test/preprocessing/pp027.F rename to test/Preprocessing/pp027.F diff --git a/test/preprocessing/pp028.F b/test/Preprocessing/pp028.F similarity index 100% rename from test/preprocessing/pp028.F rename to test/Preprocessing/pp028.F diff --git a/test/preprocessing/pp029.F b/test/Preprocessing/pp029.F similarity index 100% rename from test/preprocessing/pp029.F rename to test/Preprocessing/pp029.F diff --git a/test/preprocessing/pp030.F b/test/Preprocessing/pp030.F similarity index 100% rename from test/preprocessing/pp030.F rename to test/Preprocessing/pp030.F diff --git a/test/preprocessing/pp031.F b/test/Preprocessing/pp031.F similarity index 100% rename from test/preprocessing/pp031.F rename to test/Preprocessing/pp031.F diff --git a/test/preprocessing/pp032.F b/test/Preprocessing/pp032.F similarity index 100% rename from test/preprocessing/pp032.F rename to test/Preprocessing/pp032.F diff --git a/test/preprocessing/pp033.F b/test/Preprocessing/pp033.F similarity index 100% rename from test/preprocessing/pp033.F rename to test/Preprocessing/pp033.F diff --git a/test/preprocessing/pp034.F b/test/Preprocessing/pp034.F similarity index 100% rename from test/preprocessing/pp034.F rename to test/Preprocessing/pp034.F diff --git a/test/preprocessing/pp035.F b/test/Preprocessing/pp035.F similarity index 100% rename from test/preprocessing/pp035.F rename to test/Preprocessing/pp035.F diff --git a/test/preprocessing/pp036.F b/test/Preprocessing/pp036.F similarity index 100% rename from test/preprocessing/pp036.F rename to test/Preprocessing/pp036.F diff --git a/test/preprocessing/pp037.F b/test/Preprocessing/pp037.F similarity index 100% rename from test/preprocessing/pp037.F rename to test/Preprocessing/pp037.F diff --git a/test/preprocessing/pp038.F b/test/Preprocessing/pp038.F similarity index 100% rename from test/preprocessing/pp038.F rename to test/Preprocessing/pp038.F diff --git a/test/preprocessing/pp039.F b/test/Preprocessing/pp039.F similarity index 100% rename from test/preprocessing/pp039.F rename to test/Preprocessing/pp039.F diff --git a/test/preprocessing/pp040.F b/test/Preprocessing/pp040.F similarity index 100% rename from test/preprocessing/pp040.F rename to test/Preprocessing/pp040.F diff --git a/test/preprocessing/pp041.F b/test/Preprocessing/pp041.F similarity index 100% rename from test/preprocessing/pp041.F rename to test/Preprocessing/pp041.F diff --git a/test/preprocessing/pp042.F b/test/Preprocessing/pp042.F similarity index 100% rename from test/preprocessing/pp042.F rename to test/Preprocessing/pp042.F diff --git a/test/preprocessing/pp043.F b/test/Preprocessing/pp043.F similarity index 100% rename from test/preprocessing/pp043.F rename to test/Preprocessing/pp043.F diff --git a/test/preprocessing/pp044.F b/test/Preprocessing/pp044.F similarity index 100% rename from test/preprocessing/pp044.F rename to test/Preprocessing/pp044.F diff --git a/test/preprocessing/pp101.F90 b/test/Preprocessing/pp101.F90 similarity index 100% rename from test/preprocessing/pp101.F90 rename to test/Preprocessing/pp101.F90 diff --git a/test/preprocessing/pp102.F90 b/test/Preprocessing/pp102.F90 similarity index 100% rename from test/preprocessing/pp102.F90 rename to test/Preprocessing/pp102.F90 diff --git a/test/preprocessing/pp103.F90 b/test/Preprocessing/pp103.F90 similarity index 100% rename from test/preprocessing/pp103.F90 rename to test/Preprocessing/pp103.F90 diff --git a/test/preprocessing/pp104.F90 b/test/Preprocessing/pp104.F90 similarity index 100% rename from test/preprocessing/pp104.F90 rename to test/Preprocessing/pp104.F90 diff --git a/test/preprocessing/pp105.F90 b/test/Preprocessing/pp105.F90 similarity index 100% rename from test/preprocessing/pp105.F90 rename to test/Preprocessing/pp105.F90 diff --git a/test/preprocessing/pp106.F90 b/test/Preprocessing/pp106.F90 similarity index 100% rename from test/preprocessing/pp106.F90 rename to test/Preprocessing/pp106.F90 diff --git a/test/preprocessing/pp107.F90 b/test/Preprocessing/pp107.F90 similarity index 100% rename from test/preprocessing/pp107.F90 rename to test/Preprocessing/pp107.F90 diff --git a/test/preprocessing/pp108.F90 b/test/Preprocessing/pp108.F90 similarity index 100% rename from test/preprocessing/pp108.F90 rename to test/Preprocessing/pp108.F90 diff --git a/test/preprocessing/pp109.F90 b/test/Preprocessing/pp109.F90 similarity index 100% rename from test/preprocessing/pp109.F90 rename to test/Preprocessing/pp109.F90 diff --git a/test/preprocessing/pp110.F90 b/test/Preprocessing/pp110.F90 similarity index 100% rename from test/preprocessing/pp110.F90 rename to test/Preprocessing/pp110.F90 diff --git a/test/preprocessing/pp111.F90 b/test/Preprocessing/pp111.F90 similarity index 100% rename from test/preprocessing/pp111.F90 rename to test/Preprocessing/pp111.F90 diff --git a/test/preprocessing/pp112.F90 b/test/Preprocessing/pp112.F90 similarity index 100% rename from test/preprocessing/pp112.F90 rename to test/Preprocessing/pp112.F90 diff --git a/test/preprocessing/pp113.F90 b/test/Preprocessing/pp113.F90 similarity index 100% rename from test/preprocessing/pp113.F90 rename to test/Preprocessing/pp113.F90 diff --git a/test/preprocessing/pp114.F90 b/test/Preprocessing/pp114.F90 similarity index 100% rename from test/preprocessing/pp114.F90 rename to test/Preprocessing/pp114.F90 diff --git a/test/preprocessing/pp115.F90 b/test/Preprocessing/pp115.F90 similarity index 100% rename from test/preprocessing/pp115.F90 rename to test/Preprocessing/pp115.F90 diff --git a/test/preprocessing/pp116.F90 b/test/Preprocessing/pp116.F90 similarity index 100% rename from test/preprocessing/pp116.F90 rename to test/Preprocessing/pp116.F90 diff --git a/test/preprocessing/pp117.F90 b/test/Preprocessing/pp117.F90 similarity index 100% rename from test/preprocessing/pp117.F90 rename to test/Preprocessing/pp117.F90 diff --git a/test/preprocessing/pp118.F90 b/test/Preprocessing/pp118.F90 similarity index 100% rename from test/preprocessing/pp118.F90 rename to test/Preprocessing/pp118.F90 diff --git a/test/preprocessing/pp119.F90 b/test/Preprocessing/pp119.F90 similarity index 100% rename from test/preprocessing/pp119.F90 rename to test/Preprocessing/pp119.F90 diff --git a/test/preprocessing/pp120.F90 b/test/Preprocessing/pp120.F90 similarity index 100% rename from test/preprocessing/pp120.F90 rename to test/Preprocessing/pp120.F90 diff --git a/test/preprocessing/pp121.F90 b/test/Preprocessing/pp121.F90 similarity index 100% rename from test/preprocessing/pp121.F90 rename to test/Preprocessing/pp121.F90 diff --git a/test/preprocessing/pp122.F90 b/test/Preprocessing/pp122.F90 similarity index 100% rename from test/preprocessing/pp122.F90 rename to test/Preprocessing/pp122.F90 diff --git a/test/preprocessing/pp123.F90 b/test/Preprocessing/pp123.F90 similarity index 100% rename from test/preprocessing/pp123.F90 rename to test/Preprocessing/pp123.F90 diff --git a/test/preprocessing/pp124.F90 b/test/Preprocessing/pp124.F90 similarity index 100% rename from test/preprocessing/pp124.F90 rename to test/Preprocessing/pp124.F90 diff --git a/test/preprocessing/pp125.F90 b/test/Preprocessing/pp125.F90 similarity index 100% rename from test/preprocessing/pp125.F90 rename to test/Preprocessing/pp125.F90 diff --git a/test/preprocessing/pp126.F90 b/test/Preprocessing/pp126.F90 similarity index 100% rename from test/preprocessing/pp126.F90 rename to test/Preprocessing/pp126.F90 diff --git a/test/preprocessing/pp127.F90 b/test/Preprocessing/pp127.F90 similarity index 100% rename from test/preprocessing/pp127.F90 rename to test/Preprocessing/pp127.F90 diff --git a/test/preprocessing/pp128.F90 b/test/Preprocessing/pp128.F90 similarity index 100% rename from test/preprocessing/pp128.F90 rename to test/Preprocessing/pp128.F90 diff --git a/test/preprocessing/pp129.F90 b/test/Preprocessing/pp129.F90 similarity index 100% rename from test/preprocessing/pp129.F90 rename to test/Preprocessing/pp129.F90 diff --git a/test/preprocessing/pp130.F90 b/test/Preprocessing/pp130.F90 similarity index 100% rename from test/preprocessing/pp130.F90 rename to test/Preprocessing/pp130.F90 diff --git a/test/runtime/CMakeLists.txt b/test/Runtime/CMakeLists.txt similarity index 92% rename from test/runtime/CMakeLists.txt rename to test/Runtime/CMakeLists.txt index feadddfa880a..239c3f86f52b 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/Runtime/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- test/runtime/CMakeLists.txt -----------------------------------------===# +#===-- test/Runtime/CMakeLists.txt -----------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/test/runtime/external-hello.cpp b/test/Runtime/external-hello.cpp similarity index 100% rename from test/runtime/external-hello.cpp rename to test/Runtime/external-hello.cpp diff --git a/test/runtime/format.cpp b/test/Runtime/format.cpp similarity index 100% rename from test/runtime/format.cpp rename to test/Runtime/format.cpp diff --git a/test/runtime/hello.cpp b/test/Runtime/hello.cpp similarity index 100% rename from test/runtime/hello.cpp rename to test/Runtime/hello.cpp diff --git a/test/semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt similarity index 98% rename from test/semantics/CMakeLists.txt rename to test/Semantics/CMakeLists.txt index ff5aa3f0e4de..ac3e9f641c0c 100644 --- a/test/semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -1,4 +1,4 @@ -#===-- test/semantics/CMakeLists.txt ---------------------------------------===# +#===-- test/Semantics/CMakeLists.txt ---------------------------------------===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. diff --git a/test/semantics/allocate01.f90 b/test/Semantics/allocate01.f90 similarity index 100% rename from test/semantics/allocate01.f90 rename to test/Semantics/allocate01.f90 diff --git a/test/semantics/allocate02.f90 b/test/Semantics/allocate02.f90 similarity index 100% rename from test/semantics/allocate02.f90 rename to test/Semantics/allocate02.f90 diff --git a/test/semantics/allocate03.f90 b/test/Semantics/allocate03.f90 similarity index 100% rename from test/semantics/allocate03.f90 rename to test/Semantics/allocate03.f90 diff --git a/test/semantics/allocate04.f90 b/test/Semantics/allocate04.f90 similarity index 100% rename from test/semantics/allocate04.f90 rename to test/Semantics/allocate04.f90 diff --git a/test/semantics/allocate05.f90 b/test/Semantics/allocate05.f90 similarity index 100% rename from test/semantics/allocate05.f90 rename to test/Semantics/allocate05.f90 diff --git a/test/semantics/allocate06.f90 b/test/Semantics/allocate06.f90 similarity index 100% rename from test/semantics/allocate06.f90 rename to test/Semantics/allocate06.f90 diff --git a/test/semantics/allocate07.f90 b/test/Semantics/allocate07.f90 similarity index 100% rename from test/semantics/allocate07.f90 rename to test/Semantics/allocate07.f90 diff --git a/test/semantics/allocate08.f90 b/test/Semantics/allocate08.f90 similarity index 100% rename from test/semantics/allocate08.f90 rename to test/Semantics/allocate08.f90 diff --git a/test/semantics/allocate09.f90 b/test/Semantics/allocate09.f90 similarity index 100% rename from test/semantics/allocate09.f90 rename to test/Semantics/allocate09.f90 diff --git a/test/semantics/allocate10.f90 b/test/Semantics/allocate10.f90 similarity index 100% rename from test/semantics/allocate10.f90 rename to test/Semantics/allocate10.f90 diff --git a/test/semantics/allocate11.f90 b/test/Semantics/allocate11.f90 similarity index 100% rename from test/semantics/allocate11.f90 rename to test/Semantics/allocate11.f90 diff --git a/test/semantics/allocate12.f90 b/test/Semantics/allocate12.f90 similarity index 100% rename from test/semantics/allocate12.f90 rename to test/Semantics/allocate12.f90 diff --git a/test/semantics/allocate13.f90 b/test/Semantics/allocate13.f90 similarity index 100% rename from test/semantics/allocate13.f90 rename to test/Semantics/allocate13.f90 diff --git a/test/semantics/altreturn01.f90 b/test/Semantics/altreturn01.f90 similarity index 100% rename from test/semantics/altreturn01.f90 rename to test/Semantics/altreturn01.f90 diff --git a/test/semantics/altreturn02.f90 b/test/Semantics/altreturn02.f90 similarity index 100% rename from test/semantics/altreturn02.f90 rename to test/Semantics/altreturn02.f90 diff --git a/test/semantics/altreturn03.f90 b/test/Semantics/altreturn03.f90 similarity index 100% rename from test/semantics/altreturn03.f90 rename to test/Semantics/altreturn03.f90 diff --git a/test/semantics/altreturn04.f90 b/test/Semantics/altreturn04.f90 similarity index 100% rename from test/semantics/altreturn04.f90 rename to test/Semantics/altreturn04.f90 diff --git a/test/semantics/altreturn05.f90 b/test/Semantics/altreturn05.f90 similarity index 100% rename from test/semantics/altreturn05.f90 rename to test/Semantics/altreturn05.f90 diff --git a/test/semantics/assign01.f90 b/test/Semantics/assign01.f90 similarity index 100% rename from test/semantics/assign01.f90 rename to test/Semantics/assign01.f90 diff --git a/test/semantics/assign02.f90 b/test/Semantics/assign02.f90 similarity index 100% rename from test/semantics/assign02.f90 rename to test/Semantics/assign02.f90 diff --git a/test/semantics/assign03.f90 b/test/Semantics/assign03.f90 similarity index 100% rename from test/semantics/assign03.f90 rename to test/Semantics/assign03.f90 diff --git a/test/semantics/bad-forward-type.f90 b/test/Semantics/bad-forward-type.f90 similarity index 100% rename from test/semantics/bad-forward-type.f90 rename to test/Semantics/bad-forward-type.f90 diff --git a/test/semantics/bindings01.f90 b/test/Semantics/bindings01.f90 similarity index 100% rename from test/semantics/bindings01.f90 rename to test/Semantics/bindings01.f90 diff --git a/test/semantics/block-data01.f90 b/test/Semantics/block-data01.f90 similarity index 100% rename from test/semantics/block-data01.f90 rename to test/Semantics/block-data01.f90 diff --git a/test/semantics/blockconstruct01.f90 b/test/Semantics/blockconstruct01.f90 similarity index 100% rename from test/semantics/blockconstruct01.f90 rename to test/Semantics/blockconstruct01.f90 diff --git a/test/semantics/blockconstruct02.f90 b/test/Semantics/blockconstruct02.f90 similarity index 100% rename from test/semantics/blockconstruct02.f90 rename to test/Semantics/blockconstruct02.f90 diff --git a/test/semantics/blockconstruct03.f90 b/test/Semantics/blockconstruct03.f90 similarity index 100% rename from test/semantics/blockconstruct03.f90 rename to test/Semantics/blockconstruct03.f90 diff --git a/test/semantics/c_f_pointer.f90 b/test/Semantics/c_f_pointer.f90 similarity index 100% rename from test/semantics/c_f_pointer.f90 rename to test/Semantics/c_f_pointer.f90 diff --git a/test/semantics/call01.f90 b/test/Semantics/call01.f90 similarity index 100% rename from test/semantics/call01.f90 rename to test/Semantics/call01.f90 diff --git a/test/semantics/call02.f90 b/test/Semantics/call02.f90 similarity index 100% rename from test/semantics/call02.f90 rename to test/Semantics/call02.f90 diff --git a/test/semantics/call03.f90 b/test/Semantics/call03.f90 similarity index 100% rename from test/semantics/call03.f90 rename to test/Semantics/call03.f90 diff --git a/test/semantics/call04.f90 b/test/Semantics/call04.f90 similarity index 100% rename from test/semantics/call04.f90 rename to test/Semantics/call04.f90 diff --git a/test/semantics/call05.f90 b/test/Semantics/call05.f90 similarity index 100% rename from test/semantics/call05.f90 rename to test/Semantics/call05.f90 diff --git a/test/semantics/call06.f90 b/test/Semantics/call06.f90 similarity index 100% rename from test/semantics/call06.f90 rename to test/Semantics/call06.f90 diff --git a/test/semantics/call07.f90 b/test/Semantics/call07.f90 similarity index 100% rename from test/semantics/call07.f90 rename to test/Semantics/call07.f90 diff --git a/test/semantics/call08.f90 b/test/Semantics/call08.f90 similarity index 100% rename from test/semantics/call08.f90 rename to test/Semantics/call08.f90 diff --git a/test/semantics/call09.f90 b/test/Semantics/call09.f90 similarity index 100% rename from test/semantics/call09.f90 rename to test/Semantics/call09.f90 diff --git a/test/semantics/call10.f90 b/test/Semantics/call10.f90 similarity index 100% rename from test/semantics/call10.f90 rename to test/Semantics/call10.f90 diff --git a/test/semantics/call11.f90 b/test/Semantics/call11.f90 similarity index 100% rename from test/semantics/call11.f90 rename to test/Semantics/call11.f90 diff --git a/test/semantics/call12.f90 b/test/Semantics/call12.f90 similarity index 100% rename from test/semantics/call12.f90 rename to test/Semantics/call12.f90 diff --git a/test/semantics/call13.f90 b/test/Semantics/call13.f90 similarity index 100% rename from test/semantics/call13.f90 rename to test/Semantics/call13.f90 diff --git a/test/semantics/call14.f90 b/test/Semantics/call14.f90 similarity index 100% rename from test/semantics/call14.f90 rename to test/Semantics/call14.f90 diff --git a/test/semantics/call15.f90 b/test/Semantics/call15.f90 similarity index 100% rename from test/semantics/call15.f90 rename to test/Semantics/call15.f90 diff --git a/test/semantics/canondo01.f90 b/test/Semantics/canondo01.f90 similarity index 100% rename from test/semantics/canondo01.f90 rename to test/Semantics/canondo01.f90 diff --git a/test/semantics/canondo02.f90 b/test/Semantics/canondo02.f90 similarity index 100% rename from test/semantics/canondo02.f90 rename to test/Semantics/canondo02.f90 diff --git a/test/semantics/canondo03.f90 b/test/Semantics/canondo03.f90 similarity index 100% rename from test/semantics/canondo03.f90 rename to test/Semantics/canondo03.f90 diff --git a/test/semantics/canondo04.f90 b/test/Semantics/canondo04.f90 similarity index 100% rename from test/semantics/canondo04.f90 rename to test/Semantics/canondo04.f90 diff --git a/test/semantics/canondo05.f90 b/test/Semantics/canondo05.f90 similarity index 100% rename from test/semantics/canondo05.f90 rename to test/Semantics/canondo05.f90 diff --git a/test/semantics/canondo06.f90 b/test/Semantics/canondo06.f90 similarity index 100% rename from test/semantics/canondo06.f90 rename to test/Semantics/canondo06.f90 diff --git a/test/semantics/canondo07.f90 b/test/Semantics/canondo07.f90 similarity index 100% rename from test/semantics/canondo07.f90 rename to test/Semantics/canondo07.f90 diff --git a/test/semantics/canondo08.f90 b/test/Semantics/canondo08.f90 similarity index 100% rename from test/semantics/canondo08.f90 rename to test/Semantics/canondo08.f90 diff --git a/test/semantics/canondo09.f90 b/test/Semantics/canondo09.f90 similarity index 100% rename from test/semantics/canondo09.f90 rename to test/Semantics/canondo09.f90 diff --git a/test/semantics/canondo10.f90 b/test/Semantics/canondo10.f90 similarity index 100% rename from test/semantics/canondo10.f90 rename to test/Semantics/canondo10.f90 diff --git a/test/semantics/canondo11.f90 b/test/Semantics/canondo11.f90 similarity index 100% rename from test/semantics/canondo11.f90 rename to test/Semantics/canondo11.f90 diff --git a/test/semantics/canondo12.f90 b/test/Semantics/canondo12.f90 similarity index 100% rename from test/semantics/canondo12.f90 rename to test/Semantics/canondo12.f90 diff --git a/test/semantics/canondo13.f90 b/test/Semantics/canondo13.f90 similarity index 100% rename from test/semantics/canondo13.f90 rename to test/Semantics/canondo13.f90 diff --git a/test/semantics/canondo14.f90 b/test/Semantics/canondo14.f90 similarity index 100% rename from test/semantics/canondo14.f90 rename to test/Semantics/canondo14.f90 diff --git a/test/semantics/canondo15.f90 b/test/Semantics/canondo15.f90 similarity index 100% rename from test/semantics/canondo15.f90 rename to test/Semantics/canondo15.f90 diff --git a/test/semantics/canondo16.f90 b/test/Semantics/canondo16.f90 similarity index 100% rename from test/semantics/canondo16.f90 rename to test/Semantics/canondo16.f90 diff --git a/test/semantics/canondo17.f90 b/test/Semantics/canondo17.f90 similarity index 100% rename from test/semantics/canondo17.f90 rename to test/Semantics/canondo17.f90 diff --git a/test/semantics/canondo18.f90 b/test/Semantics/canondo18.f90 similarity index 100% rename from test/semantics/canondo18.f90 rename to test/Semantics/canondo18.f90 diff --git a/test/semantics/canondo19.f90 b/test/Semantics/canondo19.f90 similarity index 100% rename from test/semantics/canondo19.f90 rename to test/Semantics/canondo19.f90 diff --git a/test/semantics/coarrays01.f90 b/test/Semantics/coarrays01.f90 similarity index 100% rename from test/semantics/coarrays01.f90 rename to test/Semantics/coarrays01.f90 diff --git a/test/semantics/common.sh b/test/Semantics/common.sh similarity index 100% rename from test/semantics/common.sh rename to test/Semantics/common.sh diff --git a/test/semantics/computed-goto01.f90 b/test/Semantics/computed-goto01.f90 similarity index 100% rename from test/semantics/computed-goto01.f90 rename to test/Semantics/computed-goto01.f90 diff --git a/test/semantics/computed-goto02.f90 b/test/Semantics/computed-goto02.f90 similarity index 100% rename from test/semantics/computed-goto02.f90 rename to test/Semantics/computed-goto02.f90 diff --git a/test/semantics/critical01.f90 b/test/Semantics/critical01.f90 similarity index 100% rename from test/semantics/critical01.f90 rename to test/Semantics/critical01.f90 diff --git a/test/semantics/critical02.f90 b/test/Semantics/critical02.f90 similarity index 100% rename from test/semantics/critical02.f90 rename to test/Semantics/critical02.f90 diff --git a/test/semantics/critical03.f90 b/test/Semantics/critical03.f90 similarity index 100% rename from test/semantics/critical03.f90 rename to test/Semantics/critical03.f90 diff --git a/test/semantics/critical04.f90 b/test/Semantics/critical04.f90 similarity index 100% rename from test/semantics/critical04.f90 rename to test/Semantics/critical04.f90 diff --git a/test/semantics/data01.f90 b/test/Semantics/data01.f90 similarity index 100% rename from test/semantics/data01.f90 rename to test/Semantics/data01.f90 diff --git a/test/semantics/deallocate01.f90 b/test/Semantics/deallocate01.f90 similarity index 100% rename from test/semantics/deallocate01.f90 rename to test/Semantics/deallocate01.f90 diff --git a/test/semantics/deallocate04.f90 b/test/Semantics/deallocate04.f90 similarity index 100% rename from test/semantics/deallocate04.f90 rename to test/Semantics/deallocate04.f90 diff --git a/test/semantics/deallocate05.f90 b/test/Semantics/deallocate05.f90 similarity index 100% rename from test/semantics/deallocate05.f90 rename to test/Semantics/deallocate05.f90 diff --git a/test/semantics/doconcurrent01.f90 b/test/Semantics/doconcurrent01.f90 similarity index 100% rename from test/semantics/doconcurrent01.f90 rename to test/Semantics/doconcurrent01.f90 diff --git a/test/semantics/doconcurrent02.f90 b/test/Semantics/doconcurrent02.f90 similarity index 100% rename from test/semantics/doconcurrent02.f90 rename to test/Semantics/doconcurrent02.f90 diff --git a/test/semantics/doconcurrent03.f90 b/test/Semantics/doconcurrent03.f90 similarity index 100% rename from test/semantics/doconcurrent03.f90 rename to test/Semantics/doconcurrent03.f90 diff --git a/test/semantics/doconcurrent04.f90 b/test/Semantics/doconcurrent04.f90 similarity index 100% rename from test/semantics/doconcurrent04.f90 rename to test/Semantics/doconcurrent04.f90 diff --git a/test/semantics/doconcurrent05.f90 b/test/Semantics/doconcurrent05.f90 similarity index 100% rename from test/semantics/doconcurrent05.f90 rename to test/Semantics/doconcurrent05.f90 diff --git a/test/semantics/doconcurrent06.f90 b/test/Semantics/doconcurrent06.f90 similarity index 100% rename from test/semantics/doconcurrent06.f90 rename to test/Semantics/doconcurrent06.f90 diff --git a/test/semantics/doconcurrent07.f90 b/test/Semantics/doconcurrent07.f90 similarity index 100% rename from test/semantics/doconcurrent07.f90 rename to test/Semantics/doconcurrent07.f90 diff --git a/test/semantics/doconcurrent08.f90 b/test/Semantics/doconcurrent08.f90 similarity index 99% rename from test/semantics/doconcurrent08.f90 rename to test/Semantics/doconcurrent08.f90 index b42ab6160a9f..f6773995e202 100644 --- a/test/semantics/doconcurrent08.f90 +++ b/test/Semantics/doconcurrent08.f90 @@ -239,7 +239,7 @@ subroutine s4() ! OK to invoke a PURE FINAL procedure in a DO CONCURRENT ! This case does not work currently because the compiler's test for - ! HasImpureFinal() in .../lib/semantics/tools.cc doesn't work correctly + ! HasImpureFinal() in .../lib/Semantics/tools.cc doesn't work correctly ! do concurrent (i = 1:10) ! if (i .eq. 1) deallocate(pfVar) ! end do diff --git a/test/semantics/dosemantics01.f90 b/test/Semantics/dosemantics01.f90 similarity index 100% rename from test/semantics/dosemantics01.f90 rename to test/Semantics/dosemantics01.f90 diff --git a/test/semantics/dosemantics02.f90 b/test/Semantics/dosemantics02.f90 similarity index 100% rename from test/semantics/dosemantics02.f90 rename to test/Semantics/dosemantics02.f90 diff --git a/test/semantics/dosemantics03.f90 b/test/Semantics/dosemantics03.f90 similarity index 100% rename from test/semantics/dosemantics03.f90 rename to test/Semantics/dosemantics03.f90 diff --git a/test/semantics/dosemantics04.f90 b/test/Semantics/dosemantics04.f90 similarity index 100% rename from test/semantics/dosemantics04.f90 rename to test/Semantics/dosemantics04.f90 diff --git a/test/semantics/dosemantics05.f90 b/test/Semantics/dosemantics05.f90 similarity index 100% rename from test/semantics/dosemantics05.f90 rename to test/Semantics/dosemantics05.f90 diff --git a/test/semantics/dosemantics06.f90 b/test/Semantics/dosemantics06.f90 similarity index 100% rename from test/semantics/dosemantics06.f90 rename to test/Semantics/dosemantics06.f90 diff --git a/test/semantics/dosemantics07.f90 b/test/Semantics/dosemantics07.f90 similarity index 100% rename from test/semantics/dosemantics07.f90 rename to test/Semantics/dosemantics07.f90 diff --git a/test/semantics/dosemantics08.f90 b/test/Semantics/dosemantics08.f90 similarity index 100% rename from test/semantics/dosemantics08.f90 rename to test/Semantics/dosemantics08.f90 diff --git a/test/semantics/dosemantics09.f90 b/test/Semantics/dosemantics09.f90 similarity index 100% rename from test/semantics/dosemantics09.f90 rename to test/Semantics/dosemantics09.f90 diff --git a/test/semantics/dosemantics10.f90 b/test/Semantics/dosemantics10.f90 similarity index 100% rename from test/semantics/dosemantics10.f90 rename to test/Semantics/dosemantics10.f90 diff --git a/test/semantics/dosemantics11.f90 b/test/Semantics/dosemantics11.f90 similarity index 100% rename from test/semantics/dosemantics11.f90 rename to test/Semantics/dosemantics11.f90 diff --git a/test/semantics/dosemantics12.f90 b/test/Semantics/dosemantics12.f90 similarity index 100% rename from test/semantics/dosemantics12.f90 rename to test/Semantics/dosemantics12.f90 diff --git a/test/semantics/equivalence01.f90 b/test/Semantics/equivalence01.f90 similarity index 100% rename from test/semantics/equivalence01.f90 rename to test/Semantics/equivalence01.f90 diff --git a/test/semantics/expr-errors01.f90 b/test/Semantics/expr-errors01.f90 similarity index 100% rename from test/semantics/expr-errors01.f90 rename to test/Semantics/expr-errors01.f90 diff --git a/test/semantics/expr-errors02.f90 b/test/Semantics/expr-errors02.f90 similarity index 100% rename from test/semantics/expr-errors02.f90 rename to test/Semantics/expr-errors02.f90 diff --git a/test/semantics/forall01.f90 b/test/Semantics/forall01.f90 similarity index 100% rename from test/semantics/forall01.f90 rename to test/Semantics/forall01.f90 diff --git a/test/semantics/getdefinition01.f90 b/test/Semantics/getdefinition01.f90 similarity index 100% rename from test/semantics/getdefinition01.f90 rename to test/Semantics/getdefinition01.f90 diff --git a/test/semantics/getdefinition02.f b/test/Semantics/getdefinition02.f similarity index 100% rename from test/semantics/getdefinition02.f rename to test/Semantics/getdefinition02.f diff --git a/test/semantics/getdefinition03-a.f90 b/test/Semantics/getdefinition03-a.f90 similarity index 100% rename from test/semantics/getdefinition03-a.f90 rename to test/Semantics/getdefinition03-a.f90 diff --git a/test/semantics/getdefinition03-b.f90 b/test/Semantics/getdefinition03-b.f90 similarity index 100% rename from test/semantics/getdefinition03-b.f90 rename to test/Semantics/getdefinition03-b.f90 diff --git a/test/semantics/getdefinition04.f90 b/test/Semantics/getdefinition04.f90 similarity index 100% rename from test/semantics/getdefinition04.f90 rename to test/Semantics/getdefinition04.f90 diff --git a/test/semantics/getdefinition05.f90 b/test/Semantics/getdefinition05.f90 similarity index 100% rename from test/semantics/getdefinition05.f90 rename to test/Semantics/getdefinition05.f90 diff --git a/test/semantics/getsymbols01.f90 b/test/Semantics/getsymbols01.f90 similarity index 100% rename from test/semantics/getsymbols01.f90 rename to test/Semantics/getsymbols01.f90 diff --git a/test/semantics/getsymbols02-a.f90 b/test/Semantics/getsymbols02-a.f90 similarity index 100% rename from test/semantics/getsymbols02-a.f90 rename to test/Semantics/getsymbols02-a.f90 diff --git a/test/semantics/getsymbols02-b.f90 b/test/Semantics/getsymbols02-b.f90 similarity index 100% rename from test/semantics/getsymbols02-b.f90 rename to test/Semantics/getsymbols02-b.f90 diff --git a/test/semantics/getsymbols02-c.f90 b/test/Semantics/getsymbols02-c.f90 similarity index 100% rename from test/semantics/getsymbols02-c.f90 rename to test/Semantics/getsymbols02-c.f90 diff --git a/test/semantics/getsymbols03-a.f90 b/test/Semantics/getsymbols03-a.f90 similarity index 100% rename from test/semantics/getsymbols03-a.f90 rename to test/Semantics/getsymbols03-a.f90 diff --git a/test/semantics/getsymbols03-b.f90 b/test/Semantics/getsymbols03-b.f90 similarity index 100% rename from test/semantics/getsymbols03-b.f90 rename to test/Semantics/getsymbols03-b.f90 diff --git a/test/semantics/getsymbols04.f90 b/test/Semantics/getsymbols04.f90 similarity index 100% rename from test/semantics/getsymbols04.f90 rename to test/Semantics/getsymbols04.f90 diff --git a/test/semantics/getsymbols05.f90 b/test/Semantics/getsymbols05.f90 similarity index 100% rename from test/semantics/getsymbols05.f90 rename to test/Semantics/getsymbols05.f90 diff --git a/test/semantics/if_arith01.f90 b/test/Semantics/if_arith01.f90 similarity index 100% rename from test/semantics/if_arith01.f90 rename to test/Semantics/if_arith01.f90 diff --git a/test/semantics/if_arith02.f90 b/test/Semantics/if_arith02.f90 similarity index 100% rename from test/semantics/if_arith02.f90 rename to test/Semantics/if_arith02.f90 diff --git a/test/semantics/if_arith03.f90 b/test/Semantics/if_arith03.f90 similarity index 100% rename from test/semantics/if_arith03.f90 rename to test/Semantics/if_arith03.f90 diff --git a/test/semantics/if_arith04.f90 b/test/Semantics/if_arith04.f90 similarity index 100% rename from test/semantics/if_arith04.f90 rename to test/Semantics/if_arith04.f90 diff --git a/test/semantics/if_construct01.f90 b/test/Semantics/if_construct01.f90 similarity index 100% rename from test/semantics/if_construct01.f90 rename to test/Semantics/if_construct01.f90 diff --git a/test/semantics/if_construct02.f90 b/test/Semantics/if_construct02.f90 similarity index 100% rename from test/semantics/if_construct02.f90 rename to test/Semantics/if_construct02.f90 diff --git a/test/semantics/if_stmt01.f90 b/test/Semantics/if_stmt01.f90 similarity index 100% rename from test/semantics/if_stmt01.f90 rename to test/Semantics/if_stmt01.f90 diff --git a/test/semantics/if_stmt02.f90 b/test/Semantics/if_stmt02.f90 similarity index 100% rename from test/semantics/if_stmt02.f90 rename to test/Semantics/if_stmt02.f90 diff --git a/test/semantics/if_stmt03.f90 b/test/Semantics/if_stmt03.f90 similarity index 100% rename from test/semantics/if_stmt03.f90 rename to test/Semantics/if_stmt03.f90 diff --git a/test/semantics/implicit01.f90 b/test/Semantics/implicit01.f90 similarity index 100% rename from test/semantics/implicit01.f90 rename to test/Semantics/implicit01.f90 diff --git a/test/semantics/implicit02.f90 b/test/Semantics/implicit02.f90 similarity index 100% rename from test/semantics/implicit02.f90 rename to test/Semantics/implicit02.f90 diff --git a/test/semantics/implicit03.f90 b/test/Semantics/implicit03.f90 similarity index 100% rename from test/semantics/implicit03.f90 rename to test/Semantics/implicit03.f90 diff --git a/test/semantics/implicit04.f90 b/test/Semantics/implicit04.f90 similarity index 100% rename from test/semantics/implicit04.f90 rename to test/Semantics/implicit04.f90 diff --git a/test/semantics/implicit05.f90 b/test/Semantics/implicit05.f90 similarity index 100% rename from test/semantics/implicit05.f90 rename to test/Semantics/implicit05.f90 diff --git a/test/semantics/implicit06.f90 b/test/Semantics/implicit06.f90 similarity index 100% rename from test/semantics/implicit06.f90 rename to test/Semantics/implicit06.f90 diff --git a/test/semantics/implicit07.f90 b/test/Semantics/implicit07.f90 similarity index 100% rename from test/semantics/implicit07.f90 rename to test/Semantics/implicit07.f90 diff --git a/test/semantics/implicit08.f90 b/test/Semantics/implicit08.f90 similarity index 100% rename from test/semantics/implicit08.f90 rename to test/Semantics/implicit08.f90 diff --git a/test/semantics/init01.f90 b/test/Semantics/init01.f90 similarity index 100% rename from test/semantics/init01.f90 rename to test/Semantics/init01.f90 diff --git a/test/semantics/int-literals.f90 b/test/Semantics/int-literals.f90 similarity index 100% rename from test/semantics/int-literals.f90 rename to test/Semantics/int-literals.f90 diff --git a/test/semantics/io01.f90 b/test/Semantics/io01.f90 similarity index 100% rename from test/semantics/io01.f90 rename to test/Semantics/io01.f90 diff --git a/test/semantics/io02.f90 b/test/Semantics/io02.f90 similarity index 100% rename from test/semantics/io02.f90 rename to test/Semantics/io02.f90 diff --git a/test/semantics/io03.f90 b/test/Semantics/io03.f90 similarity index 100% rename from test/semantics/io03.f90 rename to test/Semantics/io03.f90 diff --git a/test/semantics/io04.f90 b/test/Semantics/io04.f90 similarity index 100% rename from test/semantics/io04.f90 rename to test/Semantics/io04.f90 diff --git a/test/semantics/io05.f90 b/test/Semantics/io05.f90 similarity index 100% rename from test/semantics/io05.f90 rename to test/Semantics/io05.f90 diff --git a/test/semantics/io06.f90 b/test/Semantics/io06.f90 similarity index 100% rename from test/semantics/io06.f90 rename to test/Semantics/io06.f90 diff --git a/test/semantics/io07.f90 b/test/Semantics/io07.f90 similarity index 100% rename from test/semantics/io07.f90 rename to test/Semantics/io07.f90 diff --git a/test/semantics/io08.f90 b/test/Semantics/io08.f90 similarity index 100% rename from test/semantics/io08.f90 rename to test/Semantics/io08.f90 diff --git a/test/semantics/io09.f90 b/test/Semantics/io09.f90 similarity index 100% rename from test/semantics/io09.f90 rename to test/Semantics/io09.f90 diff --git a/test/semantics/io10.f90 b/test/Semantics/io10.f90 similarity index 100% rename from test/semantics/io10.f90 rename to test/Semantics/io10.f90 diff --git a/test/semantics/kinds01.f90 b/test/Semantics/kinds01.f90 similarity index 100% rename from test/semantics/kinds01.f90 rename to test/Semantics/kinds01.f90 diff --git a/test/semantics/kinds02.f90 b/test/Semantics/kinds02.f90 similarity index 100% rename from test/semantics/kinds02.f90 rename to test/Semantics/kinds02.f90 diff --git a/test/semantics/kinds03.f90 b/test/Semantics/kinds03.f90 similarity index 100% rename from test/semantics/kinds03.f90 rename to test/Semantics/kinds03.f90 diff --git a/test/semantics/label01.F90 b/test/Semantics/label01.F90 similarity index 100% rename from test/semantics/label01.F90 rename to test/Semantics/label01.F90 diff --git a/test/semantics/label02.f90 b/test/Semantics/label02.f90 similarity index 100% rename from test/semantics/label02.f90 rename to test/Semantics/label02.f90 diff --git a/test/semantics/label03.f90 b/test/Semantics/label03.f90 similarity index 100% rename from test/semantics/label03.f90 rename to test/Semantics/label03.f90 diff --git a/test/semantics/label04.f90 b/test/Semantics/label04.f90 similarity index 100% rename from test/semantics/label04.f90 rename to test/Semantics/label04.f90 diff --git a/test/semantics/label05.f90 b/test/Semantics/label05.f90 similarity index 100% rename from test/semantics/label05.f90 rename to test/Semantics/label05.f90 diff --git a/test/semantics/label06.f90 b/test/Semantics/label06.f90 similarity index 100% rename from test/semantics/label06.f90 rename to test/Semantics/label06.f90 diff --git a/test/semantics/label07.f90 b/test/Semantics/label07.f90 similarity index 100% rename from test/semantics/label07.f90 rename to test/Semantics/label07.f90 diff --git a/test/semantics/label08.f90 b/test/Semantics/label08.f90 similarity index 100% rename from test/semantics/label08.f90 rename to test/Semantics/label08.f90 diff --git a/test/semantics/label09.f90 b/test/Semantics/label09.f90 similarity index 100% rename from test/semantics/label09.f90 rename to test/Semantics/label09.f90 diff --git a/test/semantics/label10.f90 b/test/Semantics/label10.f90 similarity index 100% rename from test/semantics/label10.f90 rename to test/Semantics/label10.f90 diff --git a/test/semantics/label11.f90 b/test/Semantics/label11.f90 similarity index 100% rename from test/semantics/label11.f90 rename to test/Semantics/label11.f90 diff --git a/test/semantics/label12.f90 b/test/Semantics/label12.f90 similarity index 100% rename from test/semantics/label12.f90 rename to test/Semantics/label12.f90 diff --git a/test/semantics/label13.f90 b/test/Semantics/label13.f90 similarity index 100% rename from test/semantics/label13.f90 rename to test/Semantics/label13.f90 diff --git a/test/semantics/label14.f90 b/test/Semantics/label14.f90 similarity index 100% rename from test/semantics/label14.f90 rename to test/Semantics/label14.f90 diff --git a/test/semantics/misc-declarations.f90 b/test/Semantics/misc-declarations.f90 similarity index 100% rename from test/semantics/misc-declarations.f90 rename to test/Semantics/misc-declarations.f90 diff --git a/test/semantics/modfile01.f90 b/test/Semantics/modfile01.f90 similarity index 100% rename from test/semantics/modfile01.f90 rename to test/Semantics/modfile01.f90 diff --git a/test/semantics/modfile02.f90 b/test/Semantics/modfile02.f90 similarity index 100% rename from test/semantics/modfile02.f90 rename to test/Semantics/modfile02.f90 diff --git a/test/semantics/modfile03.f90 b/test/Semantics/modfile03.f90 similarity index 100% rename from test/semantics/modfile03.f90 rename to test/Semantics/modfile03.f90 diff --git a/test/semantics/modfile04.f90 b/test/Semantics/modfile04.f90 similarity index 100% rename from test/semantics/modfile04.f90 rename to test/Semantics/modfile04.f90 diff --git a/test/semantics/modfile05.f90 b/test/Semantics/modfile05.f90 similarity index 100% rename from test/semantics/modfile05.f90 rename to test/Semantics/modfile05.f90 diff --git a/test/semantics/modfile06.f90 b/test/Semantics/modfile06.f90 similarity index 100% rename from test/semantics/modfile06.f90 rename to test/Semantics/modfile06.f90 diff --git a/test/semantics/modfile07.f90 b/test/Semantics/modfile07.f90 similarity index 100% rename from test/semantics/modfile07.f90 rename to test/Semantics/modfile07.f90 diff --git a/test/semantics/modfile08.f90 b/test/Semantics/modfile08.f90 similarity index 100% rename from test/semantics/modfile08.f90 rename to test/Semantics/modfile08.f90 diff --git a/test/semantics/modfile09-a.f90 b/test/Semantics/modfile09-a.f90 similarity index 100% rename from test/semantics/modfile09-a.f90 rename to test/Semantics/modfile09-a.f90 diff --git a/test/semantics/modfile09-b.f90 b/test/Semantics/modfile09-b.f90 similarity index 100% rename from test/semantics/modfile09-b.f90 rename to test/Semantics/modfile09-b.f90 diff --git a/test/semantics/modfile09-c.f90 b/test/Semantics/modfile09-c.f90 similarity index 100% rename from test/semantics/modfile09-c.f90 rename to test/Semantics/modfile09-c.f90 diff --git a/test/semantics/modfile09-d.f90 b/test/Semantics/modfile09-d.f90 similarity index 100% rename from test/semantics/modfile09-d.f90 rename to test/Semantics/modfile09-d.f90 diff --git a/test/semantics/modfile10.f90 b/test/Semantics/modfile10.f90 similarity index 100% rename from test/semantics/modfile10.f90 rename to test/Semantics/modfile10.f90 diff --git a/test/semantics/modfile11.f90 b/test/Semantics/modfile11.f90 similarity index 100% rename from test/semantics/modfile11.f90 rename to test/Semantics/modfile11.f90 diff --git a/test/semantics/modfile12.f90 b/test/Semantics/modfile12.f90 similarity index 100% rename from test/semantics/modfile12.f90 rename to test/Semantics/modfile12.f90 diff --git a/test/semantics/modfile13.f90 b/test/Semantics/modfile13.f90 similarity index 100% rename from test/semantics/modfile13.f90 rename to test/Semantics/modfile13.f90 diff --git a/test/semantics/modfile14.f90 b/test/Semantics/modfile14.f90 similarity index 100% rename from test/semantics/modfile14.f90 rename to test/Semantics/modfile14.f90 diff --git a/test/semantics/modfile15.f90 b/test/Semantics/modfile15.f90 similarity index 100% rename from test/semantics/modfile15.f90 rename to test/Semantics/modfile15.f90 diff --git a/test/semantics/modfile16.f90 b/test/Semantics/modfile16.f90 similarity index 100% rename from test/semantics/modfile16.f90 rename to test/Semantics/modfile16.f90 diff --git a/test/semantics/modfile17.f90 b/test/Semantics/modfile17.f90 similarity index 100% rename from test/semantics/modfile17.f90 rename to test/Semantics/modfile17.f90 diff --git a/test/semantics/modfile18.f90 b/test/Semantics/modfile18.f90 similarity index 100% rename from test/semantics/modfile18.f90 rename to test/Semantics/modfile18.f90 diff --git a/test/semantics/modfile19.f90 b/test/Semantics/modfile19.f90 similarity index 100% rename from test/semantics/modfile19.f90 rename to test/Semantics/modfile19.f90 diff --git a/test/semantics/modfile20.f90 b/test/Semantics/modfile20.f90 similarity index 100% rename from test/semantics/modfile20.f90 rename to test/Semantics/modfile20.f90 diff --git a/test/semantics/modfile21.f90 b/test/Semantics/modfile21.f90 similarity index 100% rename from test/semantics/modfile21.f90 rename to test/Semantics/modfile21.f90 diff --git a/test/semantics/modfile22.f90 b/test/Semantics/modfile22.f90 similarity index 100% rename from test/semantics/modfile22.f90 rename to test/Semantics/modfile22.f90 diff --git a/test/semantics/modfile23.f90 b/test/Semantics/modfile23.f90 similarity index 100% rename from test/semantics/modfile23.f90 rename to test/Semantics/modfile23.f90 diff --git a/test/semantics/modfile24.f90 b/test/Semantics/modfile24.f90 similarity index 100% rename from test/semantics/modfile24.f90 rename to test/Semantics/modfile24.f90 diff --git a/test/semantics/modfile25.f90 b/test/Semantics/modfile25.f90 similarity index 100% rename from test/semantics/modfile25.f90 rename to test/Semantics/modfile25.f90 diff --git a/test/semantics/modfile26.f90 b/test/Semantics/modfile26.f90 similarity index 100% rename from test/semantics/modfile26.f90 rename to test/Semantics/modfile26.f90 diff --git a/test/semantics/modfile27.f90 b/test/Semantics/modfile27.f90 similarity index 100% rename from test/semantics/modfile27.f90 rename to test/Semantics/modfile27.f90 diff --git a/test/semantics/modfile28.f90 b/test/Semantics/modfile28.f90 similarity index 100% rename from test/semantics/modfile28.f90 rename to test/Semantics/modfile28.f90 diff --git a/test/semantics/modfile29.f90 b/test/Semantics/modfile29.f90 similarity index 100% rename from test/semantics/modfile29.f90 rename to test/Semantics/modfile29.f90 diff --git a/test/semantics/modfile30.f90 b/test/Semantics/modfile30.f90 similarity index 100% rename from test/semantics/modfile30.f90 rename to test/Semantics/modfile30.f90 diff --git a/test/semantics/modfile31.f90 b/test/Semantics/modfile31.f90 similarity index 100% rename from test/semantics/modfile31.f90 rename to test/Semantics/modfile31.f90 diff --git a/test/semantics/modfile32.f90 b/test/Semantics/modfile32.f90 similarity index 100% rename from test/semantics/modfile32.f90 rename to test/Semantics/modfile32.f90 diff --git a/test/semantics/modfile33.f90 b/test/Semantics/modfile33.f90 similarity index 100% rename from test/semantics/modfile33.f90 rename to test/Semantics/modfile33.f90 diff --git a/test/semantics/modfile34.f90 b/test/Semantics/modfile34.f90 similarity index 100% rename from test/semantics/modfile34.f90 rename to test/Semantics/modfile34.f90 diff --git a/test/semantics/modfile35.f90 b/test/Semantics/modfile35.f90 similarity index 100% rename from test/semantics/modfile35.f90 rename to test/Semantics/modfile35.f90 diff --git a/test/semantics/null01.f90 b/test/Semantics/null01.f90 similarity index 100% rename from test/semantics/null01.f90 rename to test/Semantics/null01.f90 diff --git a/test/semantics/nullify01.f90 b/test/Semantics/nullify01.f90 similarity index 100% rename from test/semantics/nullify01.f90 rename to test/Semantics/nullify01.f90 diff --git a/test/semantics/nullify02.f90 b/test/Semantics/nullify02.f90 similarity index 100% rename from test/semantics/nullify02.f90 rename to test/Semantics/nullify02.f90 diff --git a/test/semantics/omp-atomic.f90 b/test/Semantics/omp-atomic.f90 similarity index 100% rename from test/semantics/omp-atomic.f90 rename to test/Semantics/omp-atomic.f90 diff --git a/test/semantics/omp-clause-validity01.f90 b/test/Semantics/omp-clause-validity01.f90 similarity index 100% rename from test/semantics/omp-clause-validity01.f90 rename to test/Semantics/omp-clause-validity01.f90 diff --git a/test/semantics/omp-declarative-directive.f90 b/test/Semantics/omp-declarative-directive.f90 similarity index 100% rename from test/semantics/omp-declarative-directive.f90 rename to test/Semantics/omp-declarative-directive.f90 diff --git a/test/semantics/omp-device-constructs.f90 b/test/Semantics/omp-device-constructs.f90 similarity index 100% rename from test/semantics/omp-device-constructs.f90 rename to test/Semantics/omp-device-constructs.f90 diff --git a/test/semantics/omp-loop-association.f90 b/test/Semantics/omp-loop-association.f90 similarity index 100% rename from test/semantics/omp-loop-association.f90 rename to test/Semantics/omp-loop-association.f90 diff --git a/test/semantics/omp-nested01.f90 b/test/Semantics/omp-nested01.f90 similarity index 100% rename from test/semantics/omp-nested01.f90 rename to test/Semantics/omp-nested01.f90 diff --git a/test/semantics/omp-resolve01.f90 b/test/Semantics/omp-resolve01.f90 similarity index 100% rename from test/semantics/omp-resolve01.f90 rename to test/Semantics/omp-resolve01.f90 diff --git a/test/semantics/omp-resolve02.f90 b/test/Semantics/omp-resolve02.f90 similarity index 100% rename from test/semantics/omp-resolve02.f90 rename to test/Semantics/omp-resolve02.f90 diff --git a/test/semantics/omp-resolve03.f90 b/test/Semantics/omp-resolve03.f90 similarity index 100% rename from test/semantics/omp-resolve03.f90 rename to test/Semantics/omp-resolve03.f90 diff --git a/test/semantics/omp-resolve04.f90 b/test/Semantics/omp-resolve04.f90 similarity index 100% rename from test/semantics/omp-resolve04.f90 rename to test/Semantics/omp-resolve04.f90 diff --git a/test/semantics/omp-resolve05.f90 b/test/Semantics/omp-resolve05.f90 similarity index 100% rename from test/semantics/omp-resolve05.f90 rename to test/Semantics/omp-resolve05.f90 diff --git a/test/semantics/omp-symbol01.f90 b/test/Semantics/omp-symbol01.f90 similarity index 100% rename from test/semantics/omp-symbol01.f90 rename to test/Semantics/omp-symbol01.f90 diff --git a/test/semantics/omp-symbol02.f90 b/test/Semantics/omp-symbol02.f90 similarity index 100% rename from test/semantics/omp-symbol02.f90 rename to test/Semantics/omp-symbol02.f90 diff --git a/test/semantics/omp-symbol03.f90 b/test/Semantics/omp-symbol03.f90 similarity index 100% rename from test/semantics/omp-symbol03.f90 rename to test/Semantics/omp-symbol03.f90 diff --git a/test/semantics/omp-symbol04.f90 b/test/Semantics/omp-symbol04.f90 similarity index 100% rename from test/semantics/omp-symbol04.f90 rename to test/Semantics/omp-symbol04.f90 diff --git a/test/semantics/omp-symbol05.f90 b/test/Semantics/omp-symbol05.f90 similarity index 100% rename from test/semantics/omp-symbol05.f90 rename to test/Semantics/omp-symbol05.f90 diff --git a/test/semantics/omp-symbol06.f90 b/test/Semantics/omp-symbol06.f90 similarity index 100% rename from test/semantics/omp-symbol06.f90 rename to test/Semantics/omp-symbol06.f90 diff --git a/test/semantics/omp-symbol07.f90 b/test/Semantics/omp-symbol07.f90 similarity index 100% rename from test/semantics/omp-symbol07.f90 rename to test/Semantics/omp-symbol07.f90 diff --git a/test/semantics/omp-symbol08.f90 b/test/Semantics/omp-symbol08.f90 similarity index 100% rename from test/semantics/omp-symbol08.f90 rename to test/Semantics/omp-symbol08.f90 diff --git a/test/semantics/procinterface01.f90 b/test/Semantics/procinterface01.f90 similarity index 100% rename from test/semantics/procinterface01.f90 rename to test/Semantics/procinterface01.f90 diff --git a/test/semantics/resolve01.f90 b/test/Semantics/resolve01.f90 similarity index 100% rename from test/semantics/resolve01.f90 rename to test/Semantics/resolve01.f90 diff --git a/test/semantics/resolve02.f90 b/test/Semantics/resolve02.f90 similarity index 100% rename from test/semantics/resolve02.f90 rename to test/Semantics/resolve02.f90 diff --git a/test/semantics/resolve03.f90 b/test/Semantics/resolve03.f90 similarity index 100% rename from test/semantics/resolve03.f90 rename to test/Semantics/resolve03.f90 diff --git a/test/semantics/resolve04.f90 b/test/Semantics/resolve04.f90 similarity index 100% rename from test/semantics/resolve04.f90 rename to test/Semantics/resolve04.f90 diff --git a/test/semantics/resolve05.f90 b/test/Semantics/resolve05.f90 similarity index 100% rename from test/semantics/resolve05.f90 rename to test/Semantics/resolve05.f90 diff --git a/test/semantics/resolve06.f90 b/test/Semantics/resolve06.f90 similarity index 100% rename from test/semantics/resolve06.f90 rename to test/Semantics/resolve06.f90 diff --git a/test/semantics/resolve07.f90 b/test/Semantics/resolve07.f90 similarity index 100% rename from test/semantics/resolve07.f90 rename to test/Semantics/resolve07.f90 diff --git a/test/semantics/resolve08.f90 b/test/Semantics/resolve08.f90 similarity index 100% rename from test/semantics/resolve08.f90 rename to test/Semantics/resolve08.f90 diff --git a/test/semantics/resolve09.f90 b/test/Semantics/resolve09.f90 similarity index 100% rename from test/semantics/resolve09.f90 rename to test/Semantics/resolve09.f90 diff --git a/test/semantics/resolve10.f90 b/test/Semantics/resolve10.f90 similarity index 100% rename from test/semantics/resolve10.f90 rename to test/Semantics/resolve10.f90 diff --git a/test/semantics/resolve11.f90 b/test/Semantics/resolve11.f90 similarity index 100% rename from test/semantics/resolve11.f90 rename to test/Semantics/resolve11.f90 diff --git a/test/semantics/resolve12.f90 b/test/Semantics/resolve12.f90 similarity index 100% rename from test/semantics/resolve12.f90 rename to test/Semantics/resolve12.f90 diff --git a/test/semantics/resolve13.f90 b/test/Semantics/resolve13.f90 similarity index 100% rename from test/semantics/resolve13.f90 rename to test/Semantics/resolve13.f90 diff --git a/test/semantics/resolve14.f90 b/test/Semantics/resolve14.f90 similarity index 100% rename from test/semantics/resolve14.f90 rename to test/Semantics/resolve14.f90 diff --git a/test/semantics/resolve15.f90 b/test/Semantics/resolve15.f90 similarity index 100% rename from test/semantics/resolve15.f90 rename to test/Semantics/resolve15.f90 diff --git a/test/semantics/resolve16.f90 b/test/Semantics/resolve16.f90 similarity index 100% rename from test/semantics/resolve16.f90 rename to test/Semantics/resolve16.f90 diff --git a/test/semantics/resolve17.f90 b/test/Semantics/resolve17.f90 similarity index 100% rename from test/semantics/resolve17.f90 rename to test/Semantics/resolve17.f90 diff --git a/test/semantics/resolve18.f90 b/test/Semantics/resolve18.f90 similarity index 100% rename from test/semantics/resolve18.f90 rename to test/Semantics/resolve18.f90 diff --git a/test/semantics/resolve19.f90 b/test/Semantics/resolve19.f90 similarity index 100% rename from test/semantics/resolve19.f90 rename to test/Semantics/resolve19.f90 diff --git a/test/semantics/resolve20.f90 b/test/Semantics/resolve20.f90 similarity index 100% rename from test/semantics/resolve20.f90 rename to test/Semantics/resolve20.f90 diff --git a/test/semantics/resolve21.f90 b/test/Semantics/resolve21.f90 similarity index 100% rename from test/semantics/resolve21.f90 rename to test/Semantics/resolve21.f90 diff --git a/test/semantics/resolve22.f90 b/test/Semantics/resolve22.f90 similarity index 100% rename from test/semantics/resolve22.f90 rename to test/Semantics/resolve22.f90 diff --git a/test/semantics/resolve23.f90 b/test/Semantics/resolve23.f90 similarity index 100% rename from test/semantics/resolve23.f90 rename to test/Semantics/resolve23.f90 diff --git a/test/semantics/resolve24.f90 b/test/Semantics/resolve24.f90 similarity index 100% rename from test/semantics/resolve24.f90 rename to test/Semantics/resolve24.f90 diff --git a/test/semantics/resolve25.f90 b/test/Semantics/resolve25.f90 similarity index 100% rename from test/semantics/resolve25.f90 rename to test/Semantics/resolve25.f90 diff --git a/test/semantics/resolve26.f90 b/test/Semantics/resolve26.f90 similarity index 100% rename from test/semantics/resolve26.f90 rename to test/Semantics/resolve26.f90 diff --git a/test/semantics/resolve27.f90 b/test/Semantics/resolve27.f90 similarity index 100% rename from test/semantics/resolve27.f90 rename to test/Semantics/resolve27.f90 diff --git a/test/semantics/resolve28.f90 b/test/Semantics/resolve28.f90 similarity index 100% rename from test/semantics/resolve28.f90 rename to test/Semantics/resolve28.f90 diff --git a/test/semantics/resolve29.f90 b/test/Semantics/resolve29.f90 similarity index 100% rename from test/semantics/resolve29.f90 rename to test/Semantics/resolve29.f90 diff --git a/test/semantics/resolve30.f90 b/test/Semantics/resolve30.f90 similarity index 100% rename from test/semantics/resolve30.f90 rename to test/Semantics/resolve30.f90 diff --git a/test/semantics/resolve31.f90 b/test/Semantics/resolve31.f90 similarity index 100% rename from test/semantics/resolve31.f90 rename to test/Semantics/resolve31.f90 diff --git a/test/semantics/resolve32.f90 b/test/Semantics/resolve32.f90 similarity index 100% rename from test/semantics/resolve32.f90 rename to test/Semantics/resolve32.f90 diff --git a/test/semantics/resolve33.f90 b/test/Semantics/resolve33.f90 similarity index 100% rename from test/semantics/resolve33.f90 rename to test/Semantics/resolve33.f90 diff --git a/test/semantics/resolve34.f90 b/test/Semantics/resolve34.f90 similarity index 100% rename from test/semantics/resolve34.f90 rename to test/Semantics/resolve34.f90 diff --git a/test/semantics/resolve35.f90 b/test/Semantics/resolve35.f90 similarity index 100% rename from test/semantics/resolve35.f90 rename to test/Semantics/resolve35.f90 diff --git a/test/semantics/resolve36.f90 b/test/Semantics/resolve36.f90 similarity index 100% rename from test/semantics/resolve36.f90 rename to test/Semantics/resolve36.f90 diff --git a/test/semantics/resolve37.f90 b/test/Semantics/resolve37.f90 similarity index 100% rename from test/semantics/resolve37.f90 rename to test/Semantics/resolve37.f90 diff --git a/test/semantics/resolve38.f90 b/test/Semantics/resolve38.f90 similarity index 100% rename from test/semantics/resolve38.f90 rename to test/Semantics/resolve38.f90 diff --git a/test/semantics/resolve39.f90 b/test/Semantics/resolve39.f90 similarity index 100% rename from test/semantics/resolve39.f90 rename to test/Semantics/resolve39.f90 diff --git a/test/semantics/resolve40.f90 b/test/Semantics/resolve40.f90 similarity index 100% rename from test/semantics/resolve40.f90 rename to test/Semantics/resolve40.f90 diff --git a/test/semantics/resolve41.f90 b/test/Semantics/resolve41.f90 similarity index 100% rename from test/semantics/resolve41.f90 rename to test/Semantics/resolve41.f90 diff --git a/test/semantics/resolve42.f90 b/test/Semantics/resolve42.f90 similarity index 100% rename from test/semantics/resolve42.f90 rename to test/Semantics/resolve42.f90 diff --git a/test/semantics/resolve43.f90 b/test/Semantics/resolve43.f90 similarity index 100% rename from test/semantics/resolve43.f90 rename to test/Semantics/resolve43.f90 diff --git a/test/semantics/resolve44.f90 b/test/Semantics/resolve44.f90 similarity index 100% rename from test/semantics/resolve44.f90 rename to test/Semantics/resolve44.f90 diff --git a/test/semantics/resolve45.f90 b/test/Semantics/resolve45.f90 similarity index 100% rename from test/semantics/resolve45.f90 rename to test/Semantics/resolve45.f90 diff --git a/test/semantics/resolve46.f90 b/test/Semantics/resolve46.f90 similarity index 100% rename from test/semantics/resolve46.f90 rename to test/Semantics/resolve46.f90 diff --git a/test/semantics/resolve47.f90 b/test/Semantics/resolve47.f90 similarity index 100% rename from test/semantics/resolve47.f90 rename to test/Semantics/resolve47.f90 diff --git a/test/semantics/resolve48.f90 b/test/Semantics/resolve48.f90 similarity index 100% rename from test/semantics/resolve48.f90 rename to test/Semantics/resolve48.f90 diff --git a/test/semantics/resolve49.f90 b/test/Semantics/resolve49.f90 similarity index 100% rename from test/semantics/resolve49.f90 rename to test/Semantics/resolve49.f90 diff --git a/test/semantics/resolve50.f90 b/test/Semantics/resolve50.f90 similarity index 100% rename from test/semantics/resolve50.f90 rename to test/Semantics/resolve50.f90 diff --git a/test/semantics/resolve51.f90 b/test/Semantics/resolve51.f90 similarity index 100% rename from test/semantics/resolve51.f90 rename to test/Semantics/resolve51.f90 diff --git a/test/semantics/resolve52.f90 b/test/Semantics/resolve52.f90 similarity index 100% rename from test/semantics/resolve52.f90 rename to test/Semantics/resolve52.f90 diff --git a/test/semantics/resolve53.f90 b/test/Semantics/resolve53.f90 similarity index 100% rename from test/semantics/resolve53.f90 rename to test/Semantics/resolve53.f90 diff --git a/test/semantics/resolve54.f90 b/test/Semantics/resolve54.f90 similarity index 100% rename from test/semantics/resolve54.f90 rename to test/Semantics/resolve54.f90 diff --git a/test/semantics/resolve55.f90 b/test/Semantics/resolve55.f90 similarity index 100% rename from test/semantics/resolve55.f90 rename to test/Semantics/resolve55.f90 diff --git a/test/semantics/resolve56.f90 b/test/Semantics/resolve56.f90 similarity index 100% rename from test/semantics/resolve56.f90 rename to test/Semantics/resolve56.f90 diff --git a/test/semantics/resolve57.f90 b/test/Semantics/resolve57.f90 similarity index 100% rename from test/semantics/resolve57.f90 rename to test/Semantics/resolve57.f90 diff --git a/test/semantics/resolve58.f90 b/test/Semantics/resolve58.f90 similarity index 100% rename from test/semantics/resolve58.f90 rename to test/Semantics/resolve58.f90 diff --git a/test/semantics/resolve59.f90 b/test/Semantics/resolve59.f90 similarity index 100% rename from test/semantics/resolve59.f90 rename to test/Semantics/resolve59.f90 diff --git a/test/semantics/resolve60.f90 b/test/Semantics/resolve60.f90 similarity index 100% rename from test/semantics/resolve60.f90 rename to test/Semantics/resolve60.f90 diff --git a/test/semantics/resolve61.f90 b/test/Semantics/resolve61.f90 similarity index 100% rename from test/semantics/resolve61.f90 rename to test/Semantics/resolve61.f90 diff --git a/test/semantics/resolve62.f90 b/test/Semantics/resolve62.f90 similarity index 100% rename from test/semantics/resolve62.f90 rename to test/Semantics/resolve62.f90 diff --git a/test/semantics/resolve63.f90 b/test/Semantics/resolve63.f90 similarity index 100% rename from test/semantics/resolve63.f90 rename to test/Semantics/resolve63.f90 diff --git a/test/semantics/resolve64.f90 b/test/Semantics/resolve64.f90 similarity index 100% rename from test/semantics/resolve64.f90 rename to test/Semantics/resolve64.f90 diff --git a/test/semantics/resolve65.f90 b/test/Semantics/resolve65.f90 similarity index 100% rename from test/semantics/resolve65.f90 rename to test/Semantics/resolve65.f90 diff --git a/test/semantics/resolve66.f90 b/test/Semantics/resolve66.f90 similarity index 100% rename from test/semantics/resolve66.f90 rename to test/Semantics/resolve66.f90 diff --git a/test/semantics/resolve67.f90 b/test/Semantics/resolve67.f90 similarity index 100% rename from test/semantics/resolve67.f90 rename to test/Semantics/resolve67.f90 diff --git a/test/semantics/resolve68.f90 b/test/Semantics/resolve68.f90 similarity index 100% rename from test/semantics/resolve68.f90 rename to test/Semantics/resolve68.f90 diff --git a/test/semantics/resolve69.f90 b/test/Semantics/resolve69.f90 similarity index 100% rename from test/semantics/resolve69.f90 rename to test/Semantics/resolve69.f90 diff --git a/test/semantics/resolve70.f90 b/test/Semantics/resolve70.f90 similarity index 100% rename from test/semantics/resolve70.f90 rename to test/Semantics/resolve70.f90 diff --git a/test/semantics/resolve71.f90 b/test/Semantics/resolve71.f90 similarity index 100% rename from test/semantics/resolve71.f90 rename to test/Semantics/resolve71.f90 diff --git a/test/semantics/resolve72.f90 b/test/Semantics/resolve72.f90 similarity index 100% rename from test/semantics/resolve72.f90 rename to test/Semantics/resolve72.f90 diff --git a/test/semantics/separate-module-procs.f90 b/test/Semantics/separate-module-procs.f90 similarity index 100% rename from test/semantics/separate-module-procs.f90 rename to test/Semantics/separate-module-procs.f90 diff --git a/test/semantics/stop01.f90 b/test/Semantics/stop01.f90 similarity index 100% rename from test/semantics/stop01.f90 rename to test/Semantics/stop01.f90 diff --git a/test/semantics/structconst01.f90 b/test/Semantics/structconst01.f90 similarity index 100% rename from test/semantics/structconst01.f90 rename to test/Semantics/structconst01.f90 diff --git a/test/semantics/structconst02.f90 b/test/Semantics/structconst02.f90 similarity index 100% rename from test/semantics/structconst02.f90 rename to test/Semantics/structconst02.f90 diff --git a/test/semantics/structconst03.f90 b/test/Semantics/structconst03.f90 similarity index 98% rename from test/semantics/structconst03.f90 rename to test/Semantics/structconst03.f90 index 7860e7ce366c..e637bc08d3e3 100644 --- a/test/semantics/structconst03.f90 +++ b/test/Semantics/structconst03.f90 @@ -1,6 +1,6 @@ ! Error tests for structure constructors: C1594 violations ! from assigning globally-visible data to POINTER components. -! test/semantics/structconst04.f90 is this same test without type +! test/Semantics/structconst04.f90 is this same test without type ! parameters. module usefrom diff --git a/test/semantics/structconst04.f90 b/test/Semantics/structconst04.f90 similarity index 100% rename from test/semantics/structconst04.f90 rename to test/Semantics/structconst04.f90 diff --git a/test/semantics/symbol01.f90 b/test/Semantics/symbol01.f90 similarity index 100% rename from test/semantics/symbol01.f90 rename to test/Semantics/symbol01.f90 diff --git a/test/semantics/symbol02.f90 b/test/Semantics/symbol02.f90 similarity index 100% rename from test/semantics/symbol02.f90 rename to test/Semantics/symbol02.f90 diff --git a/test/semantics/symbol03.f90 b/test/Semantics/symbol03.f90 similarity index 100% rename from test/semantics/symbol03.f90 rename to test/Semantics/symbol03.f90 diff --git a/test/semantics/symbol05.f90 b/test/Semantics/symbol05.f90 similarity index 100% rename from test/semantics/symbol05.f90 rename to test/Semantics/symbol05.f90 diff --git a/test/semantics/symbol06.f90 b/test/Semantics/symbol06.f90 similarity index 100% rename from test/semantics/symbol06.f90 rename to test/Semantics/symbol06.f90 diff --git a/test/semantics/symbol07.f90 b/test/Semantics/symbol07.f90 similarity index 100% rename from test/semantics/symbol07.f90 rename to test/Semantics/symbol07.f90 diff --git a/test/semantics/symbol08.f90 b/test/Semantics/symbol08.f90 similarity index 100% rename from test/semantics/symbol08.f90 rename to test/Semantics/symbol08.f90 diff --git a/test/semantics/symbol09.f90 b/test/Semantics/symbol09.f90 similarity index 100% rename from test/semantics/symbol09.f90 rename to test/Semantics/symbol09.f90 diff --git a/test/semantics/symbol10.f90 b/test/Semantics/symbol10.f90 similarity index 100% rename from test/semantics/symbol10.f90 rename to test/Semantics/symbol10.f90 diff --git a/test/semantics/symbol11.f90 b/test/Semantics/symbol11.f90 similarity index 100% rename from test/semantics/symbol11.f90 rename to test/Semantics/symbol11.f90 diff --git a/test/semantics/symbol12.f90 b/test/Semantics/symbol12.f90 similarity index 100% rename from test/semantics/symbol12.f90 rename to test/Semantics/symbol12.f90 diff --git a/test/semantics/symbol13.f90 b/test/Semantics/symbol13.f90 similarity index 100% rename from test/semantics/symbol13.f90 rename to test/Semantics/symbol13.f90 diff --git a/test/semantics/symbol14.f90 b/test/Semantics/symbol14.f90 similarity index 100% rename from test/semantics/symbol14.f90 rename to test/Semantics/symbol14.f90 diff --git a/test/semantics/symbol15.f90 b/test/Semantics/symbol15.f90 similarity index 100% rename from test/semantics/symbol15.f90 rename to test/Semantics/symbol15.f90 diff --git a/test/semantics/symbol16.f90 b/test/Semantics/symbol16.f90 similarity index 100% rename from test/semantics/symbol16.f90 rename to test/Semantics/symbol16.f90 diff --git a/test/semantics/symbol17.f90 b/test/Semantics/symbol17.f90 similarity index 100% rename from test/semantics/symbol17.f90 rename to test/Semantics/symbol17.f90 diff --git a/test/semantics/test_any.sh b/test/Semantics/test_any.sh similarity index 100% rename from test/semantics/test_any.sh rename to test/Semantics/test_any.sh diff --git a/test/semantics/test_errors.sh b/test/Semantics/test_errors.sh similarity index 100% rename from test/semantics/test_errors.sh rename to test/Semantics/test_errors.sh diff --git a/test/semantics/test_modfile.sh b/test/Semantics/test_modfile.sh similarity index 100% rename from test/semantics/test_modfile.sh rename to test/Semantics/test_modfile.sh diff --git a/test/semantics/test_symbols.sh b/test/Semantics/test_symbols.sh similarity index 100% rename from test/semantics/test_symbols.sh rename to test/Semantics/test_symbols.sh diff --git a/tools/f18/f18-parse-demo.cpp b/tools/f18/f18-parse-demo.cpp index d52499e16416..67bc45eb499a 100644 --- a/tools/f18/f18-parse-demo.cpp +++ b/tools/f18/f18-parse-demo.cpp @@ -21,16 +21,16 @@ // scaffolding compiler driver that can test some semantic passes of the // F18 compiler under development. -#include "flang/common/Fortran-features.h" -#include "flang/common/default-kinds.h" -#include "flang/parser/characters.h" -#include "flang/parser/dump-parse-tree.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/parsing.h" -#include "flang/parser/provenance.h" -#include "flang/parser/unparse.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/default-kinds.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/parsing.h" +#include "flang/Parser/provenance.h" +#include "flang/Parser/unparse.h" #include #include #include diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index b54d1a9e2d11..608075aa33d5 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -8,21 +8,21 @@ // Temporary Fortran front end driver main program for development scaffolding. -#include "flang/common/Fortran-features.h" -#include "flang/common/default-kinds.h" -#include "flang/evaluate/expression.h" -#include "flang/lower/PFTBuilder.h" -#include "flang/parser/characters.h" -#include "flang/parser/dump-parse-tree.h" -#include "flang/parser/message.h" -#include "flang/parser/parse-tree-visitor.h" -#include "flang/parser/parse-tree.h" -#include "flang/parser/parsing.h" -#include "flang/parser/provenance.h" -#include "flang/parser/unparse.h" -#include "flang/semantics/expression.h" -#include "flang/semantics/semantics.h" -#include "flang/semantics/unparse-with-symbols.h" +#include "flang/Common/Fortran-features.h" +#include "flang/Common/default-kinds.h" +#include "flang/Evaluate/expression.h" +#include "flang/Lower/PFTBuilder.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/parsing.h" +#include "flang/Parser/provenance.h" +#include "flang/Parser/unparse.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/unparse-with-symbols.h" #include "llvm/Support/raw_ostream.h" #include #include diff --git a/tools/f18/stub-evaluate.cpp b/tools/f18/stub-evaluate.cpp index 99e2635430c0..dbaa13a338d9 100644 --- a/tools/f18/stub-evaluate.cpp +++ b/tools/f18/stub-evaluate.cpp @@ -11,7 +11,7 @@ // libraries, as here, we need to stub out the dependences on the external // destructors, which will never actually be called. -#include "flang/common/indirection.h" +#include "flang/Common/indirection.h" namespace Fortran::evaluate { struct GenericExprWrapper { From 97efc0194d411a17ec47edf5e27157d1a1afa3bc Mon Sep 17 00:00:00 2001 From: David Truby Date: Tue, 25 Feb 2020 15:59:50 +0000 Subject: [PATCH 044/345] Replace module writer posix file handling with llvm file handling. (#993) NOTE: This commit introduces a dependency on LLVM HEAD --- .drone.star | 20 ++- include/flang/Semantics/semantics.h | 10 +- lib/Semantics/CMakeLists.txt | 1 + lib/Semantics/mod-file.cpp | 125 ++++++++---------- .../Semantics/Inputs/mod-file-changed.f90 | 5 + .../Semantics/Inputs/mod-file-unchanged.f90 | 5 + test-lit/Semantics/mod-file-rewriter.f90 | 12 ++ tools/f18/f18.cpp | 7 +- 8 files changed, 111 insertions(+), 74 deletions(-) create mode 100644 test-lit/Semantics/Inputs/mod-file-changed.f90 create mode 100644 test-lit/Semantics/Inputs/mod-file-unchanged.f90 create mode 100644 test-lit/Semantics/mod-file-rewriter.f90 diff --git a/.drone.star b/.drone.star index 47dfca7c2460..7d09fdab0977 100644 --- a/.drone.star +++ b/.drone.star @@ -7,11 +7,17 @@ def clang(arch): "name": "test", "image": "ubuntu", "commands": [ - "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev", + "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev git", + "git clone https://github.com/llvm/llvm-project", + "mkdir llvm-project/build && cd llvm-project/build", + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', + "ninja install", + "cd ../..", "mkdir build && cd build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", + "ninja check-all", ], }, ], @@ -27,11 +33,17 @@ def gcc(arch): "name": "test", "image": "gcc", "commands": [ - "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev", + "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev git", + "git clone https://github.com/llvm/llvm-project", + "mkdir llvm-project/build && cd llvm-project/build", + 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', + "ninja install", + "cd ../..", "mkdir build && cd build", - 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..', + 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", + "ninja check-all", ], }, ], diff --git a/include/flang/Semantics/semantics.h b/include/flang/Semantics/semantics.h index dd384583e549..b88bcc4563ed 100644 --- a/include/flang/Semantics/semantics.h +++ b/include/flang/Semantics/semantics.h @@ -80,6 +80,7 @@ class SemanticsContext { const std::string &moduleFileSuffix() const { return moduleFileSuffix_; } bool warnOnNonstandardUsage() const { return warnOnNonstandardUsage_; } bool warningsAreErrors() const { return warningsAreErrors_; } + bool debugModuleWriter() const { return debugModuleWriter_; } const evaluate::IntrinsicProcTable &intrinsics() const { return intrinsics_; } Scope &globalScope() { return globalScope_; } parser::Messages &messages() { return messages_; } @@ -112,6 +113,11 @@ class SemanticsContext { return *this; } + SemanticsContext &set_debugModuleWriter(bool x) { + debugModuleWriter_ = x; + return *this; + } + const DeclTypeSpec &MakeNumericType(TypeCategory, int kind = 0); const DeclTypeSpec &MakeLogicalType(int kind = 0); @@ -175,6 +181,7 @@ class SemanticsContext { std::string moduleFileSuffix_{".mod"}; bool warnOnNonstandardUsage_{false}; bool warningsAreErrors_{false}; + bool debugModuleWriter_{false}; const evaluate::IntrinsicProcTable intrinsics_; Scope globalScope_; parser::Messages messages_; @@ -190,8 +197,9 @@ class SemanticsContext { class Semantics { public: explicit Semantics(SemanticsContext &context, parser::Program &program, - parser::CookedSource &cooked) + parser::CookedSource &cooked, bool debugModuleWriter = false) : context_{context}, program_{program}, cooked_{cooked} { + context.set_debugModuleWriter(debugModuleWriter); context.globalScope().AddSourceRange(parser::CharBlock{cooked.data()}); } diff --git a/lib/Semantics/CMakeLists.txt b/lib/Semantics/CMakeLists.txt index 7900eeff9da1..0c6b612586de 100644 --- a/lib/Semantics/CMakeLists.txt +++ b/lib/Semantics/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(FortranSemantics target_link_libraries(FortranSemantics FortranCommon FortranEvaluate + LLVMSupport ) install (TARGETS FortranSemantics diff --git a/lib/Semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp index 53f72b4a99a0..2810c3542446 100644 --- a/lib/Semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -15,16 +15,14 @@ #include "flang/Semantics/semantics.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include #include #include #include -#include -#include -#include -#include #include namespace Fortran::semantics { @@ -62,10 +60,10 @@ static std::ostream &PutAttrs(std::ostream &, Attrs, static std::ostream &PutAttr(std::ostream &, Attr); static std::ostream &PutType(std::ostream &, const DeclTypeSpec &); static std::ostream &PutLower(std::ostream &, const std::string &); -static int WriteFile(const std::string &, const std::string &); +static std::error_code WriteFile( + const std::string &, const std::string &, bool = true); static bool FileContentsMatch( const std::string &, const std::string &, const std::string &); -static std::size_t GetFileSize(const std::string &); static std::string CheckSum(const std::string_view &); // Collect symbols needed for a subprogram interface @@ -135,9 +133,10 @@ void ModFileWriter::Write(const Symbol &symbol) { auto path{context_.moduleDirectory() + '/' + ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())}; PutSymbols(DEREF(symbol.scope())); - if (int error{WriteFile(path, GetAsString(symbol))}) { - context_.Say(symbol.name(), "Error writing %s: %s"_err_en_US, path, - std::strerror(error)); + if (std::error_code error{ + WriteFile(path, GetAsString(symbol), context_.debugModuleWriter())}) { + context_.Say( + symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message()); } } @@ -618,53 +617,67 @@ std::ostream &PutLower(std::ostream &os, const std::string &str) { } struct Temp { - Temp() = delete; + Temp(llvm::sys::fs::file_t fd, std::string path) : fd{fd}, path{path} {} + Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {} ~Temp() { - close(fd); - unlink(path.c_str()); + if (fd >= 0) { + llvm::sys::fs::closeFile(fd); + llvm::sys::fs::remove(path.c_str()); + } } - int fd; + llvm::sys::fs::file_t fd; std::string path; }; // Create a temp file in the same directory and with the same suffix as path. // Return an open file descriptor and its path. -static Temp MkTemp(const std::string &path) { +static llvm::ErrorOr MkTemp(const std::string &path) { auto length{path.length()}; auto dot{path.find_last_of("./")}; - std::string suffix{dot < length && path[dot] == '.' ? path.substr(dot) : ""}; + std::string suffix{ + dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""}; CHECK(length > suffix.length() && path.substr(length - suffix.length()) == suffix); - auto tempPath{path.substr(0, length - suffix.length()) + "XXXXXX" + suffix}; - int fd{mkstemps(&tempPath[0], suffix.length())}; - auto mask{umask(0777)}; - umask(mask); - chmod(tempPath.c_str(), 0666 & ~mask); // temp is created with mode 0600 - return Temp{fd, tempPath}; + auto prefix{path.substr(0, length - suffix.length())}; + llvm::sys::fs::file_t fd; + llvm::SmallString<16> tempPath; + if (std::error_code err{llvm::sys::fs::createUniqueFile( + prefix + "%%%%%%" + suffix, fd, tempPath)}) { + return err; + } + return Temp{fd, tempPath.c_str()}; } // Write the module file at path, prepending header. If an error occurs, // return errno, otherwise 0. -static int WriteFile(const std::string &path, const std::string &contents) { +static std::error_code WriteFile( + const std::string &path, const std::string &contents, bool debug) { auto header{std::string{ModHeader::bom} + ModHeader::magic + CheckSum(contents) + ModHeader::terminator}; + if (debug) { + llvm::dbgs() << "Processing module " << path << ": "; + } if (FileContentsMatch(path, header, contents)) { - return 0; + if (debug) { + llvm::dbgs() << "module unchanged, not writing\n"; + } + return {}; } - Temp temp{MkTemp(path)}; - if (temp.fd < 0) { - return errno; + llvm::ErrorOr temp{MkTemp(path)}; + if (!temp) { + return temp.getError(); } - if (write(temp.fd, header.c_str(), header.size()) != - static_cast(header.size()) || - write(temp.fd, contents.c_str(), contents.size()) != - static_cast(contents.size())) { - return errno; + llvm::raw_fd_ostream writer(temp->fd, /*shouldClose=*/false); + writer << header; + writer << contents; + writer.flush(); + if (writer.has_error()) { + return writer.error(); } - if (std::rename(temp.path.c_str(), path.c_str()) == -1) { - return errno; + if (debug) { + llvm::dbgs() << "module written\n"; } - return 0; + return llvm::sys::fs::rename(temp->path, path); } // Return true if the stream matches what we would write for the mod file. @@ -672,34 +685,21 @@ static bool FileContentsMatch(const std::string &path, const std::string &header, const std::string &contents) { std::size_t hsize{header.size()}; std::size_t csize{contents.size()}; - if (GetFileSize(path) != hsize + csize) { + auto buf_or{llvm::MemoryBuffer::getFile(path)}; + if (!buf_or) { return false; } - int fd{open(path.c_str(), O_RDONLY)}; - if (fd < 0) { + auto buf = std::move(buf_or.get()); + if (buf->getBufferSize() != hsize + csize) { return false; } - constexpr std::size_t bufSize{4096}; - std::string buffer(bufSize, '\0'); - if (read(fd, &buffer[0], hsize) != static_cast(hsize) || - std::memcmp(&buffer[0], &header[0], hsize) != 0) { - close(fd); - return false; // header doesn't match - } - for (auto remaining{csize};;) { - auto bytes{std::min(bufSize, remaining)}; - auto got{read(fd, &buffer[0], bytes)}; - if (got != static_cast(bytes) || - std::memcmp(&buffer[0], &contents[csize - remaining], bytes) != 0) { - close(fd); - return false; - } - if (bytes == 0 && remaining == 0) { - close(fd); - return true; - } - remaining -= bytes; + if (!std::equal(header.begin(), header.end(), buf->getBufferStart(), + buf->getBufferStart() + hsize)) { + return false; } + + return std::equal(contents.begin(), contents.end(), + buf->getBufferStart() + hsize, buf->getBufferEnd()); } // Compute a simple hash of the contents of a module file and @@ -729,15 +729,6 @@ static bool VerifyHeader(const char *content, std::size_t len) { return expectSum == actualSum; } -static std::size_t GetFileSize(const std::string &path) { - struct stat statbuf; - if (stat(path.c_str(), &statbuf) == 0) { - return static_cast(statbuf.st_size); - } else { - return 0; - } -} - Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { std::string ancestorName; // empty for module if (ancestor) { diff --git a/test-lit/Semantics/Inputs/mod-file-changed.f90 b/test-lit/Semantics/Inputs/mod-file-changed.f90 new file mode 100644 index 000000000000..028203853fdc --- /dev/null +++ b/test-lit/Semantics/Inputs/mod-file-changed.f90 @@ -0,0 +1,5 @@ +module m + dimension :: x(10) + private :: x + real :: x +end module m diff --git a/test-lit/Semantics/Inputs/mod-file-unchanged.f90 b/test-lit/Semantics/Inputs/mod-file-unchanged.f90 new file mode 100644 index 000000000000..46c208aeb67a --- /dev/null +++ b/test-lit/Semantics/Inputs/mod-file-unchanged.f90 @@ -0,0 +1,5 @@ +module m + dimension :: x(10) + public :: x + real :: x +end module m diff --git a/test-lit/Semantics/mod-file-rewriter.f90 b/test-lit/Semantics/mod-file-rewriter.f90 new file mode 100644 index 000000000000..81252910e690 --- /dev/null +++ b/test-lit/Semantics/mod-file-rewriter.f90 @@ -0,0 +1,12 @@ +! RUN: rm -fr %t && mkdir %t && cd %t +! RUN: %f18 -fparse-only -fdebug-module-writer %s 2>&1 | FileCheck %s --check-prefix CHECK_CHANGED +! RUN: %f18 -fparse-only -fdebug-module-writer %s 2>&1 | FileCheck %s --check-prefix CHECK_UNCHANGED +! RUN: %f18 -fparse-only -fdebug-module-writer %p/Inputs/mod-file-unchanged.f90 2>&1 | FileCheck %s --check-prefix CHECK_UNCHANGED +! RUN: %f18 -fparse-only -fdebug-module-writer %p/Inputs/mod-file-changed.f90 2>&1 | FileCheck %s --check-prefix CHECK_CHANGED + +module m + real :: x(10) +end module m + +! CHECK_CHANGED: Processing module {{.*}}.mod: module written +! CHECK_UNCHANGED: Processing module {{.*}}.mod: module unchanged, not writing diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index 608075aa33d5..fe7bd8a1946f 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -98,6 +98,7 @@ struct DriverOptions { bool dumpSymbols{false}; bool debugResolveNames{false}; bool debugNoSemantics{false}; + bool debugModuleWriter{false}; bool measureTree{false}; bool unparseTypedExprsToPGF90{false}; std::vector pgf90Args; @@ -251,8 +252,8 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, if (!driver.debugNoSemantics || driver.debugResolveNames || driver.dumpSymbols || driver.dumpUnparseWithSymbols || driver.getDefinition || driver.getSymbolsSources) { - Fortran::semantics::Semantics semantics{ - semanticsContext, parseTree, parsing.cooked()}; + Fortran::semantics::Semantics semantics{semanticsContext, parseTree, + parsing.cooked(), driver.debugModuleWriter}; semantics.Perform(); semantics.EmitMessages(std::cerr); if (driver.dumpSymbols) { @@ -493,6 +494,8 @@ int main(int argc, char *const argv[]) { driver.dumpSymbols = true; } else if (arg == "-fdebug-resolve-names") { driver.debugResolveNames = true; + } else if (arg == "-fdebug-module-writer") { + driver.debugModuleWriter = true; } else if (arg == "-fdebug-measure-parse-tree") { driver.measureTree = true; } else if (arg == "-fdebug-instrumented-parse") { From a759204db583d337dda886acf748e0fe135c6259 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Mon, 24 Feb 2020 18:55:53 -0800 Subject: [PATCH 045/345] Allow for access-stmt before namelist-stmt You can declare a name in an access statement and then declare it as a namelist group name. We weren't allowing that because we didn't convert a symbol with UnknownDetails to one with NamelistDetails. Fixes #1022. --- lib/Semantics/resolve-names.cpp | 12 +++++------- test/Semantics/resolve40.f90 | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 6b9402e7ae8b..f0cfd8f8269f 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -2957,7 +2957,7 @@ void DeclarationVisitor::Post(const parser::EntityDecl &x) { if (ConvertToObjectEntity(symbol)) { Initialization(name, *init, false); } - } else if (attrs.test(Attr::PARAMETER)) { // C882, C883 + } else if (attrs.test(Attr::PARAMETER)) { // C882, C883 Say(name, "Missing initialization for parameter '%s'"_err_en_US); } } @@ -3925,13 +3925,11 @@ bool DeclarationVisitor::Pre(const parser::NamelistStmt::Group &x) { const auto &groupName{std::get(x.t)}; auto *groupSymbol{FindInScope(currScope(), groupName)}; - if (!groupSymbol) { + if (!groupSymbol || !groupSymbol->has()) { groupSymbol = &MakeSymbol(groupName, std::move(details)); - } else if (groupSymbol->has()) { - groupSymbol->get().add_objects(details.objects()); - } else { - SayAlreadyDeclared(groupName, *groupSymbol); + groupSymbol->ReplaceName(groupName.source); } + groupSymbol->get().add_objects(details.objects()); return false; } @@ -4408,7 +4406,7 @@ std::optional DeclarationVisitor::ResolveDerivedType( DerivedTypeDetails details; details.set_isForwardReferenced(); symbol->set_details(std::move(details)); - } else { // C883 + } else { // C883 Say(name, "Derived type '%s' not found"_err_en_US); return std::nullopt; } diff --git a/test/Semantics/resolve40.f90 b/test/Semantics/resolve40.f90 index 83522c62fff6..1137126740af 100644 --- a/test/Semantics/resolve40.f90 +++ b/test/Semantics/resolve40.f90 @@ -72,3 +72,19 @@ subroutine s9 !ERROR: 'i' is already declared in this scoping unit data ((x(i,i),i=1,2),i=1,2)/4*0.0/ end + +module m10 + integer :: x + public :: nl + namelist /nl/ x +end + +subroutine s11 + integer :: nl2 + !ERROR: 'nl2' is already declared in this scoping unit + namelist /nl2/x + namelist /nl3/x + !ERROR: 'nl3' is already declared in this scoping unit + integer :: nl3 + nl2 = 1 +end From a2b2a330e7dbb8d719fdab9bb28921cf84f503ca Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 25 Feb 2020 17:13:56 -0800 Subject: [PATCH 046/345] Fix misparsed structure constructor in data stmt In a data statement like `data x / a(1) /`, `a(1)` may be an array element or a structure constructor. It is parsed as an array element so if it turns out `a` is a derived type it must be rewritten as a strucutre constructor. --- include/flang/Parser/parse-tree.h | 2 ++ include/flang/Parser/tools.h | 3 +++ lib/Parser/parse-tree.cpp | 14 ++++++++++++++ lib/Semantics/resolve-names.cpp | 19 +++++++++++++++++++ test/Semantics/data01.f90 | 31 ++++++++++++++++++++++--------- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/include/flang/Parser/parse-tree.h b/include/flang/Parser/parse-tree.h index d7bb1f5686aa..1d93c13cada6 100644 --- a/include/flang/Parser/parse-tree.h +++ b/include/flang/Parser/parse-tree.h @@ -1818,6 +1818,8 @@ struct ArrayElement { ArrayElement(DataRef &&dr, std::list &&ss) : base{std::move(dr)}, subscripts(std::move(ss)) {} Substring ConvertToSubstring(); + StructureConstructor ConvertToStructureConstructor( + const semantics::DerivedTypeSpec &); DataRef base; std::list subscripts; }; diff --git a/include/flang/Parser/tools.h b/include/flang/Parser/tools.h index dcb503b31b79..9112cc4a4886 100644 --- a/include/flang/Parser/tools.h +++ b/include/flang/Parser/tools.h @@ -79,6 +79,9 @@ struct UnwrapperHelper { template const A *Unwrap(const B &x) { return UnwrapperHelper::Unwrap(x); } +template A *Unwrap(B &x) { + return const_cast(Unwrap(const_cast(x))); +} // Get the CoindexedNamedObject if the entity is a coindexed object. const CoindexedNamedObject *GetCoindexedNamedObject(const AllocateObject &); diff --git a/lib/Parser/parse-tree.cpp b/lib/Parser/parse-tree.cpp index d0722aaa571c..181bf066c587 100644 --- a/lib/Parser/parse-tree.cpp +++ b/lib/Parser/parse-tree.cpp @@ -9,6 +9,7 @@ #include "flang/Parser/parse-tree.h" #include "flang/Common/idioms.h" #include "flang/Common/indirection.h" +#include "flang/Parser/tools.h" #include "flang/Parser/user-state.h" #include @@ -186,6 +187,19 @@ StructureConstructor FunctionReference::ConvertToStructureConstructor( return StructureConstructor{std::move(spec), std::move(components)}; } +StructureConstructor ArrayElement::ConvertToStructureConstructor( + const semantics::DerivedTypeSpec &derived) { + Name name{std::get(base.u)}; + std::list components; + for (auto &subscript : subscripts) { + components.emplace_back(std::optional{}, + ComponentDataSource{std::move(*Unwrap(subscript))}); + } + DerivedTypeSpec spec{std::move(name), std::list{}}; + spec.derivedTypeSpec = &derived; + return StructureConstructor{std::move(spec), std::move(components)}; +} + Substring ArrayElement::ConvertToSubstring() { auto iter{subscripts.begin()}; CHECK(iter != subscripts.end()); diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index f0cfd8f8269f..7d6fa5d644da 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -948,6 +948,7 @@ class ConstructVisitor : public virtual DeclarationVisitor { bool Pre(const parser::AcImpliedDo &); bool Pre(const parser::DataImpliedDo &); bool Pre(const parser::DataStmtObject &); + bool Pre(const parser::DataStmtValue &); bool Pre(const parser::DoConstruct &); void Post(const parser::DoConstruct &); bool Pre(const parser::ForallConstruct &); @@ -4687,6 +4688,24 @@ bool ConstructVisitor::Pre(const parser::DataStmtObject &x) { return false; } +bool ConstructVisitor::Pre(const parser::DataStmtValue &x) { + const auto &data{std::get(x.t)}; + auto &mutableData{const_cast(data)}; + if (auto *elem{parser::Unwrap(mutableData)}) { + if (const auto *name{std::get_if(&elem->base.u)}) { + if (const Symbol * symbol{FindSymbol(*name)}) { + if (const Symbol * ultimate{GetAssociationRoot(*symbol)}) { + if (ultimate->has()) { + mutableData.u = elem->ConvertToStructureConstructor( + DerivedTypeSpec{name->source, *ultimate}); + } + } + } + } + } + return true; +} + bool ConstructVisitor::Pre(const parser::DoConstruct &x) { if (x.IsDoConcurrent()) { PushScope(Scope::Kind::Block, nullptr); diff --git a/test/Semantics/data01.f90 b/test/Semantics/data01.f90 index db978331d541..87861016c6c0 100644 --- a/test/Semantics/data01.f90 +++ b/test/Semantics/data01.f90 @@ -37,12 +37,25 @@ subroutine CheckRepeat DATA myName%age / digits(myAge) * 35 / end -!subroutine CheckValue -! use m1 -! !C883 -! !ERROR: Derived type 'persn' not found -! DATA myname / persn(2, 'Abcd Efgh') / -! !C884 -! !ERROR: Structure constructor in data value must be a constant expression -! DATA myname / person(myAge, 'Abcd Ijkl') / -!end +subroutine CheckValue + use m1 + !OK: constant structure constructor + data myname / person(1, 'Abcd Ijkl') / + !C883 + !ERROR: Must have INTEGER type, but is CHARACTER(1) + data myname / persn(2, 'Abcd Efgh') / + !C884 + !ERROR: Structure constructor in data value must be a constant expression + data myname / person(myAge, 'Abcd Ijkl') / + integer, parameter :: a(5) =(/11, 22, 33, 44, 55/) + integer :: b(5) =(/11, 22, 33, 44, 55/) + integer :: i + integer :: x + !OK: constant array element + data x / a(1) / + !C886, C887 + !ERROR: Must be a constant value + data x / a(i) / + !ERROR: Must be a constant value + data x / b(1) / +end From 7848ab146ad32062daba9f07b032655568381a1b Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 26 Feb 2020 02:01:02 -0800 Subject: [PATCH 047/345] Fix drone CI build failure due to lack of FileCheck Simply use the llvm build directory and save installing LLVM on the way. --- .drone.star | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.star b/.drone.star index 7d09fdab0977..e5859e9efc2e 100644 --- a/.drone.star +++ b/.drone.star @@ -11,10 +11,10 @@ def clang(arch): "git clone https://github.com/llvm/llvm-project", "mkdir llvm-project/build && cd llvm-project/build", 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', - "ninja install", + "ninja", "cd ../..", "mkdir build && cd build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/build/lib/cmake/llvm', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", @@ -37,10 +37,10 @@ def gcc(arch): "git clone https://github.com/llvm/llvm-project", "mkdir llvm-project/build && cd llvm-project/build", 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', - "ninja install", + "ninja", "cd ../..", "mkdir build && cd build", - 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', + 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/build/lib/cmake/llvm', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", From 5eaebc800caa90b592c5ba8cf1fd6fd3987b3636 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 26 Feb 2020 06:16:22 -0800 Subject: [PATCH 048/345] Remove clang from LLVM drone builds until needed to fasten builds --- .drone.star | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index e5859e9efc2e..5bbe018cd056 100644 --- a/.drone.star +++ b/.drone.star @@ -10,7 +10,7 @@ def clang(arch): "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev git", "git clone https://github.com/llvm/llvm-project", "mkdir llvm-project/build && cd llvm-project/build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', "ninja", "cd ../..", "mkdir build && cd build", @@ -36,7 +36,7 @@ def gcc(arch): "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev git", "git clone https://github.com/llvm/llvm-project", "mkdir llvm-project/build && cd llvm-project/build", - 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="clang;mlir" ../llvm', + 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', "ninja", "cd ../..", "mkdir build && cd build", From c95c45704e7c0afeb0d2dbec965afd368e923a3a Mon Sep 17 00:00:00 2001 From: peter klausler Date: Mon, 24 Feb 2020 17:04:54 -0800 Subject: [PATCH 049/345] Fix layout of 128-bit IEEE-754 floating-point values --- include/flang/Common/real.h | 6 +- include/flang/Decimal/decimal.h | 6 +- include/flang/Evaluate/complex.h | 2 +- include/flang/Evaluate/real.h | 2 +- include/flang/Evaluate/type.h | 2 +- lib/Decimal/binary-to-decimal.cpp | 4 +- lib/Decimal/decimal-to-binary.cpp | 2 +- lib/Evaluate/complex.cpp | 2 +- lib/Evaluate/real.cpp | 2 +- test/Evaluate/folding07.f90 | 163 +++++++++++------------------- test/Semantics/modfile26.f90 | 18 ++-- 11 files changed, 80 insertions(+), 129 deletions(-) diff --git a/include/flang/Common/real.h b/include/flang/Common/real.h index 158482e6c5c1..4688e440a82d 100644 --- a/include/flang/Common/real.h +++ b/include/flang/Common/real.h @@ -26,7 +26,7 @@ static constexpr int BitsForBinaryPrecision(int binaryPrecision) { case 53: return 64; // IEEE double precision: 1+11+52 case 64: return 80; // x87 extended precision: 1+15+64 case 106: return 128; // "double-double": 2*(1+11+52) - case 112: return 128; // IEEE quad precision: 1+16+111 + case 113: return 128; // IEEE quad precision: 1+15+112 default: return -1; } } @@ -43,7 +43,7 @@ static constexpr int MaxDecimalConversionDigits(int binaryPrecision) { case 53: return 751; case 64: return 11495; case 106: return 2 * 751; - case 112: return 22981; + case 113: return 11530; default: return -1; } } @@ -79,7 +79,7 @@ template class RealDetails { static_assert(binaryPrecision > 0); static_assert(exponentBits > 1); - static_assert(exponentBits <= 16); + static_assert(exponentBits <= 15); }; } diff --git a/include/flang/Decimal/decimal.h b/include/flang/Decimal/decimal.h index 1e792d78cc1f..5f80ab6f349f 100644 --- a/include/flang/Decimal/decimal.h +++ b/include/flang/Decimal/decimal.h @@ -92,9 +92,9 @@ extern template ConversionToDecimalResult ConvertToDecimal<53>(char *, size_t, extern template ConversionToDecimalResult ConvertToDecimal<64>(char *, size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<64>); -extern template ConversionToDecimalResult ConvertToDecimal<112>(char *, size_t, +extern template ConversionToDecimalResult ConvertToDecimal<113>(char *, size_t, enum DecimalConversionFlags, int, enum FortranRounding, - BinaryFloatingPointNumber<112>); + BinaryFloatingPointNumber<113>); template struct ConversionToBinaryResult { BinaryFloatingPointNumber binary; @@ -115,7 +115,7 @@ extern template ConversionToBinaryResult<53> ConvertToBinary<53>( const char *&, enum FortranRounding = RoundNearest); extern template ConversionToBinaryResult<64> ConvertToBinary<64>( const char *&, enum FortranRounding = RoundNearest); -extern template ConversionToBinaryResult<112> ConvertToBinary<112>( +extern template ConversionToBinaryResult<113> ConvertToBinary<113>( const char *&, enum FortranRounding = RoundNearest); } // namespace Fortran::decimal extern "C" { diff --git a/include/flang/Evaluate/complex.h b/include/flang/Evaluate/complex.h index 417d4cfecbbd..370bcfbb5bce 100644 --- a/include/flang/Evaluate/complex.h +++ b/include/flang/Evaluate/complex.h @@ -96,6 +96,6 @@ extern template class Complex, 8>>; extern template class Complex, 24>>; extern template class Complex, 53>>; extern template class Complex, 64>>; -extern template class Complex, 112>>; +extern template class Complex, 113>>; } #endif // FORTRAN_EVALUATE_COMPLEX_H_ diff --git a/include/flang/Evaluate/real.h b/include/flang/Evaluate/real.h index 0624abd3ee93..fbb52086f0cb 100644 --- a/include/flang/Evaluate/real.h +++ b/include/flang/Evaluate/real.h @@ -367,7 +367,7 @@ extern template class Real, 8>; // the "other" half format extern template class Real, 24>; // IEEE single extern template class Real, 53>; // IEEE double extern template class Real, 64>; // 80387 extended precision -extern template class Real, 112>; // IEEE quad +extern template class Real, 113>; // IEEE quad // N.B. No "double-double" support. } #endif // FORTRAN_EVALUATE_REAL_H_ diff --git a/include/flang/Evaluate/type.h b/include/flang/Evaluate/type.h index 137cf66e9cd9..97700f30e29c 100644 --- a/include/flang/Evaluate/type.h +++ b/include/flang/Evaluate/type.h @@ -275,7 +275,7 @@ class Type : public TypeBase { template<> class Type : public TypeBase { public: - using Scalar = value::Real, 112>; + using Scalar = value::Real, 113>; }; // The KIND type parameter on COMPLEX is the kind of each of its components. diff --git a/lib/Decimal/binary-to-decimal.cpp b/lib/Decimal/binary-to-decimal.cpp index 416d85097dda..f4e57681af95 100644 --- a/lib/Decimal/binary-to-decimal.cpp +++ b/lib/Decimal/binary-to-decimal.cpp @@ -370,9 +370,9 @@ template ConversionToDecimalResult ConvertToDecimal<53>(char *, std::size_t, template ConversionToDecimalResult ConvertToDecimal<64>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, BinaryFloatingPointNumber<64>); -template ConversionToDecimalResult ConvertToDecimal<112>(char *, std::size_t, +template ConversionToDecimalResult ConvertToDecimal<113>(char *, std::size_t, enum DecimalConversionFlags, int, enum FortranRounding, - BinaryFloatingPointNumber<112>); + BinaryFloatingPointNumber<113>); extern "C" { ConversionToDecimalResult ConvertFloatToDecimal(char *buffer, std::size_t size, diff --git a/lib/Decimal/decimal-to-binary.cpp b/lib/Decimal/decimal-to-binary.cpp index 7deec9f101b2..586c7f869e80 100644 --- a/lib/Decimal/decimal-to-binary.cpp +++ b/lib/Decimal/decimal-to-binary.cpp @@ -399,7 +399,7 @@ template ConversionToBinaryResult<53> ConvertToBinary<53>( const char *&, enum FortranRounding); template ConversionToBinaryResult<64> ConvertToBinary<64>( const char *&, enum FortranRounding); -template ConversionToBinaryResult<112> ConvertToBinary<112>( +template ConversionToBinaryResult<113> ConvertToBinary<113>( const char *&, enum FortranRounding); extern "C" { diff --git a/lib/Evaluate/complex.cpp b/lib/Evaluate/complex.cpp index 8f03304b6b05..ebb3898dee90 100644 --- a/lib/Evaluate/complex.cpp +++ b/lib/Evaluate/complex.cpp @@ -101,5 +101,5 @@ template class Complex, 8>>; template class Complex, 24>>; template class Complex, 53>>; template class Complex, 64>>; -template class Complex, 112>>; +template class Complex, 113>>; } diff --git a/lib/Evaluate/real.cpp b/lib/Evaluate/real.cpp index 6f9a17c6262e..fe3b5821b676 100644 --- a/lib/Evaluate/real.cpp +++ b/lib/Evaluate/real.cpp @@ -520,5 +520,5 @@ template class Real, 8>; template class Real, 24>; template class Real, 53>; template class Real, 64>; -template class Real, 112>; +template class Real, 113>; } diff --git a/test/Evaluate/folding07.f90 b/test/Evaluate/folding07.f90 index b0831472149e..b7e13eb027a3 100644 --- a/test/Evaluate/folding07.f90 +++ b/test/Evaluate/folding07.f90 @@ -29,8 +29,8 @@ module m deps10 = 5.42101086242752217003726400434970855712890625e-20_10 real(16), parameter :: & eps16 = epsilon(0._16), & - zeps16 = real(z'3fc78000000000000000000000000000', kind=16), & - deps16 = 1.925929944387235853055977942584927318538101648215388195239938795566558837890625e-34_16 + zeps16 = real(z'3f8e0000000000000000000000000000', kind=16), & + deps16 = 9.629649721936179265279889712924636592690508241076940976199693977832794189453125e-35_16 logical, parameter :: test_eps2 = eps2 == zeps2 .and. eps2 == deps2 logical, parameter :: test_eps3 = eps3 == zeps3 .and. eps3 == deps3 logical, parameter :: test_eps4 = eps4 == zeps4 .and. eps4 == deps4 @@ -125,106 +125,57 @@ module m &5325423859110896247108853858086888377772586485641459342621210866475884892600317623459607695088491496& &6244415660441955208681198977024e4932_10 real(16), parameter :: & - ahuge16 = huge(0._16), zahuge16 = real(z'7fff7fffffffffffffffffffffffffff', kind=16), & - dahuge16 = 1.4154610310449547890015530277449513287402522312014583583863873452370673496090517844595674241105526619& - &5316741942296713234339847173043444610109053451307461326030568532269393756935971597477179751917262481& - &9352114171249119710791528826008514763128252507906157761158330471593133360520566465839598159704790361& - &8052216473233827726396998249642738685081537609877654130679009320149945501056683481052234280369184890& - &5600784088324331956396165189067350444857059625918182061259630449033723034922117505897765689572130343& - &6442900178897829054258075776096285494374231192475505398864394612619318718669868907615189244643178364& - &9604710976258237438709882933414201622978713022975908448898269286373397175572572843473932165513894328& - &0948775427139028319745676136738066595818634893585336670047942640579585732561265895169788288873968502& - &3576409450602037731566868670343358720132085137732083365284366309191514754276902232302595183222780229& - &0333048746295176633732590892271134921001816237945675046107354701982076491636207145766942241484852876& - &8086489045974168028871375519412956556734569711277377659269966781151202506367906250771838987905542729& - &9602633739292957854646574862022345808897851021953319762924371865024136126553409742233338383148939476& - &2688819583034676283720590690321437702466211493942477839239904785467381975953431360199181632139817087& - &1675085388306946402985966654686374017597361994515490028221221197873309065711452256943483997268156661& - &4251522601981091437164454815585766387477985848066653010274015762006422743531061320328958737883350699& - &5010107953612418870689506323691217683866631324712647102533109103896945673048537987294807465898328021& - &0616399905018381133672495392016893435858086953047467302012798815167555583471558849452903240275817359& - &7412600246880120097460997601280569422971588083755852211710169314345911644907180640214262742781303093& - &6362054083426905944815062311426831879988031400740033624376500891481190403873103717439643860930434152& - &0077820977089318080209161337954445502789934971878811641353709185004519914977577688820318078268550811& - &9598842219332616175613533636799875893648266143272209859623766305070288663132625316020772779359622743& - &6981030373132763462837652106194804931955311030759723858460597094396859076238402414307660975253097132& - &8698643152225068610430793799791351450546432634372714107713137332348308355668038997851202770977262560& - &6860655592065973411067851629502656906251604510378685022379816969935014362348544555586522272088499721& - &9946444206136083948321352132790842341486712676660929539344838800833207748229649079300799848460228620& - &9317916049805688735829248332080887463530900166033204573480074339495592738215902398384614205016081150& - &6669327808418122123950416238344051511059531020385411336197538432312088162779143008762730602999224040& - &4412462631215227731882153726267258980995290409075434047558959478172735835465203332540303097147035552& - &5666711990525067812075528567351636372299669796556262498468798624564731519949881412251505923493700744& - &6335216604198320142967929437627737857666675125232132004943938027381035739733071077389290829983249430& - &9929671156285733762483649064261848981602065505963476296523983015653616082982446966218901161161205188& - &0573600170377567179171204447290536296387610124123253094661730508794512413417917215234868180985445947& - &4633611169558495590616526594210716678969741229385520704603028673106854460886311076850901072143976966& - &6670653340074110727042938673600505240289417987265120625262935190175503280434524070893838237104432038& - &7277721187068571883463249210788857422002436533794328830424210014284256017507940795564055131778982162& - &2057329034192487819427904384064457476669697518270041324739893645003100081320531996273644177857645536& - &8545042222437577676482965113248928599178769221842229576106346149044548609079571934297226553085916258& - &7592543596965131548109725448343837961730187194350398048494910675432603696794370092723457745633520895& - &5119608085204333293851879810252603015301753358273689011732017729549391344820270439201073177077719806& - &6340808266346052755156509338328217335355010644405352755527544829502078548861021691811911472766970454& - &1126722373686640238868728507802237164806166737169811286535298575549796654582990432275142847727629502& - &1305769671663557289499368071930128517595842219011940342211409668478145343257173098710205665337736566& - &7695994299658695511962006986219821529127829161089064469294254157734218514131756969922934643982128786& - &2715171334655164118356373428019349203374943169393056179361968313211330781317671346402999475798391627& - &7899704387839522461273739469298912609371545163597105274885675371149316619814184131715268620939484405& - &4032822037910079156926925384052248020095121565015480766860393059726428647308949687836979970427915464& - &4264625542574505988870728965217856989288717655724378129280385306319189930760735776751678832851307548& - &0329607523670401065187600417105444319087401626053234839268127494935061885924159151772184284570819457& - &9765358561330329205363145261773965226880523298786386620674889009587176503669008414757444001413680605& - &1851754109107612931858580108997222201353234246355066745929992414298433627776673565501154849803765982& - &3801743520677023503489041175120829329692997134458297062148957929737209809196458870294106726869032085& - &7789703094882329377767630717523835645468121709209222839288061729452480299859659067904004155322262027& - &4942016841932467005817058434662245916974636886994084629484965489954062257150707733514203824510029893& - &0740188138973309464198336206180093073990056649880937698690357135529391353848482852684893681528382991& - &0410280771732290741176568547605485911343042882835702704191107389084055677618161337761939546366170150& - &8064781253559946842752977101984760297902666892353040900170187885831791071277065381487511702153859041& - &5299813558768597766083566649688989266040033065035240732139305422496656316421603624955865828341705144& - &3447994139174194712038660873812088215000697427262799495041365132243338960326526617370362693352900229& - &5839660406183978610341744635787604399627725957535437386160926670026801543280519042677441316699480894& - &4875931667388354376924131257775463750780942146112953413750161644209045078267855172649330717659424934& - &8335712735674451357884496678904100317012827438683814373992652581881866811580622571804449391481344574& - &8721113352345494414054446470032427805567908094831174795204261577836387511406006137595003048602258126& - &7574174240943346072781646716338402483610109902716905722880495175295252854374708163967596343005823957& - &4178083920280304911846453277681629032012659221856535496262316447548683056149679053220966081343915763& - &6618388485315900488605284740175857970500838688820831776374452618973739739617720957620005981443081902& - &4300636730721404743123197614003206963487294419744356022978269747190299245009057165700504871383191949& - &4899374942288264992976945103110345290533057003941954989124658125216431980606017233763887218072751504& - &7096960229110614316970153571571481206941090011829947032962703662183625088631507092427471435324068470& - &0366844465251076175358580515701393054153420428006834812855173272409462525200355138959896149898062525& - &2210628999335118130478060363668307275831118339581618716511127649271497546196039918772873373199382754& - &6560828617244201387568777027698045385338478763220413303633453325803060889236256471072252843526783119& - &3479871927503164671562194351641764632839321378419898279406392257259948038445867866022404201010548148& - &7253397872999082316105642775727365630729430694476221318450355038042498725633142324796624876140016499& - &3630315406642793862426329905517326187048203464559341824317026461223979145994800153463344219940539205& - &8817280808635659115943320567223211441185330248489643784516019287510770248820455510778539577940252666& - &8183267612737208040264783872625984696777713170768969342562651487622257641705902318612115711079338922& - &2493638262892467174711936785452484818671226836798093117387826527910160787492368254620398137406505131& - &8731988042354812033108407558675436962949740512319483298252234589352170375247787814835721268045446829& - &2317117377251138299913201780559797753723849597826002363700794792393814430199924797858736790730058164& - &9952115153236795221485683501836480463808149826963803445797953052814946094155785057280525607436938816& - &8429022866291042876643873792538883921257534909031812560345020981065950461122018135436055807910398208& - &1484855520256575276221518623542883183408040492631138649359729684960991172376968732880996978620285511& - &8920978864722624039207631304837709254566567316280789050587533720412634195873847049653211138198244266& - &0719308767951120142177649511668883859734091231860247534722574046015100358565612400275756547152074564& - &1633137898850424421600859605265441281300920250103584929436220562511251780317859133490449800210440206& - &3981850043050466311875810414734973403058771563153667133211228742703665404508238629870510947427656995& - &8638542779689576904473234941595273107383541712176187177722929884736371754403934807195232649597205636& - &7221673933010790346005001099664009818203015805631886046528599114745693012019712138121354212324790087& - &6231390929321586247720337913367593147267683928201056451758630035154869092151561740273050582062026461& - &9572049643976530665071930187197763438414695944410131097851490700907933723704516209591343059105381927& - &6905146017463471917031994260130109135398832398868757935821801928877154665144538645512469367057022196& - &6054756561099772143655047379026794640167482518879370689566268763892362268011507829185486417838902115& - &2013045682596657873774794142783874686585931915497399076756434719515907361068265152702994177444969608& - &9592887399294629417678853384234804422154039327343881374143794048281132753541259730778406832518482134& - &4637540401952767039481570552371988138457076195431968762699987960258703705461489278791473966013233379& - &6757285178848543455309032854535650058524208397901319574386300684931853011117241480422198239039503450& - &5655663330591727042233318955458942000042598046293770136501981539240756523705635887060941113869993907& - &1751151952076710851450505936665537567847881544310871209165764039670118474872200059013387628379850978& - &2307116366065133838722169400801537852265629436915335433281339392e9864_16 + ahuge16 = huge(0._16), zahuge16 = real(z'7ffeffffffffffffffffffffffffffff', kind=16), & + dahuge16 = 1.1897314953572317650857593266280070161964690526416940455296988842121635797553123923249740128484620735& + &2590203356474912685975526543357380446267269875194526149085346195872502126284586579940540449357468156& + &6096686172574953791792292256220777095858112702436475442537092608935138247345677279593806773692330094& + &6157461197257841728898925219399207576542048645656733564522472781522888677006389355954564966995114417& + &5290960687851325094831139688610052683309212868397475219226638679188087369434307734815556410166997113& + &8512786874753496996549221727686770196551512812712488289469952298031867469924683981576664562667786719& + &0614996396303416570983054252372208766646300878087672561828032202122199248523759030495209113959109189& + &2120527349676858811903011159301878936803923201167140417584510885470696521560577711351625740481881769& + &5075025715299705916714352103671782759119316034498392169720631800164034124698918142227577300459309880& + &4547151796062998955075830758511951858579711731676769660579988993526318854177162953020146688023840758& + &4603622660648014297759540713505037980864913015716402406031178690879637251033587351277479527574859541& + &7572920936651398752709055215663939505589207804914540432978557623565645991208599669097180808881920063& + &7227714312184890119222096790535459636284173260024397328029395243137866685140273814343210366365711716& + &7042358647275956123197079396783927914728272019537706060212263845788320480934171752680963925353944773& + &0280863675704796054050525162959099932535265586464682793821550087166946662209865086040990507131145474& + &2674110428395423227629949387596131127438371928396826762575553883728144908453957471281620658715882191& + &0888724011665136196205080002917629993882608241754751673226993047313326125892184551681523545535431045& + &8114528303607394526100730578774092094736822286015459361126642549541799645333882549670764145955017051& + &3308000612538651401801532119293614565003435147928902055320217600618822326157365533772949809740595905& + &2018796145979938674151302850593441045360348019238334932111517181105100410859283099181138255290906487& + &3029533418691087118107895004426881765865961841419267486232005929789956207494587649901662172318722999& + &4845123258260870315619363836897406865052797752967893316136838227985970406516005241290251498948731531& + &9694209505667084746692764481259650670012944357951247923062137397808873125708979962290218382410541293& + &0483065603459863120371744282301377070153823878609951218937542956964157950988060608985782910656238116& + &1422035741047574518281708048752574462041283485138290827317223641893804935883389476643706232798207558& + &3164620541748839306283820178954721954319445090211369992596537690819279215212221282457887933650687528& + &8617303469517112245451315447164280392523574962804175375927948971096983905242318797695347043690474223& + &8132665056397611644388442665313646268512196339944341540985621273959361844218214442734315345078601616& + &1428702272098406156966033337278824103713153807737748015267058325792053556997331818811268567331899796& + &7497786786001251403873023920127717626858627038170562807276699687356274072773403132694104831615879354& + &3958115858251128378415632227616233344591881315378823557324830300859768903829697344762145934281912127& + &1714133304757786755221851743106484876037319629031012446614508707837714052853304868420427879959665251& + &4009368964527494988719996088230065668196236298805733689960371306226158464997243490564472254071897564& + &1441285398399860960455632647712855850663041779957201017448443871583297673755604162078008788300720724& + &1390865785566723954636935777578134428819598917631335685641784543423281488674422674670706697975557712& + &1788798468777700116472954103621810567107869855646414713502627836321256957407217461738363552424248762& + &4364780853518109957492932381740813319050481446127009055414257022203025376114948242287653245779337785& + &1981877869734028258091278067497905893806255685600107605770598216668682475603756961576049761981948205& + &2758118532729333127733603742149847001463931981340719681330844408263017545241644293372483217234561694& + &2639378557592944486629790954192274518015884259778696940266014279196551684158959230431151917518727133& + &4609575263460825447598815416225495259785319903964588374219923638761039583094807436598839770784963225& + &2080920941206268114832425403540515474312327876180802357701527842702008781378306569508588571830140611& + &0980426830095308627974030153554643774062498539644810004022317716657008936075218040845236685686491032& + &5886266629337247244143556352059546170104239050079561583450594483732665254246744436486149918427509748& + &5253621979537504128523848241127715641240965261646703516395599407360083455079665191393229410544185167& + &9990997876554244625589008743884056491694537267393122602348155432978423086460721901479480729284567258& + &3503954612118213364077776992584180757905173583882311275962271406750966991364528828189455892561297242& + &5252452248453502562347348900936766966136332741088135837550717443838484760651019872222926016920811114& + &6169371432077434885046020127763642567468723152059526010722289706864609324352227544963417635351891055& + &48847634608972381760403137363968e4932_16 logical, parameter :: test_ahuge2 = ahuge2 == zahuge2 .and. ahuge2 == dahuge2 logical, parameter :: test_ahuge3 = ahuge3 == zahuge3 .and. ahuge3 == dahuge3 logical, parameter :: test_ahuge4 = ahuge4 == zahuge4 .and. ahuge4 == dahuge4 @@ -237,7 +188,7 @@ module m real(4), parameter :: tiny4 = tiny(0._4), ztiny4 = real(z'00800000', kind=4) real(8), parameter :: tiny8 = tiny(0._8), ztiny8 = real(z'0010000000000000', kind=8) real(10), parameter :: tiny10 = tiny(0._10), ztiny10 = real(z'00018000000000000000', kind=10) - real(16), parameter :: tiny16 = tiny(0._16), ztiny16 = real(z'00008000000000000000000000000000', kind=16) + real(16), parameter :: tiny16 = tiny(0._16), ztiny16 = real(z'00010000000000000000000000000000', kind=16) logical, parameter :: test_tiny2 = tiny2 == ztiny2 logical, parameter :: test_tiny3 = tiny3 == ztiny3 logical, parameter :: test_tiny4 = tiny4 == ztiny4 @@ -257,7 +208,7 @@ module m logical, parameter :: test_max4 = max4 == 127 logical, parameter :: test_max8 = max8 == 1023 logical, parameter :: test_max10 = max10 == 16383 - logical, parameter :: test_max16 = max16 == 32767 + logical, parameter :: test_max16 = max16 == 16383 integer, parameter :: & min2 = minexponent(0._2), & @@ -271,7 +222,7 @@ module m logical, parameter :: test_min4 = min4 == -126 logical, parameter :: test_min8 = min8 == -1022 logical, parameter :: test_min10 = min10 == -16382 - logical, parameter :: test_min16 = min16 == -32766 + logical, parameter :: test_min16 = min16 == -16382 integer, parameter :: & irange1 = range(0_1), & @@ -297,6 +248,6 @@ module m logical, parameter :: test_zrange4 = arange4 == 37 .and. zrange4 == 37 logical, parameter :: test_zrange8 = arange8 == 307 .and. zrange8 == 307 logical, parameter :: test_zrange10 = arange10 == 4931 .and. zrange10 == 4931 - logical, parameter :: test_zrange16 = arange16 == 9863 .and. zrange16 == 9863 + logical, parameter :: test_zrange16 = arange16 == 4931 .and. zrange16 == 4931 end module diff --git a/test/Semantics/modfile26.f90 b/test/Semantics/modfile26.f90 index 90553104b8f1..44d43c6ca788 100644 --- a/test/Semantics/modfile26.f90 +++ b/test/Semantics/modfile26.f90 @@ -36,18 +36,18 @@ module m1 ! REAL(KIND=4) handles 5 <= R < 38 (if no KIND=3) ! REAL(KIND=8) handles 38 <= R < 308 ! REAL(KIND=10) handles 308 <= R < 4932 (if available; ifort is KIND=16) - ! REAL(KIND=16) handles 4932 <= R < 9864 (except Power double/double) + ! REAL(KIND=16) handles 308 <= R < 4932 (except Power double/double) integer, parameter :: realranges(*) = & [range(0._2), range(0._3), range(0._4), range(0._8), range(0._10), & range(0._16)] logical, parameter :: rrangecheck = & - all([4, 37, 37, 307, 4931, 9863] == realranges) + all([4, 37, 37, 307, 4931, 4931] == realranges) integer, parameter :: realrvals(*) = & - [0, 4, 5, 37, 38, 307, 308, 4931, 4932, 9863, 9864] + [0, 4, 5, 37, 38, 307, 308, 4931, 4932] integer, parameter :: realrkinds(*) = & [(selected_real_kind(0,realrvals(j)),j=1,size(realrvals))] logical, parameter :: realrcheck = & - all([2, 2, 3, 3, 8, 8, 10, 10, 16, 16, -2] == realrkinds) + all([2, 2, 3, 3, 8, 8, 10, 10, -2] == realrkinds) logical, parameter :: radixcheck = & all([radix(0._2), radix(0._3), radix(0._4), radix(0._8), & radix(0._10), radix(0._16)] == 2) @@ -59,7 +59,7 @@ module m1 [digits(0._2), digits(0._3), digits(0._4), digits(0._8), digits(0._10), & digits(0._16)] logical, parameter :: realdigitscheck = & - all([11, 8, 24, 53, 64, 112] == realdigits) + all([11, 8, 24, 53, 64, 113] == realdigits) end module m1 !Expect: m1.mod !module m1 @@ -74,14 +74,14 @@ end module m1 !integer(4),parameter::realpvals(1_8:*)=[INTEGER(4)::0_4,3_4,4_4,6_4,7_4,15_4,16_4,18_4,19_4,33_4,34_4] !integer(4),parameter::realpkinds(1_8:*)=[INTEGER(4)::2_4,2_4,4_4,4_4,8_4,8_4,10_4,10_4,16_4,16_4,-1_4] !logical(4),parameter::realpcheck=.true._4 -!integer(4),parameter::realranges(1_8:*)=[INTEGER(4)::4_4,37_4,37_4,307_4,4931_4,9863_4] +!integer(4),parameter::realranges(1_8:*)=[INTEGER(4)::4_4,37_4,37_4,307_4,4931_4,4931_4] !logical(4),parameter::rrangecheck=.true._4 -!integer(4),parameter::realrvals(1_8:*)=[INTEGER(4)::0_4,4_4,5_4,37_4,38_4,307_4,308_4,4931_4,4932_4,9863_4,9864_4] -!integer(4),parameter::realrkinds(1_8:*)=[INTEGER(4)::2_4,2_4,3_4,3_4,8_4,8_4,10_4,10_4,16_4,16_4,-2_4] +!integer(4),parameter::realrvals(1_8:*)=[INTEGER(4)::0_4,4_4,5_4,37_4,38_4,307_4,308_4,4931_4,4932_4] +!integer(4),parameter::realrkinds(1_8:*)=[INTEGER(4)::2_4,2_4,3_4,3_4,8_4,8_4,10_4,10_4,-2_4] !logical(4),parameter::realrcheck=.true._4 !logical(4),parameter::radixcheck=.true._4 !integer(4),parameter::intdigits(1_8:*)=[INTEGER(4)::7_4,15_4,31_4,63_4,127_4] !logical(4),parameter::intdigitscheck=.true._4 -!integer(4),parameter::realdigits(1_8:*)=[INTEGER(4)::11_4,8_4,24_4,53_4,64_4,112_4] +!integer(4),parameter::realdigits(1_8:*)=[INTEGER(4)::11_4,8_4,24_4,53_4,64_4,113_4] !logical(4),parameter::realdigitscheck=.true._4 !end From d9871fa9eb2304c4761a4a818187553396bb8924 Mon Sep 17 00:00:00 2001 From: Peter Waller Date: Wed, 26 Feb 2020 19:50:43 +0000 Subject: [PATCH 050/345] Add script to flatten git history for llvm monorepo submission (#854) This script, when run on a checkout of the f18 repository, takes the current origin/master and makes a branch called "new" with a rewritten history; The "new" branch has a flat git history (that is, a series of commits with only one parent). Flattening is done for merge commits by taking the content of the commit as it is at the merge commit. --- flatten.cpp | 916 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 916 insertions(+) create mode 100644 flatten.cpp diff --git a/flatten.cpp b/flatten.cpp new file mode 100644 index 000000000000..302bebaa0bad --- /dev/null +++ b/flatten.cpp @@ -0,0 +1,916 @@ +// Copyright (c) 2019, Arm Ltd. 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. + +// Reminder: no warranty with this program. I recommend using a fresh checkout. + +// Compile with: +// clang++ -Wall -Werror -O2 flatten.cpp -lgit2 +// Run with f18 in PWD or argv[1]: +// time ./a.out ~/.local/src/github.com/flang-compiler/f18/ + +// To get a rewritten history, do this: +// +// sudo apt install -y libgit2-dev # or equivalent +// git clone https://github.com/flang-compiler/f18 +// git remote add llvm-project https://github.com/llvm/llvm-project +// git fetch llvm-project +// clang++ -DREPLACE_REFERENCES -Wall -Werror -O2 flatten.cpp -lgit2 +// ./a.out + +// Inputs: +// * a ref called origin/master, representing f18 history +// * (optionally) a ref called llvm-project/master, representing llvm upstream +// * (optionally) branches called rebase-{12 digit merge sha}, representing the +// manual rebase of tricky cases. +// +// Outputs: +// * A branch called rewritten-history-v2, with a linearized f18 history. +// * A branch called rewritten-history-v2-llvm-project-merge, representing the +// renaming of the project under /flang/ and taking llvm-project/master as the +// new parent for the (original) root commit. +// +// This program is meant to be idempotent and should not write to the working +// directory, it simply takes refs as input and produces them as output. + +// Key concepts: +// +// * The checkout that git gives you for a commit is called a "tree", which is +// determined by a recursive checksum of the directory structure. If two +// commits have the same tree ("treesame"), then they are by definition +// equivalent when you check them out. +// +// * Lineage of the "master" branch is taken by following the first parents of +// each commit. To see this in git log, run `git log --first-parent`. This +// effectively ignores the second-parent history (i.e. commits that happened +// on branches). +// +// * By construction it is arranged that the trees of the first-parent history +// are preserved. This means "the code on the master branch is the same before +// and after rewrite". +// +// * Preserving the non-first-parent commits is trickier, and requires a rebase. +// +// * If nothing changed on the master branch during a feature branch, a rebase +// will not change the trees of the feature branch, so trees of those commits +// will still be the same. It's like rewriting the merge as a fast-forward. +// +// * However, if something happened on the master branch during the feature +// branch, then a rebase *must* create new trees. This implies code which +// might not build. As an example, imagine a case where a class is renamed on +// master, and the old name is used in the feature branch (until it's fixed at +// some point by the time it is merged). +// +// * By the end of the rebase, we assert that the trees are the same as those +// merged into master. So code in the middle of the rebased feature branch may +// not build, but at least the overall result of the feature branch will be as +// good as master was. Thankfully this is relatively rare. +// +// * If a branch exists called rebase-{sha of merge commit}, that branch is +// substituted in place of the merge commit. This allows manually rebasing +// tricky merges. +// +// * For the non-treesame, we can take a second-order diff (diff-of-diff) +// comparing those commits before and after rewrite, and ensure that only line +// numbers and context changed. This is almost totally the case. + +// Using the following script, it is possible to see whether non-TREESAME +// patches still have the same diff, modulo blank lines, by taking a +// second-order diff. +// +// git log --grep=TREESAME --invert-grep --format="%h %(trailers:key=Original-commit)" rewritten-history-v2 | +// sed -n 's|Original-commit: flang-compiler/f18@||p' | +// while read NEW ORIG +// do +// echo ORIG NEW: $ORIG $NEW +// git show $ORIG > a +// git show $NEW > b +// sed -r -i \ +// -e 's/@@ .* @@/@@ Numbers @@/g' \ +// -e '/^(commit|index) .*/d' \ +// -e '/Original-commit.*/d' \ +// -e '/^\s$/d' \ +// a b +// git diff --color --no-index a b +// done |& less -SR + +#include +#include +#include +#include +#include + +#ifndef NO_REPLACE_REFERENCES +#include +#endif + +#include +#include + +#include +#include + +void check(int error, const char *message, const char *extra) { + const git_error *err; + const char *msg = "", *spacer = ""; + + if (!error) + return; + + if ((err = giterr_last()) != NULL && err->message != NULL) { + msg = err->message; + spacer = " - "; + } + + if (extra) + fprintf(stderr, "%s '%s' [%d]%s%s\n", message, extra, error, spacer, msg); + else + fprintf(stderr, "%s [%d]%s%s\n", message, error, spacer, msg); + + exit(1); +} + +int n_conflicts = 0, n_discards = 0; + +// Copy src string to dst string, rewriting issue references. +char *rewrite_issue_references(char *dst, const char *src) { +#ifndef NO_REPLACE_REFERENCES + const char *src_end = src + strlen(src); + // return src_end; + char *new_end = std::regex_replace( + dst, src, src_end, + std::regex("(^|\\b[^a-zA-Z0-9]+)(#[0-9]+)\\b"), + "$1flang-compiler/f18$2"); + *new_end = '\0'; + return new_end; +#else + return stpcpy(dst, src); +#endif +} + +// test_rewrite_issue_references runs some test cases thorugh the string +// replacement machinery and aborts if anything is awry. +void test_rewrite_issue_references() { + #ifdef NO_REPLACE_REFERENCES + return; + #endif + struct { const char *input, *want; } tests[] = { + {"foo#123", "foo#123"}, + {"Test #123bar", "Test #123bar"}, + // Special case. + // {"commit message #123", "commit message #123"}, + + {"#123", "flang-compiler/f18#123"}, + {"Test #123", "Test flang-compiler/f18#123"}, + {"Test #123", "Test flang-compiler/f18#123"}, + {"Test (#123)", "Test (flang-compiler/f18#123)"}, + }; + + bool fail = false; + for (const auto test : tests) { + char *x = (char*)malloc(1024); + const char *new_end = rewrite_issue_references(x, test.input); + if (strcmp(x, test.want)) { + fprintf(stderr, "Got : %s\n", x); + fprintf(stderr, "Want: %s\n", test.want); + fail = true; + } + if (new_end != x + strlen(x)) { + abort(); + } + (void)new_end; + free((void*)x); + } + if (fail) + abort(); +} + +static const char mergemsg_prefix[] = "Merge pull request #"; + +// has_merge_pr_prefix returns true if the commit message begins "Merge pull +// request #". +bool has_merge_pr_prefix(const char* msg) { + int len = sizeof(mergemsg_prefix)-1; + if (strlen(msg) < len) + len = strlen(msg); + return !strncmp(mergemsg_prefix, msg, len); +} + +// tweak_commit_message +// Prepend [flang-compiler/f18#PRNUM] +// Append "Original-commit", "Reviewed-on" and "Tree-same-pre-rewrite". +// +// Allocates a new commit message. Return value must be freed. +// The Reviewed-on trailer URL is determined by "Merge pull request #(number)", +// if present. +char *tweak_commit_message(git_commit *orig_commit, git_commit *orig_merge, const git_oid *new_tree) { + const char *orig_msg = git_commit_message_raw(orig_commit); + const char *prnum = NULL, *prnum_end = NULL; + + // If the message indicates a PR, store in prnum. + if (orig_merge != NULL && has_merge_pr_prefix(git_commit_message(orig_merge))) { + const char *mergemsg = git_commit_message_raw(orig_merge); + prnum = mergemsg + sizeof(mergemsg_prefix) - 1; + prnum_end = strchr(prnum, ' '); + } + + #ifndef NO_REPLACE_REFERENCES + // Match "foo bar baz (#123)", which is the convention for "Squash" commit + // merges on GitHub. + static std::regex prnum_re("^(.*\\(#)([0-9]+)\\)$"); + std::cmatch match; + if (std::regex_match(git_commit_summary(orig_merge), match, prnum_re)) { + const char *summary = git_commit_summary(orig_merge); + prnum = summary + match.length(1); + prnum_end = prnum + match.length(2); + } + #endif + + // Gratuitous space for appending things. + const ssize_t extra_space = 102400; + ssize_t size = strlen(orig_msg) + extra_space; + char *newmsg_start = (char*)malloc(size); + char *newmsg_end = newmsg_start + (size); + char *newmsg = newmsg_start; // Pointer tracks the current write position. + newmsg[0] = 0; + + // Set to leave message unmodified except for Original-commit, useful for + // verifying second-order diffs. + const bool use_original_message = false; + if (use_original_message) { + // These are here to indicate if the checkouts are the same as a commit and/or a merge. + if (git_oid_equal(git_commit_tree_id(orig_merge), new_tree)) { + newmsg = stpncpy(newmsg, "[TREESAME master] ", newmsg_end - newmsg); + } else if (git_oid_equal(git_commit_tree_id(orig_commit), new_tree)) { + newmsg = stpncpy(newmsg, "[TREESAME commit] ", newmsg_end - newmsg); + } + + newmsg = stpcpy(newmsg, orig_msg); + + // From here on out, append trailer headers. + char buf[GIT_OID_HEXSZ+1] = {}; + newmsg = stpncpy(newmsg, "\n\nOriginal-commit: flang-compiler/f18@", newmsg_end - newmsg); + git_oid_fmt(buf, git_commit_id(orig_commit)); + newmsg = stpncpy(newmsg, buf, newmsg_end - newmsg); + newmsg = stpncpy(newmsg, "\n", newmsg_end - newmsg); + return newmsg_start; + } + + // Prepend [Flang] tag. + newmsg = stpncpy(newmsg, "[Flang] ", newmsg_end - newmsg); + + // Paste in the original message, rewriting references #123 => flang-compiler/f18#123 + newmsg = rewrite_issue_references(newmsg, orig_msg); + + // If there is a newline at the end, remove it; subsequent insertion of the + // Original-commit header will always insert it. This ensures consistent + // spacing before the header. + while (newmsg[-1] == '\n') { + newmsg[-1] = 0; + newmsg--; + } + + // From here on out, append trailer headers. + char buf[GIT_OID_HEXSZ+1] = {}; + newmsg = stpncpy(newmsg, "\n\nOriginal-commit: flang-compiler/f18@", newmsg_end - newmsg); + git_oid_fmt(buf, git_commit_id(orig_commit)); + newmsg = stpncpy(newmsg, buf, newmsg_end - newmsg); + newmsg = stpncpy(newmsg, "\n", newmsg_end - newmsg); + + if (prnum != NULL) { + newmsg = stpncpy(newmsg, "Reviewed-on: https://github.com/flang-compiler/f18/pull/", newmsg_end - newmsg); + newmsg = stpncpy(newmsg, prnum, prnum_end - prnum); + newmsg = stpncpy(newmsg, "\n", newmsg_end - newmsg); + } + + if (!git_oid_equal(git_commit_tree_id(orig_merge), new_tree)) { + // If this is present, then the contents of the tree are identical pre- + // and post- merge. If it is not present, then the patch was rebased. + newmsg = stpncpy(newmsg, "Tree-same-pre-rewrite: false\n", newmsg_end - newmsg); + } + + return newmsg_start; +} + +// insert_flang_directory sets new_root to a newly created tree with one entry +// in it: /flang/, which points at orig_root. +void insert_flang_directory(git_repository *repo, git_oid *new_root, const git_oid *orig_root) { + git_treebuilder *tb; + check(git_treebuilder_new(&tb, repo, NULL), "git_treebuilder_new", NULL); + const git_tree_entry *te; + git_treebuilder_insert(&te, tb, "flang", orig_root, GIT_FILEMODE_TREE); + git_treebuilder_write(new_root, tb); + git_treebuilder_free(tb); +} + +// count_branch_commits counts the number of on-branch (non-merge) commits in +// the given merge. +int count_branch_commits(git_commit *merge) { + git_revwalk *walk; + check(git_revwalk_new(&walk, git_commit_owner(merge)), "git_revwalk_new", NULL); + check(git_revwalk_hide(walk, git_commit_parent_id(merge, 0)), "git_revwalk_hide", NULL); + check(git_revwalk_push(walk, git_commit_parent_id(merge, 1)), "git_revwalk_push", NULL); + + git_oid commit_oid; + int n = 0; + while (!git_revwalk_next(&commit_oid, walk)) + n++; + + git_revwalk_free(walk); + return n; +} + +// tree_for_commit grabs the git_oid pointing to the tree for a given commit_id. +git_oid tree_for_commit(git_repository *repo, const git_oid *commit_id) { + git_commit *c; + check(git_commit_lookup(&c, repo, commit_id), "git_commit_lookup", NULL); + git_oid tree_id; + git_oid_cpy(&tree_id, git_commit_tree_id(c)); + git_commit_free(c); + // git_commit_ + return tree_id; +} + +// generate_authortime_to_commit_map walks the commits on the second-parent +// history of the given `merge`, computing a mapping from the author time to the +// original commit id. Since this is scoped to feature-branch commits, there are +// not likely to be collisions. +void generate_authortime_to_commit_map(std::map &authortime_to_commit, git_commit *merge) { + git_repository *repo = git_commit_owner(merge); + git_revwalk *walk; + check(git_revwalk_new(&walk, git_commit_owner(merge)), "git_revwalk_new", NULL); + check(git_revwalk_hide(walk, git_commit_parent_id(merge, 0)), "git_revwalk_hide", NULL); + check(git_revwalk_push(walk, git_commit_parent_id(merge, 1)), "git_revwalk_push", NULL); + + // Only walk first parent history on the grounds that most of those which + // introduce commits not-already-on-mainline are accidental merges of + // rebases, duplicating patches in history. Where patches are missed, they + // won't have an entry in the authortime_to_commit. + git_revwalk_simplify_first_parent(walk); + + git_oid commit_id; + while (!git_revwalk_next(&commit_id, walk)) { + git_commit *c; + check(git_commit_lookup(&c, repo, &commit_id), "git_commit_lookup", NULL); + int when = git_commit_author(c)->when.time; + + if (authortime_to_commit.count(when) != 0) { + char buf[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf, 12, &commit_id); + char buf1[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf1, 12, git_commit_id(merge)); + char buf2[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf2, 12, &authortime_to_commit[when]); + printf("Hit duplicate commit considering %s " + "(merge %s, duplicate %s)\n", buf, buf1, buf2); + // Duplicate author times. Need another strategy. + abort(); + } + authortime_to_commit[when] = commit_id; + git_commit_free(c); + } + + git_revwalk_free(walk); +} + +// try_rebase attempts to rebase orig_merge onto the new history. +// It returns true if the rebase succeeds without conflicts, and false otherwise. +// On success, new_head is set to the tip of the rebase. +bool try_rebase(git_oid **new_head, git_commit *orig_merge) { + git_repository *repo = git_commit_owner(orig_merge); + const git_oid *p0 = git_commit_parent_id(orig_merge, 0); + const git_oid *p1 = git_commit_parent_id(orig_merge, 1); + + git_annotated_commit *p0a, *p1a, *new_heada; + check(git_annotated_commit_lookup(&p0a, repo, p0), "git_annotated_commit_lookup p0", NULL); + check(git_annotated_commit_lookup(&p1a, repo, p1), "git_annotated_commit_lookup p1", NULL); + check(git_annotated_commit_lookup(&new_heada, repo, *new_head), "git_annotated_commit_lookup new_head", NULL); + + char buf[] = "refs/heads/rebase-0123456789ab\0"; + git_oid_nfmt(buf+sizeof("refs/heads/rebase-")-1, 12, git_commit_id(orig_merge)); + + bool using_manual_rebase = false; + + // Look for a branch called rebase-[12 digit SHA]. If it exists and is + // tree-same to the merge, treat it as the branch we're trying to rebase. + git_reference *manual_rebase; + int err = git_reference_lookup(&manual_rebase, repo, buf); + switch (err) { + case 0: { // Reference found. + const git_oid manual_tree = tree_for_commit(repo, git_reference_target(manual_rebase)); + + if (0 == git_oid_cmp( + git_reference_target(manual_rebase), + git_commit_id(orig_merge))) { + printf("Skip %s because it's pointing at the merge.\n", buf); + goto manual_rebase_unusable; + } + if (0 != git_oid_cmp(&manual_tree, git_commit_tree_id(orig_merge))) { + printf("Skip %s because the tip of the rebase is not " + "treesame to the merge commit\n", buf); + goto manual_rebase_unusable; + } + printf("Using manual rebase branch %s\n", buf); + using_manual_rebase = true; + + // Update p1a, the commits being rebased, to point at the branch. + // Then rebase, and this shouldn't result in any conflicts. + git_annotated_commit_free(p1a); + git_annotated_commit_lookup(&p1a, repo, git_reference_target(manual_rebase)); + + manual_rebase_unusable: + git_reference_free(manual_rebase); + + break; + } + case GIT_ENOTFOUND: + // printf("Rebase branch %s not found.\n", buf); + break; + default: + check(err, "git_reference_lookup rebase-...", NULL); + } + + git_rebase_options rb_opts; + check(git_rebase_init_options(&rb_opts, GIT_REBASE_OPTIONS_VERSION), "git_rebase_init_options", NULL); + rb_opts.inmemory = 1; + rb_opts.merge_options.flags = GIT_MERGE_FIND_RENAMES; + rb_opts.merge_options.rename_threshold = 50; + + git_rebase *rb; + check(git_rebase_init(&rb, repo, p1a, p0a, new_heada, &rb_opts), "git_rebase_init", NULL); + + bool is_success = true; // becomes false if conflicts encountered. + bool committed_at_least_one_patch = false; + git_oid rebase_tip_id; + git_oid_cpy(&rebase_tip_id, *new_head); + + std::map authortime_to_commit; + if (using_manual_rebase) { + generate_authortime_to_commit_map(authortime_to_commit, orig_merge); + } + + // Loop over each patch in the rebase, committing it. + git_rebase_operation *op; + while (!git_rebase_next(&op, rb)) { + git_index *idx; + check(git_rebase_inmemory_index(&idx, rb), "git_rebase_inmemory_index", NULL); + if (git_index_has_conflicts(idx)) { + // Conflicting case. Print a useful message. + char buf_patch[GIT_OID_HEXSZ+1] = {}; + char buf_merge[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf_patch, 12, &op->id); + git_oid_nfmt(buf_merge, 12, git_commit_id(orig_merge)); + + int discarded = count_branch_commits(orig_merge); + printf("Conflicts encountered; patch=%s merge=%s - discarding %d commits\n", buf_patch, buf_merge, discarded); + printf(" M=%s; git checkout -B rebase-${M} ${M}^2; git rebase ${M}^1\n", buf_merge); + + n_conflicts++; + n_discards += discarded; + + git_index_free(idx); + is_success = false; + // If conflicts are found, abort, fall back to taking the merge + // commit. + break; + } + git_index_free(idx); + + git_commit *orig_commit; + check(git_commit_lookup(&orig_commit, repo, &op->id), "git_commit_lookup", NULL); + + // Generate the new tree now (as opposed to within git_rebase_commit) so that it can be used for TREESAME + // diagnostics in the commit message. + git_oid new_tree; + check(git_index_write_tree_to(&new_tree, idx, repo), "git_index_write_tree_to", NULL); + + if (using_manual_rebase) { + // If in a manual rebase, need to lookup original patch. + // Use the author timestamp as a heuristic for patch equality. + const git_time_t when = git_commit_author(orig_commit)->when.time; + git_oid pre_rebase_commit_id = {}; + if (when == 1518039228) { // Wed Feb 7 13:33:48 2018 -0800 + // Hack for a single special case, a commit which was merged. + check(git_oid_fromstr(&pre_rebase_commit_id, "044148ead21f18e16716d5bc30819525c79065d0"), "git_oid_fromstr", NULL); + } else if (authortime_to_commit.count(when) == 0) { + char buf[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf, 12, &op->id); + printf("Unable to find original commit for manual " + "rebase: %s\n", buf); + git_oid_nfmt(buf, 12, git_commit_id(orig_merge)); + printf(" Merge: %s\n", buf); + abort(); + } else { + pre_rebase_commit_id = authortime_to_commit[when]; + } + + // Replace orig_commit (the rebased commit in this context) with the + // true original commit, so that the commit cross-reference + // correctly reflects a commit which exists in the f18 repository. + git_commit_free(orig_commit); + check(git_commit_lookup(&orig_commit, repo, &pre_rebase_commit_id), "git_commit_lookup", NULL); + } + + const char *msg = tweak_commit_message(orig_commit, orig_merge, &new_tree); + + int err = git_rebase_commit( + &rebase_tip_id, + rb, + NULL, + // Take the committer information from the merge commit if manually rebased. + using_manual_rebase ? git_commit_committer(orig_merge): git_commit_committer(orig_commit), + NULL, + msg + ); + free((void*)msg); + + git_commit_free(orig_commit); + if (err == GIT_EAPPLIED) { + // Applying the patch results in the same tree, so the patch is + // empty. + char buf_patch[GIT_OID_HEXSZ+1] = {}; + char buf_merge[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf_patch, 12, &op->id); + git_oid_nfmt(buf_merge, 12, git_commit_id(orig_merge)); + printf("Patch already exists in history; patch=%s merge=%s\n", buf_patch, buf_merge); + continue; + } + check(err, "git_rebase_commit", NULL); + committed_at_least_one_patch = true; + } + + if (is_success && committed_at_least_one_patch) { + // Update the growing new_head to point at our new rebase tip. + git_oid_cpy(*new_head, &rebase_tip_id); + } + + git_rebase_abort(rb); + git_rebase_free(rb); + + git_annotated_commit_free(p0a); + git_annotated_commit_free(p1a); + git_annotated_commit_free(new_heada); + + return is_success; +} + +// merge_llvm_project_tree generates a new root tree combining the llvm project +// tree and the given new_tree_id. new_tree_id is updated to point at the new tree. +void merge_llvm_project_tree( + git_oid *new_tree_id, + const git_oid *orig_tree, + const git_tree *llvm_project_tree) { + + git_repository *repo = git_tree_owner(llvm_project_tree); + + git_tree *flang_tree; + check(git_tree_lookup(&flang_tree, repo, orig_tree), "git_tree_lookup", NULL); + const git_oid *flang_dir_tree_id = git_tree_entry_id(git_tree_entry_byname(flang_tree, "flang")); + + // Effectively merges the flang/ directory into the llvm project tree. + git_treebuilder *tb; + check(git_treebuilder_new(&tb, repo, llvm_project_tree), "git_treebuilder_new", NULL); + check(git_treebuilder_insert(NULL, tb, "flang", flang_dir_tree_id, GIT_FILEMODE_TREE), "git_treebuilder_insert", NULL); + check(git_treebuilder_write(new_tree_id, tb), "git_treebuilder_write", NULL); + git_treebuilder_free(tb); + + git_tree_free(flang_tree); +} + +// generate_squash_message generates a commit message for merges which have been +// squashed. +void generate_squash_message(char **newmsg, git_commit *merge_commit) { + std::stringstream s; + + // Start the message with the existing rewritten message. + s << *newmsg; + + s << "\nDue to a conflicting rebase during the linearizing of " + "flang-compiler/f18, this commit squashes a number of " + "other commits:\n\n"; + + git_revwalk *walk; + check(git_revwalk_new(&walk, git_commit_owner(merge_commit)), "allocate git_revwalk", NULL); + git_revwalk_simplify_first_parent(walk); + git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE); + check(git_revwalk_push(walk, git_commit_parent_id(merge_commit, 1)), "git_revwalk_push", NULL); + check(git_revwalk_hide(walk, git_commit_parent_id(merge_commit, 0)), "git_revwalk_hide", NULL); + + git_oid commit_id; + while (!git_revwalk_next(&commit_id, walk)) { + char buf[GIT_OID_HEXSZ+1] = {}; + git_oid_fmt(buf, &commit_id); + + git_commit *c; + check(git_commit_lookup(&c, git_commit_owner(merge_commit), &commit_id), "git_commit_lookup", NULL); + + s << "flang-compiler/f18@" << buf << " " << git_commit_summary(c) << "\n"; + git_commit_free(c); + + } + + git_revwalk_free(walk); + + // Replace newmsg with the squashed msg. + auto result = s.str(); + char *squashmsg = (char*)malloc(result.size()+1); + squashmsg[result.size()] = 0; + strncpy(squashmsg, result.c_str(), result.size()); + free(*newmsg); + *newmsg = squashmsg; +} + +int main(int argc, char* argv[]) { + test_rewrite_issue_references(); + + git_libgit2_init(); + + const char *repo_path = "."; + if (argc > 1) + repo_path = argv[1]; + + git_repository *repo; + int error = git_repository_open(&repo, repo_path); + if (error < 0) { + fprintf(stderr, "Could not open repository: %s\n", giterr_last()->message); + exit(1); + } + + // Walk commits in reverse topological order starting from origin/master. + git_revwalk *walk; + check(git_revwalk_new(&walk, repo), "allocate git_revwalk", NULL); + git_revwalk_simplify_first_parent(walk); + git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE); + + check(git_revwalk_push_ref(walk, "refs/remotes/origin/master"), "git_revwalk_push_head", NULL); + // check(git_revwalk_push_ref(walk, "refs/heads/flatten-top"), "git_revwalk_push_ref", NULL); + // check(git_revwalk_hide_ref(walk, "refs/heads/flatten-bottom"), "git_revwalk_hide_ref", NULL); + + bool is_root = true; // First commit has no parents. + git_oid old_head = {}; + git_oid *new_head = NULL; + git_oid new_commit_id = {}; + + // For each commit in the first-parent lineage of the original history: + // + // 1. Take non-merge commits as they were. + // 2. Attempt to rebase second-parent of merge commits onto first-parent. + // 2a. Otherwise, squash them. + // + // Merge commits are preserved as empty commits. + while (!git_revwalk_next(&old_head, walk)) { + git_commit *c; + check(git_commit_lookup(&c, repo, &old_head), "git_commit_lookup", NULL); + + // Prettify the commit message - rewrite references, add trailer headers. + char *newmsg = tweak_commit_message(c, c, git_commit_tree_id(c)); + + switch (git_commit_parentcount(c)) { + default: + fprintf(stderr, "Unexpected number of parents.\n"); + exit(5); + + case 2: { + if (is_root) { + // root commit cannot be rebased. Squash instead. + // (only happens if using a restricted commit range) + break; + } + if (try_rebase(&new_head, c)) { + // Rebase succeeded. Now ensure that at the end of the rebase, + // the tree state is the same as if the merge had been done. + git_oid old_tree = tree_for_commit(repo, &old_head); + git_oid new_tree = tree_for_commit(repo, new_head); + if (!git_oid_equal(&old_tree, &new_tree)) { + char buf_old_head[GIT_OID_HEXSZ+1] = {}; + char buf_new_head[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf_old_head, 12, &old_head); + git_oid_nfmt(buf_new_head, 12, new_head); + + fprintf(stderr, "commits do not have the same tree: (old, " + "new) = %s %s", buf_old_head, buf_new_head); + exit(6); + } + + // Create an empty commit for the merge. + check(git_commit_create_from_ids( + &new_commit_id, + repo, + NULL, + git_commit_author(c), + git_commit_committer(c), + git_commit_message_encoding(c), + newmsg, + &new_tree, + is_root ? 0 : 1, + (const git_oid**)(&new_head) + ), "git_commit_create_from_ids", NULL); + new_head = &new_commit_id; + is_root = false; + + // Rebase succeeded, new_head updated. Keep going... + goto next_patch; + } + + generate_squash_message(&newmsg, c); + } + + // These are non-merge commits on the first-parent history. + // Take them as-is. + case 0: case 1: ; + } + + // Create a new commit. + check(git_commit_create_from_ids( + &new_commit_id, + repo, + NULL, + git_commit_author(c), + git_commit_committer(c), + git_commit_message_encoding(c), + newmsg, + git_commit_tree_id(c), + is_root ? 0 : 1, + (const git_oid**)(&new_head) + ), "git_commit_create_from_ids", NULL); + new_head = &new_commit_id; + is_root = false; + + next_patch: + free((void*)newmsg); + git_commit_free(c); + } + + // First pass now done. Move the directory in a second pass, and re-parent + // onto llvm-project if it is available. + + char buf[GIT_OID_HEXSZ+1] = {}; + git_oid_nfmt(buf, 12, new_head); + printf("\nConflicts encountered: %d, discarding %d commits\n", n_conflicts, n_discards); + printf("Done; rewritten-history-v4 => %s\n", buf); + + git_reference *ref; + check(git_reference_create( + &ref, + repo, + "refs/heads/rewritten-history-v4", + new_head, + 1, + "flatten.cpp update" + ), + "git_reference_create", NULL); + git_reference_free(ref); + + git_revwalk_reset(walk); + + // Now rename everything under flang/. + printf("Inserting /flang/...\n"); + { + git_oid new_commit_id; + bool is_root = true; // First commit has no parents. + git_oid *new_head_renamed = NULL; + + git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE); + check(git_revwalk_push(walk, new_head), "git_revwalk_push_head", NULL); + + // See if the upstream is available at llvm-project/master. If it is, + // we'll write the history into there, and use the LLVM project head as + // the root commit. + git_oid llvm_project_head = {}; + git_tree *llvm_project_tree; + int err = git_reference_name_to_id(&llvm_project_head, repo, "refs/remotes/llvm-project/master"); + bool have_llvm_project = err == 0; + + if (!have_llvm_project) { + fprintf(stderr, "Require llvm-project/master ref to exist before proceeding. Add llvm-project as a remote and fetch it.\n"); + exit(2); + } + + git_oid_nfmt(buf, 12, &llvm_project_head); + printf("Rewriting history on top of llvm-project@%s...\n", buf); + + // Disabled since the merged MLIR root commit has zero parents. + // Take the same approach to be consistent (= false). + const bool use_llvm_project_head_as_root = false; + if (use_llvm_project_head_as_root) { + new_head_renamed = &llvm_project_head; + is_root = false; + } + + // Grab the llvm_project_tree. + git_commit *c; + check(git_commit_lookup(&c, repo, &llvm_project_head), "git_commit_lookup", NULL); + check(git_commit_tree(&llvm_project_tree, c), "git_commit_tree", NULL); + git_commit_free(c); + + git_oid new_tree; + + // For each commit, rewrite its tree. + while (!git_revwalk_next(&old_head, walk)) { + git_commit *c; + check(git_commit_lookup(&c, repo, &old_head), "git_commit_lookup", NULL); + + insert_flang_directory(repo, &new_tree, git_commit_tree_id(c)); + + check(git_commit_create_from_ids( + &new_commit_id, + repo, + NULL, + git_commit_author(c), + git_commit_committer(c), + git_commit_message_encoding(c), + git_commit_message_raw(c), + &new_tree, + is_root ? 0 : 1, + (const git_oid**)(&new_head_renamed) + ), "git_commit_create_from_ids", NULL); + new_head_renamed = &new_commit_id; + is_root = false; + + git_commit_free(c); + } + + git_signature *merge_commit_author; + check(git_signature_default(&merge_commit_author, repo), "git_signature_default", NULL); + + const char *merge_message = + "[Flang] Merge flang-compiler/f18\n" + "\n" + "This is the initial merge of flang-compiler, which is done in this way\n" + "principally to preserve the history and git-blame, without generating a large\n" + "number of commits on the first-parent history of LLVM.\n" + "\n" + "If you don't care about the flang history during a bisect remember that you can\n" + "supply paths to git-bisect, e.g. `git bisect start clang llvm`.\n" + "\n" + "The history of f18 was rewritten to:\n" + "\n" + "* Put the code under /flang/.\n" + "* Linearize the history.\n" + "* Rewrite commit messages so that issue and PR numbers point to the old repository.\n" + "\n" + "Updates: flang-compiler/f18#876 (submission into llvm-project)\n" + "Mailing-list: http://lists.llvm.org/pipermail/llvm-dev/2020-January/137989.html ([llvm-dev] Flang landing in the monorepo - next Monday!)\n" + "Mailing-list: http://lists.llvm.org/pipermail/llvm-dev/2019-December/137661.html ([llvm-dev] Flang landing in the monorepo)\n"; + + merge_llvm_project_tree(&new_tree, &new_tree, llvm_project_tree); + + const git_oid *parents[2] = {}; + parents[0] = &llvm_project_head; + parents[1] = &new_commit_id; + + git_oid new_head_merged; + check(git_commit_create_from_ids( + &new_head_merged, + repo, + NULL, + merge_commit_author, + merge_commit_author, + NULL, + merge_message, + &new_tree, + 2, + parents + ), "git_commit_create_from_ids", NULL); + + git_signature_free(merge_commit_author); + + git_tree_free(llvm_project_tree); + + git_reference *ref; + check(git_reference_create( + &ref, + repo, + "refs/heads/rewritten-history-v4-llvm-project-merge", + &new_head_merged, + 1, + "flatten.cpp update" + ), + "git_reference_create", NULL); + git_reference_free(ref); + + git_oid_nfmt(buf, 12, &new_head_merged); + printf("Done; rewritten-history-v4-llvm-project-merge => %s\n", buf); + } + printf(" ... all done\n"); + + git_oid origin_master; + git_reference_name_to_id(&origin_master, repo, "refs/remotes/origin/master"); + git_oid_nfmt(buf, 12, &origin_master); + printf("Start point was origin/master => %s\n", buf); + + git_revwalk_free(walk); + git_repository_free(repo); + + return 0; +} From 886ccc37fbff5df00717ac728e2aba240d0a314c Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Sat, 22 Feb 2020 11:47:04 -0800 Subject: [PATCH 051/345] Remove use of std::set::merge Some versions of clang that we are building with don't have std::set::merge, even though it is part of C++17. Work around that by using std::set::insert until we can count on merge being available everywhere. --- lib/Semantics/check-do-forall.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Semantics/check-do-forall.cpp b/lib/Semantics/check-do-forall.cpp index 615e1005e36b..6dfd51c8a823 100644 --- a/lib/Semantics/check-do-forall.cpp +++ b/lib/Semantics/check-do-forall.cpp @@ -763,13 +763,27 @@ class DoContext { common::visitors{ [&](const evaluate::Assignment::BoundsSpec &spec) { for (const auto &bound : spec) { +// TODO: this is working around missing std::set::merge in some versions of +// clang that we are building with +#ifdef __clang__ + auto boundSymbols{evaluate::CollectSymbols(bound)}; + symbols.insert(boundSymbols.begin(), boundSymbols.end()); +#else symbols.merge(evaluate::CollectSymbols(bound)); +#endif } }, [&](const evaluate::Assignment::BoundsRemapping &remapping) { for (const auto &bounds : remapping) { +#ifdef __clang__ + auto lbSymbols{evaluate::CollectSymbols(bounds.first)}; + symbols.insert(lbSymbols.begin(), lbSymbols.end()); + auto ubSymbols{evaluate::CollectSymbols(bounds.second)}; + symbols.insert(ubSymbols.begin(), ubSymbols.end()); +#else symbols.merge(evaluate::CollectSymbols(bounds.first)); symbols.merge(evaluate::CollectSymbols(bounds.second)); +#endif } }, [](const auto &) {}, From 45998741e5f04bba7db6eed6a4d27c1d25209b41 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Wed, 26 Feb 2020 20:19:48 -0800 Subject: [PATCH 052/345] Semantic checks for C712 through C727 I've updated the compiler and test source with references to the contraints at the points where they were enforced and tested. Many of these were already implemented and required no code change. A few constraint checks were both implemented and tested, and I only added references to the constraint numbers in the compiler source and tests. Here are the things I had to implement: Constraint C716 states that, in a REAL constant, if both a kind-param and an exponent letter appear, the exponent letter must be 'E'. Constraints C715 and C719 require that a KIND value be actually implemented. Constraint C722 requires that functions that return assumed-length character types are external. Constraint C726 disallows assumed lenght charater types for dummy arguments and return types. --- include/flang/Semantics/expression.h | 2 +- include/flang/Semantics/tools.h | 8 ++++-- lib/Evaluate/tools.cpp | 2 +- lib/Semantics/check-declarations.cpp | 27 +++++++++++-------- lib/Semantics/expression.cpp | 27 +++++++++---------- lib/Semantics/resolve-names.cpp | 8 ++++++ lib/Semantics/tools.cpp | 35 ++++++++++++++++++++---- test/Semantics/CMakeLists.txt | 5 ++++ test/Semantics/call05.f90 | 4 +-- test/Semantics/complex01.f90 | 32 ++++++++++++++++++++++ test/Semantics/kinds02.f90 | 27 +++++++++++++++++++ test/Semantics/kinds04.f90 | 31 +++++++++++++++++++++ test/Semantics/resolve35.f90 | 1 + test/Semantics/resolve37.f90 | 1 + test/Semantics/resolve41.f90 | 1 + test/Semantics/resolve73.f90 | 40 ++++++++++++++++++++++++++++ test/Semantics/resolve74.f90 | 37 +++++++++++++++++++++++++ test/Semantics/resolve75.f90 | 13 +++++++++ 18 files changed, 265 insertions(+), 36 deletions(-) create mode 100644 test/Semantics/complex01.f90 create mode 100644 test/Semantics/kinds04.f90 create mode 100644 test/Semantics/resolve73.f90 create mode 100644 test/Semantics/resolve74.f90 create mode 100644 test/Semantics/resolve75.f90 diff --git a/include/flang/Semantics/expression.h b/include/flang/Semantics/expression.h index 2e135b0885d0..7282a96f1095 100644 --- a/include/flang/Semantics/expression.h +++ b/include/flang/Semantics/expression.h @@ -186,7 +186,7 @@ class ExpressionAnalyzer { auto result{Analyze(x.thing)}; if (result) { *result = Fold(std::move(*result)); - if (!IsConstantExpr(*result)) { //C886,C887 + if (!IsConstantExpr(*result)) { // C886, C887, C713 SayAt(x, "Must be a constant value"_err_en_US); ResetExpr(x); return std::nullopt; diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index 69ca0e35dfe9..f73958472fdf 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -48,7 +48,7 @@ const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &); const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &); const DeclTypeSpec *FindParentTypeSpec(const Scope &); const DeclTypeSpec *FindParentTypeSpec(const Symbol &); - + // Return the Symbol of the variable of a construct association, if it exists const Symbol *GetAssociationRoot(const Symbol &); @@ -78,6 +78,10 @@ bool DoesScopeContain(const Scope *, const Symbol &); bool IsUseAssociated(const Symbol &, const Scope &); bool IsHostAssociated(const Symbol &, const Scope &); bool IsDummy(const Symbol &); +bool IsStmtFunction(const Symbol &); +bool IsInStmtFunction(const Symbol &); +bool IsStmtFunctionDummy(const Symbol &); +bool IsStmtFunctionResult(const Symbol &); bool IsPointerDummy(const Symbol &); bool IsFunction(const Symbol &); bool IsPureProcedure(const Symbol &); @@ -154,7 +158,7 @@ inline bool IsAssumedSizeArray(const Symbol &symbol) { return details && details->IsAssumedSize(); } bool IsAssumedLengthCharacter(const Symbol &); -bool IsAssumedLengthCharacterFunction(const Symbol &); +bool IsAssumedLengthExternalCharacterFunction(const Symbol &); // Is the symbol modifiable in this scope std::optional WhyNotModifiable( const Symbol &, const Scope &); diff --git a/lib/Evaluate/tools.cpp b/lib/Evaluate/tools.cpp index cfa675c1bf81..624fb352a59e 100644 --- a/lib/Evaluate/tools.cpp +++ b/lib/Evaluate/tools.cpp @@ -101,7 +101,7 @@ ConvertRealOperandsResult ConvertRealOperands( return {AsSameKindExprs( ConvertTo(ry, std::move(bx)), std::move(ry))}; }, - [&](auto &&, auto &&) -> ConvertRealOperandsResult { + [&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718 messages.Say("operands must be INTEGER or REAL"_err_en_US); return std::nullopt; }, diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index 4e463319accc..1b7dd988e5b1 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -105,9 +105,11 @@ class CheckHelper { void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) { if (value.isAssumed()) { - if (!canBeAssumed) { // C795 + if (!canBeAssumed) { // C795, C721, C726 messages_.Say( - "An assumed (*) type parameter may be used only for a dummy argument, associate name, or named constant"_err_en_US); + "An assumed (*) type parameter may be used only for a (non-statement" + " function) dummy argument, associate name, named constant, or" + " external function result"_err_en_US); } } else { CheckSpecExpr(value.GetExplicit()); @@ -186,16 +188,19 @@ void CheckHelper::Check(const Symbol &symbol) { } } } - if (type) { + if (type) { // Section 7.2, paragraph 7 bool canHaveAssumedParameter{IsNamedConstant(symbol) || - IsAssumedLengthCharacterFunction(symbol) || + IsAssumedLengthExternalCharacterFunction(symbol) || // C722 symbol.test(Symbol::Flag::ParentComp)}; - if (const auto *object{symbol.detailsIf()}) { - canHaveAssumedParameter |= object->isDummy() || - (object->isFuncResult() && - type->category() == DeclTypeSpec::Character); - } else { - canHaveAssumedParameter |= symbol.has(); + if (!IsStmtFunctionDummy(symbol)) { // C726 + if (const auto *object{symbol.detailsIf()}) { + canHaveAssumedParameter |= object->isDummy() || + (object->isFuncResult() && + type->category() == DeclTypeSpec::Character) || + IsStmtFunctionResult(symbol); // Avoids multiple messages + } else { + canHaveAssumedParameter |= symbol.has(); + } } Check(*type, canHaveAssumedParameter); if (InPure() && InFunction() && IsFunctionResult(symbol)) { @@ -216,7 +221,7 @@ void CheckHelper::Check(const Symbol &symbol) { } } } - if (IsAssumedLengthCharacterFunction(symbol)) { // C723 + if (IsAssumedLengthExternalCharacterFunction(symbol)) { // C723 if (symbol.attrs().test(Attr::RECURSIVE)) { messages_.Say( "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US); diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index a41e754d5ade..5c1a040d1074 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -500,10 +500,10 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) { // Use a local message context around the real literal for better // provenance on any messages. auto restorer{GetContextualMessages().SetLocation(x.real.source)}; - // If a kind parameter appears, it defines the kind of the literal and any - // letter used in an exponent part (e.g., the 'E' in "6.02214E+23") - // should agree. In the absence of an explicit kind parameter, any exponent - // letter determines the kind. Otherwise, defaults apply. + // If a kind parameter appears, it defines the kind of the literal and the + // letter used in an exponent part must be 'E' (e.g., the 'E' in + // "6.02214E+23"). In the absence of an explicit kind parameter, any + // exponent letter determines the kind. Otherwise, defaults apply. auto &defaults{context_.defaultKinds()}; int defaultKind{defaults.GetDefaultKind(TypeCategory::Real)}; const char *end{x.real.source.end()}; @@ -525,14 +525,13 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) { defaultKind = *letterKind; } auto kind{AnalyzeKindParam(x.kind, defaultKind)}; - if (letterKind && kind != *letterKind && expoLetter != 'e') { - Say("Explicit kind parameter on real constant disagrees with " - "exponent letter '%c'"_en_US, - expoLetter); + if (x.kind && letterKind && expoLetter != 'e') { // C716 + Say("Explicit kind parameter on REAL constant can only be used with" + " exponent letter 'E'"_err_en_US); } auto result{common::SearchTypes( RealTypeVisitor{kind, x.real.source, GetFoldingContext()})}; - if (!result) { + if (!result) { // C717 Say("Unsupported REAL(KIND=%d)"_err_en_US, kind); } return AsMaybeExpr(std::move(result)); @@ -704,7 +703,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::NamedConstant &n) { if (IsConstantExpr(folded)) { return {folded}; } - Say(n.v.source, "must be a constant"_err_en_US); + Say(n.v.source, "must be a constant"_err_en_US); // C718 } return std::nullopt; } @@ -1820,8 +1819,8 @@ void ExpressionAnalyzer::CheckForBadRecursion( if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3) msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US, callSite); - } else if (IsAssumedLengthCharacterFunction(proc)) { // 15.6.2.1(3) - msg = Say( + } else if (IsAssumedLengthExternalCharacterFunction(proc)) { + msg = Say( // 15.6.2.1(3) "Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US, callSite); } @@ -2422,7 +2421,7 @@ DynamicType ExpressionAnalyzer::GetDefaultKindOfType( bool ExpressionAnalyzer::CheckIntrinsicKind( TypeCategory category, std::int64_t kind) { - if (IsValidKindOfIntrinsicType(category, kind)) { + if (IsValidKindOfIntrinsicType(category, kind)) { // C712, C714, C715 return true; } else { Say("%s(KIND=%jd) is not a supported type"_err_en_US, @@ -2471,7 +2470,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { // C885 + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 7d6fa5d644da..670cec7ed970 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -2602,6 +2602,7 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) { if (resultType) { resultDetails.set_type(*resultType); } + resultDetails.set_funcResult(true); Symbol &result{MakeSymbol(name, std::move(resultDetails))}; ApplyImplicitRules(result); details.set_result(result); @@ -3271,6 +3272,13 @@ void DeclarationVisitor::Post(const parser::IntrinsicTypeSpec::Character &) { } void DeclarationVisitor::Post(const parser::CharSelector::LengthAndKind &x) { charInfo_.kind = EvaluateSubscriptIntExpr(x.kind); + std::optional intKind{ToInt64(charInfo_.kind)}; + if (intKind && + !evaluate::IsValidKindOfIntrinsicType( + TypeCategory::Character, *intKind)) { // C715, C719 + Say(currStmtSource().value(), + "KIND value (%jd) not valid for CHARACTER"_err_en_US, *intKind); + } if (x.length) { charInfo_.length = GetParamValue(*x.length, common::TypeParamAttr::Len); } diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index b4a2a281ee7d..d5fc39c987b6 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -196,6 +196,29 @@ bool IsDummy(const Symbol &symbol) { } } +bool IsStmtFunction(const Symbol &symbol) { + const auto *subprogram{symbol.detailsIf()}; + if (subprogram && subprogram->stmtFunction()) { + return true; + } + return false; +} + +bool IsInStmtFunction(const Symbol &symbol) { + if (const Symbol * function{symbol.owner().symbol()}) { + return IsStmtFunction(*function); + } + return false; +} + +bool IsStmtFunctionDummy(const Symbol &symbol) { + return IsDummy(symbol) && IsInStmtFunction(symbol); +} + +bool IsStmtFunctionResult(const Symbol &symbol) { + return IsFunctionResult(symbol) && IsInStmtFunction(symbol); +} + bool IsPointerDummy(const Symbol &symbol) { return IsPointer(symbol) && IsDummy(symbol); } @@ -686,11 +709,13 @@ bool IsAssumedLengthCharacter(const Symbol &symbol) { } } -bool IsAssumedLengthCharacterFunction(const Symbol &symbol) { - // Assumed-length character functions only appear as such in their - // definitions; their interfaces, pointers to them, and dummy procedures - // cannot be assumed-length. - return symbol.has() && IsAssumedLengthCharacter(symbol); +// C722 and C723: For a function to be assumed length, it must be external and +// of CHARACTER type +bool IsAssumedLengthExternalCharacterFunction(const Symbol &symbol) { + return IsAssumedLengthCharacter(symbol) && + ((symbol.has() && symbol.owner().IsGlobal()) || + (symbol.test(Symbol::Flag::Function) && + symbol.attrs().test(Attr::EXTERNAL))); } const Symbol *IsExternalInPureContext( diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt index ac3e9f641c0c..51cc8e4850ff 100644 --- a/test/Semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -31,6 +31,7 @@ set(ERROR_TESTS io09.f90 io10.f90 kinds02.f90 + kinds04.f90 resolve01.f90 resolve02.f90 resolve03.f90 @@ -103,6 +104,9 @@ set(ERROR_TESTS resolve70.f90 resolve71.f90 resolve72.f90 + resolve73.f90 + resolve74.f90 + resolve75.f90 stop01.f90 structconst01.f90 structconst02.f90 @@ -207,6 +211,7 @@ set(ERROR_TESTS critical02.f90 critical03.f90 block-data01.f90 + complex01.f90 data01.f90 ) diff --git a/test/Semantics/call05.f90 b/test/Semantics/call05.f90 index 09a2c1327b19..368ec59b33b8 100644 --- a/test/Semantics/call05.f90 +++ b/test/Semantics/call05.f90 @@ -19,9 +19,9 @@ module m class(t2), allocatable :: pa2(:) class(*), pointer :: up(:) class(*), allocatable :: ua(:) - !ERROR: An assumed (*) type parameter may be used only for a dummy argument, associate name, or named constant + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result type(pdt(*)), pointer :: amp(:) - !ERROR: An assumed (*) type parameter may be used only for a dummy argument, associate name, or named constant + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result type(pdt(*)), allocatable :: ama(:) type(pdt(:)), pointer :: dmp(:) type(pdt(:)), allocatable :: dma(:) diff --git a/test/Semantics/complex01.f90 b/test/Semantics/complex01.f90 new file mode 100644 index 000000000000..4fb46ba56b71 --- /dev/null +++ b/test/Semantics/complex01.f90 @@ -0,0 +1,32 @@ +! C718 Each named constant in a complex literal constant shall be of type +! integer or real. +subroutine s() + integer :: ivar = 35 + integer, parameter :: iconst = 35 + real :: rvar = 68.9 + real, parameter :: rconst = 68.9 + character :: cvar = 'hello' + character, parameter :: cconst = 'hello' + logical :: lvar = .true. + logical, parameter :: lconst = .true. + complex :: cvar1 = (1, 1) + complex :: cvar2 = (1.0, 1.0) + complex :: cvar3 = (1.0, 1) + complex :: cvar4 = (1, 1.0) + complex :: cvar5 = (iconst, 1.0) + complex :: cvar6 = (iconst, rconst) + complex :: cvar7 = (rconst, iconst) + + !ERROR: must be a constant + complex :: cvar8 = (ivar, 1.0) + !ERROR: must be a constant + !ERROR: must be a constant + complex :: cvar9 = (ivar, rvar) + !ERROR: must be a constant + !ERROR: must be a constant + complex :: cvar10 = (rvar, ivar) + !ERROR: operands must be INTEGER or REAL + complex :: cvar11 = (cconst, 1.0) + !ERROR: operands must be INTEGER or REAL + complex :: cvar12 = (lconst, 1.0) +end subroutine s diff --git a/test/Semantics/kinds02.f90 b/test/Semantics/kinds02.f90 index 4ad99ad5f01b..9fb921345d85 100644 --- a/test/Semantics/kinds02.f90 +++ b/test/Semantics/kinds02.f90 @@ -1,3 +1,15 @@ +! C712 The value of scalar-int-constant-expr shall be nonnegative and +! shall specify a representation method that exists on the processor. +! C714 The value of kind-param shall be nonnegative. +! C715 The value of kind-param shall specify a representation method that +! exists on the processor. +! C719 The value of scalar-int-constant-expr shall be nonnegative and shall +! specify a representation method that exists on the processor. +! C725 The optional comma in a length-selector is permitted only if no +! double-colon separator appears in the typedeclaration- stmt. +! C727 The value of kind-param shall specify a representation method that +! exists on the processor. +! !ERROR: INTEGER(KIND=0) is not a supported type integer(kind=0) :: j0 !ERROR: INTEGER(KIND=-1) is not a supported type @@ -40,4 +52,19 @@ logical(kind=3) :: l3 !ERROR: LOGICAL(KIND=16) is not a supported type logical(kind=16) :: l16 +character (len=99, kind=1) :: cvar1 +character (len=99, kind=2) :: cvar2 +character *4, cvar3 +character *(5), cvar4 +!ERROR: KIND value (3) not valid for CHARACTER +character (len=99, kind=3) :: cvar5 +!ERROR: KIND value (-1) not valid for CHARACTER +character (len=99, kind=-1) :: cvar6 +character(len=*), parameter :: cvar7 = 1_"abcd" +character(len=*), parameter :: cvar8 = 2_"abcd" +!ERROR: CHARACTER(KIND=3) is not a supported type +character(len=*), parameter :: cvar9 = 3_"abcd" +character(len=*), parameter :: cvar10 = 4_"abcd" +!ERROR: CHARACTER(KIND=8) is not a supported type +character(len=*), parameter :: cvar11 = 8_"abcd" end program diff --git a/test/Semantics/kinds04.f90 b/test/Semantics/kinds04.f90 new file mode 100644 index 000000000000..a44c62bae47e --- /dev/null +++ b/test/Semantics/kinds04.f90 @@ -0,0 +1,31 @@ +! C716 If both kind-param and exponent-letter appear, exponent-letter +! shall be E. +! C717 The value of kind-param shall specify an approximation method that +! exists on the processor. +subroutine s(var) + real :: realvar1 = 4.0E6_4 + real :: realvar2 = 4.0D6 + real :: realvar3 = 4.0Q6 + !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' + real :: realvar4 = 4.0D6_8 + !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' + real :: realvar5 = 4.0Q6_16 + real :: realvar6 = 4.0E6_8 + real :: realvar7 = 4.0E6_10 + real :: realvar8 = 4.0E6_16 + !ERROR: Unsupported REAL(KIND=32) + real :: realvar9 = 4.0E6_32 + + double precision :: doublevar1 = 4.0E6_4 + double precision :: doublevar2 = 4.0D6 + double precision :: doublevar3 = 4.0Q6 + !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' + double precision :: doublevar4 = 4.0D6_8 + !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' + double precision :: doublevar5 = 4.0Q6_16 + double precision :: doublevar6 = 4.0E6_8 + double precision :: doublevar7 = 4.0E6_10 + double precision :: doublevar8 = 4.0E6_16 + !ERROR: Unsupported REAL(KIND=32) + double precision :: doublevar9 = 4.0E6_32 +end subroutine s diff --git a/test/Semantics/resolve35.f90 b/test/Semantics/resolve35.f90 index 2598d9ca82e8..6acd24f49b5e 100644 --- a/test/Semantics/resolve35.f90 +++ b/test/Semantics/resolve35.f90 @@ -66,6 +66,7 @@ subroutine s6b integer :: l = 4 forall(integer(k) :: i = 1:10) end forall + ! C713 A scalar-int-constant-name shall be a named constant of type integer. !ERROR: Must be a constant value forall(integer(l) :: i = 1:10) end forall diff --git a/test/Semantics/resolve37.f90 b/test/Semantics/resolve37.f90 index a33e3700a932..ccc05f3d1715 100644 --- a/test/Semantics/resolve37.f90 +++ b/test/Semantics/resolve37.f90 @@ -6,6 +6,7 @@ !ERROR: Must be a constant value parameter(m=n) integer(k) :: x +! C713 A scalar-int-constant-name shall be a named constant of type integer. !ERROR: Must have INTEGER type, but is REAL(4) integer(l) :: y !ERROR: Must be a constant value diff --git a/test/Semantics/resolve41.f90 b/test/Semantics/resolve41.f90 index 3e5c48e9aaa6..2f618675de60 100644 --- a/test/Semantics/resolve41.f90 +++ b/test/Semantics/resolve41.f90 @@ -4,6 +4,7 @@ module m !ERROR: Must have INTEGER type, but is REAL(4) integer :: aa = 2_a integer :: b = 8 + ! C713 A scalar-int-constant-name shall be a named constant of type integer. !ERROR: Must be a constant value integer :: bb = 2_b !TODO: should get error -- not scalar diff --git a/test/Semantics/resolve73.f90 b/test/Semantics/resolve73.f90 new file mode 100644 index 000000000000..191be316b620 --- /dev/null +++ b/test/Semantics/resolve73.f90 @@ -0,0 +1,40 @@ +! C721 A type-param-value of * shall be used only +! * to declare a dummy argument, +! * to declare a named constant, +! * in the type-spec of an ALLOCATE statement wherein each allocate-object is +! a dummy argument of type CHARACTER with an assumed character length, +! * in the type-spec or derived-type-spec of a type guard statement (11.1.11), +! or +! * in an external function, to declare the character length parameter of the function result. +subroutine s(arg) + character(len=*), pointer :: arg + character*(*), parameter :: cvar1 = "abc" + character*4, cvar2 + character(len=4_4) :: cvar3 + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + character(len=*) :: cvar4 + + type derived(param) + integer, len :: param + class(*), allocatable :: x + end type + type(derived(34)) :: a + interface + function fun() + character(len=4) :: fun + end function fun + end interface + + select type (ax => a%x) + type is (integer) + print *, "hello" + type is (character(len=*)) + print *, "hello" + class is (derived(param=*)) + print *, "hello" + class default + print *, "hello" + end select + + allocate (character(len=*) :: arg) +end subroutine s diff --git a/test/Semantics/resolve74.f90 b/test/Semantics/resolve74.f90 new file mode 100644 index 000000000000..a674b1f37ac2 --- /dev/null +++ b/test/Semantics/resolve74.f90 @@ -0,0 +1,37 @@ +! C722 A function name shall not be declared with an asterisk type-param-value +! unless it is of type CHARACTER and is the name of a dummy function or the +! name of the result of an external function. +subroutine s() + + type derived(param) + integer, len :: param + end type + type(derived(34)) :: a + + procedure(character(len=*)) :: externCharFunc + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + procedure(type(derived(param =*))) :: externDerivedFunc + + interface + subroutine subr(dummyFunc) + character(len=*) :: dummyFunc + end subroutine subr + end interface + + contains + function works() + type(derived(param=4)) :: works + end function works + + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + function fails1() + character(len=*) :: fails1 + end function fails1 + + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + function fails2() + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + type(derived(param=*)) :: fails2 + end function fails2 + +end subroutine s diff --git a/test/Semantics/resolve75.f90 b/test/Semantics/resolve75.f90 new file mode 100644 index 000000000000..2c63a36fe523 --- /dev/null +++ b/test/Semantics/resolve75.f90 @@ -0,0 +1,13 @@ +! C726 The length specified for a character statement function or for a +! statement function dummy argument of type character shall be a constant +! expression. +subroutine s() + implicit character(len=3) (c) + implicit character(len=*) (d) + stmtFunc1 (x) = x * 32 + cStmtFunc2 (x) = "abc" + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + cStmtFunc3 (dummy) = "abc" + !ERROR: An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, named constant, or external function result + dStmtFunc3 (x) = "abc" +end subroutine s From e81f57fcf54ee0493044d53a6d1bf05f661e2660 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Thu, 27 Feb 2020 08:49:40 -0800 Subject: [PATCH 053/345] Responses to pull request comments I cleaned up some code and reverted a change to semantic checking for the exponent letter in REAL literals. --- lib/Semantics/expression.cpp | 8 +++++--- lib/Semantics/resolve-names.cpp | 3 ++- lib/Semantics/tools.cpp | 5 +---- test/Semantics/kinds04.f90 | 4 ---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 5c1a040d1074..a09b1ac62558 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -524,10 +524,12 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) { if (letterKind) { defaultKind = *letterKind; } + // C716 requires 'E' as an exponent, but this is more useful auto kind{AnalyzeKindParam(x.kind, defaultKind)}; - if (x.kind && letterKind && expoLetter != 'e') { // C716 - Say("Explicit kind parameter on REAL constant can only be used with" - " exponent letter 'E'"_err_en_US); + if (letterKind && kind != *letterKind && expoLetter != 'e') { + Say("Explicit kind parameter on real constant disagrees with " + "exponent letter '%c'"_en_US, + expoLetter); } auto result{common::SearchTypes( RealTypeVisitor{kind, x.real.source, GetFoldingContext()})}; diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 670cec7ed970..3cb31c9484b9 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -3277,7 +3277,8 @@ void DeclarationVisitor::Post(const parser::CharSelector::LengthAndKind &x) { !evaluate::IsValidKindOfIntrinsicType( TypeCategory::Character, *intKind)) { // C715, C719 Say(currStmtSource().value(), - "KIND value (%jd) not valid for CHARACTER"_err_en_US, *intKind); + "KIND value (%jd) not valid for CHARACTER"_err_en_US, + static_cast(*intKind)); } if (x.length) { charInfo_.length = GetParamValue(*x.length, common::TypeParamAttr::Len); diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index d5fc39c987b6..57980dd76875 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -198,10 +198,7 @@ bool IsDummy(const Symbol &symbol) { bool IsStmtFunction(const Symbol &symbol) { const auto *subprogram{symbol.detailsIf()}; - if (subprogram && subprogram->stmtFunction()) { - return true; - } - return false; + return subprogram && subprogram->stmtFunction(); } bool IsInStmtFunction(const Symbol &symbol) { diff --git a/test/Semantics/kinds04.f90 b/test/Semantics/kinds04.f90 index a44c62bae47e..ecf3a446cc3d 100644 --- a/test/Semantics/kinds04.f90 +++ b/test/Semantics/kinds04.f90 @@ -6,9 +6,7 @@ subroutine s(var) real :: realvar1 = 4.0E6_4 real :: realvar2 = 4.0D6 real :: realvar3 = 4.0Q6 - !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' real :: realvar4 = 4.0D6_8 - !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' real :: realvar5 = 4.0Q6_16 real :: realvar6 = 4.0E6_8 real :: realvar7 = 4.0E6_10 @@ -19,9 +17,7 @@ subroutine s(var) double precision :: doublevar1 = 4.0E6_4 double precision :: doublevar2 = 4.0D6 double precision :: doublevar3 = 4.0Q6 - !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' double precision :: doublevar4 = 4.0D6_8 - !ERROR: Explicit kind parameter on REAL constant can only be used with exponent letter 'E' double precision :: doublevar5 = 4.0Q6_16 double precision :: doublevar6 = 4.0E6_8 double precision :: doublevar7 = 4.0E6_10 From 8134fc477e843d9bf525eeb552ba1d234140e7a7 Mon Sep 17 00:00:00 2001 From: Varun Jayathirtha Date: Tue, 25 Feb 2020 18:01:23 -0800 Subject: [PATCH 054/345] Add semantic checks C8104, C8105. Add tests for C8103, C8104, C8105 --- lib/Semantics/CMakeLists.txt | 1 + lib/Semantics/check-namelist.cpp | 40 ++++++++++++++++++++++++++ lib/Semantics/check-namelist.h | 25 ++++++++++++++++ lib/Semantics/semantics.cpp | 11 +++---- test/Semantics/CMakeLists.txt | 1 + test/Semantics/namelist01.f90 | 49 ++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 lib/Semantics/check-namelist.cpp create mode 100644 lib/Semantics/check-namelist.h create mode 100644 test/Semantics/namelist01.f90 diff --git a/lib/Semantics/CMakeLists.txt b/lib/Semantics/CMakeLists.txt index 0c6b612586de..cbe9fc9b6b30 100644 --- a/lib/Semantics/CMakeLists.txt +++ b/lib/Semantics/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(FortranSemantics check-do-forall.cpp check-if-stmt.cpp check-io.cpp + check-namelist.cpp check-nullify.cpp check-omp-structure.cpp check-purity.cpp diff --git a/lib/Semantics/check-namelist.cpp b/lib/Semantics/check-namelist.cpp new file mode 100644 index 000000000000..c720552bfe08 --- /dev/null +++ b/lib/Semantics/check-namelist.cpp @@ -0,0 +1,40 @@ +//===-- lib/Semantics/check-namelist.cpp ----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "check-namelist.h" + +namespace Fortran::semantics { + +void NamelistChecker::Leave(const parser::NamelistStmt &nmlStmt) { + for (const auto &x : nmlStmt.v) { + if (const auto *nml{std::get(x.t).symbol}) { + for (const auto &nmlObjName : std::get>(x.t)) { + const auto *nmlObjSymbol{nmlObjName.symbol}; + if (nmlObjSymbol && nmlObjSymbol->has()) { + const auto *symDetails{ + std::get_if(&nmlObjSymbol->details())}; + if (symDetails && symDetails->IsAssumedSize()) { // C8104 + context_.Say(nmlObjName.source, + "A namelist group object '%s' must not be" + " assumed-size"_err_en_US, + nmlObjSymbol->name()); + } + if (nml->attrs().test(Attr::PUBLIC) && + nmlObjSymbol->attrs().test(Attr::PRIVATE)) { // C8105 + context_.Say(nmlObjName.source, + "A PRIVATE namelist group object '%s' must not be in a " + "PUBLIC namelist"_err_en_US, + nmlObjSymbol->name()); + } + } + } + } + } +} + +} diff --git a/lib/Semantics/check-namelist.h b/lib/Semantics/check-namelist.h new file mode 100644 index 000000000000..7989ed404bcf --- /dev/null +++ b/lib/Semantics/check-namelist.h @@ -0,0 +1,25 @@ +//===-------lib/Semantics/check-namelist.h --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_CHECK_NAMELIST_H_ +#define FORTRAN_SEMANTICS_CHECK_NAMELIST_H_ + +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" + +namespace Fortran::semantics { +class NamelistChecker : public virtual BaseChecker { +public: + NamelistChecker(SemanticsContext &context) : context_{context} {} + void Leave(const parser::NamelistStmt &); + +private: + SemanticsContext &context_; +}; +} +#endif // FORTRAN_SEMANTICS_CHECK_NAMELIST_H_ diff --git a/lib/Semantics/semantics.cpp b/lib/Semantics/semantics.cpp index 058bf227a40a..745143267d6d 100644 --- a/lib/Semantics/semantics.cpp +++ b/lib/Semantics/semantics.cpp @@ -19,6 +19,7 @@ #include "check-do-forall.h" #include "check-if-stmt.h" #include "check-io.h" +#include "check-namelist.h" #include "check-nullify.h" #include "check-omp-structure.h" #include "check-purity.h" @@ -111,11 +112,11 @@ template class SemanticsVisitor : public virtual C... { }; using StatementSemanticsPass1 = ExprChecker; -using StatementSemanticsPass2 = SemanticsVisitor< - AllocateChecker, ArithmeticIfStmtChecker, AssignmentChecker, CoarrayChecker, - DataChecker, DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, - NullifyChecker, OmpStructureChecker, PurityChecker, ReturnStmtChecker, - StopChecker>; +using StatementSemanticsPass2 = SemanticsVisitor; static bool PerformStatementSemantics( SemanticsContext &context, parser::Program &program) { diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt index 51cc8e4850ff..e6b6ad87982d 100644 --- a/test/Semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -213,6 +213,7 @@ set(ERROR_TESTS block-data01.f90 complex01.f90 data01.f90 + namelist01.f90 ) # These test files have expected symbols in the source diff --git a/test/Semantics/namelist01.f90 b/test/Semantics/namelist01.f90 new file mode 100644 index 000000000000..81acecbfc725 --- /dev/null +++ b/test/Semantics/namelist01.f90 @@ -0,0 +1,49 @@ +! Test for checking namelist constraints, C8103-C8105 + +module dup + integer dupName + integer uniqueName +end module dup + +subroutine C8103a(x) + use dup, only: uniqueName, dupName + integer :: x + !ERROR: 'dupname' is already declared in this scoping unit + namelist /dupName/ x, x +end subroutine C8103a + +subroutine C8103b(y) + use dup, only: uniqueName + integer :: y + namelist /dupName/ y, y +end subroutine C8103b + +subroutine C8104a(ivar, jvar) + integer :: ivar(10,8) + integer :: jvar(*) + NAMELIST /NLIST/ ivar + !ERROR: A namelist group object 'jvar' must not be assumed-size + NAMELIST /NLIST/ jvar +end subroutine C8104a + +subroutine C8104b(ivar, jvar) + integer, dimension(*) :: jvar + !ERROR: A namelist group object 'jvar' must not be assumed-size + NAMELIST /NLIST/ ivar, jvar +end subroutine C8104b + +subroutine C8104c(jvar) + integer :: jvar(10, 3:*) + !ERROR: A namelist group object 'jvar' must not be assumed-size + NAMELIST /NLIST/ jvar +end subroutine C8104c + +module C8105 + integer, private :: x + public :: NLIST + !ERROR: A PRIVATE namelist group object 'x' must not be in a PUBLIC namelist + NAMELIST /NLIST/ x + !ERROR: A PRIVATE namelist group object 'x' must not be in a PUBLIC namelist + NAMELIST /NLIST2/ x + public :: NLIST2 +end module C8105 From 2260abe4c27a76e374b286df072e303aa9ea1418 Mon Sep 17 00:00:00 2001 From: David Truby Date: Mon, 2 Mar 2020 13:35:24 +0000 Subject: [PATCH 055/345] Link against zlib when LLVM does. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b7a823f7ec3..f293762cd2ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,10 @@ message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}" ) find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION} in ${LLVM_DIR}") +# If LLVM links to zlib we need the imported targets so we can too. +if(LLVM_ENABLE_ZLIB) + find_package(ZLIB REQUIRED) +endif() list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR}) From 8bdaf0a521f6630ec2c369b2c41484eff4803d6e Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Mon, 2 Mar 2020 07:59:29 -0800 Subject: [PATCH 056/345] Expression analysis on DataStmtConstant Data statements contains expressions but they are not wrapped in one of the kinds of parse tree nodes that are analyzed, like `parser::Expr`. So potential errors were not discovered. Change `ExprChecker` to handle `DataStmtConstant` and analyze any expressions that are contained in it. Note that the analyzed form of the expression is not yet saved in the parse tree. --- include/flang/Semantics/expression.h | 8 ++++--- lib/Semantics/expression.cpp | 29 ++++++++++++++++++++++++++ test/Semantics/CMakeLists.txt | 1 + test/Semantics/data02.f90 | 31 ++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 test/Semantics/data02.f90 diff --git a/include/flang/Semantics/expression.h b/include/flang/Semantics/expression.h index 7282a96f1095..bf04275d6004 100644 --- a/include/flang/Semantics/expression.h +++ b/include/flang/Semantics/expression.h @@ -230,6 +230,10 @@ class ExpressionAnalyzer { return Analyze(dr); } MaybeExpr Analyze(const parser::StructureComponent &); + MaybeExpr Analyze(const parser::SignedIntLiteralConstant &); + MaybeExpr Analyze(const parser::SignedRealLiteralConstant &); + MaybeExpr Analyze(const parser::SignedComplexLiteralConstant &); + MaybeExpr Analyze(const parser::StructureConstructor &); void Analyze(const parser::CallStmt &); const Assignment *Analyze(const parser::AssignmentStmt &); @@ -240,9 +244,7 @@ class ExpressionAnalyzer { private: MaybeExpr Analyze(const parser::IntLiteralConstant &); - MaybeExpr Analyze(const parser::SignedIntLiteralConstant &); MaybeExpr Analyze(const parser::RealLiteralConstant &); - MaybeExpr Analyze(const parser::SignedRealLiteralConstant &); MaybeExpr Analyze(const parser::ComplexPart &); MaybeExpr Analyze(const parser::ComplexLiteralConstant &); MaybeExpr Analyze(const parser::LogicalLiteralConstant &); @@ -255,7 +257,6 @@ class ExpressionAnalyzer { MaybeExpr Analyze(const parser::CoindexedNamedObject &); MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &); MaybeExpr Analyze(const parser::ArrayConstructor &); - MaybeExpr Analyze(const parser::StructureConstructor &); MaybeExpr Analyze(const parser::FunctionReference &, std::optional * = nullptr); MaybeExpr Analyze(const parser::Expr::Parentheses &); @@ -448,6 +449,7 @@ class ExprChecker { AnalyzePointerAssignmentStmt(context_, x); return false; } + bool Pre(const parser::DataStmtConstant &); template bool Pre(const parser::Scalar &x) { AnalyzeExpr(context_, x); diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index a09b1ac62558..09af89f6b61a 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -454,11 +454,14 @@ MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) { } MaybeExpr ExpressionAnalyzer::Analyze(const parser::IntLiteralConstant &x) { + auto restorer{ + GetContextualMessages().SetLocation(std::get(x.t))}; return IntLiteralConstant(x); } MaybeExpr ExpressionAnalyzer::Analyze( const parser::SignedIntLiteralConstant &x) { + auto restorer{GetContextualMessages().SetLocation(x.source)}; return IntLiteralConstant(x); } @@ -553,6 +556,18 @@ MaybeExpr ExpressionAnalyzer::Analyze( return std::nullopt; } +MaybeExpr ExpressionAnalyzer::Analyze( + const parser::SignedComplexLiteralConstant &x) { + auto result{Analyze(std::get(x.t))}; + if (!result) { + return std::nullopt; + } else if (std::get(x.t) == parser::Sign::Negative) { + return AsGenericExpr(-std::move(std::get>(result->u))); + } else { + return result; + } +} + MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexPart &x) { return Analyze(x.u); } @@ -2949,4 +2964,18 @@ bool ExprChecker::Walk(const parser::Program &program) { parser::Walk(program, *this); return !context_.AnyFatalError(); } + +bool ExprChecker::Pre(const parser::DataStmtConstant &x) { + std::visit( + common::visitors{ + [&](const parser::NullInit &) {}, + [&](const parser::InitialDataTarget &y) { + AnalyzeExpr(context_, y.value()); + }, + [&](const auto &y) { AnalyzeExpr(context_, y); }, + }, + x.u); + return false; +} + } diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt index e6b6ad87982d..7144ff9cffd1 100644 --- a/test/Semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -213,6 +213,7 @@ set(ERROR_TESTS block-data01.f90 complex01.f90 data01.f90 + data02.f90 namelist01.f90 ) diff --git a/test/Semantics/data02.f90 b/test/Semantics/data02.f90 new file mode 100644 index 000000000000..4cd593697b23 --- /dev/null +++ b/test/Semantics/data02.f90 @@ -0,0 +1,31 @@ +! Check that expressions are analyzed in data statements + +subroutine s1 + type :: t + character(1) :: c + end type + type(t) :: x + !ERROR: Value in structure constructor of type INTEGER(4) is incompatible with component 'c' of type CHARACTER(KIND=1,LEN=1_4) + data x /t(1)/ +end + +subroutine s2 + real :: x1, x2 + integer :: i1, i2 + !ERROR: Unsupported REAL(KIND=99) + data x1 /1.0_99/ + !ERROR: Unsupported REAL(KIND=99) + data x2 /-1.0_99/ + !ERROR: INTEGER(KIND=99) is not a supported type + data i1 /1_99/ + !ERROR: INTEGER(KIND=99) is not a supported type + data i2 /-1_99/ +end + +subroutine s3 + complex :: z1, z2 + !ERROR: Unsupported REAL(KIND=99) + data z1 /(1.0, 2.0_99)/ + !ERROR: Unsupported REAL(KIND=99) + data z2 /-(1.0, 2.0_99)/ +end From e2fd5333ff1bb1e8e88cbf35028654bcf70fe9e9 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 27 Feb 2020 18:00:45 -0800 Subject: [PATCH 057/345] Improve array element errors When something is parsed as an array element it was sometimes intended to be a function call or structure constructor. So if the base name is not found the errors can be confusing. This is an attempt to improve them. When the subscript list is empty, it was probably meant to be a function call, so report that the name is not a function. If the base is a scalar but there are subscripts, report that it is not an array. --- lib/Semantics/expression.cpp | 27 ++++++++++++++++++++------- test/Semantics/data01.f90 | 2 +- test/Semantics/resolve59.f90 | 16 ++++++++++++---- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 09af89f6b61a..8132a6bcd409 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -238,8 +238,10 @@ MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) { if (subscripts == 0) { // nothing to check } else if (subscripts != symbolRank) { - Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US, - symbolRank, symbol.name(), subscripts); + if (symbolRank != 0) { + Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US, + symbolRank, symbol.name(), subscripts); + } return std::nullopt; } else if (Component * component{ref.base().UnwrapComponent()}) { int baseRank{component->base().Rank()}; @@ -886,16 +888,25 @@ std::vector ExpressionAnalyzer::AnalyzeSectionSubscripts( } MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayElement &ae) { - std::vector subscripts{AnalyzeSectionSubscripts(ae.subscripts)}; if (MaybeExpr baseExpr{Analyze(ae.base)}) { - if (std::optional dataRef{ExtractDataRef(std::move(*baseExpr))}) { - if (!subscripts.empty()) { - return ApplySubscripts(std::move(*dataRef), std::move(subscripts)); + if (ae.subscripts.empty()) { + // will be converted to function call later or error reported + return std::nullopt; + } else if (baseExpr->Rank() == 0) { + if (const Symbol * symbol{GetLastSymbol(*baseExpr)}) { + Say("'%s' is not an array"_err_en_US, symbol->name()); } + } else if (std::optional dataRef{ + ExtractDataRef(std::move(*baseExpr))}) { + return ApplySubscripts( + std::move(*dataRef), AnalyzeSectionSubscripts(ae.subscripts)); } else { Say("Subscripts may be applied only to an object, component, or array constant"_err_en_US); } } + // error was reported: analyze subscripts without reporting more errors + auto restorer{GetContextualMessages().DiscardMessages()}; + AnalyzeSectionSubscripts(ae.subscripts); return std::nullopt; } @@ -2296,7 +2307,9 @@ static void CheckFuncRefToArrayElementRefHasSubscripts( name = &std::get(proc.u).v.thing.component; } auto &msg{context.Say(funcRef.v.source, - "Reference to array '%s' with empty subscript list"_err_en_US, + name->symbol && name->symbol->Rank() == 0 + ? "'%s' is not a function"_err_en_US + : "Reference to array '%s' with empty subscript list"_err_en_US, name->source)}; if (name->symbol) { if (semantics::IsFunctionResultWithSameNameAsFunction(*name->symbol)) { diff --git a/test/Semantics/data01.f90 b/test/Semantics/data01.f90 index 87861016c6c0..c8af31a50d07 100644 --- a/test/Semantics/data01.f90 +++ b/test/Semantics/data01.f90 @@ -42,7 +42,7 @@ subroutine CheckValue !OK: constant structure constructor data myname / person(1, 'Abcd Ijkl') / !C883 - !ERROR: Must have INTEGER type, but is CHARACTER(1) + !ERROR: 'persn' is not an array data myname / persn(2, 'Abcd Efgh') / !C884 !ERROR: Structure constructor in data value must be a constant expression diff --git a/test/Semantics/resolve59.f90 b/test/Semantics/resolve59.f90 index e2c2936d6c7b..e34fcaea01d2 100644 --- a/test/Semantics/resolve59.f90 +++ b/test/Semantics/resolve59.f90 @@ -9,7 +9,7 @@ module m_no_result ! testing with data object results function f1() real :: x, f1 - !ERROR: Reference to array 'f1' with empty subscript list + !ERROR: 'f1' is not a function x = acos(f1()) f1 = x x = acos(f1) !OK @@ -17,7 +17,7 @@ function f1() function f2(i) integer i real :: x, f2 - !ERROR: Reference to rank-0 object 'f2' has 1 subscripts + !ERROR: 'f2' is not an array x = acos(f2(i+1)) f2 = x x = acos(f2) !OK @@ -62,7 +62,7 @@ function f6() result(f6) !OKI (warning) end function function f7() result(f7) !OKI (warning) real :: x, f7 - !ERROR: Reference to array 'f7' with empty subscript list + !ERROR: 'f7' is not a function x = acos(f7()) f7 = x x = acos(f7) !OK @@ -123,7 +123,15 @@ function f5(x) result(r) ! testing that calling the result is also caught function f6() result(r) real :: x, r - !ERROR: Reference to array 'r' with empty subscript list + !ERROR: 'r' is not a function x = r() end function end module + +subroutine array_rank_test() + real :: x(10, 10), y + !ERROR: Reference to rank-2 object 'x' has 1 subscripts + y = x(1) + !ERROR: Reference to rank-2 object 'x' has 3 subscripts + y = x(1, 2, 3) +end From 99ce156e49ec0847ce42a6d10f0a62664714092b Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Mon, 2 Mar 2020 16:43:01 -0800 Subject: [PATCH 058/345] Improve checking of structure constructor arguments When a misparsed FunctionReference was converted to a StructureConstructor, the components accessed were not checked for accessibility. The conversion happens in expression analysis so that where the accessibity must be checked. So move `CheckAccessibleComponent` to `tools.h` so that it can be shared by `resolve-names.cpp` and `expression.cpp`. Add FindModuleContaining to help implement this and use it other places. Check that an access-spec can only appear in a module. Remove some unnecessary "semantics::" qualifiers. --- include/flang/Semantics/tools.h | 4 ++ lib/Semantics/expression.cpp | 14 +++--- lib/Semantics/resolve-names.cpp | 81 ++++++++++----------------------- lib/Semantics/symbol.cpp | 11 +---- lib/Semantics/tools.cpp | 72 ++++++++++++++++++----------- test/Semantics/resolve10.f90 | 38 ++++++++++++++-- test/Semantics/resolve34.f90 | 40 ++++++++++++++++ 7 files changed, 157 insertions(+), 103 deletions(-) diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index f73958472fdf..2c201954fcb4 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -30,6 +30,7 @@ class DerivedTypeSpec; class Scope; class Symbol; +const Scope *FindModuleContaining(const Scope &); const Symbol *FindCommonBlockContaining(const Symbol &object); const Scope *FindProgramUnitContaining(const Scope &); const Scope *FindProgramUnitContaining(const Symbol &); @@ -167,6 +168,9 @@ std::unique_ptr WhyNotModifiable(SourceName, const SomeExpr &, const Symbol *IsExternalInPureContext(const Symbol &, const Scope &); bool HasCoarray(const parser::Expr &); bool IsPolymorphicAllocatable(const Symbol &); +// Return an error if component symbol is not accessible from scope (7.5.4.8(2)) +std::optional CheckAccessibleComponent( + const semantics::Scope &, const Symbol &); // Analysis of image control statements bool IsImageControlStmt(const parser::ExecutableConstruct &); diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index a09b1ac62558..e4e6999b5815 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -1402,6 +1402,11 @@ MaybeExpr ExpressionAnalyzer::Analyze( } } if (symbol) { + if (const auto *currScope{context_.globalScope().FindScope(source)}) { + if (auto msg{CheckAccessibleComponent(*currScope, *symbol)}) { + Say(source, *msg); + } + } if (checkConflicts) { auto componentIter{ std::find(components.begin(), components.end(), *symbol)}; @@ -1433,13 +1438,10 @@ MaybeExpr ExpressionAnalyzer::Analyze( } else if (symbol->has()) { // C1594(4) const auto &innermost{context_.FindScope(expr.source)}; - if (const auto *pureProc{ - semantics::FindPureProcedureContaining(innermost)}) { - if (const Symbol * - pointer{semantics::FindPointerComponent(*symbol)}) { + if (const auto *pureProc{FindPureProcedureContaining(innermost)}) { + if (const Symbol * pointer{FindPointerComponent(*symbol)}) { if (const Symbol * - object{semantics::FindExternallyVisibleObject( - *value, *pureProc)}) { + object{FindExternallyVisibleObject(*value, *pureProc)}) { if (auto *msg{Say(expr.source, "Externally visible object '%s' may not be " "associated with pointer component '%s' in a " diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 3cb31c9484b9..38a9ba909135 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -230,7 +230,6 @@ class AttrsVisitor : public virtual BaseVisitor { bool SetPassNameOn(Symbol &); bool SetBindNameOn(Symbol &); void Post(const parser::LanguageBindingSpec &); - bool Pre(const parser::AccessSpec &); bool Pre(const parser::IntentSpec &); bool Pre(const parser::Pass &); @@ -435,8 +434,6 @@ class ScopeHandler : public ImplicitRulesVisitor { Scope &currScope() { return DEREF(currScope_); } // The enclosing scope, skipping blocks and derived types. Scope &InclusiveScope(); - // The global scope, containing program units. - Scope &GlobalScope(); // Create a new scope and push it on the scope stack. void PushScope(Scope::Kind kind, Symbol *symbol); @@ -699,6 +696,7 @@ class DeclarationVisitor : public ArraySpecVisitor, bool Pre(const parser::NamedConstant &); void Post(const parser::EnumDef &); bool Pre(const parser::Enumerator &); + bool Pre(const parser::AccessSpec &); bool Pre(const parser::AsynchronousStmt &); bool Pre(const parser::ContiguousStmt &); bool Pre(const parser::ExternalStmt &); @@ -804,7 +802,6 @@ class DeclarationVisitor : public ArraySpecVisitor, const parser::Name &, const std::optional &); bool CheckUseError(const parser::Name &); void CheckAccessibility(const SourceName &, bool, Symbol &); - bool CheckAccessibleComponent(const SourceName &, const Symbol &); void CheckCommonBlocks(); void CheckSaveStmts(); void CheckEquivalenceSets(); @@ -1545,10 +1542,6 @@ void AttrsVisitor::Post(const parser::LanguageBindingSpec &x) { bindName_ = EvaluateExpr(*x.v); } } -bool AttrsVisitor::Pre(const parser::AccessSpec &x) { - attrs_->set(AccessSpecToAttr(x)); - return false; -} bool AttrsVisitor::Pre(const parser::IntentSpec &x) { CHECK(attrs_); attrs_->set(IntentSpecToAttr(x)); @@ -1907,16 +1900,9 @@ Scope &ScopeHandler::InclusiveScope() { return *scope; } } - common::die("inclusive scope not found"); -} -Scope &ScopeHandler::GlobalScope() { - for (auto *scope = currScope_; scope; scope = &scope->parent()) { - if (scope->IsGlobal()) { - return *scope; - } - } - common::die("global scope not found"); + DIE("inclusive scope not found"); } + void ScopeHandler::PushScope(Scope::Kind kind, Symbol *symbol) { PushScope(currScope().MakeScope(kind, symbol)); } @@ -2879,37 +2865,6 @@ void DeclarationVisitor::CheckAccessibility( } } -// Check that component is accessible from current scope. -bool DeclarationVisitor::CheckAccessibleComponent( - const SourceName &name, const Symbol &symbol) { - if (!symbol.attrs().test(Attr::PRIVATE)) { - return true; - } - // component must be in a module/submodule because of PRIVATE: - const Scope *moduleScope{&symbol.owner()}; - CHECK(moduleScope->IsDerivedType()); - while ( - moduleScope->kind() != Scope::Kind::Module && !moduleScope->IsGlobal()) { - moduleScope = &moduleScope->parent(); - } - if (moduleScope->kind() == Scope::Kind::Module) { - for (auto *scope{&currScope()}; !scope->IsGlobal(); - scope = &scope->parent()) { - if (scope == moduleScope) { - return true; - } - } - Say(name, - "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US, - name.ToString(), moduleScope->GetName().value()); - } else { - Say(name, - "PRIVATE component '%s' is only accessible within its module"_err_en_US, - name.ToString()); - } - return false; -} - void DeclarationVisitor::Post(const parser::TypeDeclarationStmt &) { if (!GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { // C702 if (const auto *typeSpec{GetDeclTypeSpec()}) { @@ -3067,6 +3022,19 @@ void DeclarationVisitor::Post(const parser::EnumDef &) { enumerationState_ = EnumeratorState{}; } +bool DeclarationVisitor::Pre(const parser::AccessSpec &x) { + Attr attr{AccessSpecToAttr(x)}; + const Scope &scope{ + currScope().IsDerivedType() ? currScope().parent() : currScope()}; + if (!scope.IsModule()) { // C817 + Say(currStmtSource().value(), + "%s attribute may only appear in the specification part of a module"_err_en_US, + EnumToString(attr)); + } + attrs_->set(attr); + return false; +} + bool DeclarationVisitor::Pre(const parser::AsynchronousStmt &x) { return HandleAttributeStmt(Attr::ASYNCHRONOUS, x.v); } @@ -3833,12 +3801,7 @@ bool DeclarationVisitor::Pre(const parser::StructureConstructor &x) { // we need to resolve its symbol in the scope of the derived type. Walk(std::get(component.t)); if (const auto &kw{std::get>(component.t)}) { - if (Symbol * symbol{FindInTypeOrParents(*typeScope, kw->v)}) { - if (!kw->v.symbol) { - kw->v.symbol = symbol; - } - CheckAccessibleComponent(kw->v.source, *symbol); - } + FindInTypeOrParents(*typeScope, kw->v); } } return false; @@ -5214,9 +5177,11 @@ const parser::Name *DeclarationVisitor::FindComponent( } else if (const DerivedTypeSpec * derived{type->AsDerived()}) { if (const Scope * scope{derived->scope()}) { if (Resolve(component, scope->FindComponent(component.source))) { - if (CheckAccessibleComponent(component.source, *component.symbol)) { - return &component; + if (auto msg{ + CheckAccessibleComponent(currScope(), *component.symbol)}) { + context().Say(component.source, *msg); } + return &component; } else { SayDerivedType(component.source, "Component '%s' not found in derived type '%s'"_err_en_US, *scope); @@ -5517,7 +5482,7 @@ bool ResolveNamesVisitor::SetProcFlag( bool ModuleVisitor::Pre(const parser::AccessStmt &x) { Attr accessAttr{AccessSpecToAttr(std::get(x.t))}; - if (currScope().kind() != Scope::Kind::Module) { + if (!currScope().IsModule()) { // C869 Say(currStmtSource().value(), "%s statement may only appear in the specification part of a module"_err_en_US, EnumToString(accessAttr)); @@ -5525,7 +5490,7 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) { } const auto &accessIds{std::get>(x.t)}; if (accessIds.empty()) { - if (prevAccessStmt_) { + if (prevAccessStmt_) { // C869 Say("The default accessibility of this module has already been declared"_err_en_US) .Attach(*prevAccessStmt_, "Previous declaration"_en_US); } diff --git a/lib/Semantics/symbol.cpp b/lib/Semantics/symbol.cpp index f69748ab9eb9..92e2f5fdf7af 100644 --- a/lib/Semantics/symbol.cpp +++ b/lib/Semantics/symbol.cpp @@ -72,16 +72,7 @@ const Scope *ModuleDetails::parent() const { return isSubmodule_ && scope_ ? &scope_->parent() : nullptr; } const Scope *ModuleDetails::ancestor() const { - if (!isSubmodule_ || !scope_) { - return nullptr; - } - for (auto *scope{scope_};;) { - auto *parent{&scope->parent()}; - if (parent->kind() != Scope::Kind::Module) { - return scope; - } - scope = parent; - } + return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr; } void ModuleDetails::set_scope(const Scope *scope) { CHECK(!scope_); diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index 57980dd76875..922f38f44d0b 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -24,6 +24,24 @@ namespace Fortran::semantics { +// Find this or containing scope that matches predicate +static const Scope *FindScopeContaining( + const Scope &start, std::function predicate) { + for (const Scope *scope{&start};; scope = &scope->parent()) { + if (predicate(*scope)) { + return scope; + } + if (scope->IsGlobal()) { + return nullptr; + } + } +} + +const Scope *FindModuleContaining(const Scope &start) { + return FindScopeContaining( + start, [](const Scope &scope) { return scope.IsModule(); }); +} + const Symbol *FindCommonBlockContaining(const Symbol &object) { if (const auto *details{object.detailsIf()}) { return details->commonBlock(); @@ -33,21 +51,15 @@ const Symbol *FindCommonBlockContaining(const Symbol &object) { } const Scope *FindProgramUnitContaining(const Scope &start) { - const Scope *scope{&start}; - while (scope) { - switch (scope->kind()) { + return FindScopeContaining(start, [](const Scope &scope) { + switch (scope.kind()) { case Scope::Kind::Module: case Scope::Kind::MainProgram: case Scope::Kind::Subprogram: - case Scope::Kind::BlockData: return scope; - case Scope::Kind::Global: return nullptr; - case Scope::Kind::DerivedType: - case Scope::Kind::Block: - case Scope::Kind::Forall: - case Scope::Kind::ImpliedDos: scope = &scope->parent(); + case Scope::Kind::BlockData: return true; + default: return false; } - } - return nullptr; + }); } const Scope *FindProgramUnitContaining(const Symbol &symbol) { @@ -164,16 +176,9 @@ bool IsUseAssociated(const Symbol &symbol, const Scope &scope) { bool DoesScopeContain( const Scope *maybeAncestor, const Scope &maybeDescendent) { - if (maybeAncestor) { - const Scope *scope{&maybeDescendent}; - while (!scope->IsGlobal()) { - scope = &scope->parent(); - if (scope == maybeAncestor) { - return true; - } - } - } - return false; + return maybeAncestor && !maybeDescendent.IsGlobal() && + FindScopeContaining(maybeDescendent.parent(), + [&](const Scope &scope) { return &scope == maybeAncestor; }); } bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) { @@ -717,7 +722,7 @@ bool IsAssumedLengthExternalCharacterFunction(const Symbol &symbol) { const Symbol *IsExternalInPureContext( const Symbol &symbol, const Scope &scope) { - if (const auto *pureProc{semantics::FindPureProcedureContaining(scope)}) { + if (const auto *pureProc{FindPureProcedureContaining(scope)}) { if (const Symbol * root{GetAssociationRoot(symbol)}) { if (const Symbol * visible{FindExternallyVisibleObject(*root, *pureProc)}) { @@ -956,6 +961,21 @@ bool IsPolymorphicAllocatable(const Symbol &symbol) { return IsAllocatable(symbol) && IsPolymorphic(symbol); } +std::optional CheckAccessibleComponent( + const Scope &scope, const Symbol &symbol) { + CHECK(symbol.owner().IsDerivedType()); // symbol must be a component + if (symbol.attrs().test(Attr::PRIVATE)) { + if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) { + if (!moduleScope->sourceRange().Contains(scope.sourceRange())) { + return parser::MessageFormattedText{ + "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US, + symbol.name(), moduleScope->GetName().value()}; + } + } + } + return std::nullopt; +} + std::list OrderParameterNames(const Symbol &typeSymbol) { std::list result; if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) { @@ -1227,10 +1247,10 @@ const Symbol *FindImmediateComponent(const DerivedTypeSpec &type, } bool IsFunctionResult(const Symbol &symbol) { - return (symbol.has() && - symbol.get().isFuncResult()) || - (symbol.has() && - symbol.get().isFuncResult()); + return (symbol.has() && + symbol.get().isFuncResult()) || + (symbol.has() && + symbol.get().isFuncResult()); } bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) { diff --git a/test/Semantics/resolve10.f90 b/test/Semantics/resolve10.f90 index 5866faafe503..75a44a4f5e57 100644 --- a/test/Semantics/resolve10.f90 +++ b/test/Semantics/resolve10.f90 @@ -1,10 +1,42 @@ module m public + type t + integer, private :: i + end type !ERROR: The default accessibility of this module has already been declared - private + private !C869 end -subroutine s +subroutine s1 !ERROR: PUBLIC statement may only appear in the specification part of a module - public + public !C869 +end + +subroutine s2 + !ERROR: PRIVATE attribute may only appear in the specification part of a module + integer, private :: i !C817 +end + +subroutine s3 + type t + !ERROR: PUBLIC attribute may only appear in the specification part of a module + integer, public :: i !C817 + end type +end + +module m4 + interface + module subroutine s() + end subroutine + end interface +end +submodule(m4) sm4 + !ERROR: PUBLIC statement may only appear in the specification part of a module + public !C869 + !ERROR: PUBLIC attribute may only appear in the specification part of a module + real, public :: x !C817 + type :: t + !ERROR: PRIVATE attribute may only appear in the specification part of a module + real, private :: y !C817 + end type end diff --git a/test/Semantics/resolve34.f90 b/test/Semantics/resolve34.f90 index 0f0c8d1d23de..c3b28bb929b8 100644 --- a/test/Semantics/resolve34.f90 +++ b/test/Semantics/resolve34.f90 @@ -91,3 +91,43 @@ subroutine s7 !ERROR: PRIVATE component 't1' is only accessible within module 'm7' j = x%t1%i1 end + +! 7.5.4.8(2) +module m8 + type :: t + integer :: i1 + integer, private :: i2 + end type +contains + subroutine s0 + type(t) :: x + x = t(i1=2, i2=5) !OK + end +end +subroutine s8 + use m8 + type(t) :: x + !ERROR: PRIVATE component 'i2' is only accessible within module 'm8' + x = t(2, 5) + !ERROR: PRIVATE component 'i2' is only accessible within module 'm8' + x = t(i1=2, i2=5) +end + +! 7.5.4.8(2) +module m9 + interface + module subroutine s() + end subroutine + end interface + type :: t + integer :: i1 + integer, private :: i2 + end type +end +submodule(m9) sm8 +contains + module subroutine s + type(t) :: x + x = t(i1=2, i2=5) !OK + end +end From a807862ab85f2d2206f8fa77d927b0e110513620 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 05:40:44 -0800 Subject: [PATCH 059/345] Don't link to libm (#1038) * Don't link to libm on windows * Don't link to libm in Unix as well --- lib/Evaluate/CMakeLists.txt | 1 - test/Evaluate/CMakeLists.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/Evaluate/CMakeLists.txt b/lib/Evaluate/CMakeLists.txt index 8da75eddff69..a3391dc40394 100644 --- a/lib/Evaluate/CMakeLists.txt +++ b/lib/Evaluate/CMakeLists.txt @@ -38,7 +38,6 @@ target_link_libraries(FortranEvaluate FortranCommon FortranDecimal FortranParser - m ) install (TARGETS FortranEvaluate diff --git a/test/Evaluate/CMakeLists.txt b/test/Evaluate/CMakeLists.txt index f1bbc1dfd7c6..b08c1431df70 100644 --- a/test/Evaluate/CMakeLists.txt +++ b/test/Evaluate/CMakeLists.txt @@ -98,7 +98,6 @@ target_link_libraries(real-test FortranEvaluate FortranDecimal FortranSemantics - m ) add_executable(reshape-test From 0e32eebf7fec8cd07ab3280565799cfbf2ca55e2 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 06:31:24 -0800 Subject: [PATCH 060/345] Add missing include for std::max (#1028) --- runtime/numeric-output.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/numeric-output.cpp b/runtime/numeric-output.cpp index c0c617b8c206..fdf0cde340e1 100644 --- a/runtime/numeric-output.cpp +++ b/runtime/numeric-output.cpp @@ -8,6 +8,7 @@ #include "numeric-output.h" #include "flang/Common/unsigned-const-division.h" +#include namespace Fortran::runtime::io { From a9a725eb9d2c39a203f4b4f55f1e03c5e17f6cf7 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 06:52:35 -0800 Subject: [PATCH 061/345] Use std::mutex instead of pthreads (#1006) * Use std::mutex for portability. --- runtime/lock.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/runtime/lock.h b/runtime/lock.h index a26c96542c88..ecf55889aaff 100644 --- a/runtime/lock.h +++ b/runtime/lock.h @@ -11,27 +11,23 @@ #ifndef FORTRAN_RUNTIME_LOCK_H_ #define FORTRAN_RUNTIME_LOCK_H_ -#include +#include namespace Fortran::runtime { class Lock { public: - Lock() { pthread_mutex_init(&mutex_, nullptr); } - ~Lock() { pthread_mutex_destroy(&mutex_); } - void Take() { pthread_mutex_lock(&mutex_); } - bool Try() { return pthread_mutex_trylock(&mutex_) != 0; } - void Drop() { pthread_mutex_unlock(&mutex_); } - + void Take() { mutex_.lock(); } + bool Try() { return mutex_.try_lock(); } + void Drop() { mutex_.unlock(); } void CheckLocked(const Terminator &terminator) { if (Try()) { Drop(); terminator.Crash("Lock::CheckLocked() failed"); } } - private: - pthread_mutex_t mutex_; + std::mutex mutex_; }; class CriticalSection { From a8edb328f717305143ac827132c6d6f45e6e11b9 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 07:09:29 -0800 Subject: [PATCH 062/345] Use a file descriptor in Temp struct (#1036) The struct Temp is used in the function call createUniqueFile which only takes in a file descriptor instead of a file handler. In Unix these are the same thing, but in Windows they are different. Therefore, the type of the member of struct Temp is changed from file handler to file descriptor and when closing the file the file descriptor is converted to a file handler. --- lib/Semantics/mod-file.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp index 2810c3542446..7251e3fa285a 100644 --- a/lib/Semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -617,15 +617,16 @@ std::ostream &PutLower(std::ostream &os, const std::string &str) { } struct Temp { - Temp(llvm::sys::fs::file_t fd, std::string path) : fd{fd}, path{path} {} + Temp(int fd, std::string path) : fd{fd}, path{path} {} Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {} ~Temp() { if (fd >= 0) { - llvm::sys::fs::closeFile(fd); + llvm::sys::fs::file_t native{llvm::sys::fs::convertFDToNativeFile(fd)}; + llvm::sys::fs::closeFile(native); llvm::sys::fs::remove(path.c_str()); } } - llvm::sys::fs::file_t fd; + int fd; std::string path; }; @@ -639,7 +640,7 @@ static llvm::ErrorOr MkTemp(const std::string &path) { CHECK(length > suffix.length() && path.substr(length - suffix.length()) == suffix); auto prefix{path.substr(0, length - suffix.length())}; - llvm::sys::fs::file_t fd; + int fd; llvm::SmallString<16> tempPath; if (std::error_code err{llvm::sys::fs::createUniqueFile( prefix + "%%%%%%" + suffix, fd, tempPath)}) { From 3cbe344f82a0898c9e20b6718d5f80d52670838b Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 07:12:06 -0800 Subject: [PATCH 063/345] Change README to refer to LLVM_BUILD_DIR. (#1033) * Change README to refer to LLVM_BUILD_DIR. LLVM_INSTALL_TOOLS doesn't to install llvm-lit. However pointing to the cmake file in the build directory works fine, and lit and FileCheck will be picked up correctly this way. --- README.md | 17 +++-------------- test-lit/CMakeLists.txt | 7 +------ 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d6d87c79e32c..d0887a305a14 100644 --- a/README.md +++ b/README.md @@ -77,21 +77,10 @@ the variable `LLVM_DIR` to find the installed components. To get the correct LLVM libraries included in your f18 build, define LLVM_DIR on the cmake command line. ``` -LLVM=/lib/cmake/llvm cmake -DLLVM_DIR=$LLVM ... +LLVM=/lib/cmake/llvm cmake -DLLVM_DIR=$LLVM ... ``` -where `LLVM_INSTALLATION_DIR` is -the top-level directory -where llvm is installed. - -### LLVM dependency for lit Regression tests - -F18 has tests that use the lit framework, these tests rely on the -presence of llvm tools as llvm-lit, FileCheck, and others. -These tools are installed when LLVM build set: -``` -LLVM_INSTALL_UTILS=On -``` -to run the regression tests on f18. +where `LLVM_BUILD_DIR` is +the top-level directory where LLVM was built. ### Building f18 with GCC diff --git a/test-lit/CMakeLists.txt b/test-lit/CMakeLists.txt index 111814303b0b..965adff78976 100644 --- a/test-lit/CMakeLists.txt +++ b/test-lit/CMakeLists.txt @@ -14,17 +14,12 @@ set(FLANG_TEST_PARAMS flang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py) set(FLANG_TEST_DEPENDS - flang f18 - llvm-lit - FileCheck - count - not ) add_lit_testsuite(check-all "Running the Flang regression tests" ${CMAKE_CURRENT_BINARY_DIR} PARAMS ${FLANG_TEST_PARAMS} - DEPENDS ${FLANG_TEST_DEPENS} + DEPENDS ${FLANG_TEST_DEPENDS} ) set_target_properties(check-all PROPERTIES FOLDER "Tests") From 70fa71ff7be3024a44b8214be663b9701adc5b1f Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Wed, 26 Feb 2020 12:05:28 -0600 Subject: [PATCH 064/345] Fix an ambiguous overload error --- include/flang/Parser/dump-parse-tree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/flang/Parser/dump-parse-tree.h b/include/flang/Parser/dump-parse-tree.h index d132e93eeab5..818dfbd625bc 100644 --- a/include/flang/Parser/dump-parse-tree.h +++ b/include/flang/Parser/dump-parse-tree.h @@ -784,7 +784,7 @@ class ParseTreeDumper { std::is_same_v || std::is_same_v) { ss << x; } - if (ss.tellp() != 0) { + if (ss.tellp()) { return ss.str(); } if constexpr (std::is_same_v || HasSource::value) { From d971333025bb316529d233528a725fcc726f6615 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 5 Mar 2020 08:06:58 -0800 Subject: [PATCH 065/345] Fix for 'wrong constant folding of assumed-rank array' (#1010) https://github.com/flang-compiler/f18/issues/990 --- include/flang/Semantics/tools.h | 4 ++++ lib/Evaluate/fold-integer.cpp | 14 +++++++++++++- lib/Semantics/expression.cpp | 2 +- test/Semantics/assign03.f90 | 6 ++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index 2c201954fcb4..0dd43a281c06 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -158,6 +158,10 @@ inline bool IsAssumedSizeArray(const Symbol &symbol) { const auto *details{symbol.detailsIf()}; return details && details->IsAssumedSize(); } +inline bool IsAssumedRankArray(const Symbol &symbol) { + const auto *details{symbol.detailsIf()}; + return details && details->IsAssumedRank(); +} bool IsAssumedLengthCharacter(const Symbol &); bool IsAssumedLengthExternalCharacterFunction(const Symbol &); // Is the symbol modifiable in this scope diff --git a/lib/Evaluate/fold-integer.cpp b/lib/Evaluate/fold-integer.cpp index 1e1e06a28681..bf0eb6ca6c3e 100644 --- a/lib/Evaluate/fold-integer.cpp +++ b/lib/Evaluate/fold-integer.cpp @@ -509,7 +509,19 @@ Expr> FoldIntrinsicFunction( cx->u)}; } } else if (name == "rank") { - // TODO assumed-rank dummy argument + if (const auto *array{UnwrapExpr>(args[0])}) { + if (auto named{ExtractNamedEntity(*array)}) { + const Symbol &symbol{named->GetLastSymbol()}; + if (semantics::IsAssumedRankArray(symbol)) { + // DescriptorInquiry can only be placed in expression of kind + // DescriptorInquiry::Result::kind. + return ConvertToType(Expr< + Type>{ + DescriptorInquiry{*named, DescriptorInquiry::Field::Rank}}); + } + } + return Expr{args[0].value().Rank()}; + } return Expr{args[0].value().Rank()}; } else if (name == "selected_char_kind") { if (const auto *chCon{UnwrapExpr>>(args[0])}) { diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index ef7c76429a1b..2e13b211b7fe 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -2502,7 +2502,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { // C885 + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); diff --git a/test/Semantics/assign03.f90 b/test/Semantics/assign03.f90 index 7c4895368c52..6127de22de4f 100644 --- a/test/Semantics/assign03.f90 +++ b/test/Semantics/assign03.f90 @@ -191,5 +191,11 @@ subroutine s12 !ERROR: Must be a constant value logical, parameter :: l5 = is_contiguous(y(v,1)%a(1,1)) end + subroutine test3(b) + integer, intent(inout) :: b(..) + !ERROR: Must be a constant value + integer, parameter :: i = rank(b) + end subroutine + end From 4c6e5608adddf25a53fbdbfd5f0e1b98278cc5ec Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Fri, 6 Mar 2020 00:51:41 -0600 Subject: [PATCH 066/345] Rename EXTERN_C_END to FORTRAN_EXTERN_C_END Since EXTERN_C_END is a macro defined in Windows system headers --- runtime/c-or-cpp.h | 4 ++-- runtime/main.h | 4 ++-- runtime/stop.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/c-or-cpp.h b/runtime/c-or-cpp.h index b514838cdc6c..d9029261ae4a 100644 --- a/runtime/c-or-cpp.h +++ b/runtime/c-or-cpp.h @@ -20,8 +20,8 @@ #define DEFAULT_VALUE(x) #endif -#define EXTERN_C_BEGIN IF_CPLUSPLUS(extern "C" {) -#define EXTERN_C_END IF_CPLUSPLUS( \ +#define FORTRAN_EXTERN_C_BEGIN IF_CPLUSPLUS(extern "C" {) +#define FORTRAN_EXTERN_C_END IF_CPLUSPLUS( \ }) #define NORETURN IF_CPLUSPLUS([[noreturn]]) #define NO_ARGUMENTS IF_NOT_CPLUSPLUS(void) diff --git a/runtime/main.h b/runtime/main.h index 2f2504826465..94ce3a93b70b 100644 --- a/runtime/main.h +++ b/runtime/main.h @@ -12,8 +12,8 @@ #include "c-or-cpp.h" #include "entry-names.h" -EXTERN_C_BEGIN +FORTRAN_EXTERN_C_BEGIN void RTNAME(ProgramStart)(int, const char *[], const char *[]); -EXTERN_C_END +FORTRAN_EXTERN_C_END #endif // FORTRAN_RUNTIME_MAIN_H_ diff --git a/runtime/stop.h b/runtime/stop.h index 8fb1d8d4cffd..24fea0b4f7db 100644 --- a/runtime/stop.h +++ b/runtime/stop.h @@ -13,7 +13,7 @@ #include "entry-names.h" #include -EXTERN_C_BEGIN +FORTRAN_EXTERN_C_BEGIN // Program-initiated image stop NORETURN void RTNAME(StopStatement)(int code DEFAULT_VALUE(EXIT_SUCCESS), @@ -23,6 +23,6 @@ NORETURN void RTNAME(StopStatementText)(const char *, NORETURN void RTNAME(FailImageStatement)(NO_ARGUMENTS); NORETURN void RTNAME(ProgramEndStatement)(NO_ARGUMENTS); -EXTERN_C_END +FORTRAN_EXTERN_C_END #endif // FORTRAN_RUNTIME_STOP_H_ From cede2971ff863b8dd6a2c8727f6566a444ac7a52 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Fri, 6 Mar 2020 17:05:04 -0800 Subject: [PATCH 067/345] Fix handling of DataRef when analyzing CoindexedNamedObject As we loop through the Components, maintain a pointer to the current DataRef rather than moving it. This is more efficient and the previous behavior caused illegal memory accesses. --- lib/Semantics/expression.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 2e13b211b7fe..dd016f9433d3 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -1023,22 +1023,23 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::StructureComponent &sc) { } MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) { - if (auto dataRef{ExtractDataRef(Analyze(x.base))}) { + if (auto maybeDataRef{ExtractDataRef(Analyze(x.base))}) { + DataRef *dataRef{&*maybeDataRef}; std::vector subscripts; SymbolVector reversed; if (auto *aRef{std::get_if(&dataRef->u)}) { subscripts = std::move(aRef->subscript()); reversed.push_back(aRef->GetLastSymbol()); if (Component * component{aRef->base().UnwrapComponent()}) { - *dataRef = std::move(component->base()); + dataRef = &component->base(); } else { - dataRef.reset(); + dataRef = nullptr; } } if (dataRef) { while (auto *component{std::get_if(&dataRef->u)}) { reversed.push_back(component->GetLastSymbol()); - *dataRef = std::move(component->base()); + dataRef = &component->base(); } if (auto *baseSym{std::get_if(&dataRef->u)}) { reversed.push_back(*baseSym); @@ -2502,7 +2503,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { // C885 + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); From 864e9cfc7ee98e3646052fdc757943011d641691 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 5 Mar 2020 12:56:30 -0800 Subject: [PATCH 068/345] Change WhyNotModifiable to return optional One overload of WhyNotModifiable returned an optional message while the other returns a unique_ptr. Change the latter to be consistent with the former and other message-returning functions in this file. Also, reorder the if clauses to reduce the level of indentation. --- include/flang/Semantics/tools.h | 2 +- lib/Semantics/check-call.cpp | 7 +++---- lib/Semantics/tools.cpp | 32 +++++++++++++------------------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index 0dd43a281c06..e2179075e087 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -167,7 +167,7 @@ bool IsAssumedLengthExternalCharacterFunction(const Symbol &); // Is the symbol modifiable in this scope std::optional WhyNotModifiable( const Symbol &, const Scope &); -std::unique_ptr WhyNotModifiable(SourceName, const SomeExpr &, +std::optional WhyNotModifiable(SourceName, const SomeExpr &, const Scope &, bool vectorSubscriptIsOk = false); const Symbol *IsExternalInPureContext(const Symbol &, const Scope &); bool HasCoarray(const parser::Expr &); diff --git a/lib/Semantics/check-call.cpp b/lib/Semantics/check-call.cpp index bca65e9909b7..3273ed22da40 100644 --- a/lib/Semantics/check-call.cpp +++ b/lib/Semantics/check-call.cpp @@ -329,13 +329,12 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy, } if (reason && scope) { bool vectorSubscriptIsOk{isElemental || dummyIsValue}; // 15.5.2.4(21) - std::unique_ptr why{ - WhyNotModifiable(messages.at(), actual, *scope, vectorSubscriptIsOk)}; - if (why.get()) { + if (auto why{WhyNotModifiable( + messages.at(), actual, *scope, vectorSubscriptIsOk)}) { if (auto *msg{messages.Say( "Actual argument associated with %s %s must be definable"_err_en_US, reason, dummyName)}) { - msg->Attach(std::move(why)); + msg->Attach(*why); } } } diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index 922f38f44d0b..de9131d8f86b 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -789,29 +789,23 @@ std::optional WhyNotModifiable( } } -std::unique_ptr WhyNotModifiable(parser::CharBlock at, +std::optional WhyNotModifiable(parser::CharBlock at, const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) { - if (evaluate::IsVariable(expr)) { - if (auto dataRef{evaluate::ExtractDataRef(expr)}) { - if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) { - return std::make_unique( - at, "variable has a vector subscript"_en_US); - } else { - const Symbol &symbol{dataRef->GetFirstSymbol()}; - if (auto maybeWhy{WhyNotModifiable(symbol, scope)}) { - return std::make_unique(symbol.name(), - parser::MessageFormattedText{ - std::move(*maybeWhy), symbol.name()}); - } - } - } else { - // reference to function returning POINTER + if (!evaluate::IsVariable(expr)) { + return parser::Message{at, "Expression is not a variable"_en_US}; + } else if (auto dataRef{evaluate::ExtractDataRef(expr)}) { + if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) { + return parser::Message{at, "Variable has a vector subscript"_en_US}; + } + const Symbol &symbol{dataRef->GetFirstSymbol()}; + if (auto maybeWhy{WhyNotModifiable(symbol, scope)}) { + return parser::Message{symbol.name(), + parser::MessageFormattedText{std::move(*maybeWhy), symbol.name()}}; } } else { - return std::make_unique( - at, "expression is not a variable"_en_US); + // reference to function returning POINTER } - return {}; + return std::nullopt; } class ImageControlStmtHelper { From 305a3470e572161483b0f430514b77ba19f932ea Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 5 Mar 2020 13:05:45 -0800 Subject: [PATCH 069/345] Change CheckDefinabilityInPureScope to return bool Have CheckDefinabilityInPureScope and CheckCopyabilityInPureScope return false when their checks fail and report errors so that we will be able to avoid reporting extra errors in those cases. --- lib/Semantics/assignment.cpp | 68 ++++++++++++++++++++---------------- lib/Semantics/assignment.h | 4 +-- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/lib/Semantics/assignment.cpp b/lib/Semantics/assignment.cpp index dfa11e800804..b309433e68f8 100644 --- a/lib/Semantics/assignment.cpp +++ b/lib/Semantics/assignment.cpp @@ -43,7 +43,7 @@ class AssignmentContext { void Analyze(const parser::ConcurrentControl &); private: - void CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, + bool CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, parser::CharBlock rhsSource, bool isPointerAssignment); void CheckShape(parser::CharBlock, const SomeExpr *); template @@ -110,16 +110,18 @@ static const char *WhyBaseObjectIsSuspicious( } } -// Checks C1594(1,2) -void CheckDefinabilityInPureScope(parser::ContextualMessages &messages, +// Checks C1594(1,2); false if check fails +bool CheckDefinabilityInPureScope(parser::ContextualMessages &messages, const Symbol &lhs, const Scope &context, const Scope &pure) { if (pure.symbol()) { if (const char *why{WhyBaseObjectIsSuspicious(lhs, context)}) { evaluate::SayWithDeclaration(messages, lhs, "Pure subprogram '%s' may not define '%s' because it is %s"_err_en_US, pure.symbol()->name(), lhs.name(), why); + return false; } } + return true; } static std::optional GetPointerComponentDesignatorName( @@ -135,21 +137,24 @@ static std::optional GetPointerComponentDesignatorName( return std::nullopt; } -// Checks C1594(5,6) -void CheckCopyabilityInPureScope(parser::ContextualMessages &messages, +// Checks C1594(5,6); false if check fails +bool CheckCopyabilityInPureScope(parser::ContextualMessages &messages, const SomeExpr &expr, const Scope &scope) { if (const Symbol * base{GetFirstSymbol(expr)}) { if (const char *why{WhyBaseObjectIsSuspicious(*base, scope)}) { if (auto pointer{GetPointerComponentDesignatorName(expr)}) { evaluate::SayWithDeclaration(messages, *base, - "A pure subprogram may not copy the value of '%s' because it is %s and has the POINTER component '%s'"_err_en_US, + "A pure subprogram may not copy the value of '%s' because it is %s" + " and has the POINTER component '%s'"_err_en_US, base->name(), why, *pointer); + return false; } } } + return true; } -void AssignmentContext::CheckForPureContext(const SomeExpr &lhs, +bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs, parser::CharBlock source, bool isPointerAssignment) { const Scope &scope{context_.FindScope(source)}; if (const Scope * pure{FindPureProcedureContaining(scope)}) { @@ -160,13 +165,12 @@ void AssignmentContext::CheckForPureContext(const SomeExpr &lhs, "A pure subprogram may not define a coindexed object"_err_en_US); } else if (const Symbol * base{GetFirstSymbol(lhs)}) { if (const auto *assoc{base->detailsIf()}) { - if (auto dataRef{ExtractDataRef(assoc->expr())}) { - // ASSOCIATE(a=>x) -- check x, not a, for "a=..." - CheckDefinabilityInPureScope( - messages, dataRef->GetFirstSymbol(), scope, *pure); - } - } else { - CheckDefinabilityInPureScope(messages, *base, scope, *pure); + auto dataRef{ExtractDataRef(assoc->expr())}; + // ASSOCIATE(a=>x) -- check x, not a, for "a=..." + base = dataRef ? &dataRef->GetFirstSymbol() : nullptr; + } + if (!CheckDefinabilityInPureScope(messages, *base, scope, *pure)) { + return false; } } if (isPointerAssignment) { @@ -176,29 +180,31 @@ void AssignmentContext::CheckForPureContext(const SomeExpr &lhs, evaluate::SayWithDeclaration(messages, *base, "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US, base->name(), why); + return false; } } - } else { - if (auto type{evaluate::DynamicType::From(lhs)}) { - // C1596 checks for polymorphic deallocation in a pure subprogram - // due to automatic reallocation on assignment - if (type->IsPolymorphic()) { - context_.Say( - "Deallocation of polymorphic object is not permitted in a pure subprogram"_err_en_US); - } - if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) { - if (auto bad{FindPolymorphicAllocatableNonCoarrayUltimateComponent( - *derived)}) { - evaluate::SayWithDeclaration(messages, *bad, - "Deallocation of polymorphic non-coarray component '%s' is not permitted in a pure subprogram"_err_en_US, - bad.BuildResultDesignatorName()); - } else { - CheckCopyabilityInPureScope(messages, rhs, scope); - } + } else if (auto type{evaluate::DynamicType::From(lhs)}) { + // C1596 checks for polymorphic deallocation in a pure subprogram + // due to automatic reallocation on assignment + if (type->IsPolymorphic()) { + context_.Say( + "Deallocation of polymorphic object is not permitted in a pure subprogram"_err_en_US); + return false; + } + if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) { + if (auto bad{FindPolymorphicAllocatableNonCoarrayUltimateComponent( + *derived)}) { + evaluate::SayWithDeclaration(messages, *bad, + "Deallocation of polymorphic non-coarray component '%s' is not permitted in a pure subprogram"_err_en_US, + bad.BuildResultDesignatorName()); + return false; + } else { + return CheckCopyabilityInPureScope(messages, rhs, scope); } } } } + return true; } // 10.2.3.1(2) The masks and LHS of assignments must all have the same shape diff --git a/lib/Semantics/assignment.h b/lib/Semantics/assignment.h index ad18577ac883..ab8b4bfca1a5 100644 --- a/lib/Semantics/assignment.h +++ b/lib/Semantics/assignment.h @@ -30,10 +30,10 @@ class Scope; class Symbol; // Applies checks from C1594(1-2) on definitions in pure subprograms -void CheckDefinabilityInPureScope(parser::ContextualMessages &, const Symbol &, +bool CheckDefinabilityInPureScope(parser::ContextualMessages &, const Symbol &, const Scope &context, const Scope &pure); // Applies checks from C1594(5-6) on copying pointers in pure subprograms -void CheckCopyabilityInPureScope(parser::ContextualMessages &, +bool CheckCopyabilityInPureScope(parser::ContextualMessages &, const evaluate::Expr &, const Scope &); class AssignmentChecker : public virtual BaseChecker { From f2d2657aab051b938ec4e4702926aadb436c19aa Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 5 Mar 2020 17:55:51 -0800 Subject: [PATCH 070/345] Perform definability checks on LHS of assignment If the pure context check succeeds, call `WhyNotModifiable` to verify the LHS can be modified. Detect assignment to whole assumed-size array. Change `IsVariable` to return false for a parameter or a component or array reference whose base it a parameter. When analyzing an assignment statement, report an error if the LHS is a constant expression. Otherwise it might get folded and when we detect the problem later the error will be confusing. Handle Substring on LHS of assignment. Change ExtractDataRef and IsVariable to work on a Substring. Fix IsImpliedShape and IsAssumedSize predicates in ArraySpec. Fix C709 check in check-declarations.cpp. --- include/flang/Evaluate/tools.h | 11 ++- lib/Evaluate/tools.cpp | 25 ++++++ lib/Semantics/assignment.cpp | 18 ++++- lib/Semantics/check-declarations.cpp | 8 +- lib/Semantics/expression.cpp | 12 ++- lib/Semantics/type.cpp | 8 +- test/Semantics/CMakeLists.txt | 1 + test/Semantics/assign04.f90 | 110 +++++++++++++++++++++++++++ test/Semantics/resolve72.f90 | 2 +- 9 files changed, 178 insertions(+), 17 deletions(-) create mode 100644 test/Semantics/assign04.f90 diff --git a/include/flang/Evaluate/tools.h b/include/flang/Evaluate/tools.h index 7f867813b2bc..2d2a46fc3eb1 100644 --- a/include/flang/Evaluate/tools.h +++ b/include/flang/Evaluate/tools.h @@ -64,9 +64,10 @@ struct IsVariableHelper IsVariableHelper() : Base{*this} {} using Base::operator(); Result operator()(const StaticDataObject &) const { return false; } - Result operator()(const Symbol &) const { return true; } - Result operator()(const Component &) const { return true; } - Result operator()(const ArrayRef &) const { return true; } + Result operator()(const Symbol &) const; + Result operator()(const Component &) const; + Result operator()(const ArrayRef &) const; + Result operator()(const Substring &) const; Result operator()(const CoarrayRef &) const { return true; } Result operator()(const ComplexPart &) const { return true; } Result operator()(const ProcedureDesignator &) const; @@ -218,6 +219,9 @@ std::optional ExtractDataRef(const Designator &d) { if constexpr (common::HasMember) { return DataRef{x}; } + if constexpr (std::is_same_v, Substring>) { + return ExtractDataRef(x); + } return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning }, d.u); @@ -234,6 +238,7 @@ std::optional ExtractDataRef(const std::optional &x) { return std::nullopt; } } +std::optional ExtractDataRef(const Substring &); // Predicate: is an expression is an array element reference? template bool IsArrayElement(const Expr &expr) { diff --git a/lib/Evaluate/tools.cpp b/lib/Evaluate/tools.cpp index 624fb352a59e..e9e2c4f0bd5c 100644 --- a/lib/Evaluate/tools.cpp +++ b/lib/Evaluate/tools.cpp @@ -11,6 +11,7 @@ #include "flang/Evaluate/characteristics.h" #include "flang/Evaluate/traverse.h" #include "flang/Parser/message.h" +#include "flang/Semantics/tools.h" #include #include @@ -37,7 +38,31 @@ Expr Parenthesize(Expr &&expr) { std::move(expr.u)); } +std::optional ExtractDataRef(const Substring &substring) { + return std::visit( + common::visitors{ + [&](const DataRef &x) -> std::optional { return x; }, + [&](const StaticDataObject::Pointer &) -> std::optional { + return std::nullopt; + }, + }, + substring.parent()); +} + // IsVariable() + +auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result { + return !symbol.attrs().test(semantics::Attr::PARAMETER); +} +auto IsVariableHelper::operator()(const Component &x) const -> Result { + return (*this)(x.base()); +} +auto IsVariableHelper::operator()(const ArrayRef &x) const -> Result { + return (*this)(x.base()); +} +auto IsVariableHelper::operator()(const Substring &x) const -> Result { + return (*this)(x.GetBaseObject()); +} auto IsVariableHelper::operator()(const ProcedureDesignator &x) const -> Result { const Symbol *symbol{x.GetSymbol()}; diff --git a/lib/Semantics/assignment.cpp b/lib/Semantics/assignment.cpp index b309433e68f8..bd8fe2cf0311 100644 --- a/lib/Semantics/assignment.cpp +++ b/lib/Semantics/assignment.cpp @@ -66,10 +66,23 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { const SomeExpr &rhs{assignment->rhs}; auto lhsLoc{std::get(stmt.t).GetSource()}; auto rhsLoc{std::get(stmt.t).source}; + auto shape{evaluate::GetShape(foldingContext(), lhs)}; + if (shape && !shape->empty() && !shape->back().has_value()) { // C1014 + Say(lhsLoc, + "Left-hand side of assignment may not be a whole assumed-size array"_err_en_US); + } + if (CheckForPureContext(lhs, rhs, rhsLoc, false)) { + const Scope &scope{context_.FindScope(lhsLoc)}; + if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope)}) { + if (auto *msg{Say(lhsLoc, + "Left-hand side of assignment is not modifiable"_err_en_US)}) { + msg->Attach(*whyNot); + } + } + } if (whereDepth_ > 0) { CheckShape(lhsLoc, &lhs); } - CheckForPureContext(lhs, rhs, rhsLoc, false); } } @@ -169,7 +182,8 @@ bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs, // ASSOCIATE(a=>x) -- check x, not a, for "a=..." base = dataRef ? &dataRef->GetFirstSymbol() : nullptr; } - if (!CheckDefinabilityInPureScope(messages, *base, scope, *pure)) { + if (base && + !CheckDefinabilityInPureScope(messages, *base, scope, *pure)) { return false; } } diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index 1b7dd988e5b1..63d36e342cdb 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -333,10 +333,10 @@ void CheckHelper::CheckAssumedTypeEntity( // C709 "Assumed-type argument '%s' cannot be a coarray"_err_en_US, symbol.name()); } - if (details.IsArray() && - !(details.IsAssumedShape() || details.IsAssumedSize())) { - messages_.Say("Assumed-type argument '%s' must be assumed shape" - " or assumed size array"_err_en_US, + if (details.IsArray() && details.shape().IsExplicitShape()) { + messages_.Say( + "Assumed-type array argument 'arg8' must be assumed shape," + " assumed size, or assumed rank"_err_en_US, symbol.name()); } } diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index dd016f9433d3..302ef7fdcaf8 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -2588,10 +2588,16 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef( void ArgumentAnalyzer::Analyze(const parser::Variable &x) { source_.ExtendToCover(x.GetSource()); if (MaybeExpr expr{context_.Analyze(x)}) { - actuals_.emplace_back(std::move(*expr)); - } else { - fatalErrors_ = true; + if (!IsConstantExpr(*expr)) { + actuals_.emplace_back(std::move(*expr)); + return; + } + const Symbol *symbol{GetFirstSymbol(*expr)}; + context_.Say(x.GetSource(), + "Assignment to constant '%s' is not allowed"_err_en_US, + symbol ? symbol->name() : x.GetSource()); } + fatalErrors_ = true; } void ArgumentAnalyzer::Analyze( diff --git a/lib/Semantics/type.cpp b/lib/Semantics/type.cpp index 47158c7af4ab..49bd618e3637 100644 --- a/lib/Semantics/type.cpp +++ b/lib/Semantics/type.cpp @@ -353,13 +353,13 @@ bool ArraySpec::IsDeferredShape() const { }); } bool ArraySpec::IsImpliedShape() const { - return CheckAll([](const ShapeSpec &x) { return x.ubound().isAssumed(); }); + return !IsAssumedRank() && + CheckAll([](const ShapeSpec &x) { return x.ubound().isAssumed(); }); } bool ArraySpec::IsAssumedSize() const { - return !empty() && + return !empty() && !IsAssumedRank() && back().ubound().isAssumed() && std::all_of(begin(), end() - 1, - [](const ShapeSpec &x) { return x.ubound().isExplicit(); }) && - back().ubound().isAssumed(); + [](const ShapeSpec &x) { return x.ubound().isExplicit(); }); } bool ArraySpec::IsAssumedRank() const { return Rank() == 1 && front().lbound().isAssumed(); diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt index 7144ff9cffd1..7c826d82b961 100644 --- a/test/Semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -115,6 +115,7 @@ set(ERROR_TESTS assign01.f90 assign02.f90 assign03.f90 + assign04.f90 if_arith02.f90 if_arith03.f90 if_arith04.f90 diff --git a/test/Semantics/assign04.f90 b/test/Semantics/assign04.f90 new file mode 100644 index 000000000000..e0a02160cba5 --- /dev/null +++ b/test/Semantics/assign04.f90 @@ -0,0 +1,110 @@ +! 9.4.5 +subroutine s1 + type :: t(k, l) + integer, kind :: k + integer, len :: l + end type + type(t(1, 2)) :: x + !ERROR: Assignment to constant 'x%k' is not allowed + x%k = 4 + !ERROR: Left-hand side of assignment is not modifiable + x%l = 3 +end + +! C901 +subroutine s2(x) + real, parameter :: x = 0.0 + real, parameter :: a(*) = [1, 2, 3] + character, parameter :: c(2) = "ab" + integer :: i + !ERROR: Assignment to constant 'x' is not allowed + x = 2.0 + i = 2 + !ERROR: Left-hand side of assignment is not modifiable + a(i) = 3.0 + !ERROR: Left-hand side of assignment is not modifiable + a(i:i+1) = [4, 5] + !ERROR: Left-hand side of assignment is not modifiable + c(i:2) = "cd" +end + +! C901 +subroutine s3 + type :: t + integer :: a(2) + integer :: b + end type + type(t) :: x + type(t), parameter :: y = t([1,2], 3) + integer :: i = 1 + x%a(i) = 1 + !ERROR: Left-hand side of assignment is not modifiable + y%a(i) = 2 + x%b = 4 + !ERROR: Left-hand side of assignment is not modifiable + y%b = 5 +end + +! C844 +subroutine s4 + type :: t + integer :: a(2) + end type +contains + subroutine s(x, c) + type(t), intent(in) :: x + character(10), intent(in) :: c + type(t) :: y + !ERROR: Left-hand side of assignment is not modifiable + x = y + !ERROR: Left-hand side of assignment is not modifiable + x%a(1) = 2 + !ERROR: Left-hand side of assignment is not modifiable + c(2:3) = "ab" + end +end + +! 8.5.15(2) +module m5 + real :: x + real, protected :: y + real, private :: z + type :: t + real :: a + end type + type(t), protected :: b +end +subroutine s5() + use m5 + implicit none + x = 1.0 + !ERROR: Left-hand side of assignment is not modifiable + y = 2.0 + !ERROR: No explicit type declared for 'z' + z = 3.0 + !ERROR: Left-hand side of assignment is not modifiable + b%a = 1.0 +end + +subroutine s6(x) + integer :: x(*) + x(1:3) = [1, 2, 3] + x(:3) = [1, 2, 3] + !ERROR: Assumed-size array 'x' must have explicit final subscript upper bound value + x(:) = [1, 2, 3] + !ERROR: Left-hand side of assignment may not be a whole assumed-size array + x = [1, 2, 3] +end + +module m7 + type :: t + integer :: i + end type +contains + subroutine s7(x) + type(t) :: x(*) + x(:3)%i = [1, 2, 3] + !ERROR: Left-hand side of assignment may not be a whole assumed-size array + x%i = [1, 2, 3] + end +end diff --git a/test/Semantics/resolve72.f90 b/test/Semantics/resolve72.f90 index fdead88c8fe8..6ff2603b2129 100644 --- a/test/Semantics/resolve72.f90 +++ b/test/Semantics/resolve72.f90 @@ -19,7 +19,7 @@ subroutine inner1(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) type(*), pointer :: arg6 !ERROR: Assumed-type argument 'arg7' cannot have the VALUE attribute type(*), value :: arg7 - !ERROR: Assumed-type argument 'arg8' must be assumed shape or assumed size array + !ERROR: Assumed-type array argument 'arg8' must be assumed shape, assumed size, or assumed rank type(*), dimension(3) :: arg8 end subroutine inner1 end subroutine s From 111f49061e07604670614250197a1064959fd981 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 10 Mar 2020 10:28:36 -0700 Subject: [PATCH 071/345] Changes to get a clean build of f18 with latest clang Prep for review --- include/flang/Common/reference.h | 11 +++++- include/flang/Common/uint128.h | 13 +++++-- include/flang/Evaluate/common.h | 2 +- include/flang/Evaluate/expression.h | 1 - include/flang/Evaluate/variable.h | 28 ++------------ lib/Evaluate/call.cpp | 4 ++ lib/Evaluate/expression.cpp | 59 ++++++++++++++++++++++++++++- lib/Evaluate/variable.cpp | 22 ++++++++++- lib/Semantics/resolve-labels.cpp | 10 ++--- lib/Semantics/resolve-names.cpp | 2 +- 10 files changed, 112 insertions(+), 40 deletions(-) diff --git a/include/flang/Common/reference.h b/include/flang/Common/reference.h index 37e7ab9ca523..f204b8f8b352 100644 --- a/include/flang/Common/reference.h +++ b/include/flang/Common/reference.h @@ -45,8 +45,15 @@ template class Reference { type &get() const noexcept { return *p_; } type *operator->() const { return p_; } type &operator*() const { return *p_; } - bool operator==(Reference that) const { return *p_ == *that.p_; } - bool operator!=(Reference that) const { return *p_ != *that.p_; } + + bool operator==(std::add_const_t &that) const { + return p_ == &that || *p_ == that; + } + bool operator!=(std::add_const_t &that) const { return !(*this == that); } + bool operator==(const Reference &that) const { + return p_ == that.p_ || *this == *that.p_; + } + bool operator!=(const Reference &that) const { return !(*this == that); } private: type *p_; // never null diff --git a/include/flang/Common/uint128.h b/include/flang/Common/uint128.h index 4d4fed94a002..48e1f49e8e90 100644 --- a/include/flang/Common/uint128.h +++ b/include/flang/Common/uint128.h @@ -25,11 +25,18 @@ namespace Fortran::common { class UnsignedInt128 { public: constexpr UnsignedInt128() {} - constexpr UnsignedInt128(std::uint64_t n) : low_{n} {} - constexpr UnsignedInt128(std::int64_t n) + // This means of definition provides some portability for + // "size_t" operands. + constexpr UnsignedInt128(unsigned n) : low_{n} {} + constexpr UnsignedInt128(unsigned long n) : low_{n} {} + constexpr UnsignedInt128(unsigned long long n) : low_{n} {} + constexpr UnsignedInt128(int n) : low_{static_cast(n)}, high_{-static_cast( n < 0)} {} - constexpr UnsignedInt128(int n) + constexpr UnsignedInt128(long n) + : low_{static_cast(n)}, high_{-static_cast( + n < 0)} {} + constexpr UnsignedInt128(long long n) : low_{static_cast(n)}, high_{-static_cast( n < 0)} {} constexpr UnsignedInt128(const UnsignedInt128 &) = default; diff --git a/include/flang/Evaluate/common.h b/include/flang/Evaluate/common.h index d76126b1436b..639ff0c82e2e 100644 --- a/include/flang/Evaluate/common.h +++ b/include/flang/Evaluate/common.h @@ -195,7 +195,7 @@ using HostUnsignedInt = #define EVALUATE_UNION_CLASS_BOILERPLATE(t) \ CLASS_BOILERPLATE(t) \ UNION_CONSTRUCTORS(t) \ - bool operator==(const t &that) const { return u == that.u; } + bool operator==(const t &) const; // Forward definition of Expr<> so that it can be indirectly used in its own // definition diff --git a/include/flang/Evaluate/expression.h b/include/flang/Evaluate/expression.h index 52c17a957b9b..e4bd57f2ae1c 100644 --- a/include/flang/Evaluate/expression.h +++ b/include/flang/Evaluate/expression.h @@ -780,7 +780,6 @@ using TypelessExpression = std::variant class Expr : public ExpressionBase { public: using Result = SomeType; - EVALUATE_UNION_CLASS_BOILERPLATE(Expr) // Owning references to these generic expressions can appear in other diff --git a/include/flang/Evaluate/variable.h b/include/flang/Evaluate/variable.h index b39011aeab64..62effec2c636 100644 --- a/include/flang/Evaluate/variable.h +++ b/include/flang/Evaluate/variable.h @@ -43,27 +43,12 @@ using SymbolVector = std::vector; struct DataRef; template struct Variable; -bool AreSameSymbol(const Symbol &, const Symbol &); - -// Implements operator==() for a union type, using special case handling -// for Symbol references. -template bool TestVariableEquality(const A &x, const A &y) { - const SymbolRef *xSymbol{std::get_if(&x.u)}; - if (const SymbolRef * ySymbol{std::get_if(&y.u)}) { - return xSymbol && AreSameSymbol(*xSymbol, *ySymbol); - } else { - return x.u == y.u; - } -} - // Reference a base object in memory. This can be a Fortran symbol, // static data (e.g., CHARACTER literal), or compiler-created temporary. struct BaseObject { - CLASS_BOILERPLATE(BaseObject) - UNION_CONSTRUCTORS(BaseObject) + EVALUATE_UNION_CLASS_BOILERPLATE(BaseObject) int Rank() const; std::optional> LEN() const; - bool operator==(const BaseObject &) const; std::ostream &AsFortran(std::ostream &) const; const Symbol *symbol() const { if (const auto *result{std::get_if(&u)}) { @@ -296,10 +281,7 @@ class CoarrayRef { // a terminal substring range or complex component designator; use // R901 designator for that. struct DataRef { - CLASS_BOILERPLATE(DataRef) - UNION_CONSTRUCTORS(DataRef) - - bool operator==(const DataRef &) const; + EVALUATE_UNION_CLASS_BOILERPLATE(DataRef) int Rank() const; const Symbol &GetFirstSymbol() const; const Symbol &GetLastSymbol() const; @@ -395,15 +377,11 @@ template class Designator { using Result = T; static_assert( IsSpecificIntrinsicType || std::is_same_v); - CLASS_BOILERPLATE(Designator) - UNION_CONSTRUCTORS(Designator) + EVALUATE_UNION_CLASS_BOILERPLATE(Designator) Designator(const DataRef &that) : u{common::CopyVariant(that.u)} {} Designator(DataRef &&that) : u{common::MoveVariant(std::move(that.u))} {} - bool operator==(const Designator &that) const { - return TestVariableEquality(*this, that); - } std::optional GetType() const; int Rank() const; BaseObject GetBaseObject() const; diff --git a/lib/Evaluate/call.cpp b/lib/Evaluate/call.cpp index 52c16f2e3f3b..31ee05b30873 100644 --- a/lib/Evaluate/call.cpp +++ b/lib/Evaluate/call.cpp @@ -79,6 +79,10 @@ bool SpecificIntrinsic::operator==(const SpecificIntrinsic &that) const { ProcedureDesignator::ProcedureDesignator(Component &&c) : u{common::CopyableIndirection::Make(std::move(c))} {} +bool ProcedureDesignator::operator==(const ProcedureDesignator &that) const { + return u == that.u; +} + std::optional ProcedureDesignator::GetType() const { if (const auto *intrinsic{std::get_if(&u)}) { if (const auto &result{intrinsic->characteristics.value().functionResult}) { diff --git a/lib/Evaluate/expression.cpp b/lib/Evaluate/expression.cpp index a390c9e4d1b2..11ba35dbb8ec 100644 --- a/lib/Evaluate/expression.cpp +++ b/lib/Evaluate/expression.cpp @@ -101,7 +101,7 @@ template int ExpressionBase::Rank() const { derived().u); } -// Equality testing for classes without EVALUATE_UNION_CLASS_BOILERPLATE() +// Equality testing bool ImpliedDoIndex::operator==(const ImpliedDoIndex &that) const { return name == that.name; @@ -114,6 +114,12 @@ bool ImpliedDo::operator==(const ImpliedDo &that) const { values_ == that.values_; } +template +bool ArrayConstructorValue::operator==( + const ArrayConstructorValue &that) const { + return u == that.u; +} + template bool ArrayConstructorValues::operator==( const ArrayConstructorValues &that) const { @@ -146,6 +152,57 @@ bool StructureConstructor::operator==(const StructureConstructor &that) const { return result_ == that.result_ && values_ == that.values_; } +bool Relational::operator==(const Relational &that) const { + return u == that.u; +} + +template +bool Expr>::operator==( + const Expr> &that) const { + return u == that.u; +} + +template +bool Expr>::operator==( + const Expr> &that) const { + return u == that.u; +} + +template +bool Expr>::operator==( + const Expr> &that) const { + return u == that.u; +} + +template +bool Expr>::operator==( + const Expr> &that) const { + return u == that.u; +} + +template +bool Expr>::operator==( + const Expr> &that) const { + return u == that.u; +} + +template +bool Expr>::operator==(const Expr> &that) const { + return u == that.u; +} + +bool Expr::operator==(const Expr &that) const { + return u == that.u; +} + +bool Expr::operator==(const Expr &that) const { + return u == that.u; +} + +bool Expr::operator==(const Expr &that) const { + return u == that.u; +} + DynamicType StructureConstructor::GetType() const { return result_.GetType(); } const Expr *StructureConstructor::Find( diff --git a/lib/Evaluate/variable.cpp b/lib/Evaluate/variable.cpp index 2ed759057820..030f8866a42b 100644 --- a/lib/Evaluate/variable.cpp +++ b/lib/Evaluate/variable.cpp @@ -601,7 +601,7 @@ NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); } // For the purposes of comparing type parameter expressions while // testing the compatibility of procedure characteristics, two // object dummy arguments with the same name are considered equal. -bool AreSameSymbol(const Symbol &x, const Symbol &y) { +static bool AreSameSymbol(const Symbol &x, const Symbol &y) { if (&x == &y) { return true; } @@ -615,6 +615,17 @@ bool AreSameSymbol(const Symbol &x, const Symbol &y) { return false; } +// Implements operator==() for a union type, using special case handling +// for Symbol references. +template static bool TestVariableEquality(const A &x, const A &y) { + const SymbolRef *xSymbol{std::get_if(&x.u)}; + if (const SymbolRef * ySymbol{std::get_if(&y.u)}) { + return xSymbol && AreSameSymbol(*xSymbol, *ySymbol); + } else { + return x.u == y.u; + } +} + bool BaseObject::operator==(const BaseObject &that) const { return TestVariableEquality(*this, that); } @@ -638,6 +649,7 @@ bool Triplet::operator==(const Triplet &that) const { return lower_ == that.lower_ && upper_ == that.upper_ && stride_ == that.stride_; } +bool Subscript::operator==(const Subscript &that) const { return u == that.u; } bool ArrayRef::operator==(const ArrayRef &that) const { return base_ == that.base_ && subscript_ == that.subscript_; } @@ -659,6 +671,14 @@ bool ComplexPart::operator==(const ComplexPart &that) const { bool ProcedureRef::operator==(const ProcedureRef &that) const { return proc_ == that.proc_ && arguments_ == that.arguments_; } +template +bool Designator::operator==(const Designator &that) const { + return TestVariableEquality(*this, that); +} +template +bool Variable::operator==(const Variable &that) const { + return u == that.u; +} bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const { return field_ == that.field_ && base_ == that.base_ && dimension_ == that.dimension_; diff --git a/lib/Semantics/resolve-labels.cpp b/lib/Semantics/resolve-labels.cpp index d17352b9a440..55d60ac1729a 100644 --- a/lib/Semantics/resolve-labels.cpp +++ b/lib/Semantics/resolve-labels.cpp @@ -818,7 +818,7 @@ LabeledStatementInfoTuplePOD GetLabel( void CheckBranchesIntoDoBody(const SourceStmtList &branches, const TargetStmtMap &labels, const IndexList &loopBodies, SemanticsContext &context) { - for (const auto branch : branches) { + for (const auto &branch : branches) { const auto &label{branch.parserLabel}; auto branchTarget{GetLabel(labels, label)}; if (HasScope(branchTarget.proxyForScope)) { @@ -870,7 +870,7 @@ void CheckLabelDoConstraints(const SourceStmtList &dos, const SourceStmtList &branches, const TargetStmtMap &labels, const std::vector &scopes, SemanticsContext &context) { IndexList loopBodies; - for (const auto stmt : dos) { + for (const auto &stmt : dos) { const auto &label{stmt.parserLabel}; const auto &scope{stmt.proxyForScope}; const auto &position{stmt.parserCharBlock}; @@ -924,7 +924,7 @@ void CheckLabelDoConstraints(const SourceStmtList &dos, void CheckScopeConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, const std::vector &scopes, SemanticsContext &context) { - for (const auto stmt : stmts) { + for (const auto &stmt : stmts) { const auto &label{stmt.parserLabel}; const auto &scope{stmt.proxyForScope}; const auto &position{stmt.parserCharBlock}; @@ -943,7 +943,7 @@ void CheckScopeConstraints(const SourceStmtList &stmts, void CheckBranchTargetConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, SemanticsContext &context) { - for (const auto stmt : stmts) { + for (const auto &stmt : stmts) { const auto &label{stmt.parserLabel}; auto branchTarget{GetLabel(labels, label)}; if (HasScope(branchTarget.proxyForScope)) { @@ -981,7 +981,7 @@ void CheckBranchConstraints(const SourceStmtList &branches, void CheckDataXferTargetConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, SemanticsContext &context) { - for (const auto stmt : stmts) { + for (const auto &stmt : stmts) { const auto &label{stmt.parserLabel}; auto ioTarget{GetLabel(labels, label)}; if (HasScope(ioTarget.proxyForScope)) { diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 38a9ba909135..a37bfd64470f 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -3618,7 +3618,7 @@ void DeclarationVisitor::Post(const parser::TypeBoundProcedurePart &) { // track specifics seen for the current generic to detect duplicates: const Symbol *currGeneric{nullptr}; std::set specifics; - for (const auto [generic, bindingName] : genericBindings_) { + for (const auto &[generic, bindingName] : genericBindings_) { if (generic != currGeneric) { currGeneric = generic; specifics.clear(); From d787108637adad358ccf8af3d200acb3f66ce099 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 10 Mar 2020 15:31:02 -0700 Subject: [PATCH 072/345] Fix scope accessibility check The check for whether a private component is accessible was depending on determining whether the source range of the current scope was within the source range of the module that the component was declared in. This could fail if the current scope was of kind `ImpliedDos` and had no source range. The fix is to add `Scope::Contains` to check the relationship by traversing the parent links. These are created when the Scope is so are always reliable. The source range of a scope is built up over time. --- include/flang/Semantics/scope.h | 11 ++--------- lib/Semantics/scope.cpp | 22 ++++++++++++++++++++-- lib/Semantics/tools.cpp | 2 +- test/Semantics/resolve34.f90 | 7 +++++++ 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/include/flang/Semantics/scope.h b/include/flang/Semantics/scope.h index 6f67ecbabf31..7c12dc14563d 100644 --- a/include/flang/Semantics/scope.h +++ b/include/flang/Semantics/scope.h @@ -85,15 +85,8 @@ class Scope { const Symbol *GetSymbol() const; const Scope *GetDerivedTypeParent() const; - - std::optional GetName() const { - if (const auto *sym{GetSymbol()}) { - return sym->name(); - } else { - return std::nullopt; - } - } - + std::optional GetName() const; + bool Contains(const Scope &) const; /// Make a scope nested in this one Scope &MakeScope(Kind kind, Symbol *symbol = nullptr); diff --git a/lib/Semantics/scope.cpp b/lib/Semantics/scope.cpp index 16ee107a4612..b345c6189849 100644 --- a/lib/Semantics/scope.cpp +++ b/lib/Semantics/scope.cpp @@ -91,6 +91,25 @@ Symbol *Scope::FindComponent(SourceName name) const { } } +std::optional Scope::GetName() const { + if (const auto *sym{GetSymbol()}) { + return sym->name(); + } else { + return std::nullopt; + } +} + +bool Scope::Contains(const Scope &that) const { + for (const Scope *scope{&that};; scope = &scope->parent()) { + if (*scope == *this) { + return true; + } + if (scope->IsGlobal()) { + return false; + } + } +} + const std::list &Scope::equivalenceSets() const { return equivalenceSets_; } @@ -244,8 +263,7 @@ Scope *Scope::FindScope(parser::CharBlock source) { } void Scope::AddSourceRange(const parser::CharBlock &source) { - for (auto *scope = this; !scope->IsGlobal(); - scope = &scope->parent()) { + for (auto *scope = this; !scope->IsGlobal(); scope = &scope->parent()) { scope->sourceRange_.ExtendToCover(source); } } diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index de9131d8f86b..f77a5cc8aaf1 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -960,7 +960,7 @@ std::optional CheckAccessibleComponent( CHECK(symbol.owner().IsDerivedType()); // symbol must be a component if (symbol.attrs().test(Attr::PRIVATE)) { if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) { - if (!moduleScope->sourceRange().Contains(scope.sourceRange())) { + if (!moduleScope->Contains(scope)) { return parser::MessageFormattedText{ "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US, symbol.name(), moduleScope->GetName().value()}; diff --git a/test/Semantics/resolve34.f90 b/test/Semantics/resolve34.f90 index c3b28bb929b8..d9a2a233e8d4 100644 --- a/test/Semantics/resolve34.f90 +++ b/test/Semantics/resolve34.f90 @@ -98,11 +98,16 @@ module m8 integer :: i1 integer, private :: i2 end type + type(t) :: y + integer :: a(1) contains subroutine s0 type(t) :: x x = t(i1=2, i2=5) !OK end + subroutine s1 + a = [y%i2] !OK + end subroutine end subroutine s8 use m8 @@ -111,6 +116,8 @@ subroutine s8 x = t(2, 5) !ERROR: PRIVATE component 'i2' is only accessible within module 'm8' x = t(i1=2, i2=5) + !ERROR: PRIVATE component 'i2' is only accessible within module 'm8' + a = [y%i2] end ! 7.5.4.8(2) From 38aefd41f051546024ecf4555a3bb3385b99f3c7 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 10 Mar 2020 16:32:58 -0700 Subject: [PATCH 073/345] Allow for vector subscript on LHS of assignment --- lib/Semantics/assignment.cpp | 2 +- test/Semantics/assign04.f90 | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Semantics/assignment.cpp b/lib/Semantics/assignment.cpp index bd8fe2cf0311..18f0ffcfa29f 100644 --- a/lib/Semantics/assignment.cpp +++ b/lib/Semantics/assignment.cpp @@ -73,7 +73,7 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { } if (CheckForPureContext(lhs, rhs, rhsLoc, false)) { const Scope &scope{context_.FindScope(lhsLoc)}; - if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope)}) { + if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope, true)}) { if (auto *msg{Say(lhsLoc, "Left-hand side of assignment is not modifiable"_err_en_US)}) { msg->Attach(*whyNot); diff --git a/test/Semantics/assign04.f90 b/test/Semantics/assign04.f90 index e0a02160cba5..b4214a4766f2 100644 --- a/test/Semantics/assign04.f90 +++ b/test/Semantics/assign04.f90 @@ -108,3 +108,8 @@ subroutine s7(x) x%i = [1, 2, 3] end end + +subroutine s7 + integer :: a(10), v(10) + a(v(:)) = 1 ! vector subscript is ok +end From a2e2593fa5457a7737faab8ee74e5f3b18cd71f7 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 10 Mar 2020 16:13:09 -0700 Subject: [PATCH 074/345] Extend shape analysis to cope with ASSOCIATE construct entities better Fix incomplete copy&paste Another review comment addressed --- lib/Evaluate/shape.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Evaluate/shape.cpp b/lib/Evaluate/shape.cpp index c523f304f84b..c0b59cff3c1f 100644 --- a/lib/Evaluate/shape.cpp +++ b/lib/Evaluate/shape.cpp @@ -256,6 +256,13 @@ MaybeExtentExpr GetExtent( } } } + } else if (const auto *assoc{ + symbol.detailsIf()}) { + if (auto shape{GetShape(context, assoc->expr())}) { + if (dimension < static_cast(shape->size())) { + return std::move(shape->at(dimension)); + } + } } return std::nullopt; } @@ -316,6 +323,13 @@ MaybeExtentExpr GetUpperBound( } } } + } else if (const auto *assoc{ + symbol.detailsIf()}) { + if (auto shape{GetShape(context, assoc->expr())}) { + if (dimension < static_cast(shape->size())) { + return std::move(shape->at(dimension)); + } + } } return std::nullopt; } From dc1c95277dd6161ce2d13f971ca91cce3e771e8a Mon Sep 17 00:00:00 2001 From: peter klausler Date: Wed, 11 Mar 2020 11:00:36 -0700 Subject: [PATCH 075/345] Repair C_LOC --- module/iso_c_binding.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/iso_c_binding.f90 b/module/iso_c_binding.f90 index d1cb001c2a54..a659f2fc040c 100644 --- a/module/iso_c_binding.f90 +++ b/module/iso_c_binding.f90 @@ -91,7 +91,7 @@ end function c_associated function c_loc(x) type(c_ptr) :: c_loc - type(*), dimension(:), intent(in) :: x + type(*), dimension(..), intent(in) :: x c_loc = c_ptr(loc(x)) end function c_loc From ed2e4842d31f1329d3bec802c2f284ba62343f99 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Wed, 11 Mar 2020 13:17:03 -0700 Subject: [PATCH 076/345] Resolve known problems in shape analysis --- include/flang/Evaluate/shape.h | 7 ++- lib/Evaluate/shape.cpp | 96 +++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/include/flang/Evaluate/shape.h b/include/flang/Evaluate/shape.h index c7f453c90f0b..00724c259b2c 100644 --- a/include/flang/Evaluate/shape.h +++ b/include/flang/Evaluate/shape.h @@ -53,6 +53,8 @@ std::optional AsConstantExtents( inline int GetRank(const Shape &s) { return static_cast(s.size()); } +template std::optional GetShape(FoldingContext &, const A &); + // The dimension argument to these inquiries is zero-based, // unlike the DIM= arguments to many intrinsics. ExtentExpr GetLowerBound(FoldingContext &, const NamedEntity &, int dimension); @@ -80,16 +82,13 @@ MaybeExtentExpr GetSize(Shape &&); // Utility predicate: does an expression reference any implied DO index? bool ContainsAnyImpliedDoIndex(const ExtentExpr &); -// GetShape() -template std::optional GetShape(FoldingContext &, const A &); - class GetShapeHelper : public AnyTraverse> { public: using Result = std::optional; using Base = AnyTraverse; using Base::operator(); - GetShapeHelper(FoldingContext &c) : Base{*this}, context_{c} {} + explicit GetShapeHelper(FoldingContext &c) : Base{*this}, context_{c} {} Result operator()(const ImpliedDoIndex &) const { return Scalar(); } Result operator()(const DescriptorInquiry &) const { return Scalar(); } diff --git a/lib/Evaluate/shape.cpp b/lib/Evaluate/shape.cpp index c0b59cff3c1f..f27ee5814075 100644 --- a/lib/Evaluate/shape.cpp +++ b/lib/Evaluate/shape.cpp @@ -177,52 +177,84 @@ bool ContainsAnyImpliedDoIndex(const ExtentExpr &expr) { return MyVisitor{}(expr); } -ExtentExpr GetLowerBound( - FoldingContext &context, const NamedEntity &base, int dimension) { - const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())}; +// Determines lower bound on a dimension. This can be other than 1 only +// for a reference to a whole array object or component. (See LBOUND, 16.9.109). +// ASSOCIATE construct entities may require tranversal of their referents. +class GetLowerBoundHelper : public Traverse { +public: + using Result = ExtentExpr; + using Base = Traverse; + using Base::operator(); + GetLowerBoundHelper(FoldingContext &c, int d) + : Base{*this}, context_{c}, dimension_{d} {} + static ExtentExpr Default() { return ExtentExpr{1}; } + static ExtentExpr Combine(Result &&, Result &&) { return Default(); } + ExtentExpr operator()(const Symbol &); + ExtentExpr operator()(const Component &); + +private: + FoldingContext &context_; + int dimension_; +}; + +auto GetLowerBoundHelper::operator()(const Symbol &symbol0) -> Result { + const Symbol &symbol{symbol0.GetUltimate()}; if (const auto *details{symbol.detailsIf()}) { int j{0}; for (const auto &shapeSpec : details->shape()) { - if (j++ == dimension) { + if (j++ == dimension_) { if (const auto &bound{shapeSpec.lbound().GetExplicit()}) { - return Fold(context, common::Clone(*bound)); + return Fold(context_, common::Clone(*bound)); } else if (semantics::IsDescriptor(symbol)) { - return ExtentExpr{DescriptorInquiry{ - base, DescriptorInquiry::Field::LowerBound, dimension}}; + return ExtentExpr{DescriptorInquiry{NamedEntity{symbol0}, + DescriptorInquiry::Field::LowerBound, dimension_}}; } else { break; } } } + } else if (const auto *assoc{ + symbol.detailsIf()}) { + return (*this)(assoc->expr()); } - // When we don't know that we don't know the lower bound at compilation - // time, then we do know it, and it's one. (See LBOUND, 16.9.109). - return ExtentExpr{1}; + return Default(); } -Shape GetLowerBounds(FoldingContext &context, const NamedEntity &base) { - const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())}; - Shape result; - if (const auto *details{symbol.detailsIf()}) { - int dim{0}; - for (const auto &shapeSpec : details->shape()) { - if (const auto &bound{shapeSpec.lbound().GetExplicit()}) { - result.emplace_back(Fold(context, common::Clone(*bound))); - } else if (semantics::IsDescriptor(symbol)) { - result.emplace_back(ExtentExpr{DescriptorInquiry{ - base, DescriptorInquiry::Field::LowerBound, dim}}); - } else { - result.emplace_back(std::nullopt); +auto GetLowerBoundHelper::operator()(const Component &component) -> Result { + if (component.base().Rank() == 0) { + const Symbol &symbol{component.GetLastSymbol().GetUltimate()}; + if (const auto *details{ + symbol.detailsIf()}) { + int j{0}; + for (const auto &shapeSpec : details->shape()) { + if (j++ == dimension_) { + if (const auto &bound{shapeSpec.lbound().GetExplicit()}) { + return Fold(context_, common::Clone(*bound)); + } else if (semantics::IsDescriptor(symbol)) { + return ExtentExpr{ + DescriptorInquiry{NamedEntity{common::Clone(component)}, + DescriptorInquiry::Field::LowerBound, dimension_}}; + } else { + break; + } + } } - ++dim; - } - } else { - int rank{base.Rank()}; - for (int dim{0}; dim < rank; ++dim) { - result.emplace_back(ExtentExpr{1}); } } - CHECK(GetRank(result) == symbol.Rank()); + return Default(); +} + +ExtentExpr GetLowerBound( + FoldingContext &context, const NamedEntity &base, int dimension) { + return GetLowerBoundHelper{context, dimension}(base); +} + +Shape GetLowerBounds(FoldingContext &context, const NamedEntity &base) { + Shape result; + int rank{base.Rank()}; + for (int dim{0}; dim < rank; ++dim) { + result.emplace_back(GetLowerBound(context, base, dim)); + } return result; } @@ -327,7 +359,9 @@ MaybeExtentExpr GetUpperBound( symbol.detailsIf()}) { if (auto shape{GetShape(context, assoc->expr())}) { if (dimension < static_cast(shape->size())) { - return std::move(shape->at(dimension)); + return ComputeUpperBound(context, + GetLowerBound(context, base, dimension), + std::move(shape->at(dimension))); } } } From 50406b349609efdde76e48bf2caa039d031dd1c4 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 13 Feb 2020 14:41:56 -0800 Subject: [PATCH 077/345] Use hash table for UnitMap, avoid C++ STL binary dependence Scan FORMAT strings locally to avoid C++ binary runtime dependence when computing deepest parenthesis nesting Remove a dependency on ostream from runtime Remove remaining direct external references from runtime to C++ library binaries Remove runtime dependences on lib/common SetPos() and SetRec() Instantiate templates for input Begin input; rearrange locking, deal with CLOSE races View() Update error message in test to agree with compiler change First cut at real input More robust I/O runtime error handling Debugging of REAL input Add iostat.{h,cpp} Rename runtime/numeric-* to runtime/edit-* Move templates around, templatize integer output editing Move LOGICAL and CHARACTER output from io-api.cpp to edit-output.cpp Change pointer argument to reference More list-directed input Complex list-directed input Use enum class Direction rather than bool for templates Catch up with changes to master Undo reformatting of Lower code Use record number instead of subscripts for internal unit Unformatted sequential backspace Testing and debugging Dodge bogus GCC warning Add for std::size_t to fix CI build Address review comments --- documentation/IORuntimeInternals.md | 9 +- include/flang/Parser/char-buffer.h | 2 +- include/flang/Semantics/type.h | 4 +- lib/Parser/char-buffer.cpp | 6 +- lib/Parser/source.cpp | 2 +- lib/Semantics/check-call.cpp | 7 +- runtime/CMakeLists.txt | 6 +- runtime/ISO_Fortran_binding.cpp | 5 +- runtime/buffer.h | 18 +- runtime/connection.cpp | 18 +- runtime/connection.h | 17 +- runtime/descriptor.cpp | 164 +++++-- runtime/descriptor.h | 57 ++- runtime/edit-input.cpp | 428 ++++++++++++++++ runtime/edit-input.h | 40 ++ runtime/{numeric-output.h => edit-output.cpp} | 298 +++++++++--- runtime/edit-output.h | 111 +++++ runtime/file.cpp | 49 +- runtime/file.h | 4 - runtime/format-implementation.h | 110 +++-- runtime/format.cpp | 32 +- runtime/format.h | 25 +- runtime/internal-unit.cpp | 144 +++--- runtime/internal-unit.h | 23 +- runtime/io-api.cpp | 456 ++++++++++++------ runtime/io-api.h | 27 +- runtime/io-error.cpp | 71 ++- runtime/io-error.h | 22 +- runtime/io-stmt.cpp | 440 +++++++++++++---- runtime/io-stmt.h | 204 ++++---- runtime/iostat.cpp | 31 ++ runtime/iostat.h | 53 ++ runtime/lock.h | 3 +- runtime/magic-numbers.h | 2 +- runtime/main.cpp | 4 +- runtime/numeric-output.cpp | 153 ------ runtime/terminator.cpp | 10 + runtime/terminator.h | 4 + runtime/tools.cpp | 27 +- runtime/tools.h | 8 +- runtime/transformational.cpp | 49 +- runtime/transformational.h | 7 +- runtime/type-code.h | 2 +- runtime/unit-map.cpp | 72 +++ runtime/unit-map.h | 87 ++++ runtime/unit.cpp | 376 ++++++++++++--- runtime/unit.h | 49 +- test/Evaluate/reshape.cpp | 14 +- test/Runtime/CMakeLists.txt | 17 + test/Runtime/format.cpp | 40 +- test/Runtime/hello.cpp | 87 ++-- test/Runtime/list-input.cpp | 68 +++ test/Runtime/testing.cpp | 43 ++ test/Runtime/testing.h | 13 + test/Semantics/call15.f90 | 2 +- 55 files changed, 2953 insertions(+), 1067 deletions(-) create mode 100644 runtime/edit-input.cpp create mode 100644 runtime/edit-input.h rename runtime/{numeric-output.h => edit-output.cpp} (56%) create mode 100644 runtime/edit-output.h create mode 100644 runtime/iostat.cpp create mode 100644 runtime/iostat.h delete mode 100644 runtime/numeric-output.cpp create mode 100644 runtime/unit-map.cpp create mode 100644 runtime/unit-map.h create mode 100644 test/Runtime/list-input.cpp create mode 100644 test/Runtime/testing.cpp create mode 100644 test/Runtime/testing.h diff --git a/documentation/IORuntimeInternals.md b/documentation/IORuntimeInternals.md index 70dd0941ac76..c9b1ce4078ec 100644 --- a/documentation/IORuntimeInternals.md +++ b/documentation/IORuntimeInternals.md @@ -273,12 +273,13 @@ A Narrative Overview Of `PRINT *, 'HELLO, WORLD'` ================================================= 1. When the compiled Fortran program begins execution at the `main()` entry point exported from its main program, it calls `ProgramStart()` -with its arguments and environment. `ProgramStart()` calls -`ExternalFileUnit::InitializePredefinedUnits()` to create and -initialize Fortran units 5 and 6 and connect them with the -standard input and output file descriptors (respectively). +with its arguments and environment. 1. The generated code calls `BeginExternalListOutput()` to start the sequence of calls that implement the `PRINT` statement. +Since the Fortran runtime I/O library has not yet been used in +this process, its data structures are initialized on this +first call, and Fortran I/O units 5 and 6 are connected with +the stadard input and output file descriptors (respectively). The default unit code is converted to 6 and passed to `ExternalFileUnit::LookUpOrCrash()`, which returns a reference to unit 6's instance. diff --git a/include/flang/Parser/char-buffer.h b/include/flang/Parser/char-buffer.h index a62659b7c69d..0df690af3aa7 100644 --- a/include/flang/Parser/char-buffer.h +++ b/include/flang/Parser/char-buffer.h @@ -47,7 +47,7 @@ class CharBuffer { lastBlockEmpty_ = false; } - char *FreeSpace(std::size_t *); + char *FreeSpace(std::size_t &); void Claim(std::size_t); // The return value is the byte offset of the new data, diff --git a/include/flang/Semantics/type.h b/include/flang/Semantics/type.h index 935c8dbf7949..85930d334ebb 100644 --- a/include/flang/Semantics/type.h +++ b/include/flang/Semantics/type.h @@ -327,9 +327,7 @@ class DeclTypeSpec { bool IsUnlimitedPolymorphic() const { return category_ == TypeStar || category_ == ClassStar; } - bool IsAssumedType() const { - return category_ == TypeStar; - } + bool IsAssumedType() const { return category_ == TypeStar; } bool IsNumeric(TypeCategory) const; const NumericTypeSpec &numericTypeSpec() const; const LogicalTypeSpec &logicalTypeSpec() const; diff --git a/lib/Parser/char-buffer.cpp b/lib/Parser/char-buffer.cpp index 655dd3173d7f..4f72a7bdac31 100644 --- a/lib/Parser/char-buffer.cpp +++ b/lib/Parser/char-buffer.cpp @@ -14,7 +14,7 @@ namespace Fortran::parser { -char *CharBuffer::FreeSpace(std::size_t *n) { +char *CharBuffer::FreeSpace(std::size_t &n) { int offset{LastBlockOffset()}; if (blocks_.empty()) { blocks_.emplace_front(); @@ -24,7 +24,7 @@ char *CharBuffer::FreeSpace(std::size_t *n) { last_ = blocks_.emplace_after(last_); lastBlockEmpty_ = true; } - *n = Block::capacity - offset; + n = Block::capacity - offset; return last_->data + offset; } @@ -38,7 +38,7 @@ void CharBuffer::Claim(std::size_t n) { std::size_t CharBuffer::Put(const char *data, std::size_t n) { std::size_t chunk; for (std::size_t at{0}; at < n; at += chunk) { - char *to{FreeSpace(&chunk)}; + char *to{FreeSpace(chunk)}; chunk = std::min(n - at, chunk); Claim(chunk); std::memcpy(to, data + at, chunk); diff --git a/lib/Parser/source.cpp b/lib/Parser/source.cpp index 0fbb8e3df1b1..e1bbfcac765c 100644 --- a/lib/Parser/source.cpp +++ b/lib/Parser/source.cpp @@ -200,7 +200,7 @@ bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) { CharBuffer buffer; while (true) { std::size_t count; - char *to{buffer.FreeSpace(&count)}; + char *to{buffer.FreeSpace(count)}; ssize_t got{read(fileDescriptor_, to, count)}; if (got < 0) { *error << "could not read " << errorPath << ": " << std::strerror(errno); diff --git a/lib/Semantics/check-call.cpp b/lib/Semantics/check-call.cpp index 3273ed22da40..88997874bb97 100644 --- a/lib/Semantics/check-call.cpp +++ b/lib/Semantics/check-call.cpp @@ -613,16 +613,13 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg, const Symbol &assumed{DEREF(arg.GetAssumedTypeDummy())}; if (!object.type.type().IsAssumedType()) { messages.Say( - "Assumed-type TYPE(*) '%s' may be associated only with an" - " assumed-TYPE(*) %s"_err_en_US, + "Assumed-type '%s' may be associated only with an assumed-type %s"_err_en_US, assumed.name(), dummyName); } else if (const auto *details{ assumed.detailsIf()}) { if (!(details->IsAssumedShape() || details->IsAssumedRank())) { messages.Say( // C711 - "Assumed-type TYPE(*) '%s' must be either assumed " - "shape or assumed rank to be associated with TYPE(*) " - "%s"_err_en_US, + "Assumed-type '%s' must be either assumed shape or assumed rank to be associated with assumed-type %s"_err_en_US, assumed.name(), dummyName); } } diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 571775ce8984..ddd77f71fdbe 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -12,25 +12,27 @@ add_library(FortranRuntime connection.cpp derived-type.cpp descriptor.cpp + edit-input.cpp + edit-output.cpp environment.cpp file.cpp format.cpp internal-unit.cpp + iostat.cpp io-api.cpp io-error.cpp io-stmt.cpp main.cpp memory.cpp - numeric-output.cpp stop.cpp terminator.cpp tools.cpp transformational.cpp type-code.cpp unit.cpp + unit-map.cpp ) target_link_libraries(FortranRuntime - FortranCommon FortranDecimal ) diff --git a/runtime/ISO_Fortran_binding.cpp b/runtime/ISO_Fortran_binding.cpp index bcb0d055a740..1ebaa5858349 100644 --- a/runtime/ISO_Fortran_binding.cpp +++ b/runtime/ISO_Fortran_binding.cpp @@ -11,6 +11,7 @@ #include "../include/flang/ISO_Fortran_binding.h" #include "descriptor.h" +#include namespace Fortran::ISO { extern "C" { @@ -75,7 +76,7 @@ int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[], dim->sm = byteSize; byteSize *= extent; } - void *p{new char[byteSize]}; + void *p{std::malloc(byteSize)}; if (!p) { return CFI_ERROR_MEM_ALLOCATION; } @@ -99,7 +100,7 @@ int CFI_deallocate(CFI_cdesc_t *descriptor) { if (!descriptor->base_addr) { return CFI_ERROR_BASE_ADDR_NULL; } - delete[] static_cast(descriptor->base_addr); + std::free(descriptor->base_addr); descriptor->base_addr = nullptr; return CFI_SUCCESS; } diff --git a/runtime/buffer.h b/runtime/buffer.h index a956a3bbae1d..63bbbdc26c02 100644 --- a/runtime/buffer.h +++ b/runtime/buffer.h @@ -43,6 +43,7 @@ template class FileFrame { std::size_t FrameLength() const { return std::min(length_ - frame_, size_ - (start_ + frame_)); } + std::size_t BytesBufferedBeforeFrame() const { return frame_ - start_; } // Returns a short frame at a non-fatal EOF. Can return a long frame as well. std::size_t ReadFrame( @@ -52,10 +53,10 @@ template class FileFrame { if (at < fileOffset_ || at > fileOffset_ + length_) { Reset(at); } - frame_ = static_cast(at - fileOffset_); - if (start_ + frame_ + bytes > size_) { + frame_ = at - fileOffset_; + if (static_cast(start_ + frame_ + bytes) > size_) { DiscardLeadingBytes(frame_, handler); - if (start_ + bytes > size_) { + if (static_cast(start_ + bytes) > size_) { // Frame would wrap around; shift current data (if any) to force // contiguity. RUNTIME_CHECK(handler, length_ < size_); @@ -90,7 +91,8 @@ template class FileFrame { void WriteFrame(FileOffset at, std::size_t bytes, IoErrorHandler &handler) { if (!dirty_ || at < fileOffset_ || at > fileOffset_ + length_ || - start_ + (at - fileOffset_) + bytes > size_) { + start_ + (at - fileOffset_) + static_cast(bytes) > + size_) { Flush(handler); fileOffset_ = at; Reallocate(bytes, handler); @@ -120,11 +122,11 @@ template class FileFrame { private: STORE &Store() { return static_cast(*this); } - void Reallocate(std::size_t bytes, const Terminator &terminator) { + void Reallocate(std::int64_t bytes, const Terminator &terminator) { if (bytes > size_) { char *old{buffer_}; auto oldSize{size_}; - size_ = std::max(bytes, minBuffer); + size_ = std::max(bytes, minBuffer); buffer_ = reinterpret_cast(AllocateMemoryOrCrash(terminator, size_)); auto chunk{std::min(length_, oldSize - start_)}; @@ -141,7 +143,7 @@ template class FileFrame { dirty_ = false; } - void DiscardLeadingBytes(std::size_t n, const Terminator &terminator) { + void DiscardLeadingBytes(std::int64_t n, const Terminator &terminator) { RUNTIME_CHECK(terminator, length_ >= n); length_ -= n; if (length_ == 0) { @@ -163,7 +165,7 @@ template class FileFrame { static constexpr std::size_t minBuffer{64 << 10}; char *buffer_{nullptr}; - std::size_t size_{0}; // current allocated buffer size + std::int64_t size_{0}; // current allocated buffer size FileOffset fileOffset_{0}; // file offset corresponding to buffer valid data std::int64_t start_{0}; // buffer_[] offset of valid data std::int64_t length_{0}; // valid data length (can wrap) diff --git a/runtime/connection.cpp b/runtime/connection.cpp index ff15a40819ab..d206b050aee4 100644 --- a/runtime/connection.cpp +++ b/runtime/connection.cpp @@ -12,8 +12,20 @@ namespace Fortran::runtime::io { std::size_t ConnectionState::RemainingSpaceInRecord() const { - return recordLength.value_or( - executionEnvironment.listDirectedOutputLineLengthLimit) - - positionInRecord; + auto recl{recordLength.value_or( + executionEnvironment.listDirectedOutputLineLengthLimit)}; + return positionInRecord >= recl ? 0 : recl - positionInRecord; +} + +bool ConnectionState::IsAtEOF() const { + return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber; +} + +void ConnectionState::HandleAbsolutePosition(std::int64_t n) { + positionInRecord = std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0); +} + +void ConnectionState::HandleRelativePosition(std::int64_t n) { + positionInRecord = std::max(leftTabLimit.value_or(0), positionInRecord + n); } } diff --git a/runtime/connection.h b/runtime/connection.h index 85372dfa610d..1c0716145b47 100644 --- a/runtime/connection.h +++ b/runtime/connection.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// Fortran I/O connection state (internal & external) +// Fortran I/O connection state (abstracted over internal & external units) #ifndef FORTRAN_RUNTIME_IO_CONNECTION_H_ #define FORTRAN_RUNTIME_IO_CONNECTION_H_ @@ -17,6 +17,7 @@ namespace Fortran::runtime::io { +enum class Direction { Output, Input }; enum class Access { Sequential, Direct, Stream }; inline bool IsRecordFile(Access a) { return a != Access::Stream; } @@ -25,24 +26,30 @@ inline bool IsRecordFile(Access a) { return a != Access::Stream; } // established in an OPEN statement. struct ConnectionAttributes { Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' - std::optional recordLength; // RECL= when fixed-length + std::optional recordLength; // RECL= when fixed-length bool isUnformatted{false}; // FORM='UNFORMATTED' bool isUTF8{false}; // ENCODING='UTF-8' }; struct ConnectionState : public ConnectionAttributes { + bool IsAtEOF() const; // true when read has hit EOF or endfile record std::size_t RemainingSpaceInRecord() const; - // Positions in a record file (sequential or direct, but not stream) - std::int64_t recordOffsetInFile{0}; + void HandleAbsolutePosition(std::int64_t); + void HandleRelativePosition(std::int64_t); + + // Positions in a record file (sequential or direct, not stream) std::int64_t currentRecordNumber{1}; // 1 is first std::int64_t positionInRecord{0}; // offset in current record std::int64_t furthestPositionInRecord{0}; // max(positionInRecord) bool nonAdvancing{false}; // ADVANCE='NO' + // Set at end of non-advancing I/O data transfer std::optional leftTabLimit; // offset in current record + // currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement - // on a sequential access file + // or an end-of-file READ condition on a sequential access file std::optional endfileRecordNumber; + // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP }; diff --git a/runtime/descriptor.cpp b/runtime/descriptor.cpp index 9e91c1b80199..00a57cd1e216 100644 --- a/runtime/descriptor.cpp +++ b/runtime/descriptor.cpp @@ -7,7 +7,8 @@ //===----------------------------------------------------------------------===// #include "descriptor.h" -#include "flang/Common/idioms.h" +#include "memory.h" +#include "terminator.h" #include #include #include @@ -27,11 +28,13 @@ Descriptor::~Descriptor() { void Descriptor::Establish(TypeCode t, std::size_t elementBytes, void *p, int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute, bool addendum) { - CHECK(ISO::CFI_establish(&raw_, p, attribute, t.raw(), elementBytes, rank, - extent) == CFI_SUCCESS); + Terminator terminator{__FILE__, __LINE__}; + RUNTIME_CHECK(terminator, + ISO::CFI_establish(&raw_, p, attribute, t.raw(), elementBytes, rank, + extent) == CFI_SUCCESS); raw_.f18Addendum = addendum; DescriptorAddendum *a{Addendum()}; - CHECK(addendum == (a != nullptr)); + RUNTIME_CHECK(terminator, addendum == (a != nullptr)); if (a) { new (a) DescriptorAddendum{}; } @@ -44,11 +47,13 @@ void Descriptor::Establish(TypeCategory c, int kind, void *p, int rank, if (c == TypeCategory::Complex) { elementBytes *= 2; } - CHECK(ISO::CFI_establish(&raw_, p, attribute, TypeCode(c, kind).raw(), - elementBytes, rank, extent) == CFI_SUCCESS); + Terminator terminator{__FILE__, __LINE__}; + RUNTIME_CHECK(terminator, + ISO::CFI_establish(&raw_, p, attribute, TypeCode(c, kind).raw(), + elementBytes, rank, extent) == CFI_SUCCESS); raw_.f18Addendum = addendum; DescriptorAddendum *a{Addendum()}; - CHECK(addendum == (a != nullptr)); + RUNTIME_CHECK(terminator, addendum == (a != nullptr)); if (a) { new (a) DescriptorAddendum{}; } @@ -56,41 +61,45 @@ void Descriptor::Establish(TypeCategory c, int kind, void *p, int rank, void Descriptor::Establish(const DerivedType &dt, void *p, int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) { - CHECK(ISO::CFI_establish(&raw_, p, attribute, CFI_type_struct, - dt.SizeInBytes(), rank, extent) == CFI_SUCCESS); + Terminator terminator{__FILE__, __LINE__}; + RUNTIME_CHECK(terminator, + ISO::CFI_establish(&raw_, p, attribute, CFI_type_struct, dt.SizeInBytes(), + rank, extent) == CFI_SUCCESS); raw_.f18Addendum = true; DescriptorAddendum *a{Addendum()}; - CHECK(a); + RUNTIME_CHECK(terminator, a); new (a) DescriptorAddendum{&dt}; } -std::unique_ptr Descriptor::Create(TypeCode t, - std::size_t elementBytes, void *p, int rank, const SubscriptValue *extent, +OwningPtr Descriptor::Create(TypeCode t, std::size_t elementBytes, + void *p, int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) { std::size_t bytes{SizeInBytes(rank, true)}; - Descriptor *result{reinterpret_cast(new char[bytes])}; - CHECK(result); + Terminator terminator{__FILE__, __LINE__}; + Descriptor *result{ + reinterpret_cast(AllocateMemoryOrCrash(terminator, bytes))}; result->Establish(t, elementBytes, p, rank, extent, attribute, true); - return std::unique_ptr{result}; + return OwningPtr{result}; } -std::unique_ptr Descriptor::Create(TypeCategory c, int kind, - void *p, int rank, const SubscriptValue *extent, - ISO::CFI_attribute_t attribute) { +OwningPtr Descriptor::Create(TypeCategory c, int kind, void *p, + int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) { std::size_t bytes{SizeInBytes(rank, true)}; - Descriptor *result{reinterpret_cast(new char[bytes])}; - CHECK(result); + Terminator terminator{__FILE__, __LINE__}; + Descriptor *result{ + reinterpret_cast(AllocateMemoryOrCrash(terminator, bytes))}; result->Establish(c, kind, p, rank, extent, attribute, true); - return std::unique_ptr{result}; + return OwningPtr{result}; } -std::unique_ptr Descriptor::Create(const DerivedType &dt, void *p, +OwningPtr Descriptor::Create(const DerivedType &dt, void *p, int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) { std::size_t bytes{SizeInBytes(rank, true, dt.lenParameters())}; - Descriptor *result{reinterpret_cast(new char[bytes])}; - CHECK(result); + Terminator terminator{__FILE__, __LINE__}; + Descriptor *result{ + reinterpret_cast(AllocateMemoryOrCrash(terminator, bytes))}; result->Establish(dt, p, rank, extent, attribute); - return std::unique_ptr{result}; + return OwningPtr{result}; } std::size_t Descriptor::SizeInBytes() const { @@ -141,42 +150,103 @@ void Descriptor::Destroy(char *data, bool finalize) const { } } +bool Descriptor::IncrementSubscripts( + SubscriptValue *subscript, const int *permutation) const { + for (int j{0}; j < raw_.rank; ++j) { + int k{permutation ? permutation[j] : j}; + const Dimension &dim{GetDimension(k)}; + if (subscript[k]++ < dim.UpperBound()) { + return true; + } + subscript[k] = dim.LowerBound(); + } + return false; +} + +bool Descriptor::DecrementSubscripts( + SubscriptValue *subscript, const int *permutation) const { + for (int j{raw_.rank - 1}; j >= 0; --j) { + int k{permutation ? permutation[j] : j}; + const Dimension &dim{GetDimension(k)}; + if (--subscript[k] >= dim.LowerBound()) { + return true; + } + subscript[k] = dim.UpperBound(); + } + return false; +} + +std::size_t Descriptor::ZeroBasedElementNumber( + const SubscriptValue *subscript, const int *permutation) const { + std::size_t result{0}; + std::size_t coefficient{1}; + for (int j{0}; j < raw_.rank; ++j) { + int k{permutation ? permutation[j] : j}; + const Dimension &dim{GetDimension(k)}; + result += coefficient * (subscript[k] - dim.LowerBound()); + coefficient *= dim.Extent(); + } + return result; +} + +bool Descriptor::SubscriptsForZeroBasedElementNumber(SubscriptValue *subscript, + std::size_t elementNumber, const int *permutation) const { + std::size_t coefficient{1}; + std::size_t dimCoefficient[maxRank]; + for (int j{0}; j < raw_.rank; ++j) { + int k{permutation ? permutation[j] : j}; + const Dimension &dim{GetDimension(k)}; + dimCoefficient[j] = coefficient; + coefficient *= dim.Extent(); + } + if (elementNumber >= coefficient) { + return false; // out of range + } + for (int j{raw_.rank - 1}; j >= 0; --j) { + int k{permutation ? permutation[j] : j}; + const Dimension &dim{GetDimension(k)}; + std::size_t quotient{j ? elementNumber / dimCoefficient[j] : 0}; + subscript[k] = + dim.LowerBound() + elementNumber - dimCoefficient[j] * quotient; + elementNumber = quotient; + } + return true; +} + void Descriptor::Check() const { // TODO } -std::ostream &Descriptor::Dump(std::ostream &o) const { - o << "Descriptor @ 0x" << std::hex << reinterpret_cast(this) - << std::dec << ":\n"; - o << " base_addr 0x" << std::hex - << reinterpret_cast(raw_.base_addr) << std::dec << '\n'; - o << " elem_len " << raw_.elem_len << '\n'; - o << " version " << raw_.version - << (raw_.version == CFI_VERSION ? "(ok)" : "BAD!") << '\n'; - o << " rank " << static_cast(raw_.rank) << '\n'; - o << " type " << static_cast(raw_.type) << '\n'; - o << " attribute " << static_cast(raw_.attribute) << '\n'; - o << " addendum? " << static_cast(raw_.f18Addendum) << '\n'; +void Descriptor::Dump(FILE *f) const { + std::fprintf(f, "Descriptor @ %p:\n", reinterpret_cast(this)); + std::fprintf(f, " base_addr %p\n", raw_.base_addr); + std::fprintf(f, " elem_len %zd\n", static_cast(raw_.elem_len)); + std::fprintf(f, " version %d\n", static_cast(raw_.version)); + std::fprintf(f, " rank %d\n", static_cast(raw_.rank)); + std::fprintf(f, " type %d\n", static_cast(raw_.type)); + std::fprintf(f, " attribute %d\n", static_cast(raw_.attribute)); + std::fprintf(f, " addendum %d\n", static_cast(raw_.f18Addendum)); for (int j{0}; j < raw_.rank; ++j) { - o << " dim[" << j << "] lower_bound " << raw_.dim[j].lower_bound << '\n'; - o << " extent " << raw_.dim[j].extent << '\n'; - o << " sm " << raw_.dim[j].sm << '\n'; + std::fprintf(f, " dim[%d] lower_bound %jd\n", j, + static_cast(raw_.dim[j].lower_bound)); + std::fprintf(f, " extent %jd\n", + static_cast(raw_.dim[j].extent)); + std::fprintf(f, " sm %jd\n", + static_cast(raw_.dim[j].sm)); } if (const DescriptorAddendum * addendum{Addendum()}) { - addendum->Dump(o); + addendum->Dump(f); } - return o; } std::size_t DescriptorAddendum::SizeInBytes() const { return SizeInBytes(LenParameters()); } -std::ostream &DescriptorAddendum::Dump(std::ostream &o) const { - o << " derivedType @ 0x" << std::hex - << reinterpret_cast(derivedType_) << std::dec << '\n'; - o << " flags " << flags_ << '\n'; +void DescriptorAddendum::Dump(FILE *f) const { + std::fprintf( + f, " derivedType @ %p\n", reinterpret_cast(derivedType_)); + std::fprintf(f, " flags 0x%jx\n", static_cast(flags_)); // TODO: LEN parameter values - return o; } } diff --git a/runtime/descriptor.h b/runtime/descriptor.h index bb8a428c83ec..8f7a88e10731 100644 --- a/runtime/descriptor.h +++ b/runtime/descriptor.h @@ -19,14 +19,14 @@ // but should never reference this internal header. #include "derived-type.h" +#include "memory.h" #include "type-code.h" #include "flang/ISO_Fortran_binding.h" #include #include #include +#include #include -#include -#include namespace Fortran::runtime { @@ -93,7 +93,7 @@ class DescriptorAddendum { len_[which] = x; } - std::ostream &Dump(std::ostream &) const; + void Dump(FILE * = stdout) const; private: const DerivedType *derivedType_{nullptr}; @@ -141,17 +141,15 @@ class Descriptor { const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); - static std::unique_ptr Create(TypeCode t, - std::size_t elementBytes, void *p = nullptr, int rank = maxRank, - const SubscriptValue *extent = nullptr, - ISO::CFI_attribute_t attribute = CFI_attribute_other); - static std::unique_ptr Create(TypeCategory, int kind, + static OwningPtr Create(TypeCode t, std::size_t elementBytes, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); - static std::unique_ptr Create(const DerivedType &dt, - void *p = nullptr, int rank = maxRank, - const SubscriptValue *extent = nullptr, + static OwningPtr Create(TypeCategory, int kind, void *p = nullptr, + int rank = maxRank, const SubscriptValue *extent = nullptr, + ISO::CFI_attribute_t attribute = CFI_attribute_other); + static OwningPtr Create(const DerivedType &dt, void *p = nullptr, + int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); ISO::CFI_cdesc_t &raw() { return raw_; } @@ -192,33 +190,42 @@ class Descriptor { return offset; } - template A *Element(std::size_t offset) const { + template A *OffsetElement(std::size_t offset) const { return reinterpret_cast( reinterpret_cast(raw_.base_addr) + offset); } template A *Element(const SubscriptValue *subscript) const { - return Element(SubscriptsToByteOffset(subscript)); + return OffsetElement(SubscriptsToByteOffset(subscript)); } - void GetLowerBounds(SubscriptValue *subscript) const { - for (int j{0}; j < raw_.rank; ++j) { - subscript[j] = GetDimension(j).LowerBound(); + template A *ZeroBasedIndexedElement(std::size_t n) const { + SubscriptValue at[maxRank]; + if (SubscriptsForZeroBasedElementNumber(at, n)) { + return Element(at); } + return nullptr; } - void IncrementSubscripts( - SubscriptValue *subscript, const int *permutation = nullptr) const { + void GetLowerBounds(SubscriptValue *subscript) const { for (int j{0}; j < raw_.rank; ++j) { - int k{permutation ? permutation[j] : j}; - const Dimension &dim{GetDimension(k)}; - if (subscript[k]++ < dim.UpperBound()) { - break; - } - subscript[k] = dim.LowerBound(); + subscript[j] = GetDimension(j).LowerBound(); } } + // When the passed subscript vector contains the last (or first) + // subscripts of the array, these wrap the subscripts around to + // their first (or last) values and return false. + bool IncrementSubscripts( + SubscriptValue *, const int *permutation = nullptr) const; + bool DecrementSubscripts( + SubscriptValue *, const int *permutation = nullptr) const; + // False when out of range. + bool SubscriptsForZeroBasedElementNumber(SubscriptValue *, + std::size_t elementNumber, const int *permutation = nullptr) const; + std::size_t ZeroBasedElementNumber( + const SubscriptValue *, const int *permutation = nullptr) const; + DescriptorAddendum *Addendum() { if (raw_.f18Addendum != 0) { return reinterpret_cast(&GetDimension(rank())); @@ -270,7 +277,7 @@ class Descriptor { // TODO: creation of array sections - std::ostream &Dump(std::ostream &) const; + void Dump(FILE * = stdout) const; private: ISO::CFI_cdesc_t raw_; diff --git a/runtime/edit-input.cpp b/runtime/edit-input.cpp new file mode 100644 index 000000000000..5b7884021067 --- /dev/null +++ b/runtime/edit-input.cpp @@ -0,0 +1,428 @@ +//===-- runtime/edit-input.cpp ----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "edit-input.h" +#include "flang/Common/real.h" +#include "flang/Common/uint128.h" + +namespace Fortran::runtime::io { + +static bool EditBOZInput(IoStatementState &io, const DataEdit &edit, void *n, + int base, int totalBitSize) { + std::optional remaining; + if (edit.width) { + remaining = std::max(0, *edit.width); + } + io.SkipSpaces(remaining); + std::optional next{io.NextInField(remaining)}; + common::UnsignedInt128 value{0}; + for (; next; next = io.NextInField(remaining)) { + char32_t ch{*next}; + if (ch == ' ') { + continue; + } + int digit{0}; + if (ch >= '0' && ch <= '1') { + digit = ch - '0'; + } else if (base >= 8 && ch >= '2' && ch <= '7') { + digit = ch - '0'; + } else if (base >= 10 && ch >= '8' && ch <= '9') { + digit = ch - '0'; + } else if (base == 16 && ch >= 'A' && ch <= 'Z') { + digit = ch + 10 - 'A'; + } else if (base == 16 && ch >= 'a' && ch <= 'z') { + digit = ch + 10 - 'a'; + } else { + io.GetIoErrorHandler().SignalError( + "Bad character '%lc' in B/O/Z input field", ch); + return false; + } + value *= base; + value += digit; + } + // TODO: check for overflow + std::memcpy(n, &value, totalBitSize >> 3); + return true; +} + +// Returns false if there's a '-' sign +static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit, + std::optional &next, std::optional &remaining) { + if (edit.descriptor != DataEdit::ListDirected && edit.width) { + remaining = std::max(0, *edit.width); + } else { + // list-directed, namelist, or (nonstandard) 0-width input editing + remaining.reset(); + } + io.SkipSpaces(remaining); + next = io.NextInField(remaining); + bool negative{false}; + if (next) { + negative = *next == '-'; + if (negative || *next == '+') { + next = io.NextInField(remaining); + } + } + return negative; +} + +bool EditIntegerInput( + IoStatementState &io, const DataEdit &edit, void *n, int kind) { + RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1))); + switch (edit.descriptor) { + case DataEdit::ListDirected: + case 'G': + case 'I': break; + case 'B': return EditBOZInput(io, edit, n, 2, kind << 3); + case 'O': return EditBOZInput(io, edit, n, 8, kind << 3); + case 'Z': return EditBOZInput(io, edit, n, 16, kind << 3); + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used with an INTEGER data item", + edit.descriptor); + return false; + } + std::optional remaining; + std::optional next; + bool negate{ScanNumericPrefix(io, edit, next, remaining)}; + common::UnsignedInt128 value; + for (; next; next = io.NextInField(remaining)) { + char32_t ch{*next}; + if (ch == ' ') { + if (edit.modes.editingFlags & blankZero) { + ch = '0'; // BZ mode - treat blank as if it were zero + } else { + continue; + } + } + int digit{0}; + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else { + io.GetIoErrorHandler().SignalError( + "Bad character '%lc' in INTEGER input field", ch); + return false; + } + value *= 10; + value += digit; + } + if (negate) { + value = -value; + } + std::memcpy(n, &value, kind); + return true; +} + +static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io, + const DataEdit &edit, int &exponent) { + std::optional remaining; + std::optional next; + int got{0}; + std::optional decimalPoint; + if (ScanNumericPrefix(io, edit, next, remaining) && next) { + if (got < bufferSize) { + buffer[got++] = '-'; + } + } + if (!next) { // empty field means zero + if (got < bufferSize) { + buffer[got++] = '0'; + } + return got; + } + if (got < bufferSize) { + buffer[got++] = '.'; // input field is normalized to a fraction + } + char32_t decimal = edit.modes.editingFlags & decimalComma ? ',' : '.'; + auto start{got}; + if ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z')) { + // NaN or infinity - convert to upper case + for (; next && + ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z')); + next = io.NextInField(remaining)) { + if (got < bufferSize) { + if (*next >= 'a' && *next <= 'z') { + buffer[got++] = *next - 'a' + 'A'; + } else { + buffer[got++] = *next; + } + } + } + if (next && *next == '(') { // NaN(...) + while (next && *next != ')') { + next = io.NextInField(remaining); + } + } + exponent = 0; + } else if (*next == decimal || (*next >= '0' && *next <= '9')) { + for (; next; next = io.NextInField(remaining)) { + char32_t ch{*next}; + if (ch == ' ') { + if (edit.modes.editingFlags & blankZero) { + ch = '0'; // BZ mode - treat blank as if it were zero + } else { + continue; + } + } + if (ch == '0' && got == start) { + // omit leading zeroes + } else if (ch >= '0' && ch <= '9') { + if (got < bufferSize) { + buffer[got++] = ch; + } + } else if (ch == decimal && !decimalPoint) { + // the decimal point is *not* copied to the buffer + decimalPoint = got - start; // # of digits before the decimal point + } else { + break; + } + } + if (got == start && got < bufferSize) { + buffer[got++] = '0'; // all digits were zeroes + } + if (next && + (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' || + *next == 'q' || *next == 'Q')) { + io.SkipSpaces(remaining); + next = io.NextInField(remaining); + } + exponent = -edit.modes.scale; // default exponent is -kP + if (next && + (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9'))) { + bool negExpo{*next == '-'}; + if (negExpo || *next == '+') { + next = io.NextInField(remaining); + } + for (exponent = 0; next && (*next >= '0' && *next <= '9'); + next = io.NextInField(remaining)) { + exponent = 10 * exponent + *next - '0'; + } + if (negExpo) { + exponent = -exponent; + } + } + if (decimalPoint) { + exponent += *decimalPoint; + } else { + // When no decimal point (or comma) appears in the value, the 'd' + // part of the edit descriptor must be interpreted as the number of + // digits in the value to be interpreted as being to the *right* of + // the assumed decimal point (13.7.2.3.2) + exponent += got - start - edit.digits.value_or(0); + } + } else { + // TODO: hex FP input + exponent = 0; + return 0; + } + if (remaining) { + while (next && *next == ' ') { + next = io.NextInField(remaining); + } + if (next) { + return 0; // error: unused nonblank character in fixed-width field + } + } + return got; +} + +template +bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) { + static constexpr int maxDigits{ + common::MaxDecimalConversionDigits(binaryPrecision)}; + static constexpr int bufferSize{maxDigits + 18}; + char buffer[bufferSize]; + int exponent{0}; + int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)}; + if (got >= maxDigits + 2) { + io.GetIoErrorHandler().Crash("EditRealInput: buffer was too small"); + return false; + } + if (got == 0) { + io.GetIoErrorHandler().SignalError("Bad REAL input value"); + return false; + } + bool hadExtra{got > maxDigits}; + if (exponent != 0) { + got += std::snprintf(&buffer[got], bufferSize - got, "e%d", exponent); + } + buffer[got] = '\0'; + const char *p{buffer}; + decimal::ConversionToBinaryResult converted{ + decimal::ConvertToBinary(p, edit.modes.round)}; + if (hadExtra) { + converted.flags = static_cast( + converted.flags | decimal::Inexact); + } + // TODO: raise converted.flags as exceptions? + *reinterpret_cast *>(n) = + converted.binary; + return true; +} + +template +bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) { + switch (edit.descriptor) { + case DataEdit::ListDirected: + case 'F': + case 'E': // incl. EN, ES, & EX + case 'D': + case 'G': return EditCommonRealInput(io, edit, n); + case 'B': + return EditBOZInput( + io, edit, n, 2, common::BitsForBinaryPrecision(binaryPrecision)); + case 'O': + return EditBOZInput( + io, edit, n, 8, common::BitsForBinaryPrecision(binaryPrecision)); + case 'Z': + return EditBOZInput( + io, edit, n, 16, common::BitsForBinaryPrecision(binaryPrecision)); + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used for REAL input", + edit.descriptor); + return false; + } +} + +// 13.7.3 in Fortran 2018 +bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) { + switch (edit.descriptor) { + case DataEdit::ListDirected: + case 'L': + case 'G': break; + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used for LOGICAL input", + edit.descriptor); + return false; + } + std::optional remaining; + if (edit.width) { + remaining = std::max(0, *edit.width); + } + io.SkipSpaces(remaining); + std::optional next{io.NextInField(remaining)}; + if (next && *next == '.') { // skip optional period + next = io.NextInField(remaining); + } + if (!next) { + io.GetIoErrorHandler().SignalError("Empty LOGICAL input field"); + return false; + } + switch (*next) { + case 'T': + case 't': x = true; break; + case 'F': + case 'f': x = false; break; + default: + io.GetIoErrorHandler().SignalError( + "Bad character '%lc' in LOGICAL input field", *next); + return false; + } + if (remaining) { // ignore the rest of the field + io.HandleRelativePosition(*remaining); + } + return true; +} + +// See 13.10.3.1 paragraphs 7-9 in Fortran 2018 +static bool EditDelimitedCharacterInput( + IoStatementState &io, char *x, std::size_t length, char32_t delimiter) { + while (true) { + if (auto ch{io.GetCurrentChar()}) { + io.HandleRelativePosition(1); + if (*ch == delimiter) { + ch = io.GetCurrentChar(); + if (ch && *ch == delimiter) { + // Repeated delimiter: use as character value. Can't straddle a + // record boundary. + io.HandleRelativePosition(1); + } else { + std::fill_n(x, length, ' '); + return true; + } + } + if (length > 0) { + *x++ = *ch; + --length; + } + } else if (!io.AdvanceRecord()) { // EOF + std::fill_n(x, length, ' '); + return false; + } + } +} + +static bool EditListDirectedDefaultCharacterInput( + IoStatementState &io, char *x, std::size_t length) { + auto ch{io.GetCurrentChar()}; + if (ch && (*ch == '\'' || *ch == '"')) { + io.HandleRelativePosition(1); + return EditDelimitedCharacterInput(io, x, length, *ch); + } + // Undelimited list-directed character input: stop at a value separator + // or the end of the current record. + std::optional remaining{length}; + for (std::optional next{io.NextInField(remaining)}; next; + next = io.NextInField(remaining)) { + switch (*next) { + case ' ': + case ',': + case ';': + case '/': + remaining = 0; // value separator: stop + break; + default: *x++ = *next; --length; + } + } + std::fill_n(x, length, ' '); + return true; +} + +bool EditDefaultCharacterInput( + IoStatementState &io, const DataEdit &edit, char *x, std::size_t length) { + switch (edit.descriptor) { + case DataEdit::ListDirected: + return EditListDirectedDefaultCharacterInput(io, x, length); + case 'A': + case 'G': break; + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used with a CHARACTER data item", + edit.descriptor); + return false; + } + std::optional remaining{length}; + if (edit.width && *edit.width > 0) { + remaining = *edit.width; + } + // When the field is wider than the variable, we drop the leading + // characters. When the variable is wider than the field, there's + // trailing padding. + std::int64_t skip{*remaining - static_cast(length)}; + for (std::optional next{io.NextInField(remaining)}; next; + next = io.NextInField(remaining)) { + if (skip > 0) { + --skip; + } else { + *x++ = *next; + --length; + } + } + std::fill_n(x, length, ' '); + return true; +} + +template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<11>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<24>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<53>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<64>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<113>(IoStatementState &, const DataEdit &, void *); +} diff --git a/runtime/edit-input.h b/runtime/edit-input.h new file mode 100644 index 000000000000..b62d81804dd6 --- /dev/null +++ b/runtime/edit-input.h @@ -0,0 +1,40 @@ +//===-- runtime/edit-input.h ------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_RUNTIME_EDIT_INPUT_H_ +#define FORTRAN_RUNTIME_EDIT_INPUT_H_ + +#include "format.h" +#include "io-stmt.h" +#include "flang/Decimal/decimal.h" + +namespace Fortran::runtime::io { + +bool EditIntegerInput(IoStatementState &, const DataEdit &, void *, int kind); + +template +bool EditRealInput(IoStatementState &, const DataEdit &, void *); + +bool EditLogicalInput(IoStatementState &, const DataEdit &, bool &); +bool EditDefaultCharacterInput( + IoStatementState &, const DataEdit &, char *, std::size_t); + +extern template bool EditRealInput<8>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<11>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<24>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<53>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<64>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<113>( + IoStatementState &, const DataEdit &, void *); +} +#endif // FORTRAN_RUNTIME_EDIT_INPUT_H_ diff --git a/runtime/numeric-output.h b/runtime/edit-output.cpp similarity index 56% rename from runtime/numeric-output.h rename to runtime/edit-output.cpp index c3826ffdf563..47e3b2918788 100644 --- a/runtime/numeric-output.h +++ b/runtime/edit-output.cpp @@ -1,4 +1,4 @@ -//===-- runtime/numeric-output.h --------------------------------*- C++ -*-===// +//===-- runtime/edit-output.cpp ---------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,86 +6,155 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ -#define FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ - -// Output data editing templates implementing the FORMAT data editing -// descriptors E, EN, ES, EX, D, F, and G for REAL data (and COMPLEX -// components, I and G for INTEGER, and B/O/Z for both. -// See subclauses in 13.7.2.3 of Fortran 2018 for the -// detailed specifications of these descriptors. -// List-directed output (13.10.4) for numeric types is also done here. -// Drives the same fast binary-to-decimal formatting templates used -// in the f18 front-end. - -#include "format.h" -#include "io-stmt.h" -#include "flang/Decimal/decimal.h" +#include "edit-output.h" +#include "flang/Common/uint128.h" +#include "flang/Common/unsigned-const-division.h" namespace Fortran::runtime::io { -class IoStatementState; - -// I, B, O, Z, and G output editing for INTEGER. -// edit is const here (and elsewhere in this header) so that one -// edit descriptor with a repeat factor may safely serve to edit -// multiple elements of an array. -bool EditIntegerOutput(IoStatementState &, const DataEdit &, std::int64_t); - -// Encapsulates the state of a REAL output conversion. -class RealOutputEditingBase { -protected: - explicit RealOutputEditingBase(IoStatementState &io) : io_{io} {} - - static bool IsDecimalNumber(const char *p) { - if (!p) { - return false; +template +bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, INT n) { + char buffer[130], *end = &buffer[sizeof buffer], *p = end; + bool isNegative{false}; + if constexpr (std::is_same_v) { + isNegative = (n >> (8 * sizeof(INT) - 1)) != 0; + } else { + isNegative = n < 0; + } + UINT un{static_cast(isNegative ? -n : n)}; + int signChars{0}; + switch (edit.descriptor) { + case DataEdit::ListDirected: + case 'G': + case 'I': + if (isNegative || (edit.modes.editingFlags & signPlus)) { + signChars = 1; // '-' or '+' } - if (*p == '-' || *p == '+') { - ++p; + while (un > 0) { + auto quotient{common::DivideUnsignedBy(un)}; + *--p = '0' + static_cast(un - UINT{10} * quotient); + un = quotient; } - return *p >= '0' && *p <= '9'; + break; + case 'B': + for (; un > 0; un >>= 1) { + *--p = '0' + (static_cast(un) & 1); + } + break; + case 'O': + for (; un > 0; un >>= 3) { + *--p = '0' + (static_cast(un) & 7); + } + break; + case 'Z': + for (; un > 0; un >>= 4) { + int digit = static_cast(un) & 0xf; + *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; + } + break; + default: + io.GetIoErrorHandler().Crash( + "Data edit descriptor '%c' may not be used with an INTEGER data item", + edit.descriptor); + return false; } - const char *FormatExponent(int, const DataEdit &edit, int &length); - bool EmitPrefix(const DataEdit &, std::size_t length, std::size_t width); - bool EmitSuffix(const DataEdit &); - - IoStatementState &io_; - int trailingBlanks_{0}; // created when Gw editing maps to Fw - char exponent_[16]; -}; - -template -class RealOutputEditing : public RealOutputEditingBase { -public: - template - RealOutputEditing(IoStatementState &io, A x) - : RealOutputEditingBase{io}, x_{x} {} - bool Edit(const DataEdit &); - -private: - using BinaryFloatingPoint = - decimal::BinaryFloatingPointNumber; - - // The DataEdit arguments here are const references or copies so that - // the original DataEdit can safely serve multiple array elements when - // it has a repeat count. - bool EditEorDOutput(const DataEdit &); - bool EditFOutput(const DataEdit &); - DataEdit EditForGOutput(DataEdit); // returns an E or F edit - bool EditEXOutput(const DataEdit &); - bool EditListDirectedOutput(const DataEdit &); + int digits = end - p; + int leadingZeroes{0}; + int editWidth{edit.width.value_or(0)}; + if (edit.digits && digits <= *edit.digits) { // Iw.m + if (*edit.digits == 0 && n == 0) { + // Iw.0 with zero value: output field must be blank. For I0.0 + // and a zero value, emit one blank character. + signChars = 0; // in case of SP + editWidth = std::max(1, editWidth); + } else { + leadingZeroes = *edit.digits - digits; + } + } else if (n == 0) { + leadingZeroes = 1; + } + int total{signChars + leadingZeroes + digits}; + if (editWidth > 0 && total > editWidth) { + return io.EmitRepeated('*', editWidth); + } + int leadingSpaces{std::max(0, editWidth - total)}; + if (edit.IsListDirected()) { + if (static_cast(total) > + io.GetConnectionState().RemainingSpaceInRecord() && + !io.AdvanceRecord()) { + return false; + } + leadingSpaces = 1; + } + return io.EmitRepeated(' ', leadingSpaces) && + io.Emit(n < 0 ? "-" : "+", signChars) && + io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits); +} - bool IsZero() const { return x_.IsZero(); } +// Formats the exponent (see table 13.1 for all the cases) +const char *RealOutputEditingBase::FormatExponent( + int expo, const DataEdit &edit, int &length) { + char *eEnd{&exponent_[sizeof exponent_]}; + char *exponent{eEnd}; + for (unsigned e{static_cast(std::abs(expo))}; e > 0;) { + unsigned quotient{common::DivideUnsignedBy(e)}; + *--exponent = '0' + e - 10 * quotient; + e = quotient; + } + if (edit.expoDigits) { + if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 + while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { + *--exponent = '0'; + } + } else if (exponent == eEnd) { + *--exponent = '0'; // Ew.dE0 with zero-valued exponent + } + } else { // ensure at least two exponent digits + while (exponent + 2 > eEnd) { + *--exponent = '0'; + } + } + *--exponent = expo < 0 ? '-' : '+'; + if (edit.expoDigits || exponent + 3 == eEnd) { + *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' + } + length = eEnd - exponent; + return exponent; +} - decimal::ConversionToDecimalResult Convert( - int significantDigits, const DataEdit &, int flags = 0); +bool RealOutputEditingBase::EmitPrefix( + const DataEdit &edit, std::size_t length, std::size_t width) { + if (edit.IsListDirected()) { + int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart + ? 2 + : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0 : 1}; + int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart || + edit.descriptor == DataEdit::ListDirectedImaginaryPart + ? 1 + : 0}; + length += prefixLength + suffixLength; + ConnectionState &connection{io_.GetConnectionState()}; + return (connection.positionInRecord == 0 || + length <= connection.RemainingSpaceInRecord() || + io_.AdvanceRecord()) && + io_.Emit(" (", prefixLength); + } else if (width > length) { + return io_.EmitRepeated(' ', width - length); + } else { + return true; + } +} - BinaryFloatingPoint x_; - char buffer_[BinaryFloatingPoint::maxDecimalConversionDigits + - EXTRA_DECIMAL_CONVERSION_SPACE]; -}; +bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) { + if (edit.descriptor == DataEdit::ListDirectedRealPart) { + return io_.Emit(edit.modes.editingFlags & decimalComma ? ";" : ",", 1); + } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { + return io_.Emit(")", 1); + } else { + return true; + } +} template decimal::ConversionToDecimalResult RealOutputEditing::Convert( @@ -331,7 +400,7 @@ bool RealOutputEditing::Edit(const DataEdit &edit) { if (edit.IsListDirected()) { return EditListDirectedOutput(edit); } - io_.GetIoErrorHandler().Crash( + io_.GetIoErrorHandler().SignalError(IostatErrorInFormat, "Data edit descriptor '%c' may not be used with a REAL data item", edit.descriptor); return false; @@ -339,5 +408,88 @@ bool RealOutputEditing::Edit(const DataEdit &edit) { return false; } +bool ListDirectedLogicalOutput(IoStatementState &io, + ListDirectedStatementState &list, bool truth) { + return list.EmitLeadingSpaceOrAdvance(io, 1) && io.Emit(truth ? "T" : "F", 1); +} + +bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) { + switch (edit.descriptor) { + case 'L': + case 'G': return io.Emit(truth ? "T" : "F", 1); + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used with a LOGICAL data item", + edit.descriptor); + return false; + } +} + +bool ListDirectedDefaultCharacterOutput(IoStatementState &io, + ListDirectedStatementState &list, const char *x, + std::size_t length) { + bool ok{list.EmitLeadingSpaceOrAdvance(io, length, true)}; + MutableModes &modes{io.mutableModes()}; + ConnectionState &connection{io.GetConnectionState()}; + if (modes.delim) { + // Value is delimited with ' or " marks, and interior + // instances of that character are doubled. When split + // over multiple lines, delimit each lines' part. + ok &= io.Emit(&modes.delim, 1); + for (std::size_t j{0}; j < length; ++j) { + if (list.NeedAdvance(connection, 2)) { + ok &= io.Emit(&modes.delim, 1) && io.AdvanceRecord() && + io.Emit(&modes.delim, 1); + } + if (x[j] == modes.delim) { + ok &= io.EmitRepeated(modes.delim, 2); + } else { + ok &= io.Emit(&x[j], 1); + } + } + ok &= io.Emit(&modes.delim, 1); + } else { + // Undelimited list-directed output + std::size_t put{0}; + while (put < length) { + auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())}; + ok &= io.Emit(x + put, chunk); + put += chunk; + if (put < length) { + ok &= io.AdvanceRecord() && io.Emit(" ", 1); + } + } + list.lastWasUndelimitedCharacter = true; + } + return ok; +} + +bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit, + const char *x, std::size_t length) { + switch (edit.descriptor) { + case 'A': + case 'G': break; + default: + io.GetIoErrorHandler().SignalError(IostatErrorInFormat, + "Data edit descriptor '%c' may not be used with a CHARACTER data item", + edit.descriptor); + return false; + } + int len{static_cast(length)}; + int width{edit.width.value_or(len)}; + return io.EmitRepeated(' ', std::max(0, width - len)) && + io.Emit(x, std::min(width, len)); +} + +template bool EditIntegerOutput( + IoStatementState &, const DataEdit &, std::int64_t); +template bool EditIntegerOutput( + IoStatementState &, const DataEdit &, common::uint128_t); + +template class RealOutputEditing<8>; +template class RealOutputEditing<11>; +template class RealOutputEditing<24>; +template class RealOutputEditing<53>; +template class RealOutputEditing<64>; +template class RealOutputEditing<113>; } -#endif // FORTRAN_RUNTIME_NUMERIC_OUTPUT_H_ diff --git a/runtime/edit-output.h b/runtime/edit-output.h new file mode 100644 index 000000000000..559491b584c1 --- /dev/null +++ b/runtime/edit-output.h @@ -0,0 +1,111 @@ +//===-- runtime/edit-output.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_RUNTIME_EDIT_OUTPUT_H_ +#define FORTRAN_RUNTIME_EDIT_OUTPUT_H_ + +// Output data editing templates implementing the FORMAT data editing +// descriptors E, EN, ES, EX, D, F, and G for REAL data (and COMPLEX +// components, I and G for INTEGER, and B/O/Z for both. +// See subclauses in 13.7.2.3 of Fortran 2018 for the +// detailed specifications of these descriptors. +// List-directed output (13.10.4) for numeric types is also done here. +// Drives the same fast binary-to-decimal formatting templates used +// in the f18 front-end. + +#include "format.h" +#include "io-stmt.h" +#include "flang/Common/uint128.h" +#include "flang/Decimal/decimal.h" + +namespace Fortran::runtime::io { + +// I, B, O, Z, and G output editing for INTEGER. +// The DataEdit reference is const here (and elsewhere in this header) so that +// one edit descriptor with a repeat factor may safely serve to edit +// multiple elements of an array. +template +bool EditIntegerOutput(IoStatementState &, const DataEdit &, INT); + +// Encapsulates the state of a REAL output conversion. +class RealOutputEditingBase { +protected: + explicit RealOutputEditingBase(IoStatementState &io) : io_{io} {} + + static bool IsDecimalNumber(const char *p) { + if (!p) { + return false; + } + if (*p == '-' || *p == '+') { + ++p; + } + return *p >= '0' && *p <= '9'; + } + + const char *FormatExponent(int, const DataEdit &edit, int &length); + bool EmitPrefix(const DataEdit &, std::size_t length, std::size_t width); + bool EmitSuffix(const DataEdit &); + + IoStatementState &io_; + int trailingBlanks_{0}; // created when Gw editing maps to Fw + char exponent_[16]; +}; + +template +class RealOutputEditing : public RealOutputEditingBase { +public: + template + RealOutputEditing(IoStatementState &io, A x) + : RealOutputEditingBase{io}, x_{x} {} + bool Edit(const DataEdit &); + +private: + using BinaryFloatingPoint = + decimal::BinaryFloatingPointNumber; + + // The DataEdit arguments here are const references or copies so that + // the original DataEdit can safely serve multiple array elements when + // it has a repeat count. + bool EditEorDOutput(const DataEdit &); + bool EditFOutput(const DataEdit &); + DataEdit EditForGOutput(DataEdit); // returns an E or F edit + bool EditEXOutput(const DataEdit &); + bool EditListDirectedOutput(const DataEdit &); + + bool IsZero() const { return x_.IsZero(); } + + decimal::ConversionToDecimalResult Convert( + int significantDigits, const DataEdit &, int flags = 0); + + BinaryFloatingPoint x_; + char buffer_[BinaryFloatingPoint::maxDecimalConversionDigits + + EXTRA_DECIMAL_CONVERSION_SPACE]; +}; + +bool ListDirectedLogicalOutput( + IoStatementState &, ListDirectedStatementState &, bool); +bool EditLogicalOutput(IoStatementState &, const DataEdit &, bool); +bool ListDirectedDefaultCharacterOutput(IoStatementState &, + ListDirectedStatementState &, const char *, std::size_t); +bool EditDefaultCharacterOutput( + IoStatementState &, const DataEdit &, const char *, std::size_t); + +extern template bool EditIntegerOutput( + IoStatementState &, const DataEdit &, std::int64_t); +extern template bool EditIntegerOutput( + IoStatementState &, const DataEdit &, common::uint128_t); + +extern template class RealOutputEditing<8>; +extern template class RealOutputEditing<11>; +extern template class RealOutputEditing<24>; +extern template class RealOutputEditing<53>; +extern template class RealOutputEditing<64>; +extern template class RealOutputEditing<113>; + +} +#endif // FORTRAN_RUNTIME_EDIT_OUTPUT_H_ diff --git a/runtime/file.cpp b/runtime/file.cpp index 9ee4ae3c4318..31b4246ee03d 100644 --- a/runtime/file.cpp +++ b/runtime/file.cpp @@ -34,7 +34,7 @@ void OpenFile::Open( case OpenStatus::New: flags |= O_CREAT | O_EXCL; break; case OpenStatus::Scratch: if (path_.get()) { - handler.Crash("FILE= must not appear with STATUS='SCRATCH'"); + handler.SignalError("FILE= must not appear with STATUS='SCRATCH'"); path_.reset(); } { @@ -54,7 +54,8 @@ void OpenFile::Open( flags |= O_CREAT; break; } - // If we reach this point, we're opening a new file + // If we reach this point, we're opening a new file. + // TODO: Fortran shouldn't create a new file until the first WRITE. if (fd_ >= 0) { if (fd_ <= 2) { // don't actually close a standard file descriptor, we might need it @@ -63,8 +64,9 @@ void OpenFile::Open( } } if (!path_.get()) { - handler.Crash( + handler.SignalError( "FILE= is required unless STATUS='OLD' and unit is connected"); + return; } fd_ = ::open(path_.get(), flags, 0600); if (fd_ < 0) { @@ -79,7 +81,6 @@ void OpenFile::Open( } void OpenFile::Predefine(int fd) { - CriticalSection criticalSection{lock_}; fd_ = fd; path_.reset(); pathLength_ = 0; @@ -90,7 +91,6 @@ void OpenFile::Predefine(int fd) { } void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) { - CriticalSection criticalSection{lock_}; CheckOpen(handler); pending_.reset(); knownSize_.reset(); @@ -116,7 +116,6 @@ std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes, if (maxBytes == 0) { return 0; } - CriticalSection criticalSection{lock_}; CheckOpen(handler); if (!Seek(at, handler)) { return 0; @@ -150,7 +149,6 @@ std::size_t OpenFile::Write(FileOffset at, const char *buffer, if (bytes == 0) { return 0; } - CriticalSection criticalSection{lock_}; CheckOpen(handler); if (!Seek(at, handler)) { return 0; @@ -176,7 +174,6 @@ std::size_t OpenFile::Write(FileOffset at, const char *buffer, } void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) { - CriticalSection criticalSection{lock_}; CheckOpen(handler); if (!knownSize_ || *knownSize_ != at) { if (::ftruncate(fd_, at) != 0) { @@ -191,7 +188,6 @@ void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) { // TODO: True asynchronicity int OpenFile::ReadAsynchronously( FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) { - CriticalSection criticalSection{lock_}; CheckOpen(handler); int iostat{0}; for (std::size_t got{0}; got < bytes;) { @@ -221,7 +217,6 @@ int OpenFile::ReadAsynchronously( // TODO: True asynchronicity int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer, std::size_t bytes, IoErrorHandler &handler) { - CriticalSection criticalSection{lock_}; CheckOpen(handler); int iostat{0}; for (std::size_t put{0}; put < bytes;) { @@ -247,19 +242,16 @@ int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer, void OpenFile::Wait(int id, IoErrorHandler &handler) { std::optional ioStat; - { - CriticalSection criticalSection{lock_}; - Pending *prev{nullptr}; - for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) { - if (p->id == id) { - ioStat = p->ioStat; - if (prev) { - prev->next.reset(p->next.release()); - } else { - pending_.reset(p->next.release()); - } - break; + Pending *prev{nullptr}; + for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) { + if (p->id == id) { + ioStat = p->ioStat; + if (prev) { + prev->next.reset(p->next.release()); + } else { + pending_.reset(p->next.release()); } + break; } } if (ioStat) { @@ -270,14 +262,11 @@ void OpenFile::Wait(int id, IoErrorHandler &handler) { void OpenFile::WaitAll(IoErrorHandler &handler) { while (true) { int ioStat; - { - CriticalSection criticalSection{lock_}; - if (pending_) { - ioStat = pending_->ioStat; - pending_.reset(pending_->next.release()); - } else { - return; - } + if (pending_) { + ioStat = pending_->ioStat; + pending_.reset(pending_->next.release()); + } else { + return; } handler.SignalError(ioStat); } diff --git a/runtime/file.h b/runtime/file.h index 9ed1c250364a..c74da330bb7d 100644 --- a/runtime/file.h +++ b/runtime/file.h @@ -12,7 +12,6 @@ #define FORTRAN_RUNTIME_FILE_H_ #include "io-error.h" -#include "lock.h" #include "memory.h" #include #include @@ -27,7 +26,6 @@ class OpenFile { public: using FileOffset = std::int64_t; - Lock &lock() { return lock_; } const char *path() const { return path_.get(); } void set_path(OwningPtr &&, std::size_t bytes); std::size_t pathLength() const { return pathLength_; } @@ -76,14 +74,12 @@ class OpenFile { OwningPtr next; }; - // lock_ must be held for these void CheckOpen(const Terminator &); bool Seek(FileOffset, IoErrorHandler &); bool RawSeek(FileOffset); bool RawSeekToEnd(); int PendingResult(const Terminator &, int); - Lock lock_; int fd_{-1}; OwningPtr path_; std::size_t pathLength_; diff --git a/runtime/format-implementation.h b/runtime/format-implementation.h index e066ba04debc..43efd6e1f130 100644 --- a/runtime/format-implementation.h +++ b/runtime/format-implementation.h @@ -25,39 +25,69 @@ FormatControl::FormatControl(const Terminator &terminator, const CharType *format, std::size_t formatLength, int maxHeight) : maxHeight_{static_cast(maxHeight)}, format_{format}, formatLength_{static_cast(formatLength)} { - if (maxHeight != maxHeight_) { - terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); - } - if (formatLength != static_cast(formatLength_)) { - terminator.Crash( - "internal Fortran runtime error: formatLength %zd", formatLength); - } + RUNTIME_CHECK(terminator, maxHeight == maxHeight_); + RUNTIME_CHECK( + terminator, formatLength == static_cast(formatLength_)); stack_[0].start = offset_; stack_[0].remaining = Iteration::unlimited; // 13.4(8) } template int FormatControl::GetMaxParenthesisNesting( - const Terminator &terminator, const CharType *format, - std::size_t formatLength) { - using Validator = common::FormatValidator; - typename Validator::Reporter reporter{ - [&](const common::FormatMessage &message) { - terminator.Crash(message.text, message.arg); - return false; // crashes on error above - }}; - Validator validator{format, formatLength, reporter}; - validator.Check(); - return validator.maxNesting(); + IoErrorHandler &handler, const CharType *format, std::size_t formatLength) { + int maxNesting{0}; + int nesting{0}; + const CharType *end{format + formatLength}; + std::optional quote; + int repeat{0}; + for (const CharType *p{format}; p < end; ++p) { + if (quote) { + if (*p == *quote) { + quote.reset(); + } + } else if (*p >= '0' && *p <= '9') { + repeat = 10 * repeat + *p - '0'; + } else if (*p != ' ') { + switch (*p) { + case '\'': + case '"': quote = *p; break; + case 'h': + case 'H': // 9HHOLLERITH + p += repeat; + if (p >= end) { + handler.SignalError(IostatErrorInFormat, + "Hollerith (%dH) too long in FORMAT", repeat); + return maxNesting; + } + break; + case ' ': break; + case '(': + ++nesting; + maxNesting = std::max(nesting, maxNesting); + break; + case ')': nesting = std::max(nesting - 1, 0); break; + } + repeat = 0; + } + } + if (quote) { + handler.SignalError( + IostatErrorInFormat, "Unbalanced quotation marks in FORMAT string"); + } else if (nesting) { + handler.SignalError( + IostatErrorInFormat, "Unbalanced parentheses in FORMAT string"); + } + return maxNesting; } template int FormatControl::GetIntField( - const Terminator &terminator, CharType firstCh) { + IoErrorHandler &handler, CharType firstCh) { CharType ch{firstCh ? firstCh : PeekNext()}; if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) { - terminator.Crash( + handler.SignalError(IostatErrorInFormat, "Invalid FORMAT: integer expected at '%c'", static_cast(ch)); + return 0; } int result{0}; bool negate{ch == '-'}; @@ -68,7 +98,9 @@ int FormatControl::GetIntField( while (ch >= '0' && ch <= '9') { if (result > std::numeric_limits::max() / 10 - (static_cast(ch) - '0')) { - terminator.Crash("FORMAT integer field out of range"); + handler.SignalError( + IostatErrorInFormat, "FORMAT integer field out of range"); + return result; } result = 10 * result + ch - '0'; if (firstCh) { @@ -79,7 +111,8 @@ int FormatControl::GetIntField( ch = PeekNext(); } if (negate && (result *= -1) > 0) { - terminator.Crash("FORMAT integer field out of range"); + handler.SignalError( + IostatErrorInFormat, "FORMAT integer field out of range"); } return result; } @@ -156,9 +189,11 @@ static void HandleControl(CONTEXT &context, char ch, char next, int n) { default: break; } if (next) { - context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next); + context.SignalError(IostatErrorInFormat, + "Unknown '%c%c' edit descriptor in FORMAT", ch, next); } else { - context.Crash("Unknown '%c' edit descriptor in FORMAT", ch); + context.SignalError( + IostatErrorInFormat, "Unknown '%c' edit descriptor in FORMAT", ch); } } @@ -188,12 +223,16 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { unlimited = true; ch = GetNextChar(context); if (ch != '(') { - context.Crash("Invalid FORMAT: '*' may appear only before '('"); + context.SignalError(IostatErrorInFormat, + "Invalid FORMAT: '*' may appear only before '('"); + return 0; } } if (ch == '(') { if (height_ >= maxHeight_) { - context.Crash("FORMAT stack overflow: too many nested parentheses"); + context.SignalError(IostatErrorInFormat, + "FORMAT stack overflow: too many nested parentheses"); + return 0; } stack_[height_].start = offset_ - 1; // the '(' if (unlimited || height_ == 0) { @@ -209,7 +248,8 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { } ++height_; } else if (height_ == 0) { - context.Crash("FORMAT lacks initial '('"); + context.SignalError(IostatErrorInFormat, "FORMAT lacks initial '('"); + return 0; } else if (ch == ')') { if (height_ == 1) { if (stop) { @@ -220,7 +260,7 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { if (stack_[height_ - 1].remaining == Iteration::unlimited) { offset_ = stack_[height_ - 1].start + 1; if (offset_ == unlimitedLoopCheck) { - context.Crash( + context.SignalError(IostatErrorInFormat, "Unlimited repetition in FORMAT lacks data edit descriptors"); } } else if (stack_[height_ - 1].remaining-- > 0) { @@ -236,7 +276,9 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { ++offset_; } if (offset_ >= formatLength_) { - context.Crash("FORMAT missing closing quote on character literal"); + context.SignalError(IostatErrorInFormat, + "FORMAT missing closing quote on character literal"); + return 0; } ++offset_; std::size_t chars{ @@ -252,7 +294,9 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { } else if (ch == 'H') { // 9HHOLLERITH if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { - context.Crash("Invalid width on Hollerith in FORMAT"); + context.SignalError( + IostatErrorInFormat, "Invalid width on Hollerith in FORMAT"); + return 0; } context.Emit(format_ + offset_, static_cast(*repeat)); offset_ += *repeat; @@ -282,7 +326,9 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { } else if (ch == '/') { context.AdvanceRecord(repeat && *repeat > 0 ? *repeat : 1); } else { - context.Crash("Invalid character '%c' in FORMAT", static_cast(ch)); + context.SignalError(IostatErrorInFormat, + "Invalid character '%c' in FORMAT", static_cast(ch)); + return 0; } } } @@ -348,7 +394,7 @@ DataEdit FormatControl::GetNextDataEdit( } template -void FormatControl::FinishOutput(Context &context) { +void FormatControl::Finish(Context &context) { CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); } } diff --git a/runtime/format.cpp b/runtime/format.cpp index 91a6b6749514..6702ab97fe69 100644 --- a/runtime/format.cpp +++ b/runtime/format.cpp @@ -30,24 +30,34 @@ bool DefaultFormatControlCallbacks::Emit(const char32_t *, std::size_t) { "I/O statement"); return {}; } +std::optional DefaultFormatControlCallbacks::GetCurrentChar() { + Crash("DefaultFormatControlCallbacks::GetCurrentChar() called for non-input " + "I/O " + "statement"); + return {}; +} bool DefaultFormatControlCallbacks::AdvanceRecord(int) { Crash("DefaultFormatControlCallbacks::AdvanceRecord() called unexpectedly"); return {}; } -bool DefaultFormatControlCallbacks::HandleAbsolutePosition(std::int64_t) { +void DefaultFormatControlCallbacks::BackspaceRecord() { + Crash("DefaultFormatControlCallbacks::BackspaceRecord() called unexpectedly"); +} +void DefaultFormatControlCallbacks::HandleAbsolutePosition(std::int64_t) { Crash("DefaultFormatControlCallbacks::HandleAbsolutePosition() called for " - "non-formatted " - "I/O statement"); - return {}; + "non-formatted I/O statement"); } -bool DefaultFormatControlCallbacks::HandleRelativePosition(std::int64_t) { +void DefaultFormatControlCallbacks::HandleRelativePosition(std::int64_t) { Crash("DefaultFormatControlCallbacks::HandleRelativePosition() called for " - "non-formatted " - "I/O statement"); - return {}; + "non-formatted I/O statement"); } -template class FormatControl>; -template class FormatControl>; -template class FormatControl>; +template class FormatControl< + InternalFormattedIoStatementState>; +template class FormatControl< + InternalFormattedIoStatementState>; +template class FormatControl< + ExternalFormattedIoStatementState>; +template class FormatControl< + ExternalFormattedIoStatementState>; } diff --git a/runtime/format.h b/runtime/format.h index a4899f829ea5..4875b44f08bb 100644 --- a/runtime/format.h +++ b/runtime/format.h @@ -13,7 +13,6 @@ #include "environment.h" #include "io-error.h" -#include "terminator.h" #include "flang/Common/Fortran.h" #include "flang/Decimal/decimal.h" #include @@ -41,10 +40,11 @@ struct MutableModes { struct DataEdit { char descriptor; // capitalized: one of A, I, B, O, Z, F, E(N/S/X), D, G - // Special internal data edit descriptors to distinguish list-directed I/O + // Special internal data edit descriptors for list-directed I/O static constexpr char ListDirected{'g'}; // non-COMPLEX list-directed static constexpr char ListDirectedRealPart{'r'}; // emit "(r," or "(r;" static constexpr char ListDirectedImaginaryPart{'z'}; // emit "z)" + static constexpr char ListDirectedNullValue{'n'}; // see 13.10.3.2 constexpr bool IsListDirected() const { return descriptor == ListDirected || descriptor == ListDirectedRealPart || descriptor == ListDirectedImaginaryPart; @@ -66,9 +66,11 @@ struct DefaultFormatControlCallbacks : public IoErrorHandler { bool Emit(const char *, std::size_t); bool Emit(const char16_t *, std::size_t); bool Emit(const char32_t *, std::size_t); + std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); - bool HandleAbsolutePosition(std::int64_t); - bool HandleRelativePosition(std::int64_t); + void BackspaceRecord(); + void HandleAbsolutePosition(std::int64_t); + void HandleRelativePosition(std::int64_t); }; // Generates a sequence of DataEdits from a FORMAT statement or @@ -86,7 +88,7 @@ template class FormatControl { // Determines the max parenthesis nesting level by scanning and validating // the FORMAT string. static int GetMaxParenthesisNesting( - const Terminator &, const CharType *format, std::size_t formatLength); + IoErrorHandler &, const CharType *format, std::size_t formatLength); // For attempting to allocate in a user-supplied stack area static std::size_t GetNeededSize(int maxHeight) { @@ -98,8 +100,9 @@ template class FormatControl { // along the way. DataEdit GetNextDataEdit(Context &, int maxRepeat = 1); - // Emit any remaining character literals after the last data item. - void FinishOutput(Context &); + // Emit any remaining character literals after the last data item (on output) + // and perform remaining record positioning actions. + void Finish(Context &); private: static constexpr std::uint8_t maxMaxHeight{100}; @@ -119,14 +122,16 @@ template class FormatControl { SkipBlanks(); return offset_ < formatLength_ ? format_[offset_] : '\0'; } - CharType GetNextChar(const Terminator &terminator) { + CharType GetNextChar(IoErrorHandler &handler) { SkipBlanks(); if (offset_ >= formatLength_) { - terminator.Crash("FORMAT missing at least one ')'"); + handler.SignalError( + IostatErrorInFormat, "FORMAT missing at least one ')'"); + return '\n'; } return format_[offset_++]; } - int GetIntField(const Terminator &, CharType firstCh = '\0'); + int GetIntField(IoErrorHandler &, CharType firstCh = '\0'); // Advances through the FORMAT until the next data edit // descriptor has been found; handles control edit descriptors diff --git a/runtime/internal-unit.cpp b/runtime/internal-unit.cpp index 737f0856e33f..e5a71f735a12 100644 --- a/runtime/internal-unit.cpp +++ b/runtime/internal-unit.cpp @@ -14,8 +14,8 @@ namespace Fortran::runtime::io { -template -InternalDescriptorUnit::InternalDescriptorUnit( +template +InternalDescriptorUnit

::InternalDescriptorUnit( Scalar scalar, std::size_t length) { recordLength = length; endfileRecordNumber = 2; @@ -24,8 +24,8 @@ InternalDescriptorUnit::InternalDescriptorUnit( CFI_attribute_pointer); } -template -InternalDescriptorUnit::InternalDescriptorUnit( +template +InternalDescriptorUnit::InternalDescriptorUnit( const Descriptor &that, const Terminator &terminator) { RUNTIME_CHECK(terminator, that.type().IsCharacter()); Descriptor &d{descriptor()}; @@ -35,95 +35,107 @@ InternalDescriptorUnit::InternalDescriptorUnit( d.Check(); recordLength = d.ElementBytes(); endfileRecordNumber = d.Elements() + 1; - d.GetLowerBounds(at_); } -template void InternalDescriptorUnit::EndIoStatement() { - if constexpr (!isInput) { - // blank fill - while (currentRecordNumber < endfileRecordNumber.value_or(0)) { - char *record{descriptor().template Element(at_)}; - std::fill_n(record + furthestPositionInRecord, - recordLength.value_or(0) - furthestPositionInRecord, ' '); +template void InternalDescriptorUnit::EndIoStatement() { + if constexpr (DIR == Direction::Output) { // blank fill + while (char *record{CurrentRecord()}) { + if (furthestPositionInRecord < + recordLength.value_or(furthestPositionInRecord)) { + std::fill_n(record + furthestPositionInRecord, + *recordLength - furthestPositionInRecord, ' '); + } furthestPositionInRecord = 0; ++currentRecordNumber; - descriptor().IncrementSubscripts(at_); } } } -template -bool InternalDescriptorUnit::Emit( +template +bool InternalDescriptorUnit::Emit( const char *data, std::size_t bytes, IoErrorHandler &handler) { - if constexpr (isInput) { + if constexpr (DIR == Direction::Input) { + handler.Crash("InternalDescriptorUnit::Emit() called"); + return false && data[bytes] != 0; // bogus compare silences GCC warning + } else { + if (bytes <= 0) { + return true; + } + char *record{CurrentRecord()}; + if (!record) { + handler.SignalError(IostatInternalWriteOverrun); + return false; + } + auto furthestAfter{std::max(furthestPositionInRecord, + positionInRecord + static_cast(bytes))}; + bool ok{true}; + if (furthestAfter > static_cast(recordLength.value_or(0))) { + handler.SignalError(IostatRecordWriteOverrun); + furthestAfter = recordLength.value_or(0); + bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); + ok = false; + } else if (positionInRecord > furthestPositionInRecord) { + std::fill_n(record + furthestPositionInRecord, + positionInRecord - furthestPositionInRecord, ' '); + } + std::memcpy(record + positionInRecord, data, bytes); + positionInRecord += bytes; + furthestPositionInRecord = furthestAfter; + return ok; + } +} + +template +std::optional InternalDescriptorUnit::GetCurrentChar( + IoErrorHandler &handler) { + if constexpr (DIR == Direction::Output) { handler.Crash( - "InternalDescriptorUnit::Emit() called for an input statement"); - return false; + "InternalDescriptorUnit::GetCurrentChar() called"); + return std::nullopt; } - if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { + const char *record{CurrentRecord()}; + if (!record) { handler.SignalEnd(); - return false; + return std::nullopt; } - char *record{descriptor().template Element(at_)}; - auto furthestAfter{std::max(furthestPositionInRecord, - positionInRecord + static_cast(bytes))}; - bool ok{true}; - if (furthestAfter > static_cast(recordLength.value_or(0))) { - handler.SignalEor(); - furthestAfter = recordLength.value_or(0); - bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); - ok = false; + if (positionInRecord >= recordLength.value_or(positionInRecord)) { + return std::nullopt; } - std::memcpy(record + positionInRecord, data, bytes); - positionInRecord += bytes; - furthestPositionInRecord = furthestAfter; - return ok; + if (isUTF8) { + // TODO: UTF-8 decoding + } + return record[positionInRecord]; } -template -bool InternalDescriptorUnit::AdvanceRecord(IoErrorHandler &handler) { +template +bool InternalDescriptorUnit::AdvanceRecord(IoErrorHandler &handler) { if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { handler.SignalEnd(); return false; } - if (!HandleAbsolutePosition(recordLength.value_or(0), handler)) { - return false; + if constexpr (DIR == Direction::Output) { // blank fill + if (furthestPositionInRecord < + recordLength.value_or(furthestPositionInRecord)) { + char *record{CurrentRecord()}; + RUNTIME_CHECK(handler, record != nullptr); + std::fill_n(record + furthestPositionInRecord, + *recordLength - furthestPositionInRecord, ' '); + } } ++currentRecordNumber; - descriptor().IncrementSubscripts(at_); positionInRecord = 0; furthestPositionInRecord = 0; return true; } -template -bool InternalDescriptorUnit::HandleAbsolutePosition( - std::int64_t n, IoErrorHandler &handler) { - n = std::max(0, n); - bool ok{true}; - if (n > static_cast(recordLength.value_or(n))) { - handler.SignalEor(); - n = *recordLength; - ok = false; - } - if (n > furthestPositionInRecord && ok) { - if constexpr (!isInput) { - char *record{descriptor().template Element(at_)}; - std::fill_n( - record + furthestPositionInRecord, n - furthestPositionInRecord, ' '); - } - furthestPositionInRecord = n; - } - positionInRecord = n; - return ok; -} - -template -bool InternalDescriptorUnit::HandleRelativePosition( - std::int64_t n, IoErrorHandler &handler) { - return HandleAbsolutePosition(positionInRecord + n, handler); +template +void InternalDescriptorUnit::BackspaceRecord(IoErrorHandler &handler) { + RUNTIME_CHECK(handler, currentRecordNumber > 1); + --currentRecordNumber; + positionInRecord = 0; + furthestPositionInRecord = 0; } -template class InternalDescriptorUnit; -template class InternalDescriptorUnit; +template class InternalDescriptorUnit; +template class InternalDescriptorUnit; } diff --git a/runtime/internal-unit.h b/runtime/internal-unit.h index 837ddc6f588f..7f4f11e976f6 100644 --- a/runtime/internal-unit.h +++ b/runtime/internal-unit.h @@ -22,25 +22,32 @@ class IoErrorHandler; // Points to (but does not own) a CHARACTER scalar or array for internal I/O. // Does not buffer. -template class InternalDescriptorUnit : public ConnectionState { +template class InternalDescriptorUnit : public ConnectionState { public: - using Scalar = std::conditional_t; + using Scalar = + std::conditional_t; InternalDescriptorUnit(Scalar, std::size_t); InternalDescriptorUnit(const Descriptor &, const Terminator &); void EndIoStatement(); - bool Emit(const char *, std::size_t bytes, IoErrorHandler &); + bool Emit(const char *, std::size_t, IoErrorHandler &); + std::optional GetCurrentChar(IoErrorHandler &); bool AdvanceRecord(IoErrorHandler &); - bool HandleAbsolutePosition(std::int64_t, IoErrorHandler &); - bool HandleRelativePosition(std::int64_t, IoErrorHandler &); + void BackspaceRecord(IoErrorHandler &); private: Descriptor &descriptor() { return staticDescriptor_.descriptor(); } + const Descriptor &descriptor() const { + return staticDescriptor_.descriptor(); + } + Scalar CurrentRecord() const { + return descriptor().template ZeroBasedIndexedElement( + currentRecordNumber - 1); + } StaticDescriptor staticDescriptor_; - SubscriptValue at_[maxRank]; }; -extern template class InternalDescriptorUnit; -extern template class InternalDescriptorUnit; +extern template class InternalDescriptorUnit; +extern template class InternalDescriptorUnit; } #endif // FORTRAN_RUNTIME_IO_INTERNAL_UNIT_H_ diff --git a/runtime/io-api.cpp b/runtime/io-api.cpp index 969315a49fa7..cf18b8f0519b 100644 --- a/runtime/io-api.cpp +++ b/runtime/io-api.cpp @@ -9,11 +9,12 @@ // Implements the I/O statement API #include "io-api.h" +#include "edit-input.h" +#include "edit-output.h" #include "environment.h" #include "format.h" #include "io-stmt.h" #include "memory.h" -#include "numeric-output.h" #include "terminator.h" #include "tools.h" #include "unit.h" @@ -22,116 +23,212 @@ namespace Fortran::runtime::io { -Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &descriptor, +template +Cookie BeginInternalArrayListIO(const Descriptor &descriptor, void ** /*scratchArea*/, std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { Terminator oom{sourceFile, sourceLine}; - return &New>{}( + return &New>{}( oom, descriptor, sourceFile, sourceLine) .ioStatementState(); } -Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor, +Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &descriptor, + void **scratchArea, std::size_t scratchBytes, const char *sourceFile, + int sourceLine) { + return BeginInternalArrayListIO( + descriptor, scratchArea, scratchBytes, sourceFile, sourceLine); +} + +Cookie IONAME(BeginInternalArrayListInput)(const Descriptor &descriptor, + void **scratchArea, std::size_t scratchBytes, const char *sourceFile, + int sourceLine) { + return BeginInternalArrayListIO( + descriptor, scratchArea, scratchBytes, sourceFile, sourceLine); +} + +template +Cookie BeginInternalArrayFormattedIO(const Descriptor &descriptor, const char *format, std::size_t formatLength, void ** /*scratchArea*/, std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { Terminator oom{sourceFile, sourceLine}; - return &New>{}( + return &New>{}( oom, descriptor, format, formatLength, sourceFile, sourceLine) .ioStatementState(); } -Cookie IONAME(BeginInternalListOutput)(char *internal, - std::size_t internalLength, void ** /*scratchArea*/, - std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { - Terminator oom{sourceFile, sourceLine}; - return &New>{}( - oom, internal, internalLength, sourceFile, sourceLine) - .ioStatementState(); +Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor, + const char *format, std::size_t formatLength, void **scratchArea, + std::size_t scratchBytes, const char *sourceFile, int sourceLine) { + return BeginInternalArrayFormattedIO(descriptor, format, + formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); } -Cookie IONAME(BeginInternalFormattedOutput)(char *internal, +Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &descriptor, + const char *format, std::size_t formatLength, void **scratchArea, + std::size_t scratchBytes, const char *sourceFile, int sourceLine) { + return BeginInternalArrayFormattedIO(descriptor, format, + formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); +} + +template +Cookie BeginInternalFormattedIO( + std::conditional_t *internal, std::size_t internalLength, const char *format, std::size_t formatLength, void ** /*scratchArea*/, std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { Terminator oom{sourceFile, sourceLine}; - return &New>{}(oom, internal, + return &New>{}(oom, internal, internalLength, format, formatLength, sourceFile, sourceLine) .ioStatementState(); } -Cookie IONAME(BeginInternalFormattedInput)(char *internal, +Cookie IONAME(BeginInternalFormattedOutput)(char *internal, std::size_t internalLength, const char *format, std::size_t formatLength, - void ** /*scratchArea*/, std::size_t /*scratchBytes*/, - const char *sourceFile, int sourceLine) { + void **scratchArea, std::size_t scratchBytes, const char *sourceFile, + int sourceLine) { Terminator oom{sourceFile, sourceLine}; - return &New>{}(oom, internal, - internalLength, format, formatLength, sourceFile, sourceLine) - .ioStatementState(); + return BeginInternalFormattedIO(internal, internalLength, + format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); } -Cookie IONAME(BeginExternalListOutput)( +Cookie IONAME(BeginInternalFormattedInput)(const char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + void **scratchArea, std::size_t scratchBytes, const char *sourceFile, + int sourceLine) { + Terminator oom{sourceFile, sourceLine}; + return BeginInternalFormattedIO(internal, internalLength, + format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); +} + +template +Cookie BeginExternalListIO( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - int unit{unitNumber == DefaultUnit ? 6 : unitNumber}; - ExternalFileUnit &file{ExternalFileUnit::LookUpOrCrash(unit, terminator)}; - if (file.isUnformatted) { - terminator.Crash("List-directed output attempted to unformatted file"); + if (unitNumber == DefaultUnit) { + unitNumber = DIR == Direction::Input ? 5 : 6; } - return &file.BeginIoStatement>( - file, sourceFile, sourceLine); + ExternalFileUnit &unit{ + ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; + if (unit.access == Access::Direct) { + terminator.Crash("List-directed I/O attempted on direct access file"); + return nullptr; + } + if (unit.isUnformatted) { + terminator.Crash("List-directed I/O attempted on unformatted file"); + return nullptr; + } + IoStatementState &io{unit.BeginIoStatement>( + unit, sourceFile, sourceLine)}; + if constexpr (DIR == Direction::Input) { + io.AdvanceRecord(); + } + return &io; } -Cookie IONAME(BeginExternalFormattedOutput)(const char *format, - std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, - int sourceLine) { +Cookie IONAME(BeginExternalListOutput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + return BeginExternalListIO( + unitNumber, sourceFile, sourceLine); +} + +Cookie IONAME(BeginExternalListInput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + return BeginExternalListIO( + unitNumber, sourceFile, sourceLine); +} + +template +Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength, + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - int unit{unitNumber == DefaultUnit ? 6 : unitNumber}; - ExternalFileUnit &file{ExternalFileUnit::LookUpOrCrash(unit, terminator)}; - if (file.isUnformatted) { - terminator.Crash("Formatted output attempted to unformatted file"); + if (unitNumber == DefaultUnit) { + unitNumber = DIR == Direction::Input ? 5 : 6; + } + ExternalFileUnit &unit{ + ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; + if (unit.isUnformatted) { + terminator.Crash("Formatted I/O attempted on unformatted file"); + return nullptr; } IoStatementState &io{ - file.BeginIoStatement>( - file, format, formatLength, sourceFile, sourceLine)}; + unit.BeginIoStatement>( + unit, format, formatLength, sourceFile, sourceLine)}; + if constexpr (DIR == Direction::Input) { + io.AdvanceRecord(); + } return &io; } -Cookie IONAME(BeginUnformattedOutput)( +Cookie IONAME(BeginExternalFormattedOutput)(const char *format, + std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, + int sourceLine) { + return BeginExternalFormattedIO( + format, formatLength, unitNumber, sourceFile, sourceLine); +} + +Cookie IONAME(BeginExternalFormattedInput)(const char *format, + std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, + int sourceLine) { + return BeginExternalFormattedIO( + format, formatLength, unitNumber, sourceFile, sourceLine); +} + +template +Cookie BeginUnformattedIO( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; ExternalFileUnit &file{ ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; if (!file.isUnformatted) { - terminator.Crash("Unformatted output attempted to formatted file"); + terminator.Crash("Unformatted output attempted on formatted file"); } - IoStatementState &io{ - file.BeginIoStatement>( - file, sourceFile, sourceLine)}; - if (file.access == Access::Sequential && !file.recordLength.has_value()) { - // Filled in by UnformattedIoStatementState::EndIoStatement() - io.Emit("\0\0\0\0", 4); // placeholder for record length header + IoStatementState &io{file.BeginIoStatement>( + file, sourceFile, sourceLine)}; + if constexpr (DIR == Direction::Input) { + io.AdvanceRecord(); + } else { + if (file.access == Access::Sequential && !file.recordLength.has_value()) { + // Create space for (sub)record header to be completed by + // UnformattedIoStatementState::EndIoStatement() + io.Emit("\0\0\0\0", 4); // placeholder for record length header + } } return &io; } +Cookie IONAME(BeginUnformattedOutput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + return BeginUnformattedIO( + unitNumber, sourceFile, sourceLine); +} + +Cookie IONAME(BeginUnformattedInput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + return BeginUnformattedIO( + unitNumber, sourceFile, sourceLine); +} + Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { bool wasExtant{false}; + Terminator terminator{sourceFile, sourceLine}; ExternalFileUnit &unit{ - ExternalFileUnit::LookUpOrCreate(unitNumber, &wasExtant)}; + ExternalFileUnit::LookUpOrCreate(unitNumber, terminator, &wasExtant)}; return &unit.BeginIoStatement( unit, wasExtant, sourceFile, sourceLine); } Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) const char *sourceFile, int sourceLine) { + Terminator terminator{sourceFile, sourceLine}; return IONAME(BeginOpenUnit)( - ExternalFileUnit::NewUnit(), sourceFile, sourceLine); + ExternalFileUnit::NewUnit(terminator), sourceFile, sourceLine); } Cookie IONAME(BeginClose)( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { - if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { + if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) { return &unit->BeginIoStatement( *unit, sourceFile, sourceLine); } else { @@ -144,8 +241,8 @@ Cookie IONAME(BeginClose)( // Control list items -void IONAME(EnableHandlers)( - Cookie cookie, bool hasIoStat, bool hasErr, bool hasEnd, bool hasEor) { +void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr, + bool hasEnd, bool hasEor, bool hasIoMsg) { IoErrorHandler &handler{cookie->GetIoErrorHandler()}; if (hasIoStat) { handler.HasIoStat(); @@ -159,17 +256,20 @@ void IONAME(EnableHandlers)( if (hasEor) { handler.HasEorLabel(); } + if (hasIoMsg) { + handler.HasIoMsg(); + } } static bool YesOrNo(const char *keyword, std::size_t length, const char *what, - const Terminator &terminator) { + IoErrorHandler &handler) { static const char *keywords[]{"YES", "NO", nullptr}; switch (IdentifyValue(keyword, length, keywords)) { case 0: return true; case 1: return false; default: - terminator.Crash( - "Invalid %s='%.*s'", what, static_cast(length), keyword); + handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what, + static_cast(length), keyword); return false; } } @@ -180,6 +280,10 @@ bool IONAME(SetAdvance)( ConnectionState &connection{io.GetConnectionState()}; connection.nonAdvancing = !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler()); + if (connection.nonAdvancing && connection.access == Access::Direct) { + io.GetIoErrorHandler().SignalError( + "Non-advancing I/O attempted on direct access file"); + } return true; } @@ -191,7 +295,7 @@ bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { case 0: connection.modes.editingFlags &= ~blankZero; return true; case 1: connection.modes.editingFlags |= blankZero; return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid BLANK='%.*s'", static_cast(length), keyword); return false; } @@ -206,7 +310,7 @@ bool IONAME(SetDecimal)( case 0: connection.modes.editingFlags |= decimalComma; return true; case 1: connection.modes.editingFlags &= ~decimalComma; return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid DECIMAL='%.*s'", static_cast(length), keyword); return false; } @@ -221,7 +325,7 @@ bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { case 1: connection.modes.delim = '"'; return true; case 2: connection.modes.delim = '\0'; return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid DELIM='%.*s'", static_cast(length), keyword); return false; } @@ -235,8 +339,50 @@ bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { return true; } -// TODO: SetPos (stream I/O) -// TODO: SetRec (direct I/O) +bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + if (connection.access != Access::Stream) { + io.GetIoErrorHandler().SignalError( + "REC= may not appear unless ACCESS='STREAM'"); + return false; + } + if (pos < 1) { + io.GetIoErrorHandler().SignalError( + "POS=%zd is invalid", static_cast(pos)); + return false; + } + if (auto *unit{io.GetExternalFileUnit()}) { + unit->SetPosition(pos); + return true; + } + io.GetIoErrorHandler().Crash("SetPos() on internal unit"); + return false; +} + +bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) { + IoStatementState &io{*cookie}; + ConnectionState &connection{io.GetConnectionState()}; + if (connection.access != Access::Direct) { + io.GetIoErrorHandler().SignalError( + "REC= may not appear unless ACCESS='DIRECT'"); + return false; + } + if (!connection.recordLength) { + io.GetIoErrorHandler().SignalError("RECL= was not specified"); + return false; + } + if (rec < 1) { + io.GetIoErrorHandler().SignalError( + "REC=%zd is invalid", static_cast(rec)); + return false; + } + connection.currentRecordNumber = rec; + if (auto *unit{io.GetExternalFileUnit()}) { + unit->SetPosition(rec * *connection.recordLength); + } + return true; +} bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; @@ -253,7 +399,7 @@ bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { connection.modes.round = executionEnvironment.defaultOutputRoundingMode; return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid ROUND='%.*s'", static_cast(length), keyword); return false; } @@ -270,7 +416,7 @@ bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { connection.modes.editingFlags &= ~signPlus; return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid SIGN='%.*s'", static_cast(length), keyword); return false; } @@ -291,11 +437,12 @@ bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { case 1: access = Access::Direct; break; case 2: access = Access::Stream; break; default: - open->Crash("Invalid ACCESS='%.*s'", static_cast(length), keyword); + open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'", + static_cast(length), keyword); } if (access != connection.access) { if (open->wasExtant()) { - open->Crash("ACCESS= may not be changed on an open unit"); + open->SignalError("ACCESS= may not be changed on an open unit"); } connection.access = access; } @@ -317,13 +464,14 @@ bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { case 1: mayRead = false; break; case 2: break; default: - open->Crash("Invalid ACTION='%.*s'", static_cast(length), keyword); + open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'", + static_cast(length), keyword); return false; } if (mayRead != open->unit().mayRead() || mayWrite != open->unit().mayWrite()) { if (open->wasExtant()) { - open->Crash("ACTION= may not be changed on an open unit"); + open->SignalError("ACTION= may not be changed on an open unit"); } open->unit().set_mayRead(mayRead); open->unit().set_mayWrite(mayWrite); @@ -344,8 +492,8 @@ bool IONAME(SetAsynchronous)( case 0: open->unit().set_mayAsynchronous(true); return true; case 1: open->unit().set_mayAsynchronous(false); return true; default: - open->Crash( - "Invalid ASYNCHRONOUS='%.*s'", static_cast(length), keyword); + open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'", + static_cast(length), keyword); return false; } } @@ -364,11 +512,12 @@ bool IONAME(SetEncoding)( case 0: isUTF8 = true; break; case 1: isUTF8 = false; break; default: - open->Crash("Invalid ENCODING='%.*s'", static_cast(length), keyword); + open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'", + static_cast(length), keyword); } if (isUTF8 != open->unit().isUTF8) { if (open->wasExtant()) { - open->Crash("ENCODING= may not be changed on an open unit"); + open->SignalError("ENCODING= may not be changed on an open unit"); } open->unit().isUTF8 = isUTF8; } @@ -388,11 +537,12 @@ bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { case 0: isUnformatted = false; break; case 1: isUnformatted = true; break; default: - open->Crash("Invalid FORM='%.*s'", static_cast(length), keyword); + open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'", + static_cast(length), keyword); } if (isUnformatted != open->unit().isUnformatted) { if (open->wasExtant()) { - open->Crash("FORM= may not be changed on an open unit"); + open->SignalError("FORM= may not be changed on an open unit"); } open->unit().isUnformatted = isUnformatted; } @@ -413,7 +563,7 @@ bool IONAME(SetPosition)( case 1: open->set_position(Position::Rewind); return true; case 2: open->set_position(Position::Append); return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid POSITION='%.*s'", static_cast(length), keyword); } return true; @@ -426,9 +576,12 @@ bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { io.GetIoErrorHandler().Crash( "SetRecl() called when not in an OPEN statement"); } + if (n <= 0) { + io.GetIoErrorHandler().SignalError("RECL= must be greater than zero"); + } if (open->wasExtant() && open->unit().recordLength.has_value() && - *open->unit().recordLength != n) { - open->Crash("RECL= may not be changed for an open unit"); + *open->unit().recordLength != static_cast(n)) { + open->SignalError("RECL= may not be changed for an open unit"); } open->unit().recordLength = n; return true; @@ -446,7 +599,7 @@ bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { case 3: open->set_status(OpenStatus::Replace); return true; case 4: open->set_status(OpenStatus::Unknown); return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid STATUS='%.*s'", static_cast(length), keyword); } return false; @@ -457,7 +610,7 @@ bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { case 0: close->set_status(CloseStatus::Keep); return true; case 1: close->set_status(CloseStatus::Delete); return true; default: - io.GetIoErrorHandler().Crash( + io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, "Invalid STATUS='%.*s'", static_cast(length), keyword); } return false; @@ -499,13 +652,12 @@ bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { "GetNewUnit() called when not in an OPEN statement"); } if (!SetInteger(unit, kind, open->unit().unitNumber())) { - open->Crash("GetNewUnit(): Bad INTEGER kind(%d) for result"); + open->SignalError("GetNewUnit(): Bad INTEGER kind(%d) for result"); } return true; } // Data transfers -// TODO: Input bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &) { IoStatementState &io{*cookie}; @@ -516,7 +668,7 @@ bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &) { bool IONAME(OutputUnformattedBlock)( Cookie cookie, const char *x, std::size_t length) { IoStatementState &io{*cookie}; - if (auto *unf{io.get_if>()}) { + if (auto *unf{io.get_if>()}) { return unf->Emit(x, length); } io.GetIoErrorHandler().Crash("OutputUnformatted() called for an I/O " @@ -531,7 +683,26 @@ bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { "OutputInteger64() called for a non-output I/O statement"); return false; } - return EditIntegerOutput(io, io.GetNextDataEdit(), n); + if (auto edit{io.GetNextDataEdit()}) { + return EditIntegerOutput(io, *edit, n); + } + return false; +} + +bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { + IoStatementState &io{*cookie}; + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "InputInteger64() called for a non-input I/O statement"); + return false; + } + if (auto edit{io.GetNextDataEdit()}) { + if (edit->descriptor == DataEdit::ListDirectedNullValue) { + return true; + } + return EditIntegerInput(io, *edit, reinterpret_cast(&n), kind); + } + return false; } bool IONAME(OutputReal64)(Cookie cookie, double x) { @@ -541,12 +712,31 @@ bool IONAME(OutputReal64)(Cookie cookie, double x) { "OutputReal64() called for a non-output I/O statement"); return false; } - return RealOutputEditing<53>{io, x}.Edit(io.GetNextDataEdit()); + if (auto edit{io.GetNextDataEdit()}) { + return RealOutputEditing<53>{io, x}.Edit(*edit); + } + return false; +} + +bool IONAME(InputReal64)(Cookie cookie, double &x) { + IoStatementState &io{*cookie}; + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "InputReal64() called for a non-input I/O statement"); + return false; + } + if (auto edit{io.GetNextDataEdit()}) { + if (edit->descriptor == DataEdit::ListDirectedNullValue) { + return true; + } + return EditRealInput<53>(io, *edit, reinterpret_cast(&x)); + } + return false; } bool IONAME(OutputComplex64)(Cookie cookie, double r, double z) { IoStatementState &io{*cookie}; - if (io.get_if>()) { + if (io.get_if>()) { DataEdit real, imaginary; real.descriptor = DataEdit::ListDirectedRealPart; imaginary.descriptor = DataEdit::ListDirectedImaginaryPart; @@ -563,53 +753,29 @@ bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { "OutputAscii() called for a non-output I/O statement"); return false; } - bool ok{true}; - if (auto *list{io.get_if>()}) { - // List-directed default CHARACTER output - ok &= list->EmitLeadingSpaceOrAdvance(io, length, true); - MutableModes &modes{io.mutableModes()}; - ConnectionState &connection{io.GetConnectionState()}; - if (modes.delim) { - ok &= io.Emit(&modes.delim, 1); - for (std::size_t j{0}; j < length; ++j) { - if (list->NeedAdvance(connection, 2)) { - ok &= io.Emit(&modes.delim, 1) && io.AdvanceRecord() && - io.Emit(&modes.delim, 1); - } - if (x[j] == modes.delim) { - ok &= io.EmitRepeated(modes.delim, 2); - } else { - ok &= io.Emit(&x[j], 1); - } - } - ok &= io.Emit(&modes.delim, 1); - } else { - std::size_t put{0}; - while (put < length) { - auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())}; - ok &= io.Emit(x + put, chunk); - put += chunk; - if (put < length) { - ok &= io.AdvanceRecord() && io.Emit(" ", 1); - } - } - list->lastWasUndelimitedCharacter = true; - } + if (auto *list{io.get_if>()}) { + return ListDirectedDefaultCharacterOutput(io, *list, x, length); + } else if (auto edit{io.GetNextDataEdit()}) { + return EditDefaultCharacterOutput(io, *edit, x, length); } else { - // Formatted default CHARACTER output - DataEdit edit{io.GetNextDataEdit()}; - if (edit.descriptor != 'A' && edit.descriptor != 'G') { - io.GetIoErrorHandler().Crash("Data edit descriptor '%c' may not be used " - "with a CHARACTER data item", - edit.descriptor); - return false; + return false; + } +} + +bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) { + IoStatementState &io{*cookie}; + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "InputAscii() called for a non-input I/O statement"); + return false; + } + if (auto edit{io.GetNextDataEdit()}) { + if (edit->descriptor == DataEdit::ListDirectedNullValue) { + return true; } - int len{static_cast(length)}; - int width{edit.width.value_or(len)}; - ok &= io.EmitRepeated(' ', std::max(0, width - len)) && - io.Emit(x, std::min(width, len)); + return EditDefaultCharacterInput(io, *edit, x, length); } - return ok; + return false; } bool IONAME(OutputLogical)(Cookie cookie, bool truth) { @@ -619,24 +785,36 @@ bool IONAME(OutputLogical)(Cookie cookie, bool truth) { "OutputLogical() called for a non-output I/O statement"); return false; } - if (auto *unf{io.get_if>()}) { - char x = truth; - return unf->Emit(&x, 1); - } - bool ok{true}; - if (auto *list{io.get_if>()}) { - ok &= list->EmitLeadingSpaceOrAdvance(io, 1); + if (auto *list{io.get_if>()}) { + return ListDirectedLogicalOutput(io, *list, truth); + } else if (auto edit{io.GetNextDataEdit()}) { + return EditLogicalOutput(io, *edit, truth); } else { - DataEdit edit{io.GetNextDataEdit()}; - if (edit.descriptor != 'L' && edit.descriptor != 'G') { - io.GetIoErrorHandler().Crash( - "Data edit descriptor '%c' may not be used with a LOGICAL data item", - edit.descriptor); - return false; + return false; + } +} + +bool IONAME(InputLogical)(Cookie cookie, bool &truth) { + IoStatementState &io{*cookie}; + if (!io.get_if()) { + io.GetIoErrorHandler().Crash( + "InputLogical() called for a non-input I/O statement"); + return false; + } + if (auto edit{io.GetNextDataEdit()}) { + if (edit->descriptor == DataEdit::ListDirectedNullValue) { + return true; } - ok &= io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)); + return EditLogicalInput(io, *edit, truth); + } + return false; +} + +void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) { + IoErrorHandler &handler{cookie->GetIoErrorHandler()}; + if (handler.GetIoStat()) { // leave "msg" alone when no error + handler.GetIoMsg(msg, length); } - return ok && io.Emit(truth ? "T" : "F", 1); } enum Iostat IONAME(EndIoStatement)(Cookie cookie) { diff --git a/runtime/io-api.h b/runtime/io-api.h index 417c0b5a3981..0efbf6d1953f 100644 --- a/runtime/io-api.h +++ b/runtime/io-api.h @@ -12,7 +12,7 @@ #define FORTRAN_RUNTIME_IO_API_H_ #include "entry-names.h" -#include "magic-numbers.h" +#include "iostat.h" #include #include @@ -73,7 +73,7 @@ Cookie IONAME(BeginInternalListOutput)(char *internal, std::size_t internalLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInternalListInput)(char *internal, +Cookie IONAME(BeginInternalListInput)(const char *internal, std::size_t internalLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); @@ -81,7 +81,7 @@ Cookie IONAME(BeginInternalFormattedOutput)(char *internal, std::size_t internalLength, const char *format, std::size_t formatLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IONAME(BeginInternalFormattedInput)(char *internal, +Cookie IONAME(BeginInternalFormattedInput)(const char *internal, std::size_t internalLength, const char *format, std::size_t formatLength, void **scratchArea = nullptr, std::size_t scratchBytes = 0, const char *sourceFile = nullptr, int sourceLine = 0); @@ -172,7 +172,7 @@ Cookie IONAME(BeginInquireIoLength)( // } // if (EndIoStatement(cookie) == FORTRAN_RUTIME_IOSTAT_END) goto label666; void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false, - bool hasEnd = false, bool hasEor = false); + bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false); // Control list options. These return false on a error that the // Begin...() call has specified will be handled by the caller. @@ -214,7 +214,7 @@ bool IONAME(InputDescriptor)(Cookie, const Descriptor &); bool IONAME(OutputUnformattedBlock)(Cookie, const char *, std::size_t); bool IONAME(InputUnformattedBlock)(Cookie, char *, std::size_t); bool IONAME(OutputInteger64)(Cookie, std::int64_t); -bool IONAME(InputInteger64)(Cookie, std::int64_t &, int kind = 8); +bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8); bool IONAME(OutputReal32)(Cookie, float); bool IONAME(InputReal32)(Cookie, float &); bool IONAME(OutputReal64)(Cookie, double); @@ -282,23 +282,6 @@ bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &); bool IONAME(InquireInteger64)( Cookie, const char *specifier, std::int64_t &, int kind = 8); -// The value of IOSTAT= is zero when no error, end-of-record, -// or end-of-file condition has arisen; errors are positive values. -// (See 12.11.5 in Fortran 2018 for the complete requirements; -// these constants must match the values of their corresponding -// named constants in the predefined module ISO_FORTRAN_ENV, so -// they're actually defined in another magic-numbers.h header file -// so that they can be included both here and there.) -enum Iostat { - // Other errors have values >1 - IostatInquireInternalUnit = FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT, - IostatOk = 0, - IostatEnd = FORTRAN_RUNTIME_IOSTAT_END, // end-of-file & no error - IostatEor = FORTRAN_RUNTIME_IOSTAT_EOR, // end-of-record & no error or EOF - IostatFlush = - FORTRAN_RUNTIME_IOSTAT_FLUSH, // attempt to FLUSH an unflushable unit -}; - // This function must be called to end an I/O statement, and its // cookie value may not be used afterwards unless it is recycled // by the runtime library to serve a later I/O statement. diff --git a/runtime/io-error.cpp b/runtime/io-error.cpp index 52fff2d10cfa..9300aa701f68 100644 --- a/runtime/io-error.cpp +++ b/runtime/io-error.cpp @@ -8,7 +8,9 @@ #include "io-error.h" #include "magic-numbers.h" +#include "tools.h" #include +#include #include #include @@ -17,46 +19,63 @@ namespace Fortran::runtime::io { void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) { flags_ = 0; ioStat_ = 0; + ioMsg_.reset(); SetLocation(sourceFileName, sourceLine); } -void IoErrorHandler::SignalError(int iostatOrErrno) { - if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_END) { - SignalEnd(); - } else if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_EOR) { - SignalEor(); - } else if (iostatOrErrno != 0) { - if (flags_ & hasIoStat) { +void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) { + if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) { + if (!ioStat_ || ioStat_ < IostatEnd) { + ioStat_ = IostatEnd; + } + } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) { + if (!ioStat_ || ioStat_ < IostatEor) { + ioStat_ = IostatEor; // least priority + } + } else if (iostatOrErrno != IostatOk) { + if (flags_ & (hasIoStat | hasErr)) { if (ioStat_ <= 0) { ioStat_ = iostatOrErrno; // priority over END=/EOR= + if (msg && (flags_ & hasIoMsg)) { + char buffer[256]; + va_list ap; + va_start(ap, msg); + std::vsnprintf(buffer, sizeof buffer, msg, ap); + ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this); + } } - } else if (iostatOrErrno == FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT) { - Crash("INQUIRE on internal unit"); + } else if (msg) { + va_list ap; + va_start(ap, msg); + CrashArgs(msg, ap); + } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) { + Crash(errstr); } else { - Crash("I/O error %d: %s", iostatOrErrno, std::strerror(iostatOrErrno)); + Crash("I/O error (errno=%d): %s", iostatOrErrno, + std::strerror(iostatOrErrno)); } } } +void IoErrorHandler::SignalError(int iostatOrErrno) { + SignalError(iostatOrErrno, nullptr); +} + void IoErrorHandler::SignalErrno() { SignalError(errno); } -void IoErrorHandler::SignalEnd() { - if (flags_ & hasEnd) { - if (!ioStat_ || ioStat_ < FORTRAN_RUNTIME_IOSTAT_END) { - ioStat_ = FORTRAN_RUNTIME_IOSTAT_END; - } - } else { - Crash("End of file"); - } -} +void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } -void IoErrorHandler::SignalEor() { - if (flags_ & hasEor) { - if (!ioStat_ || ioStat_ < FORTRAN_RUNTIME_IOSTAT_EOR) { - ioStat_ = FORTRAN_RUNTIME_IOSTAT_EOR; // least priority - } - } else { - Crash("End of record"); +void IoErrorHandler::SignalEor() { SignalError(IostatEor); } + +bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) { + const char *msg{ioMsg_.get()}; + if (!msg) { + msg = IostatErrorString(ioStat_); + } + if (msg) { + ToFortranDefaultCharacter(buffer, bufferLength, msg); + return true; } + return ::strerror_r(ioStat_, buffer, bufferLength) == 0; } } diff --git a/runtime/io-error.h b/runtime/io-error.h index 80f5fa817910..246b12beaf3a 100644 --- a/runtime/io-error.h +++ b/runtime/io-error.h @@ -9,15 +9,20 @@ // Distinguishes I/O error conditions; fatal ones lead to termination, // and those that the user program has chosen to handle are recorded // so that the highest-priority one can be returned as IOSTAT=. +// IOSTAT error codes are raw errno values augmented with values for +// Fortran-specific errors. #ifndef FORTRAN_RUNTIME_IO_ERROR_H_ #define FORTRAN_RUNTIME_IO_ERROR_H_ +#include "iostat.h" +#include "memory.h" #include "terminator.h" #include namespace Fortran::runtime::io { +// See 12.11 in Fortran 2018 class IoErrorHandler : public Terminator { public: using Terminator::Terminator; @@ -27,13 +32,22 @@ class IoErrorHandler : public Terminator { void HasErrLabel() { flags_ |= hasErr; } void HasEndLabel() { flags_ |= hasEnd; } void HasEorLabel() { flags_ |= hasEor; } + void HasIoMsg() { flags_ |= hasIoMsg; } + bool InError() const { return ioStat_ != 0; } + + void SignalError(int iostatOrErrno, const char *msg, ...); void SignalError(int iostatOrErrno); - void SignalErrno(); - void SignalEnd(); - void SignalEor(); + template void SignalError(const char *msg, X &&... xs) { + SignalError(IostatGenericError, msg, std::forward(xs)...); + } + + void SignalErrno(); // SignalError(errno) + void SignalEnd(); // input only; EOF on internal write is an error + void SignalEor(); // non-advancing input only; EOR on write is an error int GetIoStat() const { return ioStat_; } + bool GetIoMsg(char *, std::size_t); private: enum Flag : std::uint8_t { @@ -41,9 +55,11 @@ class IoErrorHandler : public Terminator { hasErr = 2, // ERR= hasEnd = 4, // END= hasEor = 8, // EOR= + hasIoMsg = 16, // IOMSG= }; std::uint8_t flags_{0}; int ioStat_{0}; + OwningPtr ioMsg_; }; } diff --git a/runtime/io-stmt.cpp b/runtime/io-stmt.cpp index adc9bae6c150..ea76226d7290 100644 --- a/runtime/io-stmt.cpp +++ b/runtime/io-stmt.cpp @@ -20,33 +20,43 @@ namespace Fortran::runtime::io { int IoStatementBase::EndIoStatement() { return GetIoStat(); } -DataEdit IoStatementBase::GetNextDataEdit(int) { - Crash("IoStatementBase::GetNextDataEdit() called for non-formatted I/O " - "statement"); +std::optional IoStatementBase::GetNextDataEdit( + IoStatementState &, int) { + return std::nullopt; } -template -InternalIoStatementState::InternalIoStatementState( +template +InternalIoStatementState::InternalIoStatementState( Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine) : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length} {} -template -InternalIoStatementState::InternalIoStatementState( +template +InternalIoStatementState::InternalIoStatementState( const Descriptor &d, const char *sourceFile, int sourceLine) : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {} -template -bool InternalIoStatementState::Emit( +template +bool InternalIoStatementState::Emit( const CharType *data, std::size_t chars) { - if constexpr (isInput) { - Crash("InternalIoStatementState::Emit() called for input statement"); + if constexpr (DIR == Direction::Input) { + Crash("InternalIoStatementState::Emit() called"); return false; } return unit_.Emit(data, chars, *this); } -template -bool InternalIoStatementState::AdvanceRecord(int n) { +template +std::optional InternalIoStatementState::GetCurrentChar() { + if constexpr (DIR == Direction::Output) { + Crash( + "InternalIoStatementState::GetCurrentChar() called"); + return std::nullopt; + } + return unit_.GetCurrentChar(*this); +} + +template +bool InternalIoStatementState::AdvanceRecord(int n) { while (n-- > 0) { if (!unit_.AdvanceRecord(*this)) { return false; @@ -55,9 +65,14 @@ bool InternalIoStatementState::AdvanceRecord(int n) { return true; } -template -int InternalIoStatementState::EndIoStatement() { - if constexpr (!isInput) { +template +void InternalIoStatementState::BackspaceRecord() { + unit_.BackspaceRecord(*this); +} + +template +int InternalIoStatementState::EndIoStatement() { + if constexpr (DIR == Direction::Output) { unit_.EndIoStatement(); // fill } auto result{IoStatementBase::EndIoStatement()}; @@ -67,54 +82,51 @@ int InternalIoStatementState::EndIoStatement() { return result; } -template -InternalFormattedIoStatementState::InternalFormattedIoStatementState(Buffer buffer, std::size_t length, - const CHAR *format, std::size_t formatLength, const char *sourceFile, - int sourceLine) - : InternalIoStatementState{buffer, length, sourceFile, - sourceLine}, - ioStatementState_{*this}, format_{*this, format, formatLength} {} - -template -InternalFormattedIoStatementState::InternalFormattedIoStatementState(const Descriptor &d, - const CHAR *format, std::size_t formatLength, const char *sourceFile, - int sourceLine) - : InternalIoStatementState{d, sourceFile, sourceLine}, - ioStatementState_{*this}, format_{*this, format, formatLength} {} - -template -int InternalFormattedIoStatementState::EndIoStatement() { - if constexpr (!isInput) { - format_.FinishOutput(*this); - } - return InternalIoStatementState::EndIoStatement(); +template +void InternalIoStatementState::HandleAbsolutePosition( + std::int64_t n) { + return unit_.HandleAbsolutePosition(n); } -template -bool InternalFormattedIoStatementState::HandleAbsolutePosition( +template +void InternalIoStatementState::HandleRelativePosition( std::int64_t n) { - return unit_.HandleAbsolutePosition(n, *this); + return unit_.HandleRelativePosition(n); } -template -bool InternalFormattedIoStatementState::HandleRelativePosition( - std::int64_t n) { - return unit_.HandleRelativePosition(n, *this); +template +InternalFormattedIoStatementState::InternalFormattedIoStatementState( + Buffer buffer, std::size_t length, const CHAR *format, + std::size_t formatLength, const char *sourceFile, int sourceLine) + : InternalIoStatementState{buffer, length, sourceFile, sourceLine}, + ioStatementState_{*this}, format_{*this, format, formatLength} {} + +template +InternalFormattedIoStatementState::InternalFormattedIoStatementState( + const Descriptor &d, const CHAR *format, std::size_t formatLength, + const char *sourceFile, int sourceLine) + : InternalIoStatementState{d, sourceFile, sourceLine}, + ioStatementState_{*this}, format_{*this, format, formatLength} {} + +template +int InternalFormattedIoStatementState::EndIoStatement() { + if constexpr (DIR == Direction::Output) { + format_.Finish(*this); // ignore any remaining input positioning actions + } + return InternalIoStatementState::EndIoStatement(); } -template -InternalListIoStatementState::InternalListIoStatementState( +template +InternalListIoStatementState::InternalListIoStatementState( Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) - : InternalIoStatementState{buffer, length, sourceFile, + : InternalIoStatementState{buffer, length, sourceFile, sourceLine}, ioStatementState_{*this} {} -template -InternalListIoStatementState::InternalListIoStatementState( +template +InternalListIoStatementState::InternalListIoStatementState( const Descriptor &d, const char *sourceFile, int sourceLine) - : InternalIoStatementState{d, sourceFile, sourceLine}, + : InternalIoStatementState{d, sourceFile, sourceLine}, ioStatementState_{*this} {} ExternalIoStatementBase::ExternalIoStatementBase( @@ -149,15 +161,17 @@ void OpenStatementState::set_path( int OpenStatementState::EndIoStatement() { if (wasExtant_ && status_ != OpenStatus::Old) { - Crash("OPEN statement for connected unit must have STATUS='OLD'"); + SignalError("OPEN statement for connected unit must have STATUS='OLD'"); } unit().OpenUnit(status_, position_, std::move(path_), pathLength_, *this); - return IoStatementBase::EndIoStatement(); + return ExternalIoStatementBase::EndIoStatement(); } int CloseStatementState::EndIoStatement() { + int result{ExternalIoStatementBase::EndIoStatement()}; unit().CloseUnit(status_, *this); - return IoStatementBase::EndIoStatement(); + unit().DestroyClosed(); + return result; } int NoopCloseStatementState::EndIoStatement() { @@ -166,8 +180,8 @@ int NoopCloseStatementState::EndIoStatement() { return result; } -template int ExternalIoStatementState::EndIoStatement() { - if constexpr (!isInput) { +template int ExternalIoStatementState::EndIoStatement() { + if constexpr (DIR == Direction::Output) { if (!unit().nonAdvancing) { unit().AdvanceRecord(*this); } @@ -176,39 +190,49 @@ template int ExternalIoStatementState::EndIoStatement() { return ExternalIoStatementBase::EndIoStatement(); } -template -bool ExternalIoStatementState::Emit( - const char *data, std::size_t chars) { - if (isInput) { - Crash("ExternalIoStatementState::Emit called for input statement"); +template +bool ExternalIoStatementState::Emit(const char *data, std::size_t chars) { + if constexpr (DIR == Direction::Input) { + Crash("ExternalIoStatementState::Emit(char) called for input statement"); } return unit().Emit(data, chars * sizeof(*data), *this); } -template -bool ExternalIoStatementState::Emit( +template +bool ExternalIoStatementState::Emit( const char16_t *data, std::size_t chars) { - if (isInput) { - Crash("ExternalIoStatementState::Emit called for input statement"); + if constexpr (DIR == Direction::Input) { + Crash( + "ExternalIoStatementState::Emit(char16_t) called for input statement"); } // TODO: UTF-8 encoding return unit().Emit( reinterpret_cast(data), chars * sizeof(*data), *this); } -template -bool ExternalIoStatementState::Emit( +template +bool ExternalIoStatementState::Emit( const char32_t *data, std::size_t chars) { - if (isInput) { - Crash("ExternalIoStatementState::Emit called for input statement"); + if constexpr (DIR == Direction::Input) { + Crash( + "ExternalIoStatementState::Emit(char32_t) called for input statement"); } // TODO: UTF-8 encoding return unit().Emit( reinterpret_cast(data), chars * sizeof(*data), *this); } -template -bool ExternalIoStatementState::AdvanceRecord(int n) { +template +std::optional ExternalIoStatementState::GetCurrentChar() { + if constexpr (DIR == Direction::Output) { + Crash( + "ExternalIoStatementState::GetCurrentChar() called"); + } + return unit().GetCurrentChar(*this); +} + +template +bool ExternalIoStatementState::AdvanceRecord(int n) { while (n-- > 0) { if (!unit().AdvanceRecord(*this)) { return false; @@ -217,42 +241,58 @@ bool ExternalIoStatementState::AdvanceRecord(int n) { return true; } -template -bool ExternalIoStatementState::HandleAbsolutePosition(std::int64_t n) { - return unit().HandleAbsolutePosition(n, *this); +template void ExternalIoStatementState::BackspaceRecord() { + unit().BackspaceRecord(*this); +} + +template +void ExternalIoStatementState::HandleAbsolutePosition(std::int64_t n) { + return unit().HandleAbsolutePosition(n); } -template -bool ExternalIoStatementState::HandleRelativePosition(std::int64_t n) { - return unit().HandleRelativePosition(n, *this); +template +void ExternalIoStatementState::HandleRelativePosition(std::int64_t n) { + return unit().HandleRelativePosition(n); } -template -ExternalFormattedIoStatementState::ExternalFormattedIoStatementState(ExternalFileUnit &unit, - const CHAR *format, std::size_t formatLength, const char *sourceFile, - int sourceLine) - : ExternalIoStatementState{unit, sourceFile, sourceLine}, +template +ExternalFormattedIoStatementState::ExternalFormattedIoStatementState( + ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength, + const char *sourceFile, int sourceLine) + : ExternalIoStatementState{unit, sourceFile, sourceLine}, mutableModes_{unit.modes}, format_{*this, format, formatLength} {} -template -int ExternalFormattedIoStatementState::EndIoStatement() { - format_.FinishOutput(*this); - return ExternalIoStatementState::EndIoStatement(); +template +int ExternalFormattedIoStatementState::EndIoStatement() { + format_.Finish(*this); + return ExternalIoStatementState::EndIoStatement(); } -DataEdit IoStatementState::GetNextDataEdit(int n) { - return std::visit([&](auto &x) { return x.get().GetNextDataEdit(n); }, u_); +std::optional IoStatementState::GetNextDataEdit(int n) { + return std::visit( + [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_); } bool IoStatementState::Emit(const char *data, std::size_t n) { return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_); } +std::optional IoStatementState::GetCurrentChar() { + return std::visit([&](auto &x) { return x.get().GetCurrentChar(); }, u_); +} + bool IoStatementState::AdvanceRecord(int n) { return std::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_); } +void IoStatementState::BackspaceRecord() { + std::visit([](auto &x) { x.get().BackspaceRecord(); }, u_); +} + +void IoStatementState::HandleRelativePosition(std::int64_t n) { + return std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_); +} + int IoStatementState::EndIoStatement() { return std::visit([](auto &x) { return x.get().EndIoStatement(); }, u_); } @@ -276,6 +316,10 @@ IoErrorHandler &IoStatementState::GetIoErrorHandler() const { u_); } +ExternalFileUnit *IoStatementState::GetExternalFileUnit() const { + return std::visit([](auto &x) { return x.get().GetExternalFileUnit(); }, u_); +} + bool IoStatementState::EmitRepeated(char ch, std::size_t n) { return std::visit( [=](auto &x) { @@ -302,13 +346,78 @@ bool IoStatementState::EmitField( } } -bool ListDirectedStatementState::NeedAdvance( +void IoStatementState::SkipSpaces(std::optional &remaining) { + if (!remaining || *remaining > 0) { + for (auto ch{GetCurrentChar()}; ch && ch == ' '; ch = GetCurrentChar()) { + HandleRelativePosition(1); + if (remaining && !--*remaining) { + break; + } + } + } +} + +std::optional IoStatementState::NextInField( + std::optional &remaining) { + if (!remaining) { // list-directed or namelist: check for separators + if (auto next{GetCurrentChar()}) { + switch (*next) { + case ' ': + case ',': + case ';': + case '/': + case '(': + case ')': + case '\'': + case '"': + case '*': break; + default: HandleRelativePosition(1); return next; + } + } + } else if (*remaining > 0) { + if (auto next{GetCurrentChar()}) { + --*remaining; + HandleRelativePosition(1); + return next; + } + const ConnectionState &connection{GetConnectionState()}; + if (!connection.IsAtEOF() && connection.recordLength && + connection.positionInRecord >= *connection.recordLength) { + if (connection.modes.pad) { // PAD='YES' + --*remaining; + return std::optional{' '}; + } + IoErrorHandler &handler{GetIoErrorHandler()}; + if (connection.nonAdvancing) { + handler.SignalEor(); + } else { + handler.SignalError(IostatRecordReadOverrun); + } + } + } + return std::nullopt; +} + +std::optional IoStatementState::GetNextNonBlank() { + auto ch{GetCurrentChar()}; + while (ch.value_or(' ') == ' ') { + if (ch) { + HandleRelativePosition(1); + } else if (!AdvanceRecord()) { + return std::nullopt; + } + ch = GetCurrentChar(); + } + return ch; +} + +bool ListDirectedStatementState::NeedAdvance( const ConnectionState &connection, std::size_t width) const { return connection.positionInRecord > 0 && width > connection.RemainingSpaceInRecord(); } -bool ListDirectedStatementState::EmitLeadingSpaceOrAdvance( +bool ListDirectedStatementState::EmitLeadingSpaceOrAdvance( IoStatementState &io, std::size_t length, bool isCharacter) { if (length == 0) { return true; @@ -326,9 +435,122 @@ bool ListDirectedStatementState::EmitLeadingSpaceOrAdvance( return true; } -template -int UnformattedIoStatementState::EndIoStatement() { - auto &ext{static_cast &>(*this)}; +std::optional +ListDirectedStatementState::GetNextDataEdit( + IoStatementState &io, int maxRepeat) { + DataEdit edit; + edit.descriptor = DataEdit::ListDirected; + edit.repeat = maxRepeat; + edit.modes = io.mutableModes(); + return edit; +} + +std::optional +ListDirectedStatementState::GetNextDataEdit( + IoStatementState &io, int maxRepeat) { + // N.B. list-directed transfers cannot be nonadvancing (C1221) + ConnectionState &connection{io.GetConnectionState()}; + DataEdit edit; + edit.descriptor = DataEdit::ListDirected; + edit.repeat = 1; // may be overridden below + edit.modes = connection.modes; + if (hitSlash_) { // everything after '/' is nullified + edit.descriptor = DataEdit::ListDirectedNullValue; + return edit; + } + if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress + while (connection.currentRecordNumber > initialRecordNumber_) { + io.BackspaceRecord(); + } + connection.HandleAbsolutePosition(initialPositionInRecord_); + if (!imaginaryPart_) { + edit.repeat = std::min(remaining_, maxRepeat); + } + remaining_ -= edit.repeat; + return edit; + } + // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018 + auto ch{io.GetNextNonBlank()}; + if (imaginaryPart_) { + imaginaryPart_ = false; + if (ch && *ch == ')') { + io.HandleRelativePosition(1); + ch = io.GetNextNonBlank(); + } + } else if (realPart_) { + realPart_ = false; + imaginaryPart_ = true; + } + if (!ch) { + return std::nullopt; + } + if (*ch == '/') { + hitSlash_ = true; + edit.descriptor = DataEdit::ListDirectedNullValue; + return edit; + } + char32_t comma{','}; + if (io.mutableModes().editingFlags & decimalComma) { + comma = ';'; + } + bool isFirstItem{isFirstItem_}; + isFirstItem_ = false; + if (*ch == comma) { + if (isFirstItem) { + edit.descriptor = DataEdit::ListDirectedNullValue; + return edit; + } + // Consume comma & whitespace after previous item. + io.HandleRelativePosition(1); + ch = io.GetNextNonBlank(); + if (!ch) { + return std::nullopt; + } + if (*ch == comma || *ch == '/') { + edit.descriptor = DataEdit::ListDirectedNullValue; + return edit; + } + } + if (imaginaryPart_) { // can't repeat components + return edit; + } + if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count + auto start{connection.positionInRecord}; + int r{0}; + do { + static auto constexpr clamp{(std::numeric_limits::max() - '9') / 10}; + if (r >= clamp) { + r = 0; + break; + } + r = 10 * r + (*ch - '0'); + io.HandleRelativePosition(1); + ch = io.GetCurrentChar(); + } while (ch && *ch >= '0' && *ch <= '9'); + if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero + io.HandleRelativePosition(1); + ch = io.GetCurrentChar(); + if (!ch || *ch == ' ' || *ch == comma || *ch == '/') { // "r*" null + edit.descriptor = DataEdit::ListDirectedNullValue; + return edit; + } + edit.repeat = std::min(r, maxRepeat); + remaining_ = r - edit.repeat; + initialRecordNumber_ = connection.currentRecordNumber; + initialPositionInRecord_ = connection.positionInRecord; + } else { // not a repetition count, just an integer value; rewind + connection.positionInRecord = start; + } + } + if (!imaginaryPart_ && ch && *ch == '(') { + realPart_ = true; + io.HandleRelativePosition(1); + } + return edit; +} + +template int UnformattedIoStatementState::EndIoStatement() { + auto &ext{static_cast &>(*this)}; ExternalFileUnit &unit{ext.unit()}; if (unit.access == Access::Sequential && !unit.recordLength.has_value()) { // Overwrite the first four bytes of the record with its length, @@ -342,21 +564,27 @@ int UnformattedIoStatementState::EndIoStatement() { } u; u.u = unit.furthestPositionInRecord - sizeof u.c; // TODO: Convert record length to little-endian on big-endian host? - if (!(ext.Emit(u.c, sizeof u.c) && ext.HandleAbsolutePosition(0) && - ext.Emit(u.c, sizeof u.c) && ext.AdvanceRecord())) { + if (!(ext.Emit(u.c, sizeof u.c) && + (ext.HandleAbsolutePosition(0), ext.Emit(u.c, sizeof u.c)) && + ext.AdvanceRecord())) { return false; } } return ext.EndIoStatement(); } -template class InternalIoStatementState; -template class InternalIoStatementState; -template class InternalFormattedIoStatementState; -template class InternalFormattedIoStatementState; -template class InternalListIoStatementState; -template class ExternalIoStatementState; -template class ExternalFormattedIoStatementState; -template class ExternalListIoStatementState; -template class UnformattedIoStatementState; +template class InternalIoStatementState; +template class InternalIoStatementState; +template class InternalFormattedIoStatementState; +template class InternalFormattedIoStatementState; +template class InternalListIoStatementState; +template class InternalListIoStatementState; +template class ExternalIoStatementState; +template class ExternalIoStatementState; +template class ExternalFormattedIoStatementState; +template class ExternalFormattedIoStatementState; +template class ExternalListIoStatementState; +template class ExternalListIoStatementState; +template class UnformattedIoStatementState; +template class UnformattedIoStatementState; } diff --git a/runtime/io-stmt.h b/runtime/io-stmt.h index 17549388b060..73b93a1f7e4b 100644 --- a/runtime/io-stmt.h +++ b/runtime/io-stmt.h @@ -11,6 +11,7 @@ #ifndef FORTRAN_RUNTIME_IO_STMT_H_ #define FORTRAN_RUNTIME_IO_STMT_H_ +#include "connection.h" #include "descriptor.h" #include "file.h" #include "format.h" @@ -22,19 +23,19 @@ namespace Fortran::runtime::io { -struct ConnectionState; class ExternalFileUnit; class OpenStatementState; class CloseStatementState; class NoopCloseStatementState; -template + +template class InternalFormattedIoStatementState; -template class InternalListIoStatementState; -template +template class InternalListIoStatementState; +template class ExternalFormattedIoStatementState; -template class ExternalListIoStatementState; -template class UnformattedIoStatementState; +template class ExternalListIoStatementState; +template class UnformattedIoStatementState; // The Cookie type in the I/O API is a pointer (for C) to this class. class IoStatementState { @@ -42,15 +43,20 @@ class IoStatementState { template explicit IoStatementState(A &x) : u_{x} {} // These member functions each project themselves into the active alternative. - // They're used by per-data-item routines in the I/O API(e.g., OutputReal64) + // They're used by per-data-item routines in the I/O API (e.g., OutputReal64) // to interact with the state of the I/O statement in progress. // This design avoids virtual member functions and function pointers, - // which may not have good support in some use cases. - DataEdit GetNextDataEdit(int = 1); + // which may not have good support in some runtime environments. + std::optional GetNextDataEdit(int = 1); bool Emit(const char *, std::size_t); + std::optional GetCurrentChar(); // vacant after end of record bool AdvanceRecord(int = 1); + void BackspaceRecord(); + void HandleRelativePosition(std::int64_t); int EndIoStatement(); ConnectionState &GetConnectionState(); + IoErrorHandler &GetIoErrorHandler() const; + ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit MutableModes &mutableModes(); // N.B.: this also works with base classes @@ -64,21 +70,31 @@ class IoStatementState { }, u_); } - IoErrorHandler &GetIoErrorHandler() const; bool EmitRepeated(char, std::size_t); bool EmitField(const char *, std::size_t length, std::size_t width); + void SkipSpaces(std::optional &remaining); + std::optional NextInField(std::optional &remaining); + std::optional GetNextNonBlank(); // can advance record private: std::variant, std::reference_wrapper, std::reference_wrapper, - std::reference_wrapper>, - std::reference_wrapper>, - std::reference_wrapper>, - std::reference_wrapper>, - std::reference_wrapper>, - std::reference_wrapper>> + std::reference_wrapper< + InternalFormattedIoStatementState>, + std::reference_wrapper< + InternalFormattedIoStatementState>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper< + ExternalFormattedIoStatementState>, + std::reference_wrapper< + ExternalFormattedIoStatementState>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>> u_; }; @@ -87,54 +103,80 @@ class IoStatementState { struct IoStatementBase : public DefaultFormatControlCallbacks { using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks; int EndIoStatement(); - DataEdit GetNextDataEdit(int = 1); // crashing default + std::optional GetNextDataEdit(IoStatementState &, int = 1); + ExternalFileUnit *GetExternalFileUnit() const { return nullptr; } }; struct InputStatementState {}; struct OutputStatementState {}; -template -using IoDirectionState = - std::conditional_t; +template +using IoDirectionState = std::conditional_t; struct FormattedStatementState {}; -template struct ListDirectedStatementState {}; -template<> struct ListDirectedStatementState { +// Common state for list-directed internal & external I/O +template struct ListDirectedStatementState {}; +template<> struct ListDirectedStatementState { static std::size_t RemainingSpaceInRecord(const ConnectionState &); bool NeedAdvance(const ConnectionState &, std::size_t) const; bool EmitLeadingSpaceOrAdvance( IoStatementState &, std::size_t, bool isCharacter = false); + std::optional GetNextDataEdit( + IoStatementState &, int maxRepeat = 1); bool lastWasUndelimitedCharacter{false}; }; +template<> class ListDirectedStatementState { +public: + // Skips value separators, handles repetition and null values. + // Vacant when '/' appears; present with descriptor == ListDirectedNullValue + // when a null value appears. + std::optional GetNextDataEdit( + IoStatementState &, int maxRepeat = 1); + +private: + int remaining_{0}; // for "r*" repetition + std::int64_t initialRecordNumber_; + std::int64_t initialPositionInRecord_; + bool isFirstItem_{true}; // leading separator implies null first item + bool hitSlash_{false}; // once '/' is seen, nullify further items + bool realPart_{false}; + bool imaginaryPart_{false}; +}; -template +template class InternalIoStatementState : public IoStatementBase, - public IoDirectionState { + public IoDirectionState { public: using CharType = CHAR; - using Buffer = std::conditional_t; + using Buffer = + std::conditional_t; InternalIoStatementState(Buffer, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); InternalIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); int EndIoStatement(); bool Emit(const CharType *, std::size_t chars /* not bytes */); + std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); + void BackspaceRecord(); ConnectionState &GetConnectionState() { return unit_; } MutableModes &mutableModes() { return unit_.modes; } + void HandleRelativePosition(std::int64_t); + void HandleAbsolutePosition(std::int64_t); protected: bool free_{true}; - InternalDescriptorUnit unit_; + InternalDescriptorUnit unit_; }; -template +template class InternalFormattedIoStatementState - : public InternalIoStatementState, + : public InternalIoStatementState, public FormattedStatementState { public: using CharType = CHAR; - using typename InternalIoStatementState::Buffer; + using typename InternalIoStatementState::Buffer; InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); @@ -143,42 +185,34 @@ class InternalFormattedIoStatementState int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } int EndIoStatement(); - DataEdit GetNextDataEdit(int maxRepeat = 1) { + std::optional GetNextDataEdit( + IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } - bool HandleRelativePosition(std::int64_t); - bool HandleAbsolutePosition(std::int64_t); private: IoStatementState ioStatementState_; // points to *this - using InternalIoStatementState::unit_; + using InternalIoStatementState::unit_; // format_ *must* be last; it may be partial someday FormatControl format_; }; -template -class InternalListIoStatementState - : public InternalIoStatementState, - public ListDirectedStatementState { +template +class InternalListIoStatementState : public InternalIoStatementState, + public ListDirectedStatementState { public: using CharType = CHAR; - using typename InternalIoStatementState::Buffer; + using typename InternalIoStatementState::Buffer; InternalListIoStatementState(Buffer internal, std::size_t internalLength, const char *sourceFile = nullptr, int sourceLine = 0); InternalListIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } - DataEdit GetNextDataEdit(int maxRepeat = 1) { - DataEdit edit; - edit.descriptor = DataEdit::ListDirected; - edit.repeat = maxRepeat; - edit.modes = InternalIoStatementState::mutableModes(); - return edit; - } + using ListDirectedStatementState::GetNextDataEdit; private: - using InternalIoStatementState::unit_; IoStatementState ioStatementState_; // points to *this + using InternalIoStatementState::unit_; }; class ExternalIoStatementBase : public IoStatementBase { @@ -189,29 +223,31 @@ class ExternalIoStatementBase : public IoStatementBase { MutableModes &mutableModes(); ConnectionState &GetConnectionState(); int EndIoStatement(); + ExternalFileUnit *GetExternalFileUnit() { return &unit_; } private: ExternalFileUnit &unit_; }; -template +template class ExternalIoStatementState : public ExternalIoStatementBase, - public IoDirectionState { + public IoDirectionState { public: using ExternalIoStatementBase::ExternalIoStatementBase; int EndIoStatement(); bool Emit(const char *, std::size_t chars /* not bytes */); bool Emit(const char16_t *, std::size_t chars /* not bytes */); bool Emit(const char32_t *, std::size_t chars /* not bytes */); + std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); - bool HandleRelativePosition(std::int64_t); - bool HandleAbsolutePosition(std::int64_t); + void BackspaceRecord(); + void HandleRelativePosition(std::int64_t); + void HandleAbsolutePosition(std::int64_t); }; -template -class ExternalFormattedIoStatementState - : public ExternalIoStatementState, - public FormattedStatementState { +template +class ExternalFormattedIoStatementState : public ExternalIoStatementState, + public FormattedStatementState { public: using CharType = CHAR; ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, @@ -219,7 +255,8 @@ class ExternalFormattedIoStatementState int sourceLine = 0); MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); - DataEdit GetNextDataEdit(int maxRepeat = 1) { + std::optional GetNextDataEdit( + IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } @@ -231,25 +268,18 @@ class ExternalFormattedIoStatementState FormatControl format_; }; -template -class ExternalListIoStatementState - : public ExternalIoStatementState, - public ListDirectedStatementState { +template +class ExternalListIoStatementState : public ExternalIoStatementState, + public ListDirectedStatementState { public: - using ExternalIoStatementState::ExternalIoStatementState; - DataEdit GetNextDataEdit(int maxRepeat = 1) { - DataEdit edit; - edit.descriptor = DataEdit::ListDirected; - edit.repeat = maxRepeat; - edit.modes = ExternalIoStatementState::mutableModes(); - return edit; - } + using ExternalIoStatementState::ExternalIoStatementState; + using ListDirectedStatementState::GetNextDataEdit; }; -template -class UnformattedIoStatementState : public ExternalIoStatementState { +template +class UnformattedIoStatementState : public ExternalIoStatementState { public: - using ExternalIoStatementState::ExternalIoStatementState; + using ExternalIoStatementState::ExternalIoStatementState; int EndIoStatement(); }; @@ -300,18 +330,28 @@ class NoopCloseStatementState : public IoStatementBase { ConnectionState connection_; }; -extern template class InternalIoStatementState; -extern template class InternalIoStatementState; -extern template class InternalFormattedIoStatementState; -extern template class InternalFormattedIoStatementState; -extern template class InternalListIoStatementState; -extern template class ExternalIoStatementState; -extern template class ExternalFormattedIoStatementState; -extern template class ExternalListIoStatementState; -extern template class UnformattedIoStatementState; -extern template class FormatControl>; -extern template class FormatControl>; -extern template class FormatControl>; +extern template class InternalIoStatementState; +extern template class InternalIoStatementState; +extern template class InternalFormattedIoStatementState; +extern template class InternalFormattedIoStatementState; +extern template class InternalListIoStatementState; +extern template class InternalListIoStatementState; +extern template class ExternalIoStatementState; +extern template class ExternalIoStatementState; +extern template class ExternalFormattedIoStatementState; +extern template class ExternalFormattedIoStatementState; +extern template class ExternalListIoStatementState; +extern template class ExternalListIoStatementState; +extern template class UnformattedIoStatementState; +extern template class UnformattedIoStatementState; +extern template class FormatControl< + InternalFormattedIoStatementState>; +extern template class FormatControl< + InternalFormattedIoStatementState>; +extern template class FormatControl< + ExternalFormattedIoStatementState>; +extern template class FormatControl< + ExternalFormattedIoStatementState>; } #endif // FORTRAN_RUNTIME_IO_STMT_H_ diff --git a/runtime/iostat.cpp b/runtime/iostat.cpp new file mode 100644 index 000000000000..6e146faecdae --- /dev/null +++ b/runtime/iostat.cpp @@ -0,0 +1,31 @@ +//===-- runtime/iostat.cpp --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "iostat.h" + +namespace Fortran::runtime::io { +const char *IostatErrorString(int iostat) { + switch (iostat) { + case IostatOk: return "No error"; + case IostatEnd: return "End of file during input"; + case IostatEor: return "End of record during non-advancing input"; + case IostatUnflushable: return "FLUSH not possible"; + case IostatInquireInternalUnit: return "INQUIRE on internal unit"; + case IostatGenericError: + return "I/O error"; // dummy value, there's always a message + case IostatRecordWriteOverrun: return "Excessive output to fixed-size record"; + case IostatRecordReadOverrun: return "Excessive input from fixed-size record"; + case IostatInternalWriteOverrun: + return "Internal write overran available records"; + case IostatErrorInFormat: return "Invalid FORMAT"; + case IostatErrorInKeyword: return "Bad keyword argument value"; + default: return nullptr; + } +} + +} diff --git a/runtime/iostat.h b/runtime/iostat.h new file mode 100644 index 000000000000..b5b78360feb2 --- /dev/null +++ b/runtime/iostat.h @@ -0,0 +1,53 @@ +//===-- runtime/iostat.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Defines the values returned by the runtime for IOSTAT= specifiers +// on I/O statements. + +#ifndef FORTRAN_RUNTIME_IOSTAT_H_ +#define FORTRAN_RUNTIME_IOSTAT_H_ +#include "magic-numbers.h" +namespace Fortran::runtime::io { + +// The value of IOSTAT= is zero when no error, end-of-record, +// or end-of-file condition has arisen; errors are positive values. +// (See 12.11.5 in Fortran 2018 for the complete requirements; +// these constants must match the values of their corresponding +// named constants in the predefined module ISO_FORTRAN_ENV, so +// they're actually defined in another magic-numbers.h header file +// so that they can be included both here and there.) +enum Iostat { + IostatOk = 0, // no error, EOF, or EOR condition + + // These error codes are required by Fortran (see 12.10.2.16-17) to be + // negative integer values + IostatEnd = FORTRAN_RUNTIME_IOSTAT_END, // end-of-file on input & no error + // End-of-record on non-advancing input, no EOF or error + IostatEor = FORTRAN_RUNTIME_IOSTAT_EOR, + + // This value is also required to be negative (12.11.5 bullet 6). + // It signifies a FLUSH statement on an unflushable unit. + IostatUnflushable = FORTRAN_RUNTIME_IOSTAT_FLUSH, + + // Other errors are positive. We use "errno" values unchanged. + // This error is exported in ISO_Fortran_env. + IostatInquireInternalUnit = FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT, + + // The remaining error codes are not exported. + IostatGenericError = 1001, // see IOMSG= for details + IostatRecordWriteOverrun, + IostatRecordReadOverrun, + IostatInternalWriteOverrun, + IostatErrorInFormat, + IostatErrorInKeyword, +}; + +const char *IostatErrorString(int); + +} +#endif // FORTRAN_RUNTIME_IOSTAT_H_ diff --git a/runtime/lock.h b/runtime/lock.h index ecf55889aaff..5c614801f2ae 100644 --- a/runtime/lock.h +++ b/runtime/lock.h @@ -6,11 +6,12 @@ // //===----------------------------------------------------------------------===// -// Wraps pthread_mutex_t (or whatever) +// Wraps a mutex #ifndef FORTRAN_RUNTIME_LOCK_H_ #define FORTRAN_RUNTIME_LOCK_H_ +#include "terminator.h" #include namespace Fortran::runtime { diff --git a/runtime/magic-numbers.h b/runtime/magic-numbers.h index b60722894009..55790c5ee60a 100644 --- a/runtime/magic-numbers.h +++ b/runtime/magic-numbers.h @@ -26,7 +26,7 @@ and are used "raw" as IOSTAT values. #define FORTRAN_RUNTIME_IOSTAT_END (-1) #define FORTRAN_RUNTIME_IOSTAT_EOR (-2) #define FORTRAN_RUNTIME_IOSTAT_FLUSH (-3) -#define FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT 255 +#define FORTRAN_RUNTIME_IOSTAT_INQUIRE_INTERNAL_UNIT 256 #define FORTRAN_RUNTIME_STAT_FAILED_IMAGE 10 #define FORTRAN_RUNTIME_STAT_LOCKED 11 diff --git a/runtime/main.cpp b/runtime/main.cpp index e7f4200b2777..707e35cc99a0 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -9,7 +9,6 @@ #include "main.h" #include "environment.h" #include "terminator.h" -#include "unit.h" #include #include #include @@ -28,11 +27,10 @@ static void ConfigureFloatingPoint() { } extern "C" { - void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) { std::atexit(Fortran::runtime::NotifyOtherImagesOfNormalEnd); Fortran::runtime::executionEnvironment.Configure(argc, argv, envp); ConfigureFloatingPoint(); - Fortran::runtime::io::ExternalFileUnit::InitializePredefinedUnits(); + // I/O is initialized on demand so that it works for non-Fortran main(). } } diff --git a/runtime/numeric-output.cpp b/runtime/numeric-output.cpp deleted file mode 100644 index fdf0cde340e1..000000000000 --- a/runtime/numeric-output.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//===-- runtime/numeric-output.cpp ------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "numeric-output.h" -#include "flang/Common/unsigned-const-division.h" -#include - -namespace Fortran::runtime::io { - -bool EditIntegerOutput( - IoStatementState &io, const DataEdit &edit, std::int64_t n) { - char buffer[66], *end = &buffer[sizeof buffer], *p = end; - std::uint64_t un{static_cast(n < 0 ? -n : n)}; - int signChars{0}; - switch (edit.descriptor) { - case DataEdit::ListDirected: - case 'G': - case 'I': - if (n < 0 || (edit.modes.editingFlags & signPlus)) { - signChars = 1; // '-' or '+' - } - while (un > 0) { - auto quotient{common::DivideUnsignedBy(un)}; - *--p = '0' + un - 10 * quotient; - un = quotient; - } - break; - case 'B': - for (; un > 0; un >>= 1) { - *--p = '0' + (un & 1); - } - break; - case 'O': - for (; un > 0; un >>= 3) { - *--p = '0' + (un & 7); - } - break; - case 'Z': - for (; un > 0; un >>= 4) { - int digit = un & 0xf; - *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit; - } - break; - default: - io.GetIoErrorHandler().Crash( - "Data edit descriptor '%c' may not be used with an INTEGER data item", - edit.descriptor); - return false; - } - - int digits = end - p; - int leadingZeroes{0}; - int editWidth{edit.width.value_or(0)}; - if (edit.digits && digits <= *edit.digits) { // Iw.m - if (*edit.digits == 0 && n == 0) { - // Iw.0 with zero value: output field must be blank. For I0.0 - // and a zero value, emit one blank character. - signChars = 0; // in case of SP - editWidth = std::max(1, editWidth); - } else { - leadingZeroes = *edit.digits - digits; - } - } else if (n == 0) { - leadingZeroes = 1; - } - int total{signChars + leadingZeroes + digits}; - if (editWidth > 0 && total > editWidth) { - return io.EmitRepeated('*', editWidth); - } - int leadingSpaces{std::max(0, editWidth - total)}; - if (edit.IsListDirected()) { - if (static_cast(total) > - io.GetConnectionState().RemainingSpaceInRecord() && - !io.AdvanceRecord()) { - return false; - } - leadingSpaces = 1; - } - return io.EmitRepeated(' ', leadingSpaces) && - io.Emit(n < 0 ? "-" : "+", signChars) && - io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits); -} - -// Formats the exponent (see table 13.1 for all the cases) -const char *RealOutputEditingBase::FormatExponent( - int expo, const DataEdit &edit, int &length) { - char *eEnd{&exponent_[sizeof exponent_]}; - char *exponent{eEnd}; - for (unsigned e{static_cast(std::abs(expo))}; e > 0;) { - unsigned quotient{common::DivideUnsignedBy(e)}; - *--exponent = '0' + e - 10 * quotient; - e = quotient; - } - if (edit.expoDigits) { - if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0 - while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) { - *--exponent = '0'; - } - } else if (exponent == eEnd) { - *--exponent = '0'; // Ew.dE0 with zero-valued exponent - } - } else { // ensure at least two exponent digits - while (exponent + 2 > eEnd) { - *--exponent = '0'; - } - } - *--exponent = expo < 0 ? '-' : '+'; - if (edit.expoDigits || exponent + 3 == eEnd) { - *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G' - } - length = eEnd - exponent; - return exponent; -} - -bool RealOutputEditingBase::EmitPrefix( - const DataEdit &edit, std::size_t length, std::size_t width) { - if (edit.IsListDirected()) { - int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart - ? 2 - : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0 : 1}; - int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart || - edit.descriptor == DataEdit::ListDirectedImaginaryPart - ? 1 - : 0}; - length += prefixLength + suffixLength; - ConnectionState &connection{io_.GetConnectionState()}; - return (connection.positionInRecord == 0 || - length <= connection.RemainingSpaceInRecord() || - io_.AdvanceRecord()) && - io_.Emit(" (", prefixLength); - } else if (width > length) { - return io_.EmitRepeated(' ', width - length); - } else { - return true; - } -} - -bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) { - if (edit.descriptor == DataEdit::ListDirectedRealPart) { - return io_.Emit(edit.modes.editingFlags & decimalComma ? ";" : ",", 1); - } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { - return io_.Emit(")", 1); - } else { - return true; - } -} - -} diff --git a/runtime/terminator.cpp b/runtime/terminator.cpp index 74594ba65d84..cc0f4d23a439 100644 --- a/runtime/terminator.cpp +++ b/runtime/terminator.cpp @@ -18,8 +18,18 @@ namespace Fortran::runtime { CrashArgs(message, ap); } +static void (*crashHandler)(const char *, va_list &){nullptr}; + +void Terminator::RegisterCrashHandler( + void (*handler)(const char *, va_list &)) { + crashHandler = handler; +} + [[noreturn]] void Terminator::CrashArgs( const char *message, va_list &ap) const { + if (crashHandler) { + crashHandler(message, ap); + } std::fputs("\nfatal Fortran runtime error", stderr); if (sourceFileName_) { std::fprintf(stderr, "(%s", sourceFileName_); diff --git a/runtime/terminator.h b/runtime/terminator.h index 8cfc5cc8b123..20f5abd435c0 100644 --- a/runtime/terminator.h +++ b/runtime/terminator.h @@ -33,11 +33,15 @@ class Terminator { [[noreturn]] void CheckFailed( const char *predicate, const char *file, int line) const; + // For test harnessing - overrides CrashArgs(). + static void RegisterCrashHandler(void (*)(const char *, va_list &)); + private: const char *sourceFileName_{nullptr}; int sourceLine_{0}; }; +// RUNTIME_CHECK() guarantees evaluation of its predicate. #define RUNTIME_CHECK(terminator, pred) \ if (pred) \ ; \ diff --git a/runtime/tools.cpp b/runtime/tools.cpp index b254baf07b46..f4dffb0027f1 100644 --- a/runtime/tools.cpp +++ b/runtime/tools.cpp @@ -25,13 +25,22 @@ OwningPtr SaveDefaultCharacter( static bool CaseInsensitiveMatch( const char *value, std::size_t length, const char *possibility) { - for (; length-- > 0; ++value, ++possibility) { - char ch{*value}; + for (; length-- > 0; ++possibility) { + char ch{*value++}; if (ch >= 'a' && ch <= 'z') { ch += 'A' - 'a'; } - if (*possibility == '\0' || ch != *possibility) { - return false; + if (*possibility != ch) { + if (*possibility != '\0' || ch != ' ') { + return false; + } + // Ignore trailing blanks (12.5.6.2 p1) + while (length-- > 0) { + if (*value++ != ' ') { + return false; + } + } + return true; } } return *possibility == '\0'; @@ -48,4 +57,14 @@ int IdentifyValue( } return -1; } + +void ToFortranDefaultCharacter( + char *to, std::size_t toLength, const char *from) { + std::size_t len{std::strlen(from)}; + std::memcpy(to, from, std::max(toLength, len)); + if (len < toLength) { + std::memset(to + len, ' ', toLength - len); + } +} + } diff --git a/runtime/tools.h b/runtime/tools.h index 99571782dc07..ec7982c2082f 100644 --- a/runtime/tools.h +++ b/runtime/tools.h @@ -28,10 +28,8 @@ OwningPtr SaveDefaultCharacter( int IdentifyValue( const char *value, std::size_t length, const char *possibilities[]); -// A std::map<> customized to use the runtime's memory allocator -template -using MapAllocator = Allocator, VALUE>>; -template> -using Map = std::map>; +// Truncates or pads as necessary +void ToFortranDefaultCharacter( + char *to, std::size_t toLength, const char *from); } #endif // FORTRAN_RUNTIME_TOOLS_H_ diff --git a/runtime/transformational.cpp b/runtime/transformational.cpp index f2abb37c4c43..f0577c6e7dd3 100644 --- a/runtime/transformational.cpp +++ b/runtime/transformational.cpp @@ -7,12 +7,11 @@ //===----------------------------------------------------------------------===// #include "transformational.h" -#include "flang/Common/idioms.h" +#include "memory.h" +#include "terminator.h" #include "flang/Evaluate/integer.h" #include -#include #include -#include namespace Fortran::runtime { @@ -22,18 +21,22 @@ static inline std::int64_t GetInt64(const char *p, std::size_t bytes) { case 2: return *reinterpret_cast(p); case 4: return *reinterpret_cast(p); case 8: return *reinterpret_cast(p); - default: CRASH_NO_CASE; + default: + Terminator terminator{__FILE__, __LINE__}; + terminator.Crash("no case for %dz bytes", bytes); } } // F2018 16.9.163 -std::unique_ptr RESHAPE(const Descriptor &source, - const Descriptor &shape, const Descriptor *pad, const Descriptor *order) { +OwningPtr RESHAPE(const Descriptor &source, const Descriptor &shape, + const Descriptor *pad, const Descriptor *order) { // Compute and check the rank of the result. - CHECK(shape.rank() == 1); - CHECK(shape.type().IsInteger()); + Terminator terminator{__FILE__, __LINE__}; + RUNTIME_CHECK(terminator, shape.rank() == 1); + RUNTIME_CHECK(terminator, shape.type().IsInteger()); SubscriptValue resultRank{shape.GetDimension(0).Extent()}; - CHECK(resultRank >= 0 && resultRank <= static_cast(maxRank)); + RUNTIME_CHECK(terminator, + resultRank >= 0 && resultRank <= static_cast(maxRank)); // Extract and check the shape of the result; compute its element count. SubscriptValue lowerBound[maxRank]; // all 1's @@ -45,7 +48,7 @@ std::unique_ptr RESHAPE(const Descriptor &source, lowerBound[j] = 1; resultExtent[j] = GetInt64(shape.Element(&shapeSubscript), shapeElementBytes); - CHECK(resultExtent[j] >= 0); + RUNTIME_CHECK(terminator, resultExtent[j] >= 0); resultElements *= resultExtent[j]; } @@ -55,23 +58,25 @@ std::unique_ptr RESHAPE(const Descriptor &source, std::size_t sourceElements{source.Elements()}; std::size_t padElements{pad ? pad->Elements() : 0}; if (resultElements < sourceElements) { - CHECK(padElements > 0); - CHECK(pad->ElementBytes() == elementBytes); + RUNTIME_CHECK(terminator, padElements > 0); + RUNTIME_CHECK(terminator, pad->ElementBytes() == elementBytes); } // Extract and check the optional ORDER= argument, which must be a // permutation of [1..resultRank]. int dimOrder[maxRank]; if (order) { - CHECK(order->rank() == 1); - CHECK(order->type().IsInteger()); - CHECK(order->GetDimension(0).Extent() == resultRank); - std::bitset values; + RUNTIME_CHECK(terminator, order->rank() == 1); + RUNTIME_CHECK(terminator, order->type().IsInteger()); + RUNTIME_CHECK(terminator, order->GetDimension(0).Extent() == resultRank); + std::uint64_t values{0}; SubscriptValue orderSubscript{order->GetDimension(0).LowerBound()}; for (SubscriptValue j{0}; j < resultRank; ++j, ++orderSubscript) { - auto k{GetInt64(order->Element(orderSubscript), shapeElementBytes)}; - CHECK(k >= 1 && k <= resultRank && !values.test(k - 1)); - values.set(k - 1); + auto k{GetInt64( + order->OffsetElement(orderSubscript), shapeElementBytes)}; + RUNTIME_CHECK( + terminator, k >= 1 && k <= resultRank && !((values >> k) & 1)); + values |= std::uint64_t{1} << k; dimOrder[k - 1] = j; } } else { @@ -84,7 +89,7 @@ std::unique_ptr RESHAPE(const Descriptor &source, const DescriptorAddendum *sourceAddendum{source.Addendum()}; const DerivedType *sourceDerivedType{ sourceAddendum ? sourceAddendum->derivedType() : nullptr}; - std::unique_ptr result; + OwningPtr result; if (sourceDerivedType) { result = Descriptor::Create(*sourceDerivedType, nullptr, resultRank, resultExtent, CFI_attribute_allocatable); @@ -94,7 +99,7 @@ std::unique_ptr RESHAPE(const Descriptor &source, CFI_attribute_allocatable); // TODO rearrange these arguments } DescriptorAddendum *resultAddendum{result->Addendum()}; - CHECK(resultAddendum); + RUNTIME_CHECK(terminator, resultAddendum); resultAddendum->flags() |= DescriptorAddendum::DoNotFinalize; if (sourceDerivedType) { std::size_t lenParameters{sourceDerivedType->lenParameters()}; @@ -106,7 +111,7 @@ std::unique_ptr RESHAPE(const Descriptor &source, // Allocate storage for the result's data. int status{result->Allocate(lowerBound, resultExtent, elementBytes)}; if (status != CFI_SUCCESS) { - common::die("RESHAPE: Allocate failed (error %d)", status); + terminator.Crash("RESHAPE: Allocate failed (error %d)", status); } // Populate the result's elements. diff --git a/runtime/transformational.h b/runtime/transformational.h index c57231c7951a..2785614b6937 100644 --- a/runtime/transformational.h +++ b/runtime/transformational.h @@ -10,12 +10,11 @@ #define FORTRAN_RUNTIME_TRANSFORMATIONAL_H_ #include "descriptor.h" -#include +#include "memory.h" namespace Fortran::runtime { -std::unique_ptr RESHAPE(const Descriptor &source, - const Descriptor &shape, const Descriptor *pad = nullptr, - const Descriptor *order = nullptr); +OwningPtr RESHAPE(const Descriptor &source, const Descriptor &shape, + const Descriptor *pad = nullptr, const Descriptor *order = nullptr); } #endif // FORTRAN_RUNTIME_TRANSFORMATIONAL_H_ diff --git a/runtime/type-code.h b/runtime/type-code.h index 5136bb8d32ff..af09be560651 100644 --- a/runtime/type-code.h +++ b/runtime/type-code.h @@ -9,8 +9,8 @@ #ifndef FORTRAN_RUNTIME_TYPE_CODE_H_ #define FORTRAN_RUNTIME_TYPE_CODE_H_ -#include "flang/ISO_Fortran_binding.h" #include "flang/Common/Fortran.h" +#include "flang/ISO_Fortran_binding.h" namespace Fortran::runtime { diff --git a/runtime/unit-map.cpp b/runtime/unit-map.cpp new file mode 100644 index 000000000000..c505f0b0814a --- /dev/null +++ b/runtime/unit-map.cpp @@ -0,0 +1,72 @@ +//===-- runtime/unit-map.cpp ------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "unit-map.h" + +namespace Fortran::runtime::io { + +ExternalFileUnit *UnitMap::LookUpForClose(int n) { + CriticalSection critical{lock_}; + Chain *previous{nullptr}; + int hash{Hash(n)}; + for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { + if (p->unit.unitNumber() == n) { + if (previous) { + previous->next.swap(p->next); + } else { + bucket_[hash].swap(p->next); + } + // p->next.get() == p at this point; the next swap pushes p on closing_ + closing_.swap(p->next); + return &p->unit; + } + } + return nullptr; +} + +void UnitMap::DestroyClosed(ExternalFileUnit &unit) { + Chain *p{nullptr}; + { + CriticalSection critical{lock_}; + Chain *previous{nullptr}; + for (p = closing_.get(); p; previous = p, p = p->next.get()) { + if (&p->unit == &unit) { + if (previous) { + previous->next.swap(p->next); + } else { + closing_.swap(p->next); + } + break; + } + } + } + if (p) { + p->unit.~ExternalFileUnit(); + FreeMemory(p); + } +} + +void UnitMap::CloseAll(IoErrorHandler &handler) { + CriticalSection critical{lock_}; + for (int j{0}; j < buckets_; ++j) { + while (Chain * p{bucket_[j].get()}) { + bucket_[j].swap(p->next); // pops p from head of list + p->unit.CloseUnit(CloseStatus::Keep, handler); + p->unit.~ExternalFileUnit(); + FreeMemory(p); + } + } +} + +ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) { + Chain &chain{New{}(terminator, n)}; + chain.next.reset(&chain); + bucket_[Hash(n)].swap(chain.next); // pushes new node as list head + return chain.unit; +} +} diff --git a/runtime/unit-map.h b/runtime/unit-map.h new file mode 100644 index 000000000000..550716fc25e8 --- /dev/null +++ b/runtime/unit-map.h @@ -0,0 +1,87 @@ +//===-- runtime/unit-map.h --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Maps Fortran unit numbers to their ExternalFileUnit instances. +// A simple hash table with forward-linked chains per bucket. + +#ifndef FORTRAN_RUNTIME_UNIT_MAP_H_ +#define FORTRAN_RUNTIME_UNIT_MAP_H_ + +#include "lock.h" +#include "memory.h" +#include "unit.h" + +namespace Fortran::runtime::io { + +class UnitMap { +public: + ExternalFileUnit *LookUp(int n) { + CriticalSection critical{lock_}; + return Find(n); + } + + ExternalFileUnit &LookUpOrCreate( + int n, const Terminator &terminator, bool *wasExtant) { + CriticalSection critical{lock_}; + auto *p{Find(n)}; + if (wasExtant) { + *wasExtant = p != nullptr; + } + if (p) { + return *p; + } + return Create(n, terminator); + } + + ExternalFileUnit &NewUnit(const Terminator &terminator) { + CriticalSection critical{lock_}; + return Create(nextNewUnit_--, terminator); + } + + // To prevent races, the unit is removed from the map if it exists, + // and put on the closing_ list until DestroyClosed() is called. + ExternalFileUnit *LookUpForClose(int); + + void DestroyClosed(ExternalFileUnit &); + void CloseAll(IoErrorHandler &); + +private: + struct Chain { + explicit Chain(int n) : unit{n} {} + ExternalFileUnit unit; + OwningPtr next{nullptr}; + }; + + static constexpr int buckets_{1031}; // must be prime + int Hash(int n) { return n % buckets_; } + + ExternalFileUnit *Find(int n) { + Chain *previous{nullptr}; + int hash{Hash(n)}; + for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { + if (p->unit.unitNumber() == n) { + if (previous) { + // Move found unit to front of chain for quicker lookup next time + previous->next.swap(p->next); // now p->next.get() == p + bucket_[hash].swap(p->next); // now bucket_[hash].get() == p + } + return &p->unit; + } + } + return nullptr; + } + + ExternalFileUnit &Create(int, const Terminator &); + + Lock lock_; + OwningPtr bucket_[buckets_]{}; // all owned by *this + int nextNewUnit_{-1000}; // see 12.5.6.12 in Fortran 2018 + OwningPtr closing_{nullptr}; // units during CLOSE statement +}; +} +#endif // FORTRAN_RUNTIME_UNIT_MAP_H_ diff --git a/runtime/unit.cpp b/runtime/unit.cpp index 277d36b39a08..7c9801f23411 100644 --- a/runtime/unit.cpp +++ b/runtime/unit.cpp @@ -7,21 +7,23 @@ //===----------------------------------------------------------------------===// #include "unit.h" +#include "io-error.h" #include "lock.h" -#include "memory.h" -#include "tools.h" -#include -#include +#include "unit-map.h" namespace Fortran::runtime::io { -static Lock mapLock; -static Terminator mapTerminator; -static Map unitMap{ - MapAllocator{mapTerminator}}; +// The per-unit data structures are created on demand so that Fortran I/O +// should work without a Fortran main program. +static Lock unitMapLock; +static UnitMap *unitMap{nullptr}; static ExternalFileUnit *defaultOutput{nullptr}; void FlushOutputOnCrash(const Terminator &terminator) { + if (!defaultOutput) { + return; + } + CriticalSection critical{unitMapLock}; if (defaultOutput) { IoErrorHandler handler{terminator}; handler.HasIoStat(); // prevent nested crash if flush has error @@ -30,14 +32,11 @@ void FlushOutputOnCrash(const Terminator &terminator) { } ExternalFileUnit *ExternalFileUnit::LookUp(int unit) { - CriticalSection criticalSection{mapLock}; - auto iter{unitMap.find(unit)}; - return iter == unitMap.end() ? nullptr : &iter->second; + return GetUnitMap().LookUp(unit); } ExternalFileUnit &ExternalFileUnit::LookUpOrCrash( int unit, const Terminator &terminator) { - CriticalSection criticalSection{mapLock}; ExternalFileUnit *file{LookUp(unit)}; if (!file) { terminator.Crash("Not an open I/O unit number: %d", unit); @@ -45,25 +44,22 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCrash( return *file; } -ExternalFileUnit &ExternalFileUnit::LookUpOrCreate(int unit, bool *wasExtant) { - CriticalSection criticalSection{mapLock}; - auto pair{unitMap.emplace(unit, unit)}; - if (wasExtant) { - *wasExtant = !pair.second; - } - return pair.first->second; +ExternalFileUnit &ExternalFileUnit::LookUpOrCreate( + int unit, const Terminator &terminator, bool *wasExtant) { + return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant); } -int ExternalFileUnit::NewUnit() { - CriticalSection criticalSection{mapLock}; - static int nextNewUnit{-1000}; // see 12.5.6.12 in Fortran 2018 - return --nextNewUnit; +ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) { + return GetUnitMap().LookUpForClose(unit); +} + +int ExternalFileUnit::NewUnit(const Terminator &terminator) { + return GetUnitMap().NewUnit(terminator).unitNumber(); } void ExternalFileUnit::OpenUnit(OpenStatus status, Position position, OwningPtr &&newPath, std::size_t newPathLength, IoErrorHandler &handler) { - CriticalSection criticalSection{lock()}; if (IsOpen()) { if (status == OpenStatus::Old && (!newPath.get() || @@ -82,74 +78,93 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, Position position, } void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { - { - CriticalSection criticalSection{lock()}; - Flush(handler); - Close(status, handler); - } - CriticalSection criticalSection{mapLock}; - auto iter{unitMap.find(unitNumber_)}; - if (iter != unitMap.end()) { - unitMap.erase(iter); - } + Flush(handler); + Close(status, handler); +} + +void ExternalFileUnit::DestroyClosed() { + GetUnitMap().DestroyClosed(*this); // destroys *this } -void ExternalFileUnit::InitializePredefinedUnits() { - ExternalFileUnit &out{ExternalFileUnit::LookUpOrCreate(6)}; +UnitMap &ExternalFileUnit::GetUnitMap() { + if (unitMap) { + return *unitMap; + } + CriticalSection critical{unitMapLock}; + if (unitMap) { + return *unitMap; + } + Terminator terminator{__FILE__, __LINE__}; + unitMap = &New{}(terminator); + ExternalFileUnit &out{ExternalFileUnit::LookUpOrCreate(6, terminator)}; out.Predefine(1); out.set_mayRead(false); out.set_mayWrite(true); out.set_mayPosition(false); defaultOutput = &out; - ExternalFileUnit &in{ExternalFileUnit::LookUpOrCreate(5)}; + ExternalFileUnit &in{ExternalFileUnit::LookUpOrCreate(5, terminator)}; in.Predefine(0); in.set_mayRead(true); in.set_mayWrite(false); in.set_mayPosition(false); // TODO: Set UTF-8 mode from the environment + return *unitMap; } void ExternalFileUnit::CloseAll(IoErrorHandler &handler) { - CriticalSection criticalSection{mapLock}; - defaultOutput = nullptr; - while (!unitMap.empty()) { - auto &pair{*unitMap.begin()}; - pair.second.CloseUnit(CloseStatus::Keep, handler); + CriticalSection critical{unitMapLock}; + if (unitMap) { + unitMap->CloseAll(handler); + FreeMemoryAndNullify(unitMap); } -} - -bool ExternalFileUnit::SetPositionInRecord( - std::int64_t n, IoErrorHandler &handler) { - n = std::max(0, n); - bool ok{true}; - if (n > static_cast(recordLength.value_or(n))) { - handler.SignalEor(); - n = *recordLength; - ok = false; - } - if (n > furthestPositionInRecord) { - if (!isReading_ && ok) { - WriteFrame(recordOffsetInFile, n, handler); - std::fill_n(Frame() + furthestPositionInRecord, - n - furthestPositionInRecord, ' '); - } - furthestPositionInRecord = n; - } - positionInRecord = n; - return ok; + defaultOutput = nullptr; } bool ExternalFileUnit::Emit( const char *data, std::size_t bytes, IoErrorHandler &handler) { auto furthestAfter{std::max(furthestPositionInRecord, positionInRecord + static_cast(bytes))}; - WriteFrame(recordOffsetInFile, furthestAfter, handler); + if (furthestAfter > recordLength.value_or(furthestAfter)) { + handler.SignalError(IostatRecordWriteOverrun); + return false; + } + WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler); std::memcpy(Frame() + positionInRecord, data, bytes); positionInRecord += bytes; furthestPositionInRecord = furthestAfter; return true; } +std::optional ExternalFileUnit::GetCurrentChar( + IoErrorHandler &handler) { + isReading_ = true; // TODO: manage read/write transitions + if (isUnformatted) { + handler.Crash("GetCurrentChar() called for unformatted input"); + return std::nullopt; + } + std::size_t chunk{256}; // for stream input + if (recordLength.has_value()) { + if (positionInRecord >= *recordLength) { + return std::nullopt; + } + chunk = *recordLength - positionInRecord; + } + auto at{recordOffsetInFrame_ + positionInRecord}; + std::size_t need{static_cast(at + 1)}; + std::size_t want{need + chunk}; + auto got{ReadFrame(frameOffsetInFile_, want, handler)}; + if (got <= need) { + endfileRecordNumber = currentRecordNumber; + handler.SignalEnd(); + return std::nullopt; + } + const char *p{Frame() + at}; + if (isUTF8) { + // TODO: UTF-8 decoding + } + return *p; +} + void ExternalFileUnit::SetLeftTabLimit() { leftTabLimit = furthestPositionInRecord; positionInRecord = furthestPositionInRecord; @@ -157,13 +172,29 @@ void ExternalFileUnit::SetLeftTabLimit() { bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) { bool ok{true}; - if (recordLength.has_value()) { // fill fixed-size record - ok &= SetPositionInRecord(*recordLength, handler); - } else if (!isUnformatted && !isReading_) { - ok &= SetPositionInRecord(furthestPositionInRecord, handler) && - Emit("\n", 1, handler); + if (isReading_) { + if (access == Access::Sequential) { + if (isUnformatted) { + NextSequentialUnformattedInputRecord(handler); + } else { + NextSequentialFormattedInputRecord(handler); + } + } + } else if (!isUnformatted) { + if (recordLength.has_value()) { + // fill fixed-size record + if (furthestPositionInRecord < *recordLength) { + WriteFrame(frameOffsetInFile_, *recordLength, handler); + std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, + ' ', *recordLength - furthestPositionInRecord); + } + } else { + positionInRecord = furthestPositionInRecord + 1; + ok &= Emit("\n", 1, handler); // TODO: Windows CR+LF + frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord; + recordOffsetInFrame_ = 0; + } } - recordOffsetInFile += furthestPositionInRecord; ++currentRecordNumber; positionInRecord = 0; furthestPositionInRecord = 0; @@ -171,15 +202,23 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) { return ok; } -bool ExternalFileUnit::HandleAbsolutePosition( - std::int64_t n, IoErrorHandler &handler) { - return SetPositionInRecord( - std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0), handler); -} - -bool ExternalFileUnit::HandleRelativePosition( - std::int64_t n, IoErrorHandler &handler) { - return HandleAbsolutePosition(positionInRecord + n, handler); +void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) { + if (!isReading_) { + handler.Crash("ExternalFileUnit::BackspaceRecord() called during writing"); + // TODO: create endfile record, &c. + } + if (access == Access::Sequential) { + if (isUnformatted) { + BackspaceSequentialUnformattedRecord(handler); + } else { + BackspaceSequentialFormattedRecord(handler); + } + } else { + // TODO + } + positionInRecord = 0; + furthestPositionInRecord = 0; + leftTabLimit.reset(); } void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { @@ -189,7 +228,186 @@ void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { } void ExternalFileUnit::EndIoStatement() { + frameOffsetInFile_ += recordOffsetInFrame_; + recordOffsetInFrame_ = 0; io_.reset(); u_.emplace(); + lock_.Drop(); +} + +void ExternalFileUnit::NextSequentialUnformattedInputRecord( + IoErrorHandler &handler) { + std::int32_t header{0}, footer{0}; + // Retain previous footer (if any) in frame for more efficient BACKSPACE + std::size_t retain{sizeof header}; + if (recordLength) { // not first record - advance to next + ++currentRecordNumber; + if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) { + handler.SignalEnd(); + return; + } + frameOffsetInFile_ += + recordOffsetInFrame_ + *recordLength + 2 * sizeof header; + recordOffsetInFrame_ = 0; + } else { + retain = 0; + } + std::size_t need{retain + sizeof header}; + std::size_t got{ReadFrame(frameOffsetInFile_ - retain, need, handler)}; + // Try to emit informative errors to help debug corrupted files. + const char *error{nullptr}; + if (got < need) { + if (got == retain) { + handler.SignalEnd(); + } else { + error = "Unformatted sequential file input failed at record #%jd (file " + "offset %jd): truncated record header"; + } + } else { + std::memcpy(&header, Frame() + retain, sizeof header); + need = retain + header + 2 * sizeof header; + got = ReadFrame(frameOffsetInFile_ - retain, + need + sizeof header /* next one */, handler); + if (got < need) { + error = "Unformatted sequential file input failed at record #%jd (file " + "offset %jd): hit EOF reading record with length %jd bytes"; + } else { + const char *start{Frame() + retain + sizeof header}; + std::memcpy(&footer, start + header, sizeof footer); + if (footer != header) { + error = "Unformatted sequential file input failed at record #%jd (file " + "offset %jd): record header has length %jd that does not match " + "record footer (%jd)"; + } else { + recordLength = header; + } + } + } + if (error) { + handler.SignalError(error, static_cast(currentRecordNumber), + static_cast(frameOffsetInFile_), + static_cast(header), static_cast(footer)); + } + positionInRecord = sizeof header; +} + +void ExternalFileUnit::NextSequentialFormattedInputRecord( + IoErrorHandler &handler) { + static constexpr std::size_t chunk{256}; + std::size_t length{0}; + if (recordLength.has_value()) { + // not first record - advance to next + ++currentRecordNumber; + if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) { + handler.SignalEnd(); + return; + } + if (Frame()[*recordLength] == '\r') { + ++*recordLength; + } + recordOffsetInFrame_ += *recordLength + 1; + } + while (true) { + std::size_t got{ReadFrame( + frameOffsetInFile_, recordOffsetInFrame_ + length + chunk, handler)}; + if (got <= recordOffsetInFrame_ + length) { + handler.SignalEnd(); + break; + } + const char *frame{Frame() + recordOffsetInFrame_}; + if (const char *nl{reinterpret_cast( + std::memchr(frame + length, '\n', chunk))}) { + recordLength = nl - (frame + length) + 1; + if (*recordLength > 0 && frame[*recordLength - 1] == '\r') { + --*recordLength; + } + return; + } + length += got; + } +} + +void ExternalFileUnit::BackspaceSequentialUnformattedRecord( + IoErrorHandler &handler) { + std::int32_t header{0}, footer{0}; + RUNTIME_CHECK(handler, currentRecordNumber > 1); + --currentRecordNumber; + int overhead{static_cast(2 * sizeof header)}; + // Error conditions here cause crashes, not file format errors, because the + // validity of the file structure before the current record will have been + // checked informatively in NextSequentialUnformattedInputRecord(). + RUNTIME_CHECK(handler, frameOffsetInFile_ >= overhead); + std::size_t got{ + ReadFrame(frameOffsetInFile_ - sizeof footer, sizeof footer, handler)}; + RUNTIME_CHECK(handler, got >= sizeof footer); + std::memcpy(&footer, Frame(), sizeof footer); + RUNTIME_CHECK(handler, frameOffsetInFile_ >= footer + overhead); + frameOffsetInFile_ -= footer + 2 * sizeof footer; + auto extra{std::max(sizeof footer, frameOffsetInFile_)}; + std::size_t want{extra + footer + 2 * sizeof footer}; + got = ReadFrame(frameOffsetInFile_ - extra, want, handler); + RUNTIME_CHECK(handler, got >= want); + std::memcpy(&header, Frame() + extra, sizeof header); + RUNTIME_CHECK(handler, header == footer); + positionInRecord = sizeof header; + recordLength = footer; +} + +// There's no portable memrchr(), unfortunately, and strrchr() would +// fail on a record with a NUL, so we have to do it the hard way. +static const char *FindLastNewline(const char *str, std::size_t length) { + for (const char *p{str + length}; p-- > str;) { + if (*p == '\n') { + return p; + } + } + return nullptr; +} + +void ExternalFileUnit::BackspaceSequentialFormattedRecord( + IoErrorHandler &handler) { + std::int64_t start{frameOffsetInFile_ + recordOffsetInFrame_}; + --currentRecordNumber; + RUNTIME_CHECK(handler, currentRecordNumber > 0); + if (currentRecordNumber == 1) { + // To simplify the code below, treat a backspace to the first record + // as a special case; + RUNTIME_CHECK(handler, start > 0); + *recordLength = start - 1; + frameOffsetInFile_ = 0; + recordOffsetInFrame_ = 0; + ReadFrame(0, *recordLength + 1, handler); + } else { + RUNTIME_CHECK(handler, start > 1); + std::int64_t at{start - 2}; // byte before previous record's newline + while (true) { + if (const char *p{ + FindLastNewline(Frame(), at - frameOffsetInFile_ + 1)}) { + // This is the newline that ends the record before the previous one. + recordOffsetInFrame_ = p - Frame() + 1; + *recordLength = start - 1 - (frameOffsetInFile_ + recordOffsetInFrame_); + break; + } + RUNTIME_CHECK(handler, frameOffsetInFile_ > 0); + at = frameOffsetInFile_ - 1; + if (auto bytesBefore{BytesBufferedBeforeFrame()}) { + frameOffsetInFile_ = FrameAt() - bytesBefore; + } else { + static constexpr int chunk{1024}; + frameOffsetInFile_ = std::max(0, at - chunk); + } + std::size_t want{static_cast(start - frameOffsetInFile_)}; + std::size_t got{ReadFrame(frameOffsetInFile_, want, handler)}; + RUNTIME_CHECK(handler, got >= want); + } + } + std::size_t want{ + static_cast(recordOffsetInFrame_ + *recordLength + 1)}; + RUNTIME_CHECK(handler, FrameLength() >= want); + RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ + *recordLength] == '\n'); + if (*recordLength > 0 && + Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') { + --*recordLength; + } } } diff --git a/runtime/unit.h b/runtime/unit.h index 62f664b8f32a..b32c2cde74db 100644 --- a/runtime/unit.h +++ b/runtime/unit.h @@ -27,6 +27,8 @@ namespace Fortran::runtime::io { +class UnitMap; + class ExternalFileUnit : public ConnectionState, public OpenFile, public FileFrame { @@ -36,19 +38,21 @@ class ExternalFileUnit : public ConnectionState, static ExternalFileUnit *LookUp(int unit); static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &); - static ExternalFileUnit &LookUpOrCreate(int unit, bool *wasExtant = nullptr); - static int NewUnit(); - static void InitializePredefinedUnits(); + static ExternalFileUnit &LookUpOrCreate( + int unit, const Terminator &, bool *wasExtant = nullptr); + static ExternalFileUnit *LookUpForClose(int unit); + static int NewUnit(const Terminator &); static void CloseAll(IoErrorHandler &); void OpenUnit(OpenStatus, Position, OwningPtr &&path, std::size_t pathLength, IoErrorHandler &); void CloseUnit(CloseStatus, IoErrorHandler &); + void DestroyClosed(); template IoStatementState &BeginIoStatement(X &&... xs) { - // TODO: lock().Take() here, and keep it until EndIoStatement()? - // Nested I/O from derived types wouldn't work, though. + // TODO: Child data transfer statements vs. locking + lock_.Take(); // dropped in EndIoStatement() A &state{u_.emplace(std::forward(xs)...)}; if constexpr (!std::is_same_v) { state.mutableModes() = ConnectionState::modes; @@ -58,26 +62,47 @@ class ExternalFileUnit : public ConnectionState, } bool Emit(const char *, std::size_t bytes, IoErrorHandler &); + std::optional GetCurrentChar(IoErrorHandler &); void SetLeftTabLimit(); bool AdvanceRecord(IoErrorHandler &); - bool HandleAbsolutePosition(std::int64_t, IoErrorHandler &); - bool HandleRelativePosition(std::int64_t, IoErrorHandler &); - + void BackspaceRecord(IoErrorHandler &); void FlushIfTerminal(IoErrorHandler &); void EndIoStatement(); + void SetPosition(std::int64_t pos) { + frameOffsetInFile_ = pos; + recordOffsetInFrame_ = 0; + } private: - bool SetPositionInRecord(std::int64_t, IoErrorHandler &); + static UnitMap &GetUnitMap(); + void NextSequentialUnformattedInputRecord(IoErrorHandler &); + void NextSequentialFormattedInputRecord(IoErrorHandler &); + void BackspaceSequentialUnformattedRecord(IoErrorHandler &); + void BackspaceSequentialFormattedRecord(IoErrorHandler &); int unitNumber_{-1}; bool isReading_{false}; + + Lock lock_; + // When an I/O statement is in progress on this unit, holds its state. std::variant, - ExternalListIoStatementState, UnformattedIoStatementState> + ExternalFormattedIoStatementState, + ExternalFormattedIoStatementState, + ExternalListIoStatementState, + ExternalListIoStatementState, + UnformattedIoStatementState, + UnformattedIoStatementState> u_; - // Points to the active alternative, if any, in u_, for use as a Cookie + + // Points to the active alternative (if any) in u_ for use as a Cookie std::optional io_; + + // Subtle: The beginning of the frame can't be allowed to advance + // during a single list-directed READ due to the possibility of a + // multi-record CHARACTER value with a "r*" repeat count. + std::int64_t frameOffsetInFile_{0}; + std::int64_t recordOffsetInFrame_{0}; // of currentRecordNumber }; } diff --git a/test/Evaluate/reshape.cpp b/test/Evaluate/reshape.cpp index 1680cf3644b2..db9bee325ef2 100644 --- a/test/Evaluate/reshape.cpp +++ b/test/Evaluate/reshape.cpp @@ -9,9 +9,8 @@ using namespace Fortran::runtime; int main() { static const SubscriptValue ones[]{1, 1, 1}; static const SubscriptValue sourceExtent[]{2, 3, 4}; - std::unique_ptr source{ - Descriptor::Create(TypeCategory::Integer, sizeof(std::int32_t), nullptr, - 3, sourceExtent, CFI_attribute_allocatable)}; + auto source{Descriptor::Create(TypeCategory::Integer, sizeof(std::int32_t), + nullptr, 3, sourceExtent, CFI_attribute_allocatable)}; source->Check(); MATCH(3, source->rank()); MATCH(sizeof(std::int32_t), source->ElementBytes()); @@ -25,12 +24,12 @@ int main() { MATCH(4, source->GetDimension(2).Extent()); MATCH(24, source->Elements()); for (std::size_t j{0}; j < 24; ++j) { - *source->Element(j * sizeof(std::int32_t)) = j; + *source->OffsetElement(j * sizeof(std::int32_t)) = j; } static const std::int16_t shapeData[]{8, 4}; static const SubscriptValue shapeExtent{2}; - std::unique_ptr shape{Descriptor::Create(TypeCategory::Integer, + auto shape{Descriptor::Create(TypeCategory::Integer, static_cast(sizeof shapeData[0]), const_cast(reinterpret_cast(shapeData)), 1, &shapeExtent, CFI_attribute_pointer)}; @@ -54,15 +53,14 @@ int main() { MATCH(2, pad.GetDimension(1).Extent()); MATCH(3, pad.GetDimension(2).Extent()); - std::unique_ptr result{RESHAPE(*source, *shape, &pad)}; - + auto result{RESHAPE(*source, *shape, &pad)}; TEST(result.get() != nullptr); result->Check(); MATCH(sizeof(std::int32_t), result->ElementBytes()); MATCH(2, result->rank()); TEST(result->type().IsInteger()); for (std::int32_t j{0}; j < 32; ++j) { - MATCH(j, *result->Element(j * sizeof(std::int32_t))); + MATCH(j, *result->OffsetElement(j * sizeof(std::int32_t))); } for (std::int32_t j{0}; j < 32; ++j) { SubscriptValue ss[2]{1 + (j % 8), 1 + (j / 8)}; diff --git a/test/Runtime/CMakeLists.txt b/test/Runtime/CMakeLists.txt index 239c3f86f52b..905e18e0cea2 100644 --- a/test/Runtime/CMakeLists.txt +++ b/test/Runtime/CMakeLists.txt @@ -10,12 +10,17 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions") endif() +add_library(RuntimeTesting + testing.cpp +) + add_executable(format-test format.cpp ) target_link_libraries(format-test FortranRuntime + RuntimeTesting ) add_test(Format format-test) @@ -26,6 +31,7 @@ add_executable(hello-world target_link_libraries(hello-world FortranRuntime + RuntimeTesting ) add_test(HelloWorld hello-world) @@ -37,3 +43,14 @@ add_executable(external-hello-world target_link_libraries(external-hello-world FortranRuntime ) + +add_executable(list-input-test + list-input.cpp +) + +target_link_libraries(list-input-test + FortranRuntime + RuntimeTesting +) + +add_test(ListInput list-input-test) diff --git a/test/Runtime/format.cpp b/test/Runtime/format.cpp index 05ec9d3e280b..b00f89c72c1f 100644 --- a/test/Runtime/format.cpp +++ b/test/Runtime/format.cpp @@ -1,7 +1,8 @@ // Tests basic FORMAT string traversal +#include "testing.h" #include "../runtime/format-implementation.h" -#include "../runtime/terminator.h" +#include "../runtime/io-error.h" #include #include #include @@ -12,20 +13,19 @@ using namespace Fortran::runtime; using namespace Fortran::runtime::io; using namespace std::literals::string_literals; -static int failures{0}; using Results = std::vector; // A test harness context for testing FormatControl -class TestFormatContext : public Terminator { +class TestFormatContext : public IoErrorHandler { public: using CharType = char; - TestFormatContext() : Terminator{"format.cpp", 1} {} + TestFormatContext() : IoErrorHandler{"format.cpp", 1} {} bool Emit(const char *, std::size_t); bool Emit(const char16_t *, std::size_t); bool Emit(const char32_t *, std::size_t); bool AdvanceRecord(int = 1); - bool HandleRelativePosition(std::int64_t); - bool HandleAbsolutePosition(std::int64_t); + void HandleRelativePosition(std::int64_t); + void HandleAbsolutePosition(std::int64_t); void Report(const DataEdit &); void Check(Results &); Results results; @@ -35,17 +35,6 @@ class TestFormatContext : public Terminator { MutableModes mutableModes_; }; -// Override the runtime's Crash() for testing purposes -[[noreturn]] void Fortran::runtime::Terminator::Crash( - const char *message, ...) const { - std::va_list ap; - va_start(ap, message); - char buffer[1000]; - std::vsnprintf(buffer, sizeof buffer, message, ap); - va_end(ap); - throw std::string{buffer}; -} - bool TestFormatContext::Emit(const char *s, std::size_t len) { std::string str{s, len}; results.push_back("'"s + str + '\''); @@ -67,18 +56,16 @@ bool TestFormatContext::AdvanceRecord(int n) { return true; } -bool TestFormatContext::HandleAbsolutePosition(std::int64_t n) { +void TestFormatContext::HandleAbsolutePosition(std::int64_t n) { results.push_back("T"s + std::to_string(n)); - return true; } -bool TestFormatContext::HandleRelativePosition(std::int64_t n) { +void TestFormatContext::HandleRelativePosition(std::int64_t n) { if (n < 0) { results.push_back("TL"s + std::to_string(-n)); } else { results.push_back(std::to_string(n) + 'X'); } - return true; } void TestFormatContext::Report(const DataEdit &edit) { @@ -104,7 +91,7 @@ void TestFormatContext::Report(const DataEdit &edit) { void TestFormatContext::Check(Results &expect) { if (expect != results) { - std::cerr << "expected:"; + Fail() << "expected:"; for (const std::string &s : expect) { std::cerr << ' ' << s; } @@ -113,7 +100,6 @@ void TestFormatContext::Check(Results &expect) { std::cerr << ' ' << s; } std::cerr << '\n'; - ++failures; } expect.clear(); results.clear(); @@ -127,7 +113,10 @@ static void Test(int n, const char *format, Results &&expect, int repeat = 1) { for (int j{0}; j < n; ++j) { context.Report(control.GetNextDataEdit(context, repeat)); } - control.FinishOutput(context); + control.Finish(context); + if (int iostat{context.GetIoStat()}) { + context.Crash("GetIoStat() == %d", iostat); + } } catch (const std::string &crash) { context.results.push_back("Crash:"s + crash); } @@ -135,6 +124,7 @@ static void Test(int n, const char *format, Results &&expect, int repeat = 1) { } int main() { + StartTests(); Test(1, "('PI=',F9.7)", Results{"'PI='", "F9.7"}); Test(1, "(3HPI=F9.7)", Results{"'PI='", "F9.7"}); Test(1, "(3HPI=/F9.7)", Results{"'PI='", "/", "F9.7"}); @@ -146,5 +136,5 @@ int main() { Test(2, "(*('PI=',F9.7,:),'tooFar')", Results{"'PI='", "F9.7", "'PI='", "F9.7"}); Test(1, "(3F9.7)", Results{"2*F9.7"}, 2); - return failures > 0; + return EndTests(); } diff --git a/test/Runtime/hello.cpp b/test/Runtime/hello.cpp index 4bb65acd565e..88628cecf1ed 100644 --- a/test/Runtime/hello.cpp +++ b/test/Runtime/hello.cpp @@ -1,5 +1,6 @@ // Basic sanity tests of I/O API; exhaustive testing will be done in Fortran +#include "testing.h" #include "../../runtime/descriptor.h" #include "../../runtime/io-api.h" #include @@ -8,16 +9,15 @@ using namespace Fortran::runtime; using namespace Fortran::runtime::io; -static int failures{0}; - -static void test(const char *format, const char *expect, std::string &&got) { +static bool test(const char *format, const char *expect, std::string &&got) { std::string want{expect}; want.resize(got.length(), ' '); if (got != want) { - std::cerr << '\'' << format << "' failed;\n got '" << got - << "',\nexpected '" << want << "'\n"; - ++failures; + Fail() << '\'' << format << "' failed;\n got '" << got + << "',\nexpected '" << want << "'\n"; + return false; } + return true; } static void hello() { @@ -30,9 +30,8 @@ static void hello() { IONAME(OutputInteger64)(cookie, 0xfeedface); IONAME(OutputLogical)(cookie, true); if (auto status{IONAME(EndIoStatement)(cookie)}) { - std::cerr << "hello: '" << format << "' failed, status " - << static_cast(status) << '\n'; - ++failures; + Fail() << "hello: '" << format << "' failed, status " + << static_cast(status) << '\n'; } else { test(format, "HELLO, WORLD 678 0xFEEDFACE T", std::string{buffer, sizeof buffer}); @@ -46,21 +45,18 @@ static void multiline() { SubscriptValue extent[]{4}; whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, CFI_attribute_pointer); - // whole.Dump(std::cout); + whole.Dump(); whole.Check(); Descriptor §ion{staticDescriptor[1].descriptor()}; SubscriptValue lowers[]{0}, uppers[]{3}, strides[]{1}; section.Establish(whole.type(), whole.ElementBytes(), nullptr, 1, extent, CFI_attribute_pointer); - // section.Dump(std::cout); - section.Check(); if (auto error{ CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}) { - std::cerr << "multiline: CFI_section failed: " << error << '\n'; - ++failures; + Fail() << "multiline: CFI_section failed: " << error << '\n'; return; } - section.Dump(std::cout); + section.Dump(); section.Check(); const char *format{"('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,25X,'done')"}; auto cookie{IONAME(BeginInternalArrayFormattedOutput)( @@ -69,9 +65,8 @@ static void multiline() { IONAME(OutputAscii)(cookie, "HELLO", 5); IONAME(OutputInteger64)(cookie, 789); if (auto status{IONAME(EndIoStatement)(cookie)}) { - std::cerr << "multiline: '" << format << "' failed, status " - << static_cast(status) << '\n'; - ++failures; + Fail() << "multiline: '" << format << "' failed, status " + << static_cast(status) << '\n'; } else { test(format, ">HELLO, WORLD <" @@ -88,15 +83,41 @@ static void realTest(const char *format, double x, const char *expect) { buffer, sizeof buffer, format, std::strlen(format))}; IONAME(OutputReal64)(cookie, x); if (auto status{IONAME(EndIoStatement)(cookie)}) { - std::cerr << '\'' << format << "' failed, status " - << static_cast(status) << '\n'; - ++failures; + Fail() << '\'' << format << "' failed, status " << static_cast(status) + << '\n'; } else { test(format, expect, std::string{buffer, sizeof buffer}); } } +static void realInTest( + const char *format, const char *data, std::uint64_t want) { + auto cookie{IONAME(BeginInternalFormattedInput)( + data, std::strlen(data), format, std::strlen(format))}; + union { + double x; + std::uint64_t raw; + } u; + u.raw = 0; + IONAME(EnableHandlers)(cookie, true, true, true, true, true); + IONAME(InputReal64)(cookie, u.x); + char iomsg[65]; + iomsg[0] = '\0'; + iomsg[sizeof iomsg - 1] = '\0'; + IONAME(GetIoMsg)(cookie, iomsg, sizeof iomsg - 1); + auto status{IONAME(EndIoStatement)(cookie)}; + if (status) { + Fail() << '\'' << format << "' failed reading '" << data << "', status " + << static_cast(status) << " iomsg '" << iomsg << "'\n"; + } else if (u.raw != want) { + Fail() << '\'' << format << "' failed reading '" << data << "', want 0x" + << std::hex << want << ", got 0x" << u.raw << std::dec << '\n'; + } +} + int main() { + StartTests(); + hello(); multiline(); @@ -382,10 +403,22 @@ int main() { "4040261841248583680000+306;"); realTest("(G0,';')", u.d, ".17976931348623157+309;"); - if (failures == 0) { - std::cout << "PASS\n"; - } else { - std::cout << "FAIL " << failures << " tests\n"; - } - return failures > 0; + realInTest("(F18.0)", " 0", 0x0); + realInTest("(F18.0)", " ", 0x0); + realInTest("(F18.0)", " -0", 0x8000000000000000); + realInTest("(F18.0)", " 1", 0x3ff0000000000000); + realInTest("(F18.0)", " 125.", 0x405f400000000000); + realInTest("(F18.0)", " 12.5", 0x4029000000000000); + realInTest("(F18.0)", " 1.25", 0x3ff4000000000000); + realInTest("(F18.0)", " .125", 0x3fc0000000000000); + realInTest("(F18.0)", " 125", 0x405f400000000000); + realInTest("(F18.1)", " 125", 0x4029000000000000); + realInTest("(F18.2)", " 125", 0x3ff4000000000000); + realInTest("(F18.3)", " 125", 0x3fc0000000000000); + realInTest("(-1P,F18.0)", " 125", 0x4093880000000000); // 1250 + realInTest("(1P,F18.0)", " 125", 0x4029000000000000); // 12.5 + realInTest("(BZ,F18.0)", " 125 ", 0x4093880000000000); // 1250 + realInTest("(DC,F18.0)", " 12,5", 0x4029000000000000); + + return EndTests(); } diff --git a/test/Runtime/list-input.cpp b/test/Runtime/list-input.cpp new file mode 100644 index 000000000000..cb9021e59509 --- /dev/null +++ b/test/Runtime/list-input.cpp @@ -0,0 +1,68 @@ +// Basic sanity tests for list-directed input + +#include "testing.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/io-api.h" +#include "../../runtime/io-error.h" +#include +#include +#include + +using namespace Fortran::runtime; +using namespace Fortran::runtime::io; + +int main() { + StartTests(); + + char buffer[4][32]; + int j{0}; + for (const char *p : {"1 2 2*3 ,", ",6,,8,123*", + "2*'abcdefghijklmnopqrstuvwxyzABC", "DEFGHIJKLMNOPQRSTUVWXYZ'"}) { + SetCharacter(buffer[j++], sizeof buffer[0], p); + } + for (; j < 4; ++j) { + SetCharacter(buffer[j], sizeof buffer[0], ""); + } + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{4}; + whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, + CFI_attribute_pointer); + whole.Dump(); + whole.Check(); + + try { + auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; + std::int64_t n[9]{-1, -2, -3, -4, 5, -6, 7, -8, 9}; + std::int64_t want[9]{1, 2, 3, 3, 5, 6, 7, 8, 9}; + for (j = 0; j < 9; ++j) { + IONAME(InputInteger)(cookie, n[j]); + } + char asc[2][54]{}; + IONAME(InputAscii)(cookie, asc[0], sizeof asc[0] - 1); + IONAME(InputAscii)(cookie, asc[1], sizeof asc[1] - 1); + if (auto status{IONAME(EndIoStatement)(cookie)}) { + Fail() << "list-directed input failed, status " + << static_cast(status) << '\n'; + } else { + for (j = 0; j < 9; ++j) { + if (n[j] != want[j]) { + Fail() << "wanted n[" << j << "]==" << want[j] << ", got " << n[j] + << '\n'; + } + } + for (j = 0; j < 2; ++j) { + if (std::strcmp(asc[j], + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ") != 0) { + Fail() << "wanted asc[" << j << "]=alphabets, got '" << asc[j] + << "'\n"; + } + } + } + } catch (const std::string &crash) { + Fail() << "crash: " << crash << '\n'; + } + + return EndTests(); +} diff --git a/test/Runtime/testing.cpp b/test/Runtime/testing.cpp new file mode 100644 index 000000000000..5eb86495f0cf --- /dev/null +++ b/test/Runtime/testing.cpp @@ -0,0 +1,43 @@ +#include "testing.h" +#include "../../runtime/terminator.h" +#include +#include +#include +#include +#include + +static int failures{0}; + +// Override the Fortran runtime's Crash() for testing purposes +[[noreturn]] static void CatchCrash(const char *message, va_list &ap) { + char buffer[1000]; + std::vsnprintf(buffer, sizeof buffer, message, ap); + va_end(ap); + throw std::string{buffer}; +} + +void StartTests() { + Fortran::runtime::Terminator::RegisterCrashHandler(CatchCrash); +} + +std::ostream &Fail() { + ++failures; + return std::cerr; +} + +int EndTests() { + if (failures == 0) { + std::cout << "PASS\n"; + } else { + std::cout << "FAIL " << failures << " tests\n"; + } + return failures != 0; +} + +void SetCharacter(char *to, std::size_t n, const char *from) { + auto len{std::strlen(from)}; + std::memcpy(to, from, std::min(len, n)); + if (len < n) { + std::memset(to + len, ' ', n - len); + } +} diff --git a/test/Runtime/testing.h b/test/Runtime/testing.h new file mode 100644 index 000000000000..9571a34825dc --- /dev/null +++ b/test/Runtime/testing.h @@ -0,0 +1,13 @@ +#ifndef FORTRAN_TEST_RUNTIME_TESTING_H_ +#define FORTRAN_TEST_RUNTIME_TESTING_H_ + +#include +#include + +void StartTests(); +std::ostream &Fail(); +int EndTests(); + +void SetCharacter(char *, std::size_t, const char *); + +#endif // FORTRAN_TEST_RUNTIME_TESTING_H_ diff --git a/test/Semantics/call15.f90 b/test/Semantics/call15.f90 index 204a0e6c6237..ca935b91530f 100644 --- a/test/Semantics/call15.f90 +++ b/test/Semantics/call15.f90 @@ -7,7 +7,7 @@ subroutine s(arg1, arg2, arg3) call inner(arg1) ! OK, assumed rank call inner(arg2) ! OK, assumed shape - !ERROR: Assumed-type TYPE(*) 'arg3' must be either assumed shape or assumed rank to be associated with TYPE(*) dummy argument 'dummy=' + !ERROR: Assumed-type 'arg3' must be either assumed shape or assumed rank to be associated with assumed-type dummy argument 'dummy=' call inner(arg3) contains From 30b428a51e140e5fb12bd53a0619f10ff510f408 Mon Sep 17 00:00:00 2001 From: jeanPerier Date: Wed, 11 Mar 2020 21:47:22 -0700 Subject: [PATCH 078/345] Add Fortran IR (FIR) MLIR dialect implementation (#1035) Adds FIR library that implements an MLIR dialect to which Fortran parse-tree will be lowered to. FIR is defined and documented inside FIROps.td added in this commit. It is possible to generate a more readable description FIRLangRef.md from FIROps.td following the related instructions added to the README.md by this commit. This patch adds FIR definition and implementation that allow parsing, printing, and verifying FIR. FIR transformations and lowering to Standard and LLVM dialects are not part of this patch. The FIR verifiers are verifying the basic properties of FIR operations in order to provide a sufficient frame for lowering. Verifiers for more advanced FIR properties can be added as needed. Coarrays are not covered by FIR defined in this patch. This patch also adds tco tool that is meant to process FIR input files and drives transformations on it. The tco tool is used for testing. In this patch, it is only used to demonstrate parsing/verifying/ and dumping FIR with round-trip tests. Note: This commit does not reflect an actual work log, it is a feature-based split of the changes done in the FIR experimental branch. The related work log can be found in the commits between: https://github.com/schweitzpgi/f18/commit/742edde572bd74d77cf7d447132ccf0949187fce and https://github.com/schweitzpgi/f18/commit/2ff55242126d86061f4fed9ef7b59d3636b5fd0b Changes on top of these original commits were made during this patch review. --- .drone.star | 16 +- CMakeLists.txt | 9 + README.md | 55 + .../Investigating-FIR-as-an-MLIR-dialect.md | 413 --- include/fir/.clang-format | 2 - include/flang/CMakeLists.txt | 11 +- include/flang/Optimizer/CMakeLists.txt | 1 + .../flang/Optimizer/Dialect/CMakeLists.txt | 22 + include/flang/Optimizer/Dialect/FIRAttr.h | 166 + include/flang/Optimizer/Dialect/FIRDialect.h | 92 + include/flang/Optimizer/Dialect/FIROps.h | 47 + include/flang/Optimizer/Dialect/FIROps.td | 2747 +++++++++++++++++ .../flang/Optimizer/Dialect/FIROpsSupport.h | 63 + include/flang/Optimizer/Dialect/FIRType.h | 399 +++ include/flang/Optimizer/Support/KindMapping.h | 90 + lib/CMakeLists.txt | 4 + lib/Fir/.clang-format | 2 - lib/Optimizer/CMakeLists.txt | 5 + lib/Optimizer/Dialect/CMakeLists.txt | 27 + lib/Optimizer/Dialect/FIRAttr.cpp | 238 ++ lib/Optimizer/Dialect/FIRDialect.cpp | 54 + lib/Optimizer/Dialect/FIROps.cpp | 862 ++++++ lib/Optimizer/Dialect/FIRType.cpp | 1292 ++++++++ lib/Optimizer/Support/CMakeLists.txt | 10 + lib/Optimizer/Support/KindMapping.cpp | 244 ++ test-lit/CMakeLists.txt | 5 + test-lit/Fir/fir-ops.fir | 403 +++ test-lit/Fir/fir-types.fir | 78 + test-lit/lit.cfg.py | 8 +- test-lit/lit.site.cfg.py.in | 1 + tools/CMakeLists.txt | 4 +- tools/tco/CMakeLists.txt | 24 + tools/tco/tco.cpp | 113 + 33 files changed, 7072 insertions(+), 435 deletions(-) delete mode 100644 documentation/Investigating-FIR-as-an-MLIR-dialect.md delete mode 100644 include/fir/.clang-format create mode 100644 include/flang/Optimizer/CMakeLists.txt create mode 100644 include/flang/Optimizer/Dialect/CMakeLists.txt create mode 100644 include/flang/Optimizer/Dialect/FIRAttr.h create mode 100644 include/flang/Optimizer/Dialect/FIRDialect.h create mode 100644 include/flang/Optimizer/Dialect/FIROps.h create mode 100644 include/flang/Optimizer/Dialect/FIROps.td create mode 100644 include/flang/Optimizer/Dialect/FIROpsSupport.h create mode 100644 include/flang/Optimizer/Dialect/FIRType.h create mode 100644 include/flang/Optimizer/Support/KindMapping.h delete mode 100644 lib/Fir/.clang-format create mode 100644 lib/Optimizer/CMakeLists.txt create mode 100644 lib/Optimizer/Dialect/CMakeLists.txt create mode 100644 lib/Optimizer/Dialect/FIRAttr.cpp create mode 100644 lib/Optimizer/Dialect/FIRDialect.cpp create mode 100644 lib/Optimizer/Dialect/FIROps.cpp create mode 100644 lib/Optimizer/Dialect/FIRType.cpp create mode 100644 lib/Optimizer/Support/CMakeLists.txt create mode 100644 lib/Optimizer/Support/KindMapping.cpp create mode 100644 test-lit/Fir/fir-ops.fir create mode 100644 test-lit/Fir/fir-types.fir create mode 100644 tools/tco/CMakeLists.txt create mode 100644 tools/tco/tco.cpp diff --git a/.drone.star b/.drone.star index 5bbe018cd056..27ff72e911cf 100644 --- a/.drone.star +++ b/.drone.star @@ -8,13 +8,13 @@ def clang(arch): "image": "ubuntu", "commands": [ "apt-get update && apt-get install -y clang-8 cmake ninja-build lld-8 llvm-8-dev libc++-8-dev libc++abi-8-dev libz-dev git", - "git clone https://github.com/llvm/llvm-project", + "git clone --depth=1 -b f18 https://github.com/flang-compiler/f18-llvm-project.git llvm-project", "mkdir llvm-project/build && cd llvm-project/build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', - "ninja", + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_INSTALL_UTILS=On -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', + "ninja install", "cd ../..", "mkdir build && cd build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/build/lib/cmake/llvm', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", @@ -34,13 +34,13 @@ def gcc(arch): "image": "gcc", "commands": [ "apt-get update && apt-get install -y cmake ninja-build llvm-dev libz-dev git", - "git clone https://github.com/llvm/llvm-project", + "git clone --depth=1 -b f18 https://github.com/flang-compiler/f18-llvm-project.git llvm-project", "mkdir llvm-project/build && cd llvm-project/build", - 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', - "ninja", + 'env CC=gcc CXX=g++ LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install -DLLVM_TARGETS_TO_BUILD=host -DLLVM_INSTALL_UTILS=On -DLLVM_ENABLE_PROJECTS="mlir" ../llvm', + "ninja install", "cd ../..", "mkdir build && cd build", - 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/build/lib/cmake/llvm', + 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", diff --git a/CMakeLists.txt b/CMakeLists.txt index f293762cd2ea..97a03ee4a9a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,15 @@ add_definitions(${LLVM_DEFINITIONS}) set(LLVM_EXTERNAL_LIT ${LLVM_TOOLS_BINARY_DIR}/llvm-lit CACHE STRING "Command used to spawn lit") if(LINK_WITH_FIR) + include(TableGen) + include(AddMLIR) + find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH) + # tco tool and FIR lib output directories + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin) + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/lib) + # Always build tco tool + set(LLVM_BUILD_TOOLS ON) message(STATUS "Linking driver with FIR and LLVM") llvm_map_components_to_libnames(LLVM_COMMON_LIBS support) message(STATUS "LLVM libraries: ${LLVM_COMMON_LIBS}") diff --git a/README.md b/README.md index d0887a305a14..9df131d7f325 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,55 @@ LLVM=/lib/cmake/llvm cmake -DLLVM_DIR=$LLVM ... where `LLVM_BUILD_DIR` is the top-level directory where LLVM was built. +### LLVM dependency when building f18 with Fortran IR + +If you do not want to build Fortran IR, add `-DLINK_WITH_FIR=Off` to f18 cmake +command and ignore the rest of this section. + +If you intend to build f18 with Fortran IR (`-DLINK_WITH_FIR` On by default), +you must: +- build LLVM with the same compiler and options as the one you are using +to build F18. +- pass `-DCMAKE_CXX_STANDARD=17 -DLLVM_ENABLE_PROJECTS="mlir"` +to LLVM cmake command. +- install LLVM somewhere with `make install` in order to get the required +AddMLIR cmake file (it is not generated in LLVM build directory). + +Installing LLVM from packages is most likely not an option as it will not include +MLIR and not be built following C++17 standard. + +MLIR is under active development and the most recent development version +may be incompatible. A branch named `f18` is available inside LLVM fork in +https://github.com/flang-compiler/f18-llvm-project. It contains a version of LLVM +that is known be compatible to build f18 with FIR. + +The fastest way to get set up is to do: + +``` +cd where/you/want/to/build/llvm +git clone --depth=1 -b f18 https://github.com/flang-compiler/f18-llvm-project.git +mkdir build +mkdir install +cd build +cmake ../f18-llvm-project/llvm -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_PROJECTS=mlir -DCMAKE_CXX_STANDARD=17 \ + -DLLVM_INSTALL_UTILS=On \ + -DCMAKE_INSTALL_PREFIX=../install +make +make install +``` + +Then, `-DLLVM_DIR` would have to be set to + `/install/lib/cmake/llvm` in f18 cmake command. + +To run lit tests, +`-DLLVM_EXTERNAL_LIT=/build/bin/llvm-lit` must be +added to f18 cmake command. This is because `llvm-lit` is not part of +LLVM installation. + +Note that when using some advanced options from f18 cmake file it may be +necessary to reproduce their effects in LLVM cmake command. + ### Building f18 with GCC By default, @@ -187,3 +236,9 @@ flang_site_config and flang_config. And they can be set as shown bellow: --param flang_config=/test-lit/lit.cfg.py \ ``` + +# How to Generate FIR Documentation + +If f18 was built with `-DLINK_WITH_FIR=On` (`On` by default), it is possible to +generate FIR language documentation by running `make flang-doc`. This will +create `docs/Dialect/FIRLangRef.md` in f18 build directory. diff --git a/documentation/Investigating-FIR-as-an-MLIR-dialect.md b/documentation/Investigating-FIR-as-an-MLIR-dialect.md deleted file mode 100644 index 79809cd4c1f0..000000000000 --- a/documentation/Investigating-FIR-as-an-MLIR-dialect.md +++ /dev/null @@ -1,413 +0,0 @@ - - -# Investigating FIR as an MLIR Dialect - -## Introduction - -With the availability of the extensible MLIR framework as -open-source, we've been investigating the potential to use MLIR as -a substrate upon which to build a FIR dialect. This document will -explain the motivations and the shape of this subproject using MLIR -to define and implement a high-level operational representation of -Fortran. (See also [Design: Fortran IR](https://bit.ly/2WWRus0) -for more information.) - -### What is MLIR? - -The MLIR project is an open-sourced project on Github. -[MLIR](https://github.com/tensorflow/mlir) is being built as a -common multi-level intermediate representation (IR) for the -TensorFlow machine learning tools. It provides a common IR -framework for building multiple layers of representation called -dialects. These dialects can be transformed (converted) from one to -another allowing for optimizations and successive lowering of the -IR down to backend code generators. MLIR supports multiple backends. The -LLVM IR dialect is a pre-defined MLIR dialect -that can be targeted -for final code generation using the LLVM library and/or tools. -MLIR is explicitly intended to be extensible such that -other projects can add their own dialects. - -Out of the box, MLIR supports a refined polyhedral model for -transforming affine operations on array data. These mathematically-based -representations will help realize F18's higher level goals of composing, -transforming, and optimizing Fortran array operations for HPC. - -### What is meant by a FIR dialect? - -MLIR has no support for Fortran out of the box. Support for Fortran must -be built and added through the extensibility features of MLIR. This -process of extending MLIR is called adding a dialect. In our case, we'll -add a FIR dialect for representation of Fortran programs. - -Specifically, this means that we can proceed with the plan as laid out -in the Fortran IR design document with the advantages of not having to -build from scratch our own - -- IR infrastructure -- LLVM IR bridge -- Common optimization passes (For example: CSE, DCE, loop fusion, loop - invariant code motion, loop tiling, loop unroll and jam, loop - unrolling, vectorization, etc.) - -A dialect can be thought of -as its own distinct IR, where the design and -semantics of the IR are domain-specific for the purposes of the -dialect. Specifically, MLIR supports a modern IR design with - -- A predefined set of operations with precise semantics -- Structural/logical grouping of sets of operations -- Explicit control flow -- Explicit data-flow -- Strong typing - -- Meta information - - Informally, one can construct MLIR operations that are - conceptually composable containers. These containers can have - strongly typed metadata attached to them that can describe - any properties of interest. For example, one can define an - operation called "execute" and define it to have an attribute - "order", which may have a value that describes a sequential - execution order, a fully parallel unordered execution, or some - valid execution partial ordering. What meta information is - interesting depends on the context and is up to the MLIR user. - -This document describes FIR, which is a set of extensions upon the -standard MLIR dialect. When lowering a Fortran parse tree to FIR, -the compiler will produce a mix of operations defined in the FIR -dialect, the MLIR __standard dialect__, and other pre-defined -dialects, such as the [affine dialect](https://bit.ly/2XYDPNj). - -#### Disclaimers - -This document is not a tutorial on compilers, Fortran, MLIR, LLVM, -intermediate representations, abstraction levels, semantics, nor type -theory. - -## Requirements Unchanged - -The FIR dialect has the same requirements as spelled out in the -Fortran IR design document. Specifically, the FIR dialect will -capture the control flow structure of the Fortran source-level -program. At the highest level of abstraction, Fortran computations -will be captured as coarse-grain opaque -operations corresponding to Fortran expressions -(with links to `Fortran::evaluate::Expr` objects from the -front-end), where only the peripheral use-def information is -exposed. These operations will, of course, necessarily be lowered -to other various MLIR dialects, where they can be -optimized. -The compiler will continue to lower the representation -of the input with successive transformations -to MLIR's LLVM IR dialect, the target independent LLVM IR, machine -IR, and eventually assembly and/or machine code. - -## Details - -Because MLIR is our adopted framework, our bridge to the FIR -dialect of MLIR will use the concepts, libraries, coding -conventions, etc. of the MLIR project. This means some of the class -names will necessarily be changed from the original FIR design. For -example, `Program` becomes `Module`, `Procedure` becomes -`Function`, `BasicBlock` becomes `Block`, and `Statement` becomes -`Operation`. - -Construction of the CFG structure reuses the original FIR pass that -flattens and linearizes the Fortran parse tree structure prior to -creating the FIR dialect of MLIR. - -`Afforestation` (a class in the original FIR code) -of the FIR dialect tree structure has to be rewritten -as the original framework is replaced by that of -MLIR. Conceptually, the process and objective remains the same. - -### FIR Dialect Details - -The FIR dialect is a well-defined set of operations, types, etc. that -captures the executable semantics and state of a correctly specified -Fortran program. In Fortran, action statements [Fortran 2018 R515] and -certain constructs specify the behavior of the program. Among these, -Fortran expressions [Fortran 2018 Clause 10.1] are intrinsic to the -computation of new values. - -#### FIR Operations - -Abstract expression operations are higher-level operations that are -opaque computations in FIR. FIR does not know the exact computations -involved. However, the framework requires explicit representation of how -each operation interacts with others in terms of control flow, -data-flow, and the types of its operands and results. These operations -must then be lowered to sequences of operations as required by the -standard MLIR dialect, the LLVM IR dialect, etc. - -##### Expression ops - -The following lists the FIR abstract expression operations: - -+ _Apply_Expr_ - - This operation computes a (set of) value(s) by applying an abstract - expression to a set of SSA input values. - - As well as its input values and type, an Apply_Expr takes two - attributes that serve to bind the exact opaque expression from the - front-end. The first attribute is the expression as recovered from - the parse tree. The second attribute is a dictionary that maps the - incoming values to positions in the expression representation, - allowing values to bind to nodes in the expression. - -+ _Locate_Expr_ - - This operation computes a (set of) memory reference(s) by applying - an abstract expression to a set of SSA input values. - - Exactly analogous to Apply_Expr, this operation also takes two - attributes to capture the expression from the front-end and to bind - arguments to nodes in the expression tree. - -+ _Alloca_Expr_ - - This operation allocates a temporary object of some specified - type. The resulting object is undefined. It must be explicitly - initialized with subsequent operations. - -+ _Undefined_ - - This operation yields the canonical undefined value of some - type. This operation lowers to the undef instruction in LLVM IR. It - is required for construction of register SSA form when load - operations load uninitialized values. - -+ _Load_Expr_ - - This operation promotes a reference to a Fortran object (for - example, the result of a Locate_Expr operation) to an object value - irrespective of type. It takes one argument, a reference to an - abstract storage location. - - Recall that the abstract expression operations listed here operate - on and produce SSA values. (Specifically, they are abstract - operations with precise control- and data-flow constraints.) - -+ _Store_Expr_ - - This operation demotes a value to a reference to an object. It - takes two arguments: both a value and a reference to an abstract - storage location (for example, the result of a Global_Expr - operation) of the same type. - -##### Control flow ops - -Fortran has a number of mechanisms for specifying control flow, both -structured (DO ... END DO) and unstructured (GOTO). Many of these can be -directly lowered to the standard dialect's branch and conditional branch -operations. - -There are a handful of multiway branch constructs to consider and these -will be modeled as FIR (terminator) operations. The framework supports a -generic terminator pattern operation. Specifically, a terminator is just -an operation but it is augmented with successors (references to basic -blocks) and successor arguments (for correct SSA form). - -+ _Select_ - - Terminator operation for switching based on the return value of, - for example, an I/O action. - -+ _Select_Case_ - - Terminator operation for switching on the value of an expression. - -+ _Select_Rank_ - - Terminator operation for switching on the rank attribute of an - object. Must be lowered to the requisite operations on the object's - descriptor (which shall assumably contain the rank, dimension, - type, etc. of the Fortran object). - -+ _Select_Type_ - - Terminator operation for switching on the type of an object. Must - be lowered to the requisite operations on the object's type - descriptor (which shall contain the encoded type of the Fortran - object). - -+ _Unreachable_ - - This operation is a terminator on a Block and indicates that - control flow cannot reach this point. This happens when lowering - Fortran's [ERROR] STOP statement into a runtime call. It is lowered - into the LLVM IR as an unreachable instruction. - -For now, indirect branches (such as computed GOTO statements), will be -lowered by mapping target blocks into an indirect index value used in a -small chain of conditional branches. This could be changed to use the -standard dialect indirect branch op, however. - -The standard dialect of MLIR supports calling procedures in full -generality -- that is, both function calls in expressions and CALL -statements to subroutines. Both cases will be lowered explicitly into -FIR using the standard dialect CallOp operation. The call operation is -an abstract application of a (presumably) opaque set of computations on -the SSA input values that produces a (set of) SSA result -value(s). (Semantically, a CallOp is a named morphism like the anonymous -ApplyExpr or LocateExpr.) - -##### Miscellaneous ops - -The presence of certain attributes on specific objects allow for dynamic -allocation and deallocation of those objects. These allocations and -deallocations can be explicit through the ALLOCATE and DEALLOCATE -statements or can be implied via side-effect. Dynamic allocation and -deallocation are modeled with FIR operations. It should be pointed out -that MLIR does not, at present, have a standard way of expressing an -object that has process lifetime (that is, "global" data like COMMON -blocks and MODULE variables) that is not a function. This is implemented -with the Global_Expr operation below. - -+ _Allocmem_ - - This operation allocates an object of a specified type and returns - a reference to it. The object's lifetime is dynamic/indefinite and - limited to a matching FreememOp operation that deallocates it. No - side-effect behaviors are implied. Initialization of the object - must be done explicitly in subsequent FIR operations. - -+ _Freemem_ - - This operation deallocates an object via a Fortran reference. Any - use of the reference in subsequent code is undefined - behavior. There are no implicit behaviors, so finalizers must be - lowered explicitly into FIR. - -+ _Global_Expr_ - - This operation is a placeholder for a reference to an object in a - storage location that has process lifetime (a global variable). It - must have a type and a symbol name for binding at link-time. - -+ _Extract_Value_ - - This operation is similar to LLVM's `extractvalue` instruction. - It allows the - extraction of a value from a composite structure, such as a - standard tuple. - -+ _Insert_Value_ - - The operation allows the insertion of a value into a composite - structure, such as a standard tuple. - -+ _Field_Value_ - - This operation computes the offset of a component of a derived type - for use in ExtractValue or InsertValue operations. - -A number of Fortran action statements/constructs are related to -synchronization/threading. (e.g., LOCK, UNLOCK, EVENT POST, DO -CONCURRENT, co-arrays, etc.). The current plan is not to model these -parallel execution semantics directly in FIR, but to lower these -synchronization and threading statements to Fortran runtime calls. The -implicit message passing semantics of co-arrays can be lowered to -explicit calls as well, though the exact API is TBD. - -#### FIR Types - -Some Fortran intrinsic types are familiar and map well to MLIR standard -types. (e.g., `INTEGER*k`, `REAL*k`, and `COMPLEX*k`.) -However, the intrinsic -type `CHARACTER*k(LEN=n)` has no analog. Finally, the intrinsic type -`LOGICAL*k` and derived (user-defined) types should not be prematurely -lowered to standard MLIR types because that may inhibit optimization and -add complexity. (e.g., one could lower a `LOGICAL*`1 type to the standard -`i8` type, but then it would have the same type as `INTEGER*1` and, -depending on its usage, may have better been lowered to `i1`.) - -In addition to types from the surface syntax of Fortran, it is -beneficial to introduce metatypes to FIR to capture Fortran attribute -properties that alter the underlying object in an operational -sense. Specifically, attributes such as `DIMENSION`, `CODIMENSION`, and -`POINTER`, alter the size, behavior, and accepted use of a variable but -not its type (in the Fortran sense). - -The additional FIR types are as follows. - -+ __FIRCharacterType__ - - The Fortran intrinsic type CHARACTER with a kind value. This is - meant to represent the constant-sized memory reference and is - intentionally distinct from, for instance, an array of byte-sized - integers. The LEN parameter of a CHARACTER should be represented as - a second integer member in a FIR tuple type. Concretely, a Fortran - CHARACTER type is lowered into a pair: - - `(FIRReferenceType>, Integer)` - -+ __FIRLogicalType__ - - The Fortran intrinsic type LOGICAL with a kind value. - -+ __FIRRealType__ - - The Fortran intrinsic type REAL with kind values (e.g., KIND=16) - that do not map to standard MLIR. - -+ __FIRReferenceType__ - - The Fortran concept of "reference". Actual arguments are typically - passed as references to objects of some type, for example. All - objects that reside in memory are accessible via reference types. - -+ __FIRSequenceType__ - - The Fortran concept of an object with rank > 0. Fortran does not - have an array type, but FIR characterizes objects with rank as - sequences of their base type. - - In lowering a Fortran array object, the dimensions and extents of - the array may not be known at compile time, and therefore may need - to be lowered to a tuple type that describes the array's structure - (rank, type, dimension information, other attributes). - -+ __FIRTupleType__ - - Fortran derived types naturally map to tuples. Can be used to build - other type packages as well, such as Fortran's CHARACTER type with - its LEN parameter, array object structure descriptors, etc. Each - distinct tuple type has a unique name and a run-time encoding, the - type descriptor. (Note: any subtyping relationships between derived - types must be established by and lowered from the front-end.) - -+ __FIRTypeDesc__ - - The meta-type of all type descriptors. An instance of a type - descriptor is a constant object that encodes a Fortran (intrinsic, - derived) type for use by the runtime, etc. This type is - speculative, as it may be (more) satisfactory to encode a type - descriptor as a simple dope vector sequence. - -### Changes from Original Document - -Procedure calls will be unwrapped from Fortran expressions and lowered -into FIR dialect calls to expose control flow. - -These computations will be presented in a memory-based SSA format, where -memory objects will be referenced via special operation forms. - -There will be a pass to lower -the memory-based SSA form to a -register-based (proper) SSA form. There will be no -Φ nodes. - -There will be no scope enter, scope exit pairs. - -There will be a pass to lower the FIR dialect to the MLIR standard -dialect and/or LLVM IR dialect. - diff --git a/include/fir/.clang-format b/include/fir/.clang-format deleted file mode 100644 index a74fda4b6734..000000000000 --- a/include/fir/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -BasedOnStyle: LLVM -AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/CMakeLists.txt b/include/flang/CMakeLists.txt index 7fae707bdc46..db5d26a83b56 100644 --- a/include/flang/CMakeLists.txt +++ b/include/flang/CMakeLists.txt @@ -1,8 +1,3 @@ -#===-- include/flang/CMakeLists.txt ----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - +if(LINK_WITH_FIR) + add_subdirectory(Optimizer) +endif() diff --git a/include/flang/Optimizer/CMakeLists.txt b/include/flang/Optimizer/CMakeLists.txt new file mode 100644 index 000000000000..0ca0f41c5af4 --- /dev/null +++ b/include/flang/Optimizer/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Dialect) diff --git a/include/flang/Optimizer/Dialect/CMakeLists.txt b/include/flang/Optimizer/Dialect/CMakeLists.txt new file mode 100644 index 000000000000..f83cb8f522a3 --- /dev/null +++ b/include/flang/Optimizer/Dialect/CMakeLists.txt @@ -0,0 +1,22 @@ +# This replicates part of the add_mlir_dialect cmake function from MLIR that +# cannot be used her because it expects to be run inside MLIR directory which +# is not the case for FIR. +set(LLVM_TARGET_DEFINITIONS FIROps.td) +mlir_tablegen(FIROps.h.inc -gen-op-decls) +mlir_tablegen(FIROps.cpp.inc -gen-op-defs) +add_public_tablegen_target(FIROpsIncGen) + +add_custom_target(flang-doc) +set(dialect_doc_filename "FIRLangRef") + +set(LLVM_TARGET_DEFINITIONS FIROps.td) +tablegen(MLIR ${dialect_doc_filename}.md -gen-op-doc "-I${MLIR_MAIN_SRC_DIR}" "-I${MLIR_INCLUDE_DIR}") +set(GEN_DOC_FILE ${FLANG_BINARY_DIR}/docs/Dialect/${dialect_doc_filename}.md) +add_custom_command( + OUTPUT ${GEN_DOC_FILE} + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${dialect_doc_filename}.md + ${GEN_DOC_FILE} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${dialect_doc_filename}.md) +add_custom_target(${dialect_doc_filename}DocGen DEPENDS ${GEN_DOC_FILE}) +add_dependencies(flang-doc ${dialect_doc_filename}DocGen) diff --git a/include/flang/Optimizer/Dialect/FIRAttr.h b/include/flang/Optimizer/Dialect/FIRAttr.h new file mode 100644 index 000000000000..57ce5f13bf65 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRAttr.h @@ -0,0 +1,166 @@ +//===-- Optimizer/Dialect/FIRAttr.h -- FIR attributes -----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_DIALECT_FIRATTR_H +#define OPTIMIZER_DIALECT_FIRATTR_H + +#include "mlir/IR/Attributes.h" + +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + +namespace fir { + +class FIROpsDialect; + +namespace detail { +struct RealAttributeStorage; +struct TypeAttributeStorage; +} // namespace detail + +enum AttributeKind { + FIR_ATTR = mlir::Attribute::FIRST_FIR_ATTR, + FIR_EXACTTYPE, // instance_of, precise type relation + FIR_SUBCLASS, // subsumed_by, is-a (subclass) relation + FIR_POINT, + FIR_CLOSEDCLOSED_INTERVAL, + FIR_OPENCLOSED_INTERVAL, + FIR_CLOSEDOPEN_INTERVAL, + FIR_REAL_ATTR, +}; + +class ExactTypeAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static constexpr llvm::StringRef getAttrName() { return "instance"; } + static ExactTypeAttr get(mlir::Type value); + + mlir::Type getType() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_EXACTTYPE; } +}; + +class SubclassAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static constexpr llvm::StringRef getAttrName() { return "subsumed"; } + static SubclassAttr get(mlir::Type value); + + mlir::Type getType() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_SUBCLASS; } +}; + +// Attributes for building SELECT CASE multiway branches + +/// A closed interval (including the bound values) is an interval with both an +/// upper and lower bound as given as ssa-values. +/// A case selector of `CASE (n:m)` corresponds to any value from `n` to `m` and +/// is encoded as `#fir.interval, %n, %m`. +class ClosedIntervalAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "interval"; } + static ClosedIntervalAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_CLOSEDCLOSED_INTERVAL; + } +}; + +/// An upper bound is an open interval (including the bound value) as given as +/// an ssa-value. +/// A case selector of `CASE (:m)` corresponds to any value up to and including +/// `m` and is encoded as `#fir.upper, %m`. +class UpperBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "upper"; } + static UpperBoundAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_OPENCLOSED_INTERVAL; + } +}; + +/// A lower bound is an open interval (including the bound value) as given as +/// an ssa-value. +/// A case selector of `CASE (n:)` corresponds to any value down to and +/// including `n` and is encoded as `#fir.lower, %n`. +class LowerBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "lower"; } + static LowerBoundAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_CLOSEDOPEN_INTERVAL; + } +}; + +/// A pointer interval is a closed interval as given as an ssa-value. The +/// interval contains exactly one value. +/// A case selector of `CASE (p)` corresponds to exactly the value `p` and is +/// encoded as `#fir.point, %p`. +class PointIntervalAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "point"; } + static PointIntervalAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_POINT; } +}; + +/// A real attribute is used to workaround MLIR's default parsing of a real +/// constant. +/// `#fir.real<10, 3.14>` is used to introduce a real constant of value `3.14` +/// with a kind of `10`. +class RealAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = std::pair; + + static constexpr llvm::StringRef getAttrName() { return "real"; } + static RealAttr get(mlir::MLIRContext *ctxt, const ValueType &key); + + int getFKind() const; + llvm::APFloat getValue() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_REAL_ATTR; } +}; + +mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type); + +void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, + mlir::DialectAsmPrinter &p); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRATTR_H diff --git a/include/flang/Optimizer/Dialect/FIRDialect.h b/include/flang/Optimizer/Dialect/FIRDialect.h new file mode 100644 index 000000000000..4818a7100b9c --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRDialect.h @@ -0,0 +1,92 @@ +//===-- Optimizer/Dialect/FIRDialect.h -- FIR dialect -----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_DIALECT_FIRDIALECT_H +#define OPTIMIZER_DIALECT_FIRDIALECT_H + +#include "mlir/IR/Dialect.h" +#include "mlir/InitAllDialects.h" +#include "mlir/InitAllPasses.h" + +namespace llvm { +class raw_ostream; +class StringRef; +} // namespace llvm + +namespace mlir { +class Attribute; +class DialectAsmParser; +class DialectAsmPrinter; +class Location; +class MLIRContext; +class Type; +} // namespace mlir + +namespace fir { + +/// FIR dialect +class FIROpsDialect final : public mlir::Dialect { +public: + explicit FIROpsDialect(mlir::MLIRContext *ctx); + virtual ~FIROpsDialect(); + + static llvm::StringRef getDialectNamespace() { return "fir"; } + + mlir::Type parseType(mlir::DialectAsmParser &parser) const override; + void printType(mlir::Type ty, mlir::DialectAsmPrinter &p) const override; + + mlir::Attribute parseAttribute(mlir::DialectAsmParser &parser, + mlir::Type type) const override; + void printAttribute(mlir::Attribute attr, + mlir::DialectAsmPrinter &p) const override; +}; + +/// Register the dialect with MLIR +inline void registerFIR() { + // we want to register exactly once + [[maybe_unused]] static bool init_once = [] { + mlir::registerDialect(); + mlir::registerDialect(); + mlir::registerDialect(); + mlir::registerDialect(); + mlir::registerDialect(); + mlir::registerDialect(); + return true; + }(); +} + +/// Register the standard passes we use. This comes from registerAllPasses(), +/// but is a smaller set since we aren't using many of the passes found there. +inline void registerGeneralPasses() { + mlir::createCanonicalizerPass(); + mlir::createCSEPass(); + mlir::createVectorizePass({}); + mlir::createLoopUnrollPass(); + mlir::createLoopUnrollAndJamPass(); + mlir::createSimplifyAffineStructuresPass(); + mlir::createLoopFusionPass(); + mlir::createLoopInvariantCodeMotionPass(); + mlir::createAffineLoopInvariantCodeMotionPass(); + mlir::createPipelineDataTransferPass(); + mlir::createLowerAffinePass(); + mlir::createLoopTilingPass(0); + mlir::createLoopCoalescingPass(); + mlir::createAffineDataCopyGenerationPass(0, 0); + mlir::createMemRefDataFlowOptPass(); + mlir::createStripDebugInfoPass(); + mlir::createPrintOpStatsPass(); + mlir::createInlinerPass(); + mlir::createSymbolDCEPass(); + mlir::createLocationSnapshotPass({}); +} + +inline void registerFIRPasses() { registerGeneralPasses(); } + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRDIALECT_H diff --git a/include/flang/Optimizer/Dialect/FIROps.h b/include/flang/Optimizer/Dialect/FIROps.h new file mode 100644 index 000000000000..f5763693f7bb --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROps.h @@ -0,0 +1,47 @@ +//===-- Optimizer/Dialect/FIROps.h - FIR operations -------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_DIALECT_FIROPS_H +#define OPTIMIZER_DIALECT_FIROPS_H + +#include "mlir/Dialect/StandardOps/IR/Ops.h" + +using namespace mlir; + +namespace fir { + +class FirEndOp; +class LoopOp; +class RealAttr; + +void buildCmpFOp(mlir::Builder *builder, mlir::OperationState &result, + mlir::CmpFPredicate predicate, mlir::Value lhs, + mlir::Value rhs); +void buildCmpCOp(mlir::Builder *builder, mlir::OperationState &result, + mlir::CmpFPredicate predicate, mlir::Value lhs, + mlir::Value rhs); +unsigned getCaseArgumentOffset(llvm::ArrayRef cases, + unsigned dest); +LoopOp getForInductionVarOwner(mlir::Value val); +bool isReferenceLike(mlir::Type type); +mlir::ParseResult isValidCaseAttr(mlir::Attribute attr); +mlir::ParseResult parseCmpfOp(mlir::OpAsmParser &parser, + mlir::OperationState &result); +mlir::ParseResult parseCmpcOp(mlir::OpAsmParser &parser, + mlir::OperationState &result); +mlir::ParseResult parseSelector(mlir::OpAsmParser &parser, + mlir::OperationState &result, + mlir::OpAsmParser::OperandType &selector, + mlir::Type &type); + +#define GET_OP_CLASSES +#include "flang/Optimizer/Dialect/FIROps.h.inc" + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIROPS_H diff --git a/include/flang/Optimizer/Dialect/FIROps.td b/include/flang/Optimizer/Dialect/FIROps.td new file mode 100644 index 000000000000..ab91c10e00fe --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROps.td @@ -0,0 +1,2747 @@ +//===-- FIROps.td - FIR operation definitions --------------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Definition of the FIR dialect operations +/// +//===----------------------------------------------------------------------===// + +#ifndef FIR_DIALECT_FIR_OPS +#define FIR_DIALECT_FIR_OPS + +include "mlir/Interfaces/ControlFlowInterfaces.td" + +def fir_Dialect : Dialect { + let name = "fir"; +} + +// Types and predicates + +def fir_Type : Type, + "FIR dialect type">; + +// Fortran intrinsic types +def fir_CharacterType : Type()">, + "FIR character type">; +def fir_ComplexType : Type()">, + "FIR complex type">; +def fir_IntegerType : Type()">, + "FIR integer type">; +def fir_LogicalType : Type()">, + "FIR logical type">; +def fir_RealType : Type()">, + "FIR real type">; + +// Generalized FIR and standard dialect types representing intrinsic types +def AnyIntegerLike : TypeConstraint, "any integer">; +def AnyLogicalLike : TypeConstraint, "any logical">; +def AnyRealLike : TypeConstraint, "any real">; +def AnyIntegerType : Type; + +// Fortran derived (user defined) type +def fir_RecordType : Type()">, + "FIR derived type">; + +// Fortran array attribute +def fir_SequenceType : Type()">, + "array type">; + +// Composable types +def AnyCompositeLike : TypeConstraint, "any composite">; + +// Reference to an entity type +def fir_ReferenceType : Type()">, + "reference type">; + +// Reference to an ALLOCATABLE attribute type +def fir_HeapType : Type()">, + "allocatable type">; + +// Reference to a POINTER attribute type +def fir_PointerType : Type()">, + "pointer type">; + +// Reference types +def AnyReferenceLike : TypeConstraint, "any reference">; + +// A descriptor tuple (captures a reference to an entity and other information) +def fir_BoxType : Type()">, "box type">; + +// CHARACTER type descriptor. A pair of a data reference and a LEN value. +def fir_BoxCharType : Type()">, + "box character type">; + +// PROCEDURE POINTER descriptor. A pair that can capture a host closure. +def fir_BoxProcType : Type()">, + "box procedure type">; + +def AnyBoxLike : TypeConstraint, "any box">; + +def AnyRefOrBox : TypeConstraint, + "any reference or box">; + +// A vector of Fortran triple notation describing a multidimensional array +def fir_DimsType : Type()">, "dim type">; +def AnyEmboxLike : TypeConstraint, + "any legal embox argument type">; +def AnyEmboxArg : Type; + +// A type descriptor's type +def fir_TypeDescType : Type()">, + "type desc type">; + +// A field (in a RecordType) argument's type +def fir_FieldType : Type()">, "field type">; + +// A LEN parameter (in a RecordType) argument's type +def fir_LenType : Type()">, + "LEN parameter type">; + +def AnyComponentLike : TypeConstraint, + "any coordinate index">; +def AnyComponentType : Type; + +def AnyCoordinateLike : TypeConstraint, "any coordinate index">; +def AnyCoordinateType : Type; + +// Base class for FIR operations. +// All operations automatically get a prefix of "fir.". +class fir_Op traits> + : Op; + +// Base class for FIR operations that take a single argument +class fir_SimpleOp traits> + : fir_Op { + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; +} + +// Base builder for allocate operations +def fir_AllocateOpBuilder : OpBuilder< + "Builder *builder, OperationState &result, Type inType," + "ValueRange lenParams = {}, ValueRange sizes = {}," + "ArrayRef attributes = {}", + [{ + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", TypeAttr::get(inType)); + result.addOperands(sizes); + result.addAttributes(attributes); + }]>; + +def fir_NamedAllocateOpBuilder : OpBuilder< + "Builder *builder, OperationState &result, Type inType, StringRef name," + "ValueRange lenParams = {}, ValueRange sizes = {}," + "ArrayRef attributes = {}", + [{ + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", TypeAttr::get(inType)); + result.addAttribute("name", builder->getStringAttr(name)); + result.addOperands(sizes); + result.addAttributes(attributes); + }]>; + +def fir_OneResultOpBuilder : OpBuilder< + "Builder *, OperationState &result, Type resultType," + "ValueRange operands, ArrayRef attributes = {}", + [{ + if (resultType) + result.addTypes(resultType); + result.addOperands(operands); + result.addAttributes(attributes); + }]>; + +// Base class of FIR operations that return 1 result +class fir_OneResultOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let builders = [fir_OneResultOpBuilder]; +} + +// Base class of FIR operations that have 1 argument and return 1 result +class fir_SimpleOneResultOp traits = []> : + fir_SimpleOp { + let builders = [fir_OneResultOpBuilder]; +} + +class fir_TwoBuilders { + list builders = [b1, b2]; +} + +class fir_AllocatableBaseOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let arguments = (ins + OptionalAttr:$name, + OptionalAttr:$target + ); +} + +class fir_AllocatableOp traits =[]> : + fir_AllocatableBaseOp, + fir_TwoBuilders, + Arguments<(ins TypeAttr:$in_type, Variadic:$args)> { + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype)) + return mlir::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute(inType(), mlir::TypeAttr::get(intype)); + llvm::SmallVector operands; + llvm::SmallVector typeVec; + bool hasOperands = false; + if (!parser.parseOptionalLParen()) { + // parse the LEN params of the derived type. ( : ) + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + parser.parseColonTypeList(typeVec) || + parser.parseRParen()) + return mlir::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(lenpName(), lens); + hasOperands = true; + } + if (!parser.parseOptionalComma()) { + // parse size to scale by, vector of n dimensions of type index + auto opSize = operands.size(); + if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None)) + return mlir::failure(); + for (auto i = opSize, end = operands.size(); i != end; ++i) + typeVec.push_back(builder.getIndexType()); + hasOperands = true; + } + if (hasOperands && + parser.resolveOperands(operands, typeVec, parser.getNameLoc(), + result.operands)) + return mlir::failure(); + mlir::Type restype = wrapResultType(intype); + if (!restype) { + parser.emitError(parser.getNameLoc(), "invalid allocate type: ") + << intype; + return mlir::failure(); + } + if (parser.parseOptionalAttrDict(result.attributes) || + parser.addTypeToList(restype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(inType()); + if (hasLenParams()) { + // print the LEN parameters to a derived type in parens + p << '('; + p.printOperands(getLenParams()); + p << " : "; + mlir::interleaveComma(getLenParams(), p.getStream(), + [&](const auto &opnd) { + p.printType(opnd.getType()); + }); + p << ')'; + } + // print the shape of the allocation (if any); all must be index type + for (auto sh : getShapeOperands()) { + p << ", "; + p.printOperand(sh); + } + p.printOptionalAttrDict(getAttrs(), {inType(), lenpName()}); + }]; + + string extraAllocClassDeclaration = [{ + static constexpr llvm::StringRef inType() { return "in_type"; } + static constexpr llvm::StringRef lenpName() { return "len_param_count"; } + mlir::Type getAllocatedType(); + bool hasLenParams() { return bool{getAttr(lenpName())}; } + unsigned numLenParams() { + if (auto val = getAttrOfType(lenpName())) + return val.getInt(); + return 0; + } + operand_range getLenParams() { + return {operand_begin(), operand_begin() + numLenParams()}; + } + operand_range getShapeOperands() { + return {operand_begin() + numLenParams(), operand_end()}; + } + static mlir::Type getRefTy(mlir::Type ty); + + /// Get the input type of the allocation + mlir::Type getInType() { + return getAttrOfType(inType()).getValue(); + } + }]; + + // Verify checks common to all allocation operations + string allocVerify = [{ + llvm::SmallVector visited; + if (verifyInType(getInType(), visited)) + return emitOpError("invalid type for allocation"); + if (verifyRecordLenParams(getInType(), numLenParams())) + return emitOpError("LEN params do not correspond to type"); + }]; +} + +// Memory SSA operations + +def fir_AllocaOp : fir_AllocatableOp<"alloca"> { + let summary = "allocate storage for a temporary on the stack given a type"; + let description = [{ + This primitive operation is used to allocate an object on the stack. A + reference to the object of type `!fir.ref` is returned. The returned + object has an undefined/uninitialized state. The allocation can be given + an optional name. The allocation may have a dynamic repetition count + for allocating a sequence of locations for the specified type. + + ```mlir + %c = ... : i64 + %x = fir.alloca i32 + %y = fir.alloca !fir.array<8 x i64> + %z = fir.alloca f32, %c + + %i = ... : i16 + %j = ... : i32 + %w = fir.alloca !fir.type (%i, %j : i16, i32) + ``` + + Note that in the case of `%z`, a contiguous block of memory is allocated + and its size is a runtime multiple of a 32-bit REAL value. + + In the case of `%w`, the arguments `%i` and `%j` are LEN parameters + (`len1`, `len2`) to the type `PT`. + + Finally, the operation is undefined if the ssa-value `%c` is negative. + }]; + + let results = (outs fir_ReferenceType); + + let verifier = allocVerify#[{ + mlir::Type outType = getType(); + if (!outType.isa()) + return emitOpError("must be a !fir.ref type"); + return mlir::success(); + }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; +} + +def fir_LoadOp : fir_OneResultOp<"load", []> { + let summary = "load a value from a memory reference"; + let description = [{ + Load a value from a memory reference into an ssa-value (virtual register). + Produces an immutable ssa-value of the referent type. A memory reference + has type `!fir.ref`, `!fir.heap`, or `!fir.ptr`. + + ```mlir + %a = fir.alloca i32 + %l = fir.load %a : !fir.ref + ``` + + The ssa-value has an undefined value if the memory reference is undefined + or null. + }]; + + let arguments = (ins AnyReferenceLike:$memref); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, Value refVal", + [{ + if (!refVal) { + mlir::emitError(result.location, "LoadOp has null argument"); + return; + } + auto refTy = refVal.getType().cast(); + result.addOperands(refVal); + result.addTypes(refTy.getEleTy()); + }] + >]; + + let parser = [{ + mlir::Type type; + mlir::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(oper, type, result.operands)) + return mlir::failure(); + mlir::Type eleTy; + if (getElementOf(eleTy, type) || + parser.addTypeToList(eleTy, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(memref()); + p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref().getType(); + }]; + + let extraClassDeclaration = [{ + static mlir::ParseResult getElementOf(mlir::Type &ele, mlir::Type ref); + }]; +} + +def fir_StoreOp : fir_Op<"store", []> { + let summary = "store an SSA-value to a memory location"; + + let description = [{ + Store an ssa-value (virtual register) to a memory reference. The stored + value must be of the same type as the referent type of the memory + reference. + + ```mlir + %v = ... : f64 + %p = ... : !fir.ptr + fir.store %v to %p : !fir.ptr + ``` + + The above store changes the value to which the pointer is pointing and not + the pointer itself. The operation is undefined if the memory reference, + `%p`, is undefined or null. + }]; + + let arguments = (ins AnyType:$value, AnyReferenceLike:$memref); + + let parser = [{ + mlir::Type type; + mlir::OpAsmParser::OperandType oper; + mlir::OpAsmParser::OperandType store; + if (parser.parseOperand(oper) || + parser.parseKeyword("to") || + parser.parseOperand(store) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(oper, elementType(type), + result.operands) || + parser.resolveOperand(store, type, result.operands)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(value()); + p << " to "; + p.printOperand(memref()); + p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref().getType(); + }]; + + let verifier = [{ + if (value().getType() != fir::dyn_cast_ptrEleTy(memref().getType())) + return emitOpError("store value type must match memory reference type"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static mlir::Type elementType(mlir::Type refType); + }]; +} + +def fir_UndefOp : fir_OneResultOp<"undefined", [NoSideEffect]> { + let summary = "explicit undefined value of some type"; + let description = [{ + Constructs an ssa-value of the specified type with an undefined value. + This operation is typically created internally by the mem2reg conversion + pass. An undefined value can be of any type except `!fir.ref`. + + ```mlir + %a = fir.undefined !fir.array<10 x !fir.type> + ``` + + The example creates an array shaped ssa value. The array is rank 1, extent + 10, and each element has type `!fir.type`. + }]; + + let results = (outs AnyType:$intype); + + let assemblyFormat = "type($intype) attr-dict"; + + let verifier = [{ + if (auto ref = getType().dyn_cast()) + return emitOpError("undefined values of type !fir.ref not allowed"); + return mlir::success(); + }]; +} + +def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { + let summary = "allocate storage on the heap for an object of a given type"; + + let description = [{ + Creates a heap memory reference suitable for storing a value of the + given type, T. The heap refernce returned has type `!fir.heap`. + The memory object is in an undefined state. `allocmem` operations must + be paired with `freemem` operations to avoid memory leaks. + + ```mlir + %0 = fir.allocmem !fir.array<10 x f32> + fir.freemem %0 : !fir.heap> + ``` + }]; + + let results = (outs fir_HeapType); + + let verifier = allocVerify#[{ + mlir::Type outType = getType(); + if (!outType.dyn_cast()) + return emitOpError("must be a !fir.heap type"); + return mlir::success(); + }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; +} + +def fir_FreeMemOp : fir_Op<"freemem", []> { + let summary = "free a heap object"; + + let description = [{ + Deallocates a heap memory reference that was allocated by an `allocmem`. + The memory object that is deallocated is placed in an undefined state + after `fir.freemem`. Optimizations may treat the loading of an object + in the undefined state as undefined behavior. This includes aliasing + references, such as the result of an `fir.embox`. + + ```mlir + %21 = fir.allocmem !fir.type + ... + fir.freemem %21 : !fir.heap> + ``` + }]; + + let arguments = (ins fir_HeapType:$heapref); + + let assemblyFormat = "$heapref attr-dict `:` type($heapref)"; +} + +//===----------------------------------------------------------------------===//// Terminator operations +//===----------------------------------------------------------------------===// + +class fir_SwitchTerminatorOp traits = []> : + fir_Op, Terminator])> { + + let arguments = (ins + AnyType:$selector, + Variadic:$compareArgs, + Variadic:$targetArgs + ); + + let results = (outs); + + let successors = (successor VariadicSuccessor:$targets); + + let builders = [OpBuilder< + "Builder *, OperationState &result, Value selector," + "ValueRange properOperands, ArrayRef destinations," + "ArrayRef operands = {}," + "ArrayRef attributes = {}", + [{ + result.addOperands(selector); + result.addOperands(properOperands); + for (auto kvp : llvm::zip(destinations, operands)) { + result.addSuccessors(std::get<0>(kvp)); + result.addOperands(std::get<1>(kvp)); + } + result.addAttributes(attributes); + }] + >]; + + string extraSwitchClassDeclaration = [{ + using Conditions = mlir::Value; + + static constexpr llvm::StringRef getCasesAttr() { return "case_tags"; } + + // The number of destination conditions that may be tested + unsigned getNumConditions() { return getNumDest(); } + + // The selector is the value being tested to determine the destination + mlir::Value getSelector() { return selector(); } + mlir::Value getSelector(llvm::ArrayRef operands) { + return operands[0]; + } + + // The number of blocks that may be branched to + unsigned getNumDest() { return getOperation()->getNumSuccessors(); } + + llvm::Optional getCompareOperands(unsigned cond); + llvm::Optional> getCompareOperands( + llvm::ArrayRef operands, unsigned cond); + + llvm::Optional> getSuccessorOperands( + llvm::ArrayRef operands, unsigned cond); + + // Helper function to deal with Optional operand forms + void printSuccessorAtIndex(mlir::OpAsmPrinter &p, unsigned i) { + auto *succ = getSuccessor(i); + auto ops = getSuccessorOperands(i); + if (ops.hasValue()) + p.printSuccessorAndUseList(succ, ops.getValue()); + else + p.printSuccessor(succ); + } + }]; +} + +class fir_IntegralSwitchTerminatorOp traits = []> : fir_SwitchTerminatorOp { + + let parser = [{ + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector ivalues; + llvm::SmallVector dests; + llvm::SmallVector, 8> destArgs; + while (true) { + mlir::Attribute ivalue; // Integer or Unit + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(ivalue, "i", temp) || + parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + ivalues.push_back(ivalue); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + auto &bld = parser.getBuilder(); + result.addAttribute(getCasesAttr(), bld.getArrayAttr(ivalues)); + llvm::SmallVector argOffs; + int32_t sumArgs = 0; + const auto count = dests.size(); + for (std::remove_const_t i = 0; i != count; ++i) { + result.addSuccessors(dests[i]); + result.addOperands(destArgs[i]); + auto argSize = destArgs[i].size(); + argOffs.push_back(argSize); + sumArgs += argSize; + } + result.addAttribute(getOperandSegmentSizeAttr(), + bld.getI32VectorAttr({1, 0, sumArgs})); + result.addAttribute(getTargetOffsetAttr(), bld.getI32VectorAttr(argOffs)); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + if (i) + p << ", "; + auto &attr = cases[i]; + if (auto intAttr = attr.dyn_cast_or_null()) + p << intAttr.getValue(); + else + p.printAttribute(attr); + p << ", "; + printSuccessorAtIndex(p, i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {getCasesAttr(), getCompareOffsetAttr(), + getTargetOffsetAttr(), getOperandSegmentSizeAttr()}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa())) + return emitOpError("must be an integer"); + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + auto &attr = cases[i]; + if (attr.isa() || attr.isa()) { + // ok + } else { + return emitOpError("invalid case alternative"); + } + } + return mlir::success(); + }]; + + let extraClassDeclaration = extraSwitchClassDeclaration; +} + +def fir_SelectOp : fir_IntegralSwitchTerminatorOp<"select"> { + let summary = "a multiway branch"; + + let description = [{ + A multiway branch terminator with similar semantics to C's `switch` + statement. A selector value is matched against a list of constants + of the same type for a match. When a match is found, control is + transferred to the corresponding basic block. A `select` must have + at least one basic block with a corresponding `unit` match, and + that block will be selected when all other conditions fail to match. + + ```mlir + fir.select %arg:i32 [1, ^bb1(%0 : i32), + 2, ^bb2(%2,%arg,%arg2 : i32,i32,i32), + -3, ^bb3(%arg2,%2 : i32,i32), + 4, ^bb4(%1 : i32), + unit, ^bb5] + ``` + }]; +} + +def fir_SelectRankOp : fir_IntegralSwitchTerminatorOp<"select_rank"> { + let summary = "Fortran's SELECT RANK statement"; + + let description = [{ + Similar to `select`, `select_rank` provides a way to express Fortran's + SELECT RANK construct. In this case, the rank of the selector value + is matched against constants of integer type. The structure is the + same as `select`, but `select_rank` determines the rank of the selector + variable at runtime to determine the best match. + + ```mlir + fir.select_rank %arg:i32 [1, ^bb1(%0 : i32), + 2, ^bb2(%2,%arg,%arg2 : i32,i32,i32), + 3, ^bb3(%arg2,%2 : i32,i32), + -1, ^bb4(%1 : i32), + unit, ^bb5] + ``` + }]; +} + +def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> { + let summary = "Fortran's SELECT CASE statement"; + + let description = [{ + Similar to `select`, `select_case` provides a way to express Fortran's + SELECT CASE construct. In this case, the selector value is matched + against variables (not just constants) and ranges. The structure is + the same as `select`, but `select_case` allows for the expression of + more complex match conditions. + + ```mlir + fir.select_case %arg : i32 [ + #fir.point, %0, ^bb1(%0 : i32), + #fir.lower, %1, ^bb2(%2,%arg,%arg2,%1 : i32,i32,i32,i32), + #fir.interval, %2, %3, ^bb3(%2,%arg2 : i32,i32), + #fir.upper, %arg, ^bb4(%1 : i32), + unit, ^bb5] + ``` + }]; + + let parser = "return parseSelectCase(parser, result);"; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + if (i) + p << ", "; + p << cases[i] << ", "; + if (!cases[i].isa()) { + auto caseArgs = *getCompareOperands(i); + p.printOperand(*caseArgs.begin()); + p << ", "; + if (cases[i].isa()) { + p.printOperand(*(++caseArgs.begin())); + p << ", "; + } + } + printSuccessorAtIndex(p, i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {getCasesAttr(), getCompareOffsetAttr(), + getTargetOffsetAttr(), getOperandSegmentSizeAttr()}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa())) + return emitOpError("must be an integer, character, or logical"); + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + auto &attr = cases[i]; + if (attr.isa() || + attr.isa() || + attr.isa() || + attr.isa() || + attr.isa()) { + // ok + } else { + return emitOpError("incorrect select case attribute type"); + } + } + return mlir::success(); + }]; + + let extraClassDeclaration = extraSwitchClassDeclaration; +} + +def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> { + let summary = "Fortran's SELECT TYPE statement"; + + let description = [{ + Similar to `select`, `select_type` provides a way to express Fortran's + SELECT TYPE construct. In this case, the type of the selector value + is matched against a list of type descriptors. The structure is the + same as `select`, but `select_type` determines the type of the selector + variable at runtime to determine the best match. + + ```mlir + fir.select_type %arg : !fir.box<()> [ + #fir.instance>, ^bb1(%0 : i32), + #fir.instance>, ^bb2(%2 : i32), + #fir.subsumed>, ^bb3(%2 : i32), + #fir.instance>, ^bb4(%1,%3 : i32,f32), + unit, ^bb5] + ``` + }]; + + let parser = "return parseSelectType(parser, result);"; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + if (i) + p << ", "; + p << cases[i] << ", "; + printSuccessorAtIndex(p, i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {getCasesAttr(), getCompareOffsetAttr(), + getTargetOffsetAttr(), getOperandSegmentSizeAttr()}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa())) + return emitOpError("must be a boxed type"); + auto cases = getAttrOfType(getCasesAttr()).getValue(); + auto count = getNumConditions(); + for (decltype(count) i = 0; i != count; ++i) { + auto &attr = cases[i]; + if (attr.isa() || attr.isa() || + attr.isa()) { + // ok + } else { + return emitOpError("invalid type-case alternative"); + } + } + return mlir::success(); + }]; + + let extraClassDeclaration = extraSwitchClassDeclaration; +} + +def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> { + let summary = "the unreachable instruction"; + + let description = [{ + Terminates a basic block with the assertion that the end of the block + will never be reached at runtime. This instruction can be used + immediately after a call to the Fortran runtime to terminate the + program, for example. This instruction corresponds to the LLVM IR + instruction `unreachable`. + + ```mlir + fir.unreachable + ``` + }]; + + let parser = "return mlir::success();"; + + let printer = "p << getOperationName();"; +} + +def fir_FirEndOp : fir_Op<"end", [Terminator]> { + let summary = "the end instruction"; + + let description = [{ + The end terminator is a special terminator used inside various FIR + operations that have regions. End is thus the custom invisible terminator + for these operations. It is implicit and need not appear in the textual + representation. + }]; +} + +def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> { + let summary = "terminator for GlobalOp"; + let description = [{ + The terminator for a GlobalOp with a body. + + ```mlir + global @variable : tuple { + %0 = constant 45 : i32 + %1 = constant 100.0 : f32 + %2 = fir.undefined tuple + %3 = constant 0 : index + %4 = fir.insert_value %2, %0, %3 : (tuple, i32, index) -> tuple + %5 = constant 1 : index + %6 = fir.insert_value %4, %1, %5 : (tuple, f32, index) -> tuple + fir.has_value %6 : tuple + } + ``` + }]; + + let arguments = (ins AnyType:$resval); + + let assemblyFormat = "$resval attr-dict `:` type($resval)"; +} + +// Operations on !fir.box type objects + +def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]> { + let summary = "boxes a given reference and (optional) dimension information"; + + let description = [{ + Create a boxed reference value. In Fortran, the implementation can require + extra information about an entity, such as its type, rank, etc. This + auxilliary information is packaged and abstracted as a value with box type + by the calling routine. (In Fortran, these are called descriptors.) + + ```mlir + %c1 = constant 1 : index + %c10 = constant 10 : index + %4 = fir.dims(%c1, %c10, %c1) : (index, index, index) -> !fir.dims<1> + %5 = ... : !fir.ref> + %6 = fir.embox %5, %4 : (!fir.ref>, !fir.dims<1>) -> !fir.box> + ``` + + The descriptor tuple may contain additional implementation-specific + information through the use of additional attributes. + }]; + + let arguments = (ins AnyReferenceLike:$memref, Variadic:$args); + + let results = (outs fir_BoxType); + + let parser = "return parseEmboxOp(parser, result);"; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(memref()); + if (hasLenParams()) { + p << '('; + p.printOperands(getLenParams()); + p << ')'; + } + if (getNumOperands() == 2) { + p << ", "; + p.printOperands(dims()); + } else if (auto map = getAttr(layoutName())) { + p << " [" << map << ']'; + } + p.printOptionalAttrDict(getAttrs(), {layoutName(), lenpName()}); + p << " : "; + p.printFunctionalType(getOperation()); + }]; + + let verifier = [{ + if (hasLenParams()) { + auto lenParams = numLenParams(); + auto eleTy = fir::dyn_cast_ptrEleTy(memref().getType()); + if (!eleTy) + return emitOpError("must embox a memory reference type"); + if (auto rt = eleTy.dyn_cast()) { + if (lenParams != rt.getNumLenParams()) + return emitOpError("number of LEN params does not correspond" + " to the !fir.type type"); + } else { + return emitOpError("LEN parameters require !fir.type type"); + } + for (auto lp : getLenParams()) + if (lp.getType().isa()) + return emitOpError("LEN parameters must be integral type"); + } + if (dims().size() == 0) { + // Ok. If there is no dims and no layout map, then emboxing a scalar. + // TODO: Should the type be enforced? It already must agree. + } else if (dims().size() == 1) { + auto d = *dims().begin(); + if (!d.getType().isa()) + return emitOpError("dimension argument must have !fir.dims type"); + } else { + return emitOpError("embox can only have one !fir.dim argument"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef layoutName() { return "layout_map"; } + static constexpr llvm::StringRef lenpName() { return "len_param_count"; } + bool hasLenParams() { return bool{getAttr(lenpName())}; } + unsigned numLenParams() { + if (auto x = getAttrOfType(lenpName())) + return x.getInt(); + return 0; + } + operand_range getLenParams() { + return {operand_begin(), operand_begin() + numLenParams()}; + } + operand_range dims() { + return {operand_begin() + numLenParams() + 1, operand_end()}; + } + }]; +} + +def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> { + let arguments = (ins AnyReferenceLike:$memref, AnyIntegerLike:$len); + let results = (outs fir_BoxCharType); + + let summary = "boxes a given CHARACTER reference and its LEN parameter"; + + let description = [{ + Create a boxed CHARACTER value. The CHARACTER type has the LEN type + parameter, the value of which may only be known at runtime. Therefore, + a variable of type CHARACTER has both its data reference as well as a + LEN type parameter. + + ```fortran + CHARACTER(LEN=10) :: var + ``` + ```mlir + %4 = ... : !fir.ref>> + %5 = constant 10 : i32 + %6 = fir.emboxchar %4, %5 : (!fir.ref>>, i32) -> !fir.boxchar<1> + ``` + + In the above `%4` is a memory reference to a buffer of 10 CHARACTER units. + This buffer and its LEN value (10) are wrapped into a pair in `%6`. + }]; + + let assemblyFormat = [{ + $memref `,` $len attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + auto eleTy = elementTypeOf(memref().getType()); + if (!eleTy.dyn_cast()) + return mlir::failure(); + return mlir::success(); + }]; +} + +def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]> { + + let summary = "boxes a given procedure and optional host context"; + + let description = [{ + Creates an abstract encapsulation of a PROCEDURE POINTER along with an + optional pointer to a host instance context. If the pointer is not to an + internal procedure or the internal procedure does not need a host context + then the form takes only the procedure's symbol. + + ```mlir + %0 = fir.emboxproc @f : ((i32) -> i32) -> !fir.boxproc<(i32) -> i32> + ``` + + An internal procedure requiring a host instance for correct execution uses + the second form. The closure of the host procedure's state is passed as a + reference to a tuple. It is the responsibility of the host to manage the + context's values accordingly, up to and including inhibiting register + promotion of local values. + + ```mlir + %4 = ... : !fir.ref> + %5 = fir.emboxproc @g, %4 : ((i32) -> i32, !fir.ref>) -> !fir.boxproc<(i32) -> i32> + ``` + }]; + + let arguments = (ins SymbolRefAttr:$funcname, AnyReferenceLike:$host); + + let results = (outs fir_BoxProcType); + + let parser = [{ + mlir::SymbolRefAttr procRef; + if (parser.parseAttribute(procRef, "funcname", result.attributes)) + return mlir::failure(); + bool hasTuple = false; + mlir::OpAsmParser::OperandType tupleRef; + if (!parser.parseOptionalComma()) { + if (parser.parseOperand(tupleRef)) + return mlir::failure(); + hasTuple = true; + } + mlir::FunctionType type; + if (parser.parseColon() || + parser.parseLParen() || + parser.parseType(type)) + return mlir::failure(); + result.addAttribute("functype", mlir::TypeAttr::get(type)); + if (hasTuple) { + mlir::Type tupleType; + if (parser.parseComma() || + parser.parseType(tupleType) || + parser.resolveOperand(tupleRef, tupleType, result.operands)) + return mlir::failure(); + } + mlir::Type boxType; + if (parser.parseRParen() || + parser.parseArrow() || + parser.parseType(boxType) || + parser.addTypesToList(boxType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("funcname"); + auto h = host(); + if (h) { + p << ", "; + p.printOperand(h); + } + p << " : (" << getAttr("functype"); + if (h) + p << ", " << h.getType(); + p << ") -> " << getType(); + }]; + + let verifier = [{ + // host bindings (optional) must be a reference to a tuple + if (auto h = host()) { + if (auto r = h.getType().dyn_cast()) { + if (!r.getEleTy().dyn_cast()) + return mlir::failure(); + } else { + return mlir::failure(); + } + } + return mlir::success(); + }]; +} + +def fir_UnboxOp : fir_SimpleOp<"unbox", [NoSideEffect]> { + let summary = "unbox the boxed value into a tuple value"; + + let description = [{ + Unbox a boxed value into a result of multiple values from the box's + component data. The values are, minimally, a reference to the data of the + entity, the byte-size of one element, the rank, the type descriptor, a set + of flags (packed in an integer, and an array of dimension information (of + size rank). + + ```mlir + %40 = ... : !fir.box> + %41:6 = fir.unbox %40 : (!fir.box>) -> (!fir.ref>, i32, i32, !fir.tdesc>, i32, !fir.dims<4>) + ``` + }]; + + let arguments = (ins fir_BoxType:$box); + + let results = (outs + fir_ReferenceType, // pointer to data + AnyIntegerLike, // size of a data element + AnyIntegerLike, // rank of data + fir_TypeDescType, // abstract type descriptor + AnyIntegerLike, // attribute flags (bitfields) + fir_DimsType // dimension information (if any) + ); +} + +def fir_UnboxCharOp : fir_SimpleOp<"unboxchar", [NoSideEffect]> { + let summary = "unbox a boxchar value into a pair value"; + + let description = [{ + Unboxes a value of `boxchar` type into a pair consisting of a memory + reference to the CHARACTER data and the LEN type parameter. + + ```mlir + %45 = ... : !fir.boxchar<1> + %46:2 = fir.unboxchar %45 : (!fir.boxchar<1>) -> (!fir.ref>, i32) + ``` + }]; + + let arguments = (ins fir_BoxCharType:$boxchar); + + let results = (outs fir_ReferenceType, AnyIntegerLike); +} + +def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoSideEffect]> { + let summary = "unbox a boxproc value into a pair value"; + + let description = [{ + Unboxes a value of `boxproc` type into a pair consisting of a procedure + pointer and a pointer to a host context. + + ```mlir + %47 = ... : !fir.boxproc<() -> i32> + %48:2 = fir.unboxproc %47 : (!fir.ref<() -> i32>, !fir.ref>) + ``` + }]; + + let verifier = [{ + if (auto eleTy = fir::dyn_cast_ptrEleTy(refTuple().getType())) + if (eleTy.isa()) + return mlir::success(); + return emitOpError("second output argument has bad type"); + }]; + + let arguments = (ins fir_BoxProcType:$boxproc); + + let results = (outs FunctionType, fir_ReferenceType:$refTuple); +} + +def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]> { + let summary = "return a memory reference to the boxed value"; + + let description = [{ + This operator is overloaded to work with values of type `box`, + `boxchar`, and `boxproc`. The result for each of these + cases, respectively, is the address of the data, the address of the + `CHARACTER` data, and the address of the procedure. + + ```mlir + %51 = fir.box_addr %box : (!fir.box) -> !fir.ref + %52 = fir.box_addr %boxchar : (!fir.boxchar<1>) -> !fir.ref> + %53 = fir.box_addr %boxproc : (!fir.boxproc) -> !fir.ref + ``` + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyReferenceLike); +} + +def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]> { + let summary = "return the LEN type parameter from a boxchar value"; + + let description = [{ + Extracts the LEN type parameter from a `boxchar` value. + + ```mlir + %45 = ... : !boxchar<1> // CHARACTER(20) + %59 = fir.boxchar_len %45 : (!fir.boxchar<1>) -> i64 // len=20 + ``` + }]; + + let arguments = (ins fir_BoxCharType:$val); + + let results = (outs AnyIntegerLike); +} + +def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]> { + let summary = "return the dynamic dimension information for the boxed value"; + + let description = [{ + Returns the triple of lower bound, extent, and stride for `dim` dimension + of `val`, which must have a `box` type. The dimensions are enumerated from + left to right from 0 to rank-1. This operation has undefined behavior if + `dim` is out of bounds. + + ```mlir + %c1 = constant 0 : i32 + %52:3 = fir.box_dims %40, %c1 : (!fir.box>, i32) -> (i32, i32, i32) + ``` + + The above is a request to return the left most row (at index 0) triple from + the box. The triple will be the lower bound, upper bound, and stride. + }]; + + let arguments = (ins fir_BoxType:$val, AnyIntegerLike:$dim); + + let results = (outs AnyIntegerLike, AnyIntegerLike, AnyIntegerLike); + + let assemblyFormat = [{ + $val `,` $dim attr-dict `:` functional-type(operands, results) + }]; + + let extraClassDeclaration = [{ + mlir::Type getTupleType(); + }]; +} + +def fir_BoxEleSizeOp : fir_SimpleOneResultOp<"box_elesize", [NoSideEffect]> { + let summary = "return the size of an element of the boxed value"; + + let description = [{ + Returns the size of an element in an entity of `box` type. This size may + not be known until runtime. + + ```mlir + %53 = fir.box_elesize %40 : (!fir.box, i32) -> i32 // size=4 + %54 = fir.box_elesize %40 : (!fir.box>, i32) -> i32 + ``` + + In the above example, `%53` may box an array of REAL values while `%54` + must box an array of REAL values (with dynamic rank and extent). + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyIntegerLike); +} + +def fir_BoxIsAllocOp : fir_SimpleOp<"box_isalloc", [NoSideEffect]> { + let summary = "is the boxed value an ALLOCATABLE?"; + + let description = [{ + Determine if the boxed value was from an ALLOCATABLE entity. This will + return true if the originating box value was from a `fir.embox` op + with a mem-ref value that had the type !fir.heap. + + ```mlir + %r = ... : !fir.heap + %b = fir.embox %r : (!fir.heap) -> !fir.box + %a = fir.box_isalloc %b : (!fir.box) -> i1 // true + ``` + + The canonical descriptor implementation will carry a flag to record if the + variable is an `ALLOCATABLE`. + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxIsArrayOp : fir_SimpleOp<"box_isarray", [NoSideEffect]> { + let summary = "is the boxed value an array?"; + + let description = [{ + Determine if the boxed value has a positive (> 0) rank. This will return + true if the originating box value was from a fir.embox with a memory + reference value that had the type !fir.array and/or a dims argument. + + ```mlir + %r = ... : !fir.ref + %d = fir.gendims(1, 100, 1) : (i32, i32, i32) -> !fir.dims<1> + %b = fir.embox %r, %d : (!fir.ref, !fir.dims<1>) -> !fir.box + %a = fir.box_isarray %b : (!fir.box) -> i1 // true + ``` + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxIsPtrOp : fir_SimpleOp<"box_isptr", [NoSideEffect]> { + let summary = "is the boxed value a POINTER?"; + + let description = [{ + Determine if the boxed value was from a POINTER entity. + + ```mlir + %p = ... : !fir.ptr + %b = fir.embox %p : (!fir.ptr) -> !fir.box + %a = fir.box_isptr %b : (!fir.box) -> i1 // true + ``` + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxProcHostOp : fir_SimpleOp<"boxproc_host", [NoSideEffect]> { + let summary = "returns the host instance pointer (or null)"; + + let description = [{ + Extract the host context pointer from a boxproc value. + + ```mlir + %8 = ... : !fir.boxproc<(!fir.ref>) -> i32> + %9 = fir.boxproc_host %8 : (!fir.boxproc<(!fir.ref>) -> i32>) -> !fir.ref> + ``` + + In the example, the reference to the closure over the host procedure's + variables is returned. This allows an internal procedure to access the + host's variables. It is up to lowering to determine the contract between + the host and the internal procedure. + }]; + + let arguments = (ins fir_BoxProcType:$val); + + let results = (outs fir_ReferenceType); +} + +def fir_BoxRankOp : fir_SimpleOneResultOp<"box_rank", [NoSideEffect]> { + let summary = "return the number of dimensions for the boxed value"; + + let description = [{ + Return the rank of a value of `box` type. If the value is scalar, the + rank is 0. + + ```mlir + %57 = fir.box_rank %40 : (!fir.box>) -> i32 + %58 = fir.box_rank %41 : (!fir.box) -> i32 + ``` + + The example `%57` shows how one would determine the rank of an array that + has deferred rank at runtime. This rank should be at least 1. In %58, the + descriptor may be either an array or a scalar, so the value is nonnegative. + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyIntegerType); +} + +def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoSideEffect]> { + let summary = "return the type descriptor for the boxed value"; + + let description = [{ + Return the opaque type descriptor of a value of `box` type. A type + descriptor is an implementation defined value that fully describes a type + to the Fortran runtime. + + ```mlir + %7 = fir.box_tdesc %41 : (!fir.box) -> !fir.tdesc + ``` + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs fir_TypeDescType); +} + +// Record and array type operations + +def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> { + let summary = "Finds the coordinate (location) of a value in memory"; + + let description = [{ + Compute the internal coordinate address starting from a boxed value or + unboxed memory reference. Returns a memory reference. When computing the + coordinate of an array element, the rank of the array must be known and + the number of indexing expressions must equal the rank of the array. + + This operation will apply the access map from a boxed value implicitly. + + Unlike LLVM's GEP instruction, one cannot stride over the outermost + reference; therefore, the leading 0 index must be omitted. + + ```mlir + %i = ... : index + %h = ... : !fir.heap> + %p = fir.coordinate_of %h, %i : (!fir.heap>, index) -> !fir.ref + ``` + + In the example, `%p` will be a pointer to the `%i`-th f32 value in the + array `%h`. + }]; + + let arguments = (ins AnyRefOrBox:$ref, Variadic:$coor); + + let results = (outs fir_ReferenceType); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + // Recovering a LEN type parameter only makes sense from a boxed value + for (auto co : coor()) + if (dyn_cast_or_null(co.getDefiningOp())) { + if (getNumOperands() != 2) + return emitOpError("len_param_index must be last argument"); + if (!ref().getType().dyn_cast()) + return emitOpError("len_param_index must be used on box type"); + } + return mlir::success(); + }]; +} + +def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]> { + let summary = "Extract a value from an aggregate SSA-value"; + + let description = [{ + Extract a value from an entity with a type composed of tuples, arrays, + and/or derived types. Returns the value from entity with the type of the + specified component. Cannot be used on values of `!fir.box` type. + + Note that the entity ssa-value must be of compile-time known size in order + to use this operation. + + ```mlir + %f = fir.field_index field, !fir.type + %s = ... : !fir.type + %v = fir.extract_value %s, %f : (!fir.type, !fir.field) -> i32 + ``` + }]; + + let arguments = (ins + AnyCompositeLike:$adt, + Variadic:$coor + ); + + let assemblyFormat = [{ + $adt `,` $coor attr-dict `:` functional-type(operands, results) + }]; +} + +def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> { + let summary = "create a field index value from a field identifier"; + + let description = [{ + Generate a field (offset) value from an identifier. Field values may be + lowered into exact offsets when the layout of a Fortran derived type is + known at compile-time. The type of a field value is `!fir.field` and + these values can be used with the `fir.coordinate_of`, `fir.extract_value`, + or `fir.insert_value` instructions to compute (abstract) addresses of + subobjects. + + ```mlir + %f = fir.field_index field, !fir.type + ``` + }]; + + let arguments = (ins + StrAttr:$field_id, + TypeAttr:$on_type, + Variadic:$lenparams + ); + + let parser = [{ + llvm::StringRef fieldName; + auto &builder = parser.getBuilder(); + mlir::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return mlir::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return mlir::failure(); + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(recty)); + if (!parser.parseOptionalLParen()) { + llvm::SmallVector operands; + llvm::SmallVector types; + auto loc = parser.getNameLoc(); + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + parser.parseRParen() || + parser.parseColonTypeList(types) || + parser.resolveOperands(operands, types, loc, result.operands)) + return mlir::failure(); + } + mlir::Type fieldType = fir::FieldType::get(builder.getContext()); + if (parser.addTypeToList(fieldType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' + << getAttrOfType(fieldAttrName()).getValue() << ", " + << getAttr(typeAttrName()); + if (getNumOperands()) { + p << '('; + p.printOperands(lenparams()); + auto sep = ") : "; + for (auto op : lenparams()) { + p << sep; + if (op) + p.printType(op.getType()); + else + p << "()"; + sep = ", "; + } + } + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName," + "Type recTy, ValueRange operands = {}", + [{ + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); + result.addOperands(operands); + }] + >]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef fieldAttrName() { return "field_id"; } + static constexpr llvm::StringRef typeAttrName() { return "on_type"; } + }]; +} + +def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]> { + + let summary = "generate a value of type `!fir.dims`"; + + let description = [{ + The arguments are an ordered list of integral type values that is a + multiple of 3 in length. Each such triple is defined as: the lower + index, the extent, and the stride for that dimension. The dimension + information is given in the same row-to-column order as Fortran. This + abstract dimension value must describe a reified object, so all dimension + information must be specified. The extent must be nonnegative and the + stride must not be zero. + + ```mlir + %d = fir.gendims %l, %u, %s : (index, index, index) -> !fir.dims<1> + ``` + }]; + + let arguments = (ins Variadic:$triples); + + let results = (outs fir_DimsType); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + auto size = triples().size(); + if (size < 1 || size > 16 * 3) + return emitOpError("incorrect number of args"); + if (size % 3 != 0) + return emitOpError("requires a multiple of 3 args"); + return mlir::success(); + }]; +} + +def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]> { + let summary = "insert a new sub-value into a copy of an existing aggregate"; + + let description = [{ + Insert a value from an entity with a type composed of tuples, arrays, + and/or derived types. Returns a new ssa value with the same type as the + original entity. Cannot be used on values of `!fir.box` type. + + Note that the entity ssa-value must be of compile-time known size in order + to use this operation. + + ```mlir + %a = ... : !fir.array<10xtuple> + %f = ... : f32 + %o = ... : i32 + %c = constant 1 : i32 + %b = fir.insert_value %a, %f, %o, %c : (!fir.array<10x20xtuple>, f32, i32, i32) -> !fir.array<10x20xtuple> + ``` + }]; + + let arguments = (ins AnyCompositeLike:$adt, AnyType:$val, + Variadic:$coor); + let results = (outs AnyCompositeLike); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; +} + +def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> { + let summary = + "create a field index value from a LEN type parameter identifier"; + + let description = [{ + Generate a LEN parameter (offset) value from an LEN parameter identifier. + The type of a LEN parameter value is `!fir.len` and these values can be + used with the `fir.coordinate_of` instructions to compute (abstract) + addresses of LEN parameters. + + ```mlir + %e = fir.len_param_index len1, !fir.type + %p = ... : !fir.box> + %q = fir.coordinate_of %p, %e : (!fir.box>, !fir.len) -> !fir.ref + ``` + }]; + + let arguments = (ins StrAttr:$field_id, TypeAttr:$on_type); + + let parser = [{ + llvm::StringRef fieldName; + auto &builder = parser.getBuilder(); + mlir::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return mlir::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return mlir::failure(); + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(recty)); + mlir::Type lenType = fir::LenType::get(builder.getContext()); + if (parser.addTypeToList(lenType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' + << getAttrOfType(fieldAttrName()).getValue() << ", " + << getAttr(typeAttrName()); + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName, Type recTy", + [{ + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); + }] + >]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef fieldAttrName() { return "field_id"; } + static constexpr llvm::StringRef typeAttrName() { return "on_type"; } + mlir::Type getOnType() { + return getAttrOfType(typeAttrName()).getValue(); + } + }]; +} + +// Fortran loops + +def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; + +def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { + let summary = "generalized loop operation"; + let description = [{ + Generalized high-level looping construct. This operation is similar to + MLIR's `loop.for`. An ordered loop will return the final value of `%i`. + + ```mlir + %l = constant 0 : index + %u = constant 9 : index + fir.loop %i = %l to %u unordered { + %x = fir.convert %i : (index) -> i32 + %v = fir.call @compute(%x) : (i32) -> f32 + %p = fir.coordinate_of %A, %i : (!fir.ref, index) -> !fir.ref + fir.store %v to %p : !fir.ref + } + ``` + + The above example iterates over the interval `[%l, %u]`. The unordered + keyword indicates that the iterations can be executed in any order. + }]; + + let arguments = (ins + Index:$lowerBound, + Index:$upperBound, + Variadic:$optStep, + OptionalAttr:$constantStep, + OptionalAttr:$unordered + ); + + let results = (outs Variadic:$lastVal); + + let regions = (region SizedRegion<1>:$region); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "mlir::Value lowerBound, mlir::Value upperBound," + "ValueRange step = {}, ArrayRef attributes = {}"> + ]; + + let parser = "return parseLoopOp(parser, result);"; + + let printer = [{ + p << getOperationName() << ' ' << getInductionVar() << " = " + << lowerBound() << " to " << upperBound(); + auto s = optStep(); + if (s.begin() != s.end()) { + p << " step "; + p.printOperand(*s.begin()); + } + if (unordered()) + p << " unordered"; + p.printRegion(region(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + p.printOptionalAttrDict(getAttrs(), {unorderedAttrName(), stepAttrName()}); + }]; + + let verifier = [{ + auto step = optStep(); + if (step.begin() != step.end()) { + // FIXME: size of step must be 1 + auto *s = (*step.begin()).getDefiningOp(); + if (auto cst = dyn_cast_or_null(s)) + if (cst.getValue() == 0) + return emitOpError("constant step operand must be nonzero"); + } + + // Check that the body defines as single block argument for the induction + // variable. + auto *body = getBody(); + if (body->getNumArguments() != 1 || + !body->getArgument(0).getType().isIndex()) + return emitOpError("expected body to have a single index argument for " + "the induction variable"); + if (lastVal().size() > 1) + return emitOpError("can only return one final value of iterator"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr const char *unorderedAttrName() { return "unordered"; } + static constexpr const char *stepAttrName() { return "step"; } + + /// Is this an unordered loop? + bool isUnordered() { return getAttr(unorderedAttrName()).isa(); } + + /// Does loop set (and return) the final value of the control variable? + bool hasLastValue() { return lastVal().size(); } + + /// Get the body of the loop + mlir::Block *getBody() { return ®ion().front(); } + + /// Get the block argument corresponding to the loop control value (PHI) + mlir::Value getInductionVar() { return getBody()->getArgument(0); } + + /// Get a builder to insert operations into the LoopOp + mlir::OpBuilder getBodyBuilder() { + return mlir::OpBuilder(getBody(), std::prev(getBody()->end())); + } + + void setLowerBound(mlir::Value bound) { + getOperation()->setOperand(0, bound); + } + + void setUpperBound(mlir::Value bound) { + getOperation()->setOperand(1, bound); + } + + void setStep(mlir::Value step) { + getOperation()->setOperand(2, step); + } + }]; +} + +def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { + let summary = "generalized conditional operation"; + let description = [{ + To conditionally execute operations (typically) within the body of a + `fir.loop` operation. This operation is similar to `loop.if`. + + ```mlir + %56 = ... : i1 + %78 = ... : !fir.ref + fir.where %56 { + fir.store %76 to %78 : !fir.ref + } otherwise { + fir.store %77 to %78 : !fir.ref + } + ``` + }]; + + let arguments = (ins I1:$condition); + + let regions = (region SizedRegion<1>:$whereRegion, AnyRegion:$otherRegion); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"Builder *builder, OperationState &result, " + "Value cond, bool withOtherRegion"> + ]; + + let parser = [{ return parseWhereOp(parser, result); }]; + + let printer = [{ + p << getOperationName() << ' ' << condition(); + p.printRegion(whereRegion(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + + // Print the 'else' regions if it exists and has a block. + auto &otherReg = otherRegion(); + if (!otherReg.empty()) { + p << " otherwise"; + p.printRegion(otherReg, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + } + p.printOptionalAttrDict(getAttrs()); + }]; + + let verifier = [{ + for (auto ®ion : getOperation()->getRegions()) { + if (region.empty()) + continue; + for (auto &b : region) + if (b.getNumArguments() != 0) + return emitOpError("requires that child entry blocks have no args"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + mlir::OpBuilder getWhereBodyBuilder() { + assert(!whereRegion().empty() && "Unexpected empty 'where' region."); + mlir::Block &body = whereRegion().front(); + return mlir::OpBuilder(&body, std::prev(body.end())); + } + mlir::OpBuilder getOtherBodyBuilder() { + assert(!otherRegion().empty() && "Unexpected empty 'other' region."); + mlir::Block &body = otherRegion().front(); + return mlir::OpBuilder(&body, std::prev(body.end())); + } + }]; +} + +// Procedure call operations + +def fir_CallOp : fir_Op<"call", []> { + let summary = "call a procedure"; + + let description = [{ + Call the specified function or function reference. + + Provides a custom parser and pretty printer to allow a more readable syntax + in the FIR dialect, e.g. `fir.call @sub(%12)` or `fir.call %20(%22,%23)`. + + ```mlir + %a = fir.call %funcref(%arg0) : (!fir.ref) -> f32 + %b = fir.call @function(%arg1, %arg2) : (!fir.ref, !fir.ref) -> f32 + ``` + }]; + + let arguments = (ins + OptionalAttr:$callee, + Variadic:$args + ); + + let results = (outs Variadic); + + let parser = "return parseCallOp(parser, result);"; + let printer = "printCallOp(p, *this);"; + + let extraClassDeclaration = [{ + static constexpr StringRef calleeAttrName() { return "callee"; } + }]; +} + +def fir_DispatchOp : fir_Op<"dispatch", []> { + let summary = "call a type-bound procedure"; + + let description = [{ + Perform a dynamic dispatch on the method name via the dispatch table + associated with the first argument. The attribute 'pass_arg_pos' can be + used to select a dispatch argument other than the first one. + + ```mlir + %r = fir.dispatch methodA(%o) : (!fir.box) -> i32 + ``` + }]; + + let arguments = (ins + StrAttr:$method, + fir_BoxType:$object, + Variadic:$args + ); + + let results = (outs Variadic); + + let parser = [{ + mlir::FunctionType calleeType; + llvm::SmallVector operands; + auto calleeLoc = parser.getNameLoc(); + llvm::StringRef calleeName; + if (failed(parser.parseOptionalKeyword(&calleeName))) { + mlir::StringAttr calleeAttr; + if (parser.parseAttribute(calleeAttr, "method", result.attributes)) + return mlir::failure(); + } else { + result.addAttribute("method", + parser.getBuilder().getStringAttr(calleeName)); + } + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(calleeType) || + parser.addTypesToList(calleeType.getResults(), result.types) || + parser.resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result.operands)) + return mlir::failure(); + result.addAttribute("fn_type", mlir::TypeAttr::get(calleeType)); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("method") << '('; + p.printOperand(object()); + if (arg_operand_begin() != arg_operand_end()) { + p << ", "; + p.printOperands(args()); + } + p << ')'; + p.printOptionalAttrDict(getAttrs(), {"fn_type", "method"}); + auto resTy{getResultTypes()}; + llvm::SmallVector argTy(getOperandTypes()); + p << " : " << mlir::FunctionType::get(argTy, resTy, getContext()); + }]; + + let extraClassDeclaration = [{ + mlir::FunctionType getFunctionType(); + operand_range getArgOperands() { + return {arg_operand_begin(), arg_operand_end()}; + } + operand_iterator arg_operand_begin() { return operand_begin() + 1; } + operand_iterator arg_operand_end() { return operand_end(); } + llvm::StringRef passArgAttrName() { return "pass_arg_pos"; } + unsigned passArgPos(); + }]; +} + +// Constant operations that support Fortran + +def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> { + let summary = "create a string literal constant"; + + let description = [{ + An FIR constant that represents a sequence of characters that correspond + to Fortran's CHARACTER type, including a LEN. We support CHARACTER values + of different KINDs (different constant sizes). + + ```mlir + %1 = fir.string_lit "Hello, World!"(13) : !fir.char<1> // ASCII + %2 = fir.string_lit [158, 2345](2) : !fir.char<2> // Wide chars + ``` + }]; + + let results = (outs fir_SequenceType); + + let parser = [{ + auto &builder = parser.getBuilder(); + mlir::Attribute val; + llvm::SmallVector attrs; + if (parser.parseAttribute(val, "fake", attrs)) + return mlir::failure(); + if (auto v = val.dyn_cast()) + result.attributes.push_back(builder.getNamedAttr(value(), v)); + else if (auto v = val.dyn_cast()) + result.attributes.push_back(builder.getNamedAttr(xlist(), v)); + else + return mlir::failure(); + mlir::IntegerAttr sz; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(sz, size(), result.attributes) || + parser.parseRParen() || + parser.parseColonType(type)) + return mlir::failure(); + type = fir::SequenceType::get({sz.getInt()}, type); + if (!type || + parser.addTypesToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getValue() << '('; + p << getSize().cast().getValue() << ") : "; + p.printType(getType().cast().getEleTy()); + }]; + + let verifier = [{ + if (getSize().cast().getValue().isNegative()) + return emitOpError("size must be non-negative"); + auto eleTy = getType().cast().getEleTy(); + if (!eleTy.isa()) + return emitOpError("must have !fir.char type"); + if (auto xl = getAttr(xlist())) { + auto xList = xl.cast(); + for (auto a : xList) + if (!a.isa()) + return emitOpError("values in list must be integers"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr const char *size() { return "size"; } + static constexpr const char *value() { return "value"; } + static constexpr const char *xlist() { return "xlist"; } + + // Get the LEN attribute of this character constant + mlir::Attribute getSize() { return getAttr(size()); } + // Get the string value of this character constant + mlir::Attribute getValue() { + if (auto attr = getAttr(value())) + return attr; + return getAttr(xlist()); + } + + /// Is this a wide character literal (1 character > 8 bits) + bool isWideValue(); + }]; +} + +// Complex operations + +class fir_ArithmeticOp traits = []> : + fir_Op, + Results<(outs AnyType)> { + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; + + let printer = [{ return printBinaryOp(this->getOperation(), p); }]; +} + +class fir_UnaryArithmeticOp traits = []> : + fir_Op, + Results<(outs AnyType)> { + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; + + let printer = [{ return printUnaryOp(this->getOperation(), p); }]; +} + +def FirRealAttr : Attr()">, "FIR real attr"> { + let storageType = [{ fir::RealAttr }]; + let returnType = [{ llvm::APFloat }]; +} + +def fir_ConstfOp : fir_Op<"constf", [NoSideEffect]> { + let summary = "create a floating point constant"; + + let description = [{ + A floating-point constant. This operation is to augment MLIR to be able + to represent APFloat values that are not supported in the standard dialect. + }]; + + let arguments = (ins FirRealAttr:$constant); + + let results = (outs fir_RealType:$res); + + let assemblyFormat = "`(` $constant `)` attr-dict `:` type($res)"; + + let verifier = [{ + if (!getType().isa()) + return emitOpError("must be a !fir.real type"); + return mlir::success(); + }]; +} + +class RealUnaryArithmeticOp traits = []> : + fir_UnaryArithmeticOp, + Arguments<(ins AnyRealLike:$operand)>; + +def fir_NegfOp : RealUnaryArithmeticOp<"negf">; + +class RealArithmeticOp traits = []> : + fir_ArithmeticOp, + Arguments<(ins AnyRealLike:$lhs, AnyRealLike:$rhs)>; + +def fir_AddfOp : RealArithmeticOp<"addf", [Commutative]>; +def fir_SubfOp : RealArithmeticOp<"subf">; +def fir_MulfOp : RealArithmeticOp<"mulf", [Commutative]>; +def fir_DivfOp : RealArithmeticOp<"divf">; +def fir_ModfOp : RealArithmeticOp<"modf">; +// Pow is a builtin call and not a primitive + +def fir_CmpfOp : fir_Op<"cmpf", + [NoSideEffect, SameTypeOperands, SameOperandsAndResultShape]> { + let summary = "floating-point comparison operator"; + + let description = [{ + Extends the standard floating-point comparison to handle the extended + floating-point types found in FIR. + }]; + + let arguments = (ins AnyRealLike:$lhs, AnyRealLike:$rhs); + + let results = (outs AnyLogicalLike); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, CmpFPredicate predicate," + "Value lhs, Value rhs", [{ + buildCmpFOp(builder, result, predicate, lhs, rhs); + }]>]; + + let parser = [{ return parseCmpfOp(parser, result); }]; + + let printer = [{ printCmpfOp(p, *this); }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + static CmpFPredicate getPredicateByName(llvm::StringRef name); + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} + +def fir_ConstcOp : fir_Op<"constc", [NoSideEffect]>, + Results<(outs fir_ComplexType)> { + let summary = "create a complex constant"; + + let description = [{ + A complex constant. Similar to the standard dialect complex type, but this + extension allows constants with APFloat values that are not supported in + the standard dialect. + }]; + + let parser = [{ + fir::RealAttr realp; + fir::RealAttr imagp; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(realp, realAttrName(), result.attributes) || + parser.parseComma() || + parser.parseAttribute(imagp, imagAttrName(), result.attributes) || + parser.parseRParen() || + parser.parseColonType(type) || + parser.addTypesToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (0x"; + auto f1 = getAttr(realAttrName()).cast(); + auto i1 = f1.getValue().bitcastToAPInt(); + p.getStream().write_hex(i1.getZExtValue()); + p << ", 0x"; + auto f2 = getAttr(imagAttrName()).cast(); + auto i2 = f2.getValue().bitcastToAPInt(); + p.getStream().write_hex(i2.getZExtValue()); + p << ") : "; + p.printType(getType()); + }]; + + let verifier = [{ + if (!getType().isa()) + return emitOpError("must be a !fir.complex type"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef realAttrName() { return "real"; } + static constexpr llvm::StringRef imagAttrName() { return "imaginary"; } + + mlir::Attribute getReal() { return getAttr(realAttrName()); } + mlir::Attribute getImaginary() { return getAttr(imagAttrName()); } + }]; +} + +class ComplexUnaryArithmeticOp traits = []> : + fir_UnaryArithmeticOp, + Arguments<(ins fir_ComplexType:$operand)>; + +def fir_NegcOp : ComplexUnaryArithmeticOp<"negc">; + +class ComplexArithmeticOp traits = []> : + fir_ArithmeticOp, + Arguments<(ins fir_ComplexType:$lhs, fir_ComplexType:$rhs)>; + +def fir_AddcOp : ComplexArithmeticOp<"addc", [Commutative]>; +def fir_SubcOp : ComplexArithmeticOp<"subc">; +def fir_MulcOp : ComplexArithmeticOp<"mulc", [Commutative]>; +def fir_DivcOp : ComplexArithmeticOp<"divc">; +// Pow is a builtin call and not a primitive + +def fir_CmpcOp : fir_Op<"cmpc", + [NoSideEffect, SameTypeOperands, SameOperandsAndResultShape]> { + let summary = "complex floating-point comparison operator"; + + let description = [{ + A complex comparison to handle complex types found in FIR. + }]; + + let arguments = (ins fir_ComplexType:$lhs, fir_ComplexType:$rhs); + + let results = (outs AnyLogicalLike); + + let parser = "return parseCmpcOp(parser, result);"; + + let printer = "printCmpcOp(p, *this);"; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, CmpFPredicate predicate," + "Value lhs, Value rhs", [{ + buildCmpCOp(builder, result, predicate, lhs, rhs); + }]>]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} + +// Other misc. operations + +def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { + let summary = "convert a symbol to an SSA value"; + let description = [{ + Convert a symbol (a function or global reference) to an SSA-value to be + used in other Operations. + + ```mlir + %p = fir.address_of(@symbol) : !fir.ref + ``` + }]; + + let arguments = (ins SymbolRefAttr:$symbol); + + let results = (outs fir_ReferenceType:$resTy); + + let assemblyFormat = "`(` $symbol `)` attr-dict `:` type($resTy)"; +} + +def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]> { + let summary = "encapsulates all Fortran scalar type conversions"; + let description = [{ + Generalized type conversion. Convert the ssa value from type T to type U. + Not all pairs of types have conversions. When types T and U are the same + type, this instruction is a NOP and may be folded away. + + ```mlir + %v = ... : i64 + %w = fir.convert %v : (i64) -> i32 + ``` + + The example truncates the value `%v` from an i64 to an i32. + }]; + + let arguments = (ins AnyType:$value); + + let assemblyFormat = [{ + $value attr-dict `:` functional-type($value, results) + }]; +} + +def FortranTypeAttr : Attr()">, + Or<[CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">]>]>, + "Fortran surface type"> { + let storageType = [{ TypeAttr }]; + let returnType = "Type"; + let convertFromStorage = "$_self.getValue().cast()"; +} + +def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { + let summary = "generate a type descriptor for a given type"; + let description = [{ + Generates a constant object that is an abstract type descriptor of the + specified type. The meta-type of a type descriptor for the type `T` + is `!fir.tdesc`. + + ```mlir + !T = type !fir.type + %t = fir.gentypedesc !T // returns value of !fir.tdesc + ``` + }]; + + let arguments = (ins FortranTypeAttr:$in_type); + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype)) + return mlir::failure(); + result.addAttribute("in_type", mlir::TypeAttr::get(intype)); + mlir::Type restype = TypeDescType::get(intype); + if (parser.addTypeToList(restype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + p.printOptionalAttrDict(getAttrs(), {"in_type"}); + }]; + + let builders = [ + OpBuilder<"Builder *, OperationState &result, mlir::TypeAttr inty"> + ]; + + let verifier = [{ + mlir::Type resultTy = getType(); + if (auto tdesc = resultTy.dyn_cast()) { + if (tdesc.getOfTy() != getInType()) + return emitOpError("wrapped type mismatched"); + } else { + return emitOpError("must be !fir.tdesc type"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + mlir::Type getInType() { + // get the type that the type descriptor describes + return getAttrOfType("in_type").getValue(); + } + }]; +} + +def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", + [SameOperandsAndResultType]> { + let summary = "synthetic op to prevent reassociation"; + let description = [{ + Primitive operation meant to intrusively prevent operator reassociation. + The operation is otherwise a nop and the value returned is the same as the + argument. + + The presence of this operation prevents any local optimizations. In the + example below, this would prevent possibly replacing the multiply and add + operations with a single FMA operation. + + ```mlir + %98 = mulf %96, %97 : f32 + %99 = fir.no_reassoc %98 : f32 + %a0 = addf %99, %95 : f32 + ``` + }]; + + let arguments = (ins AnyType:$val); + + let assemblyFormat = "$val attr-dict `:` type($val)"; +} + +class AtMostRegion : Region< + CPred<"$_self.getBlocks().size() <= " # numBlocks>, + "region with " # numBlocks # " blocks">; + +def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> { + let summary = "Global data"; + let description = [{ + A global variable or constant with initial values. + + The example creates a global variable (writable) named + `@_QV_Mquark_Vvarble` with some initial values. The initializer should + conform to the variable's type. + + ```mlir + fir.global @_QV_Mquark_Vvarble : tuple { + %1 = constant 1 : i32 + %2 = constant 2.0 : f32 + %3 = fir.undefined tuple + %z = constant 0 : index + %o = constant 1 : index + %4 = fir.insert_value %3, %1, %z : (tuple, i32, index) -> tuple + %5 = fir.insert_value %4, %1, %o : (tuple, f32, index) -> tuple + return %5 + } + ``` + }]; + + let arguments = (ins + StrAttr:$sym_name, + OptionalAttr:$initval, + UnitAttr:$constant, + TypeAttr:$type + ); + + let results = (outs fir_ReferenceType:$resultType); + + let regions = (region AtMostRegion<1>:$region); + + let parser = [{ + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(), + result.attributes)) + return failure(); + + auto &builder = parser.getBuilder(); + auto name = nameAttr.getRootReference(); + result.attributes.back().second = builder.getStringAttr(name); + + bool simpleInitializer = false; + if (!parser.parseOptionalLParen()) { + Attribute attr; + if (parser.parseAttribute(attr, initValAttrName(), result.attributes) || + parser.parseRParen()) + return failure(); + simpleInitializer = true; + } + + if (succeeded(parser.parseOptionalKeyword(constantAttrName()))) { + // if "constant" keyword then mark this as a constant, not a variable + result.addAttribute(constantAttrName(), builder.getUnitAttr()); + } + + mlir::Type globalType; + if (parser.parseColonType(globalType)) + return failure(); + + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(globalType)); + + if (!simpleInitializer) { + // Parse the optional initializer body. + if (parser.parseRegion(*result.addRegion(), llvm::None, llvm::None)) + return failure(); + } + + auto refTy = AllocaOp::wrapResultType(globalType); + if (parser.addTypeToList(refTy, result.types)) + return failure(); + return success(); + }]; + + let printer = [{ + auto varName = getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()).getValue(); + p << getOperationName() << " @" << varName; + if (auto iv = initval().getValueOr(Attribute())) { + p << '('; + p.printAttribute(iv); + p << ')'; + } + if (getAttr(constantAttrName())) + p << ' ' << constantAttrName(); + p << " : "; + p.printType(getType()); + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/true); + }]; + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, Type type, ArrayRef attrs = {}", + [{ + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(type)); + result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); + result.addAttributes(attrs); + }]> + ]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef constantAttrName() { return "constant"; } + static constexpr llvm::StringRef initValAttrName() { return "initval"; } + static constexpr llvm::StringRef typeAttrName() { return "type"; } + + mlir::Type getType() { + return getAttrOfType(typeAttrName()).getValue(); + } + + /// Append the next initializer value to the `GlobalOp` to construct + /// the variable's initial value. + void appendInitialValue(mlir::Operation *op); + + /// A GlobalOp has one region. + mlir::Region &getRegion() { return getOperation()->getRegion(0); } + + /// A GlobalOp has one block. + mlir::Block &getBlock() { return getRegion().front(); } + }]; +} + +def fir_GlobalLenOp : fir_Op<"global_len", []> { + let summary = "map a LEN parameter to a global"; + let description = [{ + A global entity (that is not an automatic data object) can have extra LEN + parameter (compile-time) constants associated with the instance's type. + These values can be bound to the global instance used `fir.global_len`. + + ```mlir + global @g : !fir.type { + fir.global_len len1, 10 : i32 + %1 = fir.undefined : !fir.type + return %1 : !fir.type + } + ``` + }]; + + let arguments = (ins StrAttr:$lenparam, APIntAttr:$intval); + + let parser = [{ + llvm::StringRef fieldName; + if (failed(parser.parseOptionalKeyword(&fieldName))) { + mlir::StringAttr fieldAttr; + if (parser.parseAttribute(fieldAttr, lenParamAttrName(), + result.attributes)) + return mlir::failure(); + } else { + result.addAttribute(lenParamAttrName(), + parser.getBuilder().getStringAttr(fieldName)); + } + mlir::IntegerAttr constant; + if (parser.parseComma() || + parser.parseAttribute(constant, intAttrName(), result.attributes)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(lenParamAttrName()) << ", " + << getAttr(intAttrName()); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef lenParamAttrName() { return "lenparam"; } + static constexpr llvm::StringRef intAttrName() { return "intval"; } + }]; +} + +def fir_DispatchTableOp : fir_Op<"dispatch_table", [IsolatedFromAbove, Symbol, + ImplicitFirTerminator]> { + let summary = "Dispatch table definition"; + + let description = [{ + Define a dispatch table for a derived type with type-bound procedures. + + A dispatch table is an untyped symbol that contains a list of associations + between method identifiers and corresponding `FuncOp` symbols. + + The ordering of associations in the map is determined by the front-end. + + ```mlir + fir.dispatch_table @_QDTMquuzTfoo { + fir.dt_entry method1, @_QFNMquuzTfooPmethod1AfooR + fir.dt_entry method2, @_QFNMquuzTfooPmethod2AfooII + } + ``` + }]; + + let parser = [{ + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(), + result.attributes)) + return failure(); + + // Convert the parsed name attr into a string attr. + result.attributes.back().second = + parser.getBuilder().getStringAttr(nameAttr.getRootReference()); + + // Parse the optional table body. + mlir::Region *body = result.addRegion(); + if (parser.parseOptionalRegion(*body, llvm::None, llvm::None)) + return mlir::failure(); + + ensureTerminator(*body, parser.getBuilder(), result.location); + return mlir::success(); + }]; + + let printer = [{ + auto tableName = getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()).getValue(); + p << getOperationName() << " @" << tableName; + + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + }]; + + let verifier = [{ + for (auto &op : getBlock()) + if (!(isa(op) || isa(op))) + return emitOpError("dispatch table must contain dt_entry"); + return mlir::success(); + }]; + + let regions = (region SizedRegion<1>:$region); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState *result," + "StringRef name, Type type, ArrayRef attrs = {}", + [{ + result->addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); + result->addAttributes(attrs); + }]> + ]; + + let extraClassDeclaration = [{ + /// Append a dispatch table entry to the table. + void appendTableEntry(mlir::Operation *op); + + mlir::Region &getRegion() { + return this->getOperation()->getRegion(0); + } + + mlir::Block &getBlock() { + return getRegion().front(); + } + }]; +} + +def fir_DTEntryOp : fir_Op<"dt_entry", []> { + let summary = "map entry in a dispatch table"; + + let description = [{ + An entry in a dispatch table. Allows a function symbol to be bound + to a specifier method identifier. A dispatch operation uses the dynamic + type of a distinguished argument to determine an exact dispatch table + and uses the method identifier to select the type-bound procedure to + be called. + + ```mlir + fir.dt_entry method_name, @uniquedProcedure + ``` + }]; + + let arguments = (ins StrAttr:$method, SymbolRefAttr:$proc); + + let parser = [{ + llvm::StringRef methodName; + // allow `methodName` or `"methodName"` + if (failed(parser.parseOptionalKeyword(&methodName))) { + mlir::StringAttr methodAttr; + if (parser.parseAttribute(methodAttr, methodAttrName(), + result.attributes)) + return mlir::failure(); + } else { + result.addAttribute(methodAttrName(), + parser.getBuilder().getStringAttr(methodName)); + } + mlir::SymbolRefAttr calleeAttr; + if (parser.parseComma() || + parser.parseAttribute(calleeAttr, procAttrName(), result.attributes)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(methodAttrName()) << ", " + << getAttr(procAttrName()); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef methodAttrName() { return "method"; } + static constexpr llvm::StringRef procAttrName() { return "proc"; } + }]; +} + +#endif diff --git a/include/flang/Optimizer/Dialect/FIROpsSupport.h b/include/flang/Optimizer/Dialect/FIROpsSupport.h new file mode 100644 index 000000000000..13e4e5ab71b2 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROpsSupport.h @@ -0,0 +1,63 @@ +//===-- Optimizer/Dialect/FIROpsSupport.h -- FIR op support -----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_DIALECT_FIROPSSUPPORT_H +#define OPTIMIZER_DIALECT_FIROPSSUPPORT_H + +#include "flang/Optimizer/Dialect/FIROps.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" + +namespace fir { + +/// return true iff the Operation is a non-volatile LoadOp +inline bool nonVolatileLoad(mlir::Operation *op) { + if (auto load = dyn_cast(op)) + return !load.getAttr("volatile"); + return false; +} + +/// return true iff the Operation is a call +inline bool isaCall(mlir::Operation *op) { + return isa(op) || isa(op) || + isa(op) || isa(op); +} + +/// return true iff the Operation is a fir::CallOp, fir::DispatchOp, +/// mlir::CallOp, or mlir::CallIndirectOp and not pure +/// NB: this is not the same as `!pureCall(op)` +inline bool impureCall(mlir::Operation *op) { + // Should we also auto-detect that the called function is pure if its + // arguments are not references? For now, rely on a "pure" attribute. + return op && isaCall(op) && !op->getAttr("pure"); +} + +/// return true iff the Operation is a fir::CallOp, fir::DispatchOp, +/// mlir::CallOp, or mlir::CallIndirectOp and is also pure. +/// NB: this is not the same as `!impureCall(op)` +inline bool pureCall(mlir::Operation *op) { + // Should we also auto-detect that the called function is pure if its + // arguments are not references? For now, rely on a "pure" attribute. + return op && isaCall(op) && op->getAttr("pure"); +} + +/// Get or create a FuncOp in a module. +/// +/// If `module` already contains FuncOp `name`, it is returned. Otherwise, a new +/// FuncOp is created, and that new FuncOp is returned. +mlir::FuncOp createFuncOp(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, mlir::FunctionType type, + llvm::ArrayRef attrs = {}); + +/// Get or create a GlobalOp in a module. +fir::GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, mlir::Type type, + llvm::ArrayRef attrs = {}); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIROPSSUPPORT_H diff --git a/include/flang/Optimizer/Dialect/FIRType.h b/include/flang/Optimizer/Dialect/FIRType.h new file mode 100644 index 000000000000..73174371718a --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRType.h @@ -0,0 +1,399 @@ +//===-- Optimizer/Dialect/FIRType.h -- FIR types ----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_DIALECT_FIRTYPE_H +#define OPTIMIZER_DIALECT_FIRTYPE_H + +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Types.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class raw_ostream; +class StringRef; +template +class ArrayRef; +class hash_code; +} // namespace llvm + +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + +namespace fir { + +class FIROpsDialect; + +using KindTy = int; + +namespace detail { +struct BoxTypeStorage; +struct BoxCharTypeStorage; +struct BoxProcTypeStorage; +struct CharacterTypeStorage; +struct CplxTypeStorage; +struct DimsTypeStorage; +struct FieldTypeStorage; +struct HeapTypeStorage; +struct IntTypeStorage; +struct LenTypeStorage; +struct LogicalTypeStorage; +struct PointerTypeStorage; +struct RealTypeStorage; +struct RecordTypeStorage; +struct ReferenceTypeStorage; +struct SequenceTypeStorage; +struct TypeDescTypeStorage; +} // namespace detail + +/// Integral identifier for all the types comprising the FIR type system +enum TypeKind { + // The enum starts at the range reserved for this dialect. + FIR_TYPE = mlir::Type::FIRST_FIR_TYPE, + FIR_BOX, // (static) descriptor + FIR_BOXCHAR, // CHARACTER pointer and length + FIR_BOXPROC, // procedure with host association + FIR_CHARACTER, // intrinsic type + FIR_COMPLEX, // intrinsic type + FIR_DERIVED, // derived + FIR_DIMS, + FIR_FIELD, + FIR_HEAP, + FIR_INT, // intrinsic type + FIR_LEN, + FIR_LOGICAL, // intrinsic type + FIR_POINTER, // POINTER attr + FIR_REAL, // intrinsic type + FIR_REFERENCE, + FIR_SEQUENCE, // DIMENSION attr + FIR_TYPEDESC, +}; + +// These isa_ routines follow the precedent of llvm::isa_or_null<> + +/// Is `t` any of the FIR dialect types? +bool isa_fir_type(mlir::Type t); + +/// Is `t` any of the Standard dialect types? +bool isa_std_type(mlir::Type t); + +/// Is `t` any of the FIR dialect or Standard dialect types? +bool isa_fir_or_std_type(mlir::Type t); + +/// Is `t` a FIR dialect type that implies a memory (de)reference? +bool isa_ref_type(mlir::Type t); + +/// Is `t` a FIR dialect aggregate type? +bool isa_aggregate(mlir::Type t); + +/// Extract the `Type` pointed to from a FIR memory reference type. If `t` is +/// not a memory reference type, then returns a null `Type`. +mlir::Type dyn_cast_ptrEleTy(mlir::Type t); + +/// Boilerplate mixin template +template +struct IntrinsicTypeMixin { + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return Id; } +}; + +// Intrinsic types + +/// Model of the Fortran CHARACTER intrinsic type, including the KIND type +/// parameter. The model does not include a LEN type parameter. A CharacterType +/// is thus the type of a single character value. +class CharacterType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CharacterType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +/// Model of a Fortran COMPLEX intrinsic type, including the KIND type +/// parameter. COMPLEX is a floating point type with a real and imaginary +/// member. +class CplxType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CplxType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +/// Model of a Fortran INTEGER intrinsic type, including the KIND type +/// parameter. +class IntType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static IntType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +/// Model of a Fortran LOGICAL intrinsic type, including the KIND type +/// parameter. +class LogicalType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static LogicalType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +/// Model of a Fortran REAL (and DOUBLE PRECISION) intrinsic type, including the +/// KIND type parameter. +class RealType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static RealType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +// FIR support types + +/// The type of a Fortran descriptor. Descriptors are tuples of information that +/// describe an entity being passed from a calling context. This information +/// might include (but is not limited to) whether the entity is an array, its +/// size, or what type it has. +class BoxType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxType get(mlir::Type eleTy, mlir::AffineMapAttr map = {}); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOX; } + mlir::Type getEleTy() const; + mlir::AffineMapAttr getLayoutMap() const; + + static mlir::LogicalResult + verifyConstructionInvariants(mlir::Location, mlir::Type eleTy, + mlir::AffineMapAttr map); +}; + +/// The type of a pair that describes a CHARACTER variable. Specifically, a +/// CHARACTER consists of a reference to a buffer (the string value) and a LEN +/// type parameter (the runtime length of the buffer). +class BoxCharType : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxCharType get(mlir::MLIRContext *ctxt, KindTy kind); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOXCHAR; } + CharacterType getEleTy() const; +}; + +/// The type of a pair that describes a PROCEDURE reference. Pointers to +/// internal procedures must carry an additional reference to the host's +/// variables that are referenced. +class BoxProcType : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxProcType get(mlir::Type eleTy); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOXPROC; } + mlir::Type getEleTy() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + mlir::Type eleTy); +}; + +/// The type of a runtime vector that describes triples of array dimension +/// information. A triple consists of a lower bound, upper bound, and +/// stride. Each dimension of an array entity may have an associated triple that +/// maps how elements of the array are accessed. +class DimsType : public mlir::Type::TypeBase { +public: + using Base::Base; + static DimsType get(mlir::MLIRContext *ctx, unsigned rank); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_DIMS; } + + /// returns -1 if the rank is unknown + int getRank() const; +}; + +/// The type of a field name. Implementations may defer the layout of a Fortran +/// derived type until runtime. This implies that the runtime must be able to +/// determine the offset of fields within the entity. +class FieldType : public mlir::Type::TypeBase { +public: + using Base::Base; + static FieldType get(mlir::MLIRContext *ctxt); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_FIELD; } +}; + +/// The type of a heap pointer. Fortran entities with the ALLOCATABLE attribute +/// may be allocated on the heap at runtime. These pointers are explicitly +/// distinguished to disallow the composition of multiple levels of +/// indirection. For example, an ALLOCATABLE POINTER is invalid. +class HeapType : public mlir::Type::TypeBase { +public: + using Base::Base; + static HeapType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_HEAP; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + mlir::Type eleTy); +}; + +/// The type of a LEN parameter name. Implementations may defer the layout of a +/// Fortran derived type until runtime. This implies that the runtime must be +/// able to determine the offset of LEN type parameters related to an entity. +class LenType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static LenType get(mlir::MLIRContext *ctxt); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_LEN; } +}; + +/// The type of entities with the POINTER attribute. These pointers are +/// explicitly distinguished to disallow the composition of multiple levels of +/// indirection. For example, an ALLOCATABLE POINTER is invalid. +class PointerType : public mlir::Type::TypeBase { +public: + using Base::Base; + static PointerType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_POINTER; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + mlir::Type eleTy); +}; + +/// The type of a reference to an entity in memory. +class ReferenceType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static ReferenceType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_REFERENCE; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + mlir::Type eleTy); +}; + +/// A sequence type is a multi-dimensional array of values. The sequence type +/// may have an unknown number of dimensions or the extent of dimensions may be +/// unknown. A sequence type models a Fortran array entity, giving it a type in +/// FIR. A sequence type is assumed to be stored in a column-major order, which +/// differs from LLVM IR and other dialects of MLIR. +class SequenceType : public mlir::Type::TypeBase { +public: + using Base::Base; + using Extent = int64_t; + using Shape = llvm::SmallVector; + + /// Return a sequence type with the specified shape and element type + static SequenceType get(const Shape &shape, mlir::Type elementType, + mlir::AffineMapAttr map = {}); + + /// The element type of this sequence + mlir::Type getEleTy() const; + + /// The shape of the sequence. If the sequence has an unknown shape, the shape + /// returned will be empty. + Shape getShape() const; + + mlir::AffineMapAttr getLayoutMap() const; + + /// The number of dimensions of the sequence + unsigned getDimension() const { return getShape().size(); } + + /// The value `-1` represents an unknown extent for a dimension + static constexpr Extent getUnknownExtent() { return -1; } + + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_SEQUENCE; } + + static mlir::LogicalResult + verifyConstructionInvariants(mlir::Location loc, const Shape &shape, + mlir::Type eleTy, mlir::AffineMapAttr map); +}; + +bool operator==(const SequenceType::Shape &, const SequenceType::Shape &); +llvm::hash_code hash_value(const SequenceType::Extent &); +llvm::hash_code hash_value(const SequenceType::Shape &); + +/// The type of a type descriptor object. The runtime may generate type +/// descriptor objects to determine the type of an entity at runtime, etc. +class TypeDescType : public mlir::Type::TypeBase { +public: + using Base::Base; + static TypeDescType get(mlir::Type ofType); + static constexpr bool kindof(unsigned kind) { + return kind == TypeKind::FIR_TYPEDESC; + } + mlir::Type getOfTy() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + mlir::Type ofType); +}; + +// Derived types + +/// Model of Fortran's derived type, TYPE. The name of the TYPE includes any +/// KIND type parameters. The record includes runtime slots for LEN type +/// parameters and for data components. +class RecordType : public mlir::Type::TypeBase { +public: + using Base::Base; + using TypePair = std::pair; + using TypeList = std::vector; + + llvm::StringRef getName(); + TypeList getTypeList(); + TypeList getLenParamList(); + + mlir::Type getType(llvm::StringRef ident); + mlir::Type getType(unsigned index) { + assert(index < getNumFields()); + return getTypeList()[index].second; + } + unsigned getNumFields() { return getTypeList().size(); } + unsigned getNumLenParams() { return getLenParamList().size(); } + + static RecordType get(mlir::MLIRContext *ctxt, llvm::StringRef name); + void finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return TypeKind::FIR_DERIVED; } + + detail::RecordTypeStorage const *uniqueKey() const; + + static mlir::LogicalResult verifyConstructionInvariants(mlir::Location, + llvm::StringRef name); +}; + +mlir::Type parseFirType(FIROpsDialect *, mlir::DialectAsmParser &parser); + +void printFirType(FIROpsDialect *, mlir::Type ty, mlir::DialectAsmPrinter &p); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRTYPE_H diff --git a/include/flang/Optimizer/Support/KindMapping.h b/include/flang/Optimizer/Support/KindMapping.h new file mode 100644 index 000000000000..8c8ed1f24afd --- /dev/null +++ b/include/flang/Optimizer/Support/KindMapping.h @@ -0,0 +1,90 @@ +//===-- Optimizer/Support/KindMapping.h -------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIMIZER_SUPPORT_KINDMAPPING_H +#define OPTIMIZER_SUPPORT_KINDMAPPING_H + +#include "mlir/IR/OpDefinition.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/IR/Type.h" + +namespace llvm { +template +class Optional; +struct fltSemantics; +} // namespace llvm + +namespace mlir { +class MLIRContext; +} // namespace mlir + +namespace fir { + +/// The kind mapping is an encoded string that informs FIR how the Fortran KIND +/// values from the front-end should be converted to LLVM IR types. This +/// encoding allows the mapping from front-end KIND values to backend LLVM IR +/// types to be customized by the front-end. +/// +/// The provided string uses the following syntax. +/// +/// intrinsic-key `:` kind-value (`,` intrinsic-key `:` kind-value)* +/// +/// intrinsic-key is a single character for the intrinsic type. +/// 'i' : INTEGER (size in bits) +/// 'l' : LOGICAL (size in bits) +/// 'a' : CHARACTER (size in bits) +/// 'r' : REAL (encoding value) +/// 'c' : COMPLEX (encoding value) +/// +/// kind-value is either an unsigned integer (for 'i', 'l', and 'a') or one of +/// 'Half', 'Float', 'Double', 'X86_FP80', or 'FP128' (for 'r' and 'c'). +/// +/// If LLVM adds support for new floating-point types, the final list should be +/// extended. +class KindMapping { +public: + using KindTy = unsigned; + using Bitsize = unsigned; + using LLVMTypeID = llvm::Type::TypeID; + using MatchResult = mlir::ParseResult; + + explicit KindMapping(mlir::MLIRContext *context); + explicit KindMapping(mlir::MLIRContext *context, llvm::StringRef map); + + /// Get the size in bits of !fir.char + Bitsize getCharacterBitsize(KindTy kind); + + /// Get the size in bits of !fir.int + Bitsize getIntegerBitsize(KindTy kind); + + /// Get the size in bits of !fir.logical + Bitsize getLogicalBitsize(KindTy kind); + + /// Get the LLVM Type::TypeID of !fir.real + LLVMTypeID getRealTypeID(KindTy kind); + + /// Get the LLVM Type::TypeID of !fir.complex + LLVMTypeID getComplexTypeID(KindTy kind); + + mlir::MLIRContext *getContext() const { return context; } + + /// Get the float semantics of !fir.real + const llvm::fltSemantics &getFloatSemantics(KindTy kind); + +private: + MatchResult badMapString(const llvm::Twine &ptr); + MatchResult parse(llvm::StringRef kindMap); + + mlir::MLIRContext *context; + llvm::DenseMap, Bitsize> intMap; + llvm::DenseMap, LLVMTypeID> floatMap; +}; + +} // namespace fir + +#endif // OPTIMIZER_SUPPORT_KINDMAPPING_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fae2eed92fa8..f2fe30dca5a1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,3 +12,7 @@ add_subdirectory(Decimal) add_subdirectory(Lower) add_subdirectory(Parser) add_subdirectory(Semantics) + +if(LINK_WITH_FIR) + add_subdirectory(Optimizer) +endif() diff --git a/lib/Fir/.clang-format b/lib/Fir/.clang-format deleted file mode 100644 index a74fda4b6734..000000000000 --- a/lib/Fir/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -BasedOnStyle: LLVM -AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/Optimizer/CMakeLists.txt b/lib/Optimizer/CMakeLists.txt new file mode 100644 index 000000000000..2f8cd269dd9f --- /dev/null +++ b/lib/Optimizer/CMakeLists.txt @@ -0,0 +1,5 @@ +# Sources generated by tablegen have unused parameters. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") + +add_subdirectory(Dialect) +add_subdirectory(Support) diff --git a/lib/Optimizer/Dialect/CMakeLists.txt b/lib/Optimizer/Dialect/CMakeLists.txt new file mode 100644 index 000000000000..711fc64c5a2a --- /dev/null +++ b/lib/Optimizer/Dialect/CMakeLists.txt @@ -0,0 +1,27 @@ +add_llvm_library(FIRDialect + FIRAttr.cpp + FIRDialect.cpp + FIROps.cpp + FIRType.cpp +) + +add_dependencies(FIRDialect FIROpsIncGen) + +target_link_libraries(FIRDialect + MLIRTargetLLVMIR + MLIRTargetLLVMIRModuleTranslation + MLIREDSC + MLIRExecutionEngine + MLIRParser + MLIRSupport + MLIRStandardToLLVM + MLIRTransforms + LLVMAsmParser + LLVMAsmPrinter + LLVMRemarks +) + +install (TARGETS FIRDialect + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/lib/Optimizer/Dialect/FIRAttr.cpp b/lib/Optimizer/Dialect/FIRAttr.cpp new file mode 100644 index 000000000000..1ba242b9484b --- /dev/null +++ b/lib/Optimizer/Dialect/FIRAttr.cpp @@ -0,0 +1,238 @@ +//===-- FIRAttr.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/AttributeSupport.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/Types.h" +#include "mlir/Parser.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" + +using namespace fir; + +namespace fir { +namespace detail { + +struct RealAttributeStorage : public mlir::AttributeStorage { + using KeyTy = std::pair; + + RealAttributeStorage(int kind, const llvm::APFloat &value) + : kind(kind), value(value) {} + RealAttributeStorage(const KeyTy &key) + : RealAttributeStorage(key.first, key.second) {} + + static unsigned hashKey(const KeyTy &key) { + return llvm::hash_value(key); + } + + bool operator==(const KeyTy &key) const { + return key.first == kind && + key.second.compare(value) == llvm::APFloatBase::cmpEqual; + } + + static RealAttributeStorage * + construct(mlir::AttributeStorageAllocator &allocator, const KeyTy &key) { + return new (allocator.allocate()) + RealAttributeStorage(key); + } + + int getFKind() const { return kind; } + llvm::APFloat getValue() const { return value; } + +private: + int kind; + llvm::APFloat value; +}; + +/// An attribute representing a reference to a type. +struct TypeAttributeStorage : public mlir::AttributeStorage { + using KeyTy = mlir::Type; + + TypeAttributeStorage(mlir::Type value) : value(value) { + assert(value && "must not be of Type null"); + } + + /// Key equality function. + bool operator==(const KeyTy &key) const { return key == value; } + + /// Construct a new storage instance. + static TypeAttributeStorage * + construct(mlir::AttributeStorageAllocator &allocator, KeyTy key) { + return new (allocator.allocate()) + TypeAttributeStorage(key); + } + + mlir::Type getType() const { return value; } + +private: + mlir::Type value; +}; +} // namespace detail + +ExactTypeAttr ExactTypeAttr::get(mlir::Type value) { + return Base::get(value.getContext(), FIR_EXACTTYPE, value); +} + +mlir::Type ExactTypeAttr::getType() const { return getImpl()->getType(); } + +SubclassAttr SubclassAttr::get(mlir::Type value) { + return Base::get(value.getContext(), FIR_SUBCLASS, value); +} + +mlir::Type SubclassAttr::getType() const { return getImpl()->getType(); } + +using AttributeUniquer = mlir::detail::AttributeUniquer; + +ClosedIntervalAttr ClosedIntervalAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +UpperBoundAttr UpperBoundAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +LowerBoundAttr LowerBoundAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +PointIntervalAttr PointIntervalAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +// RealAttr + +RealAttr RealAttr::get(mlir::MLIRContext *ctxt, + const RealAttr::ValueType &key) { + return Base::get(ctxt, getId(), key); +} + +int RealAttr::getFKind() const { return getImpl()->getFKind(); } + +llvm::APFloat RealAttr::getValue() const { return getImpl()->getValue(); } + +// FIR attribute parsing + +namespace { +mlir::Attribute parseFirRealAttr(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type) { + int kind = 0; + if (parser.parseLess() || parser.parseInteger(kind) || parser.parseComma()) { + parser.emitError(parser.getNameLoc(), "expected '<' kind ','"); + return {}; + } + KindMapping kindMap(dialect->getContext()); + llvm::APFloat value(0.); + if (parser.parseOptionalKeyword("i")) { + // `i` not present, so literal float must be present + double dontCare; + if (parser.parseFloat(dontCare) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected real constant '>'"); + return {}; + } + auto fltStr = parser.getFullSymbolSpec() + .drop_until([](char c) { return c == ','; }) + .drop_front() + .drop_while([](char c) { return c == ' ' || c == '\t'; }) + .take_until([](char c) { + return c == '>' || c == ' ' || c == '\t'; + }); + value = llvm::APFloat(kindMap.getFloatSemantics(kind), fltStr); + } else { + // `i` is present, so literal bitstring (hex) must be present + llvm::StringRef hex; + if (parser.parseKeyword(&hex) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected real constant '>'"); + return {}; + } + auto bits = llvm::APInt(kind * 8, hex.drop_front(), 16); + value = llvm::APFloat(kindMap.getFloatSemantics(kind), bits); + } + return RealAttr::get(dialect->getContext(), {kind, value}); +} +} // namespace + +mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type) { + auto loc = parser.getNameLoc(); + llvm::StringRef attrName; + if (parser.parseKeyword(&attrName)) { + parser.emitError(loc, "expected an attribute name"); + return {}; + } + + if (attrName == ExactTypeAttr::getAttrName()) { + mlir::Type type; + if (parser.parseLess() || parser.parseType(type) || parser.parseGreater()) { + parser.emitError(loc, "expected a type"); + return {}; + } + return ExactTypeAttr::get(type); + } + if (attrName == SubclassAttr::getAttrName()) { + mlir::Type type; + if (parser.parseLess() || parser.parseType(type) || parser.parseGreater()) { + parser.emitError(loc, "expected a subtype"); + return {}; + } + return SubclassAttr::get(type); + } + if (attrName == PointIntervalAttr::getAttrName()) + return PointIntervalAttr::get(dialect->getContext()); + if (attrName == LowerBoundAttr::getAttrName()) + return LowerBoundAttr::get(dialect->getContext()); + if (attrName == UpperBoundAttr::getAttrName()) + return UpperBoundAttr::get(dialect->getContext()); + if (attrName == ClosedIntervalAttr::getAttrName()) + return ClosedIntervalAttr::get(dialect->getContext()); + if (attrName == RealAttr::getAttrName()) + return parseFirRealAttr(dialect, parser, type); + + parser.emitError(loc, "unknown FIR attribute: ") << attrName; + return {}; +} + +// FIR attribute pretty printer + +void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, + mlir::DialectAsmPrinter &p) { + auto &os = p.getStream(); + if (auto exact = attr.dyn_cast()) { + os << fir::ExactTypeAttr::getAttrName() << '<'; + p.printType(exact.getType()); + os << '>'; + } else if (auto sub = attr.dyn_cast()) { + os << fir::SubclassAttr::getAttrName() << '<'; + p.printType(sub.getType()); + os << '>'; + } else if (attr.dyn_cast_or_null()) { + os << fir::PointIntervalAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::ClosedIntervalAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::LowerBoundAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::UpperBoundAttr::getAttrName(); + } else if (auto a = attr.dyn_cast_or_null()) { + os << fir::RealAttr::getAttrName() << '<' << a.getFKind() << ", i x"; + llvm::SmallString<40> ss; + a.getValue().bitcastToAPInt().toStringUnsigned(ss, 16); + os << ss << '>'; + } else { + llvm_unreachable("attribute pretty-printer is not implemented"); + } +} + +} // namespace fir diff --git a/lib/Optimizer/Dialect/FIRDialect.cpp b/lib/Optimizer/Dialect/FIRDialect.cpp new file mode 100644 index 000000000000..b6dd9edb2a2f --- /dev/null +++ b/lib/Optimizer/Dialect/FIRDialect.cpp @@ -0,0 +1,54 @@ +//===-- FIRDialect.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/StandardTypes.h" + +using namespace fir; + +fir::FIROpsDialect::FIROpsDialect(mlir::MLIRContext *ctx) + : mlir::Dialect("fir", ctx) { + addTypes(); + addAttributes(); + addOperations< +#define GET_OP_LIST +#include "flang/Optimizer/Dialect/FIROps.cpp.inc" + >(); +} + +// anchor the class vtable to this compilation unit +fir::FIROpsDialect::~FIROpsDialect() { + // do nothing +} + +mlir::Type fir::FIROpsDialect::parseType(mlir::DialectAsmParser &parser) const { + return parseFirType(const_cast(this), parser); +} + +void fir::FIROpsDialect::printType(mlir::Type ty, + mlir::DialectAsmPrinter &p) const { + return printFirType(const_cast(this), ty, p); +} + +mlir::Attribute +fir::FIROpsDialect::parseAttribute(mlir::DialectAsmParser &parser, + mlir::Type type) const { + return parseFirAttribute(const_cast(this), parser, type); +} + +void fir::FIROpsDialect::printAttribute(mlir::Attribute attr, + mlir::DialectAsmPrinter &p) const { + printFirAttribute(const_cast(this), attr, p); +} diff --git a/lib/Optimizer/Dialect/FIROps.cpp b/lib/Optimizer/Dialect/FIROps.cpp new file mode 100644 index 000000000000..9c46de048336 --- /dev/null +++ b/lib/Optimizer/Dialect/FIROps.cpp @@ -0,0 +1,862 @@ +//===-- FIROps.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIROpsSupport.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/ADT/TypeSwitch.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Function.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/IR/SymbolTable.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace fir; + +/// return true if the sequence type is abstract or the record type is malformed +/// or contains an abstract sequence type +static bool verifyInType(mlir::Type inType, + llvm::SmallVectorImpl &visited) { + if (auto st = inType.dyn_cast()) { + auto shape = st.getShape(); + if (shape.size() == 0) + return true; + for (auto ext : shape) + if (ext < 0) + return true; + } else if (auto rt = inType.dyn_cast()) { + // don't recurse if we're already visiting this one + if (llvm::is_contained(visited, rt.getName())) + return false; + // keep track of record types currently being visited + visited.push_back(rt.getName()); + for (auto &field : rt.getTypeList()) + if (verifyInType(field.second, visited)) + return true; + visited.pop_back(); + } else if (auto rt = inType.dyn_cast()) { + return verifyInType(rt.getEleTy(), visited); + } + return false; +} + +static bool verifyRecordLenParams(mlir::Type inType, unsigned numLenParams) { + if (numLenParams > 0) { + if (auto rt = inType.dyn_cast()) + return numLenParams != rt.getNumLenParams(); + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// AllocaOp +//===----------------------------------------------------------------------===// + +mlir::Type fir::AllocaOp::getAllocatedType() { + return getType().cast().getEleTy(); +} + +/// Create a legal memory reference as return type +mlir::Type fir::AllocaOp::wrapResultType(mlir::Type intype) { + // FIR semantics: memory references to memory references are disallowed + if (intype.isa()) + return {}; + return ReferenceType::get(intype); +} + +mlir::Type fir::AllocaOp::getRefTy(mlir::Type ty) { + return ReferenceType::get(ty); +} + +//===----------------------------------------------------------------------===// +// AllocMemOp +//===----------------------------------------------------------------------===// + +mlir::Type fir::AllocMemOp::getAllocatedType() { + return getType().cast().getEleTy(); +} + +mlir::Type fir::AllocMemOp::getRefTy(mlir::Type ty) { + return HeapType::get(ty); +} + +/// Create a legal heap reference as return type +mlir::Type fir::AllocMemOp::wrapResultType(mlir::Type intype) { + // Fortran semantics: C852 an entity cannot be both ALLOCATABLE and POINTER + // 8.5.3 note 1 prohibits ALLOCATABLE procedures as well + // FIR semantics: one may not allocate a memory reference value + if (intype.isa() || intype.isa() || + intype.isa() || intype.isa()) + return {}; + return HeapType::get(intype); +} + +//===----------------------------------------------------------------------===// +// BoxDimsOp +//===----------------------------------------------------------------------===// + +/// Get the result types packed in a tuple tuple +mlir::Type fir::BoxDimsOp::getTupleType() { + // note: triple, but 4 is nearest power of 2 + llvm::SmallVector triple{ + getResult(0).getType(), getResult(1).getType(), getResult(2).getType()}; + return mlir::TupleType::get(triple, getContext()); +} + +//===----------------------------------------------------------------------===// +// CallOp +//===----------------------------------------------------------------------===// + +static void printCallOp(mlir::OpAsmPrinter &p, fir::CallOp &op) { + auto callee = op.callee(); + bool isDirect = callee.hasValue(); + p << op.getOperationName() << ' '; + if (isDirect) + p << callee.getValue(); + else + p << op.getOperand(0); + p << '(' << op.getOperands().drop_front(isDirect ? 0 : 1) << ')'; + p.printOptionalAttrDict(op.getAttrs(), {fir::CallOp::calleeAttrName()}); + auto resultTypes{op.getResultTypes()}; + llvm::SmallVector argTypes( + llvm::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1)); + p << " : " << FunctionType::get(argTypes, resultTypes, op.getContext()); +} + +static mlir::ParseResult parseCallOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + llvm::SmallVector operands; + if (parser.parseOperandList(operands)) + return mlir::failure(); + + llvm::SmallVector attrs; + mlir::SymbolRefAttr funcAttr; + bool isDirect = operands.empty(); + if (isDirect) + if (parser.parseAttribute(funcAttr, fir::CallOp::calleeAttrName(), attrs)) + return mlir::failure(); + + Type type; + if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttrDict(attrs) || parser.parseColon() || + parser.parseType(type)) + return mlir::failure(); + + auto funcType = type.dyn_cast(); + if (!funcType) + return parser.emitError(parser.getNameLoc(), "expected function type"); + if (isDirect) { + if (parser.resolveOperands(operands, funcType.getInputs(), + parser.getNameLoc(), result.operands)) + return mlir::failure(); + } else { + auto funcArgs = + llvm::ArrayRef(operands).drop_front(); + llvm::SmallVector resultArgs( + result.operands.begin() + (result.operands.empty() ? 0 : 1), + result.operands.end()); + if (parser.resolveOperand(operands[0], funcType, result.operands) || + parser.resolveOperands(funcArgs, funcType.getInputs(), + parser.getNameLoc(), resultArgs)) + return mlir::failure(); + } + result.addTypes(funcType.getResults()); + result.attributes = attrs; + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// CmpfOp +//===----------------------------------------------------------------------===// + +// Note: getCmpFPredicateNames() is inline static in StandardOps/IR/Ops.cpp +mlir::CmpFPredicate fir::CmpfOp::getPredicateByName(llvm::StringRef name) { + auto pred = mlir::symbolizeCmpFPredicate(name); + assert(pred.hasValue() && "invalid predicate name"); + return pred.getValue(); +} + +void fir::buildCmpFOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs) { + result.addOperands({lhs, rhs}); + result.types.push_back(builder->getI1Type()); + result.addAttribute( + CmpfOp::getPredicateAttrName(), + builder->getI64IntegerAttr(static_cast(predicate))); +} + +template +static void printCmpOp(OpAsmPrinter &p, OPTY op) { + p << op.getOperationName() << ' '; + auto predSym = mlir::symbolizeCmpFPredicate( + op.template getAttrOfType(OPTY::getPredicateAttrName()) + .getInt()); + assert(predSym.hasValue() && "invalid symbol value for predicate"); + p << '"' << mlir::stringifyCmpFPredicate(predSym.getValue()) << '"' << ", "; + p.printOperand(op.lhs()); + p << ", "; + p.printOperand(op.rhs()); + p.printOptionalAttrDict(op.getAttrs(), + /*elidedAttrs=*/{OPTY::getPredicateAttrName()}); + p << " : " << op.lhs().getType(); +} + +static void printCmpfOp(OpAsmPrinter &p, CmpfOp op) { printCmpOp(p, op); } + +template +static mlir::ParseResult parseCmpOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + llvm::SmallVector ops; + llvm::SmallVector attrs; + mlir::Attribute predicateNameAttr; + mlir::Type type; + if (parser.parseAttribute(predicateNameAttr, OPTY::getPredicateAttrName(), + attrs) || + parser.parseComma() || parser.parseOperandList(ops, 2) || + parser.parseOptionalAttrDict(attrs) || parser.parseColonType(type) || + parser.resolveOperands(ops, type, result.operands)) + return failure(); + + if (!predicateNameAttr.isa()) + return parser.emitError(parser.getNameLoc(), + "expected string comparison predicate attribute"); + + // Rewrite string attribute to an enum value. + llvm::StringRef predicateName = + predicateNameAttr.cast().getValue(); + auto predicate = fir::CmpfOp::getPredicateByName(predicateName); + auto builder = parser.getBuilder(); + mlir::Type i1Type = builder.getI1Type(); + attrs[0].second = builder.getI64IntegerAttr(static_cast(predicate)); + result.attributes = attrs; + result.addTypes({i1Type}); + return success(); +} + +mlir::ParseResult fir::parseCmpfOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + return parseCmpOp(parser, result); +} + +//===----------------------------------------------------------------------===// +// CmpcOp +//===----------------------------------------------------------------------===// + +void fir::buildCmpCOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs) { + result.addOperands({lhs, rhs}); + result.types.push_back(builder->getI1Type()); + result.addAttribute( + fir::CmpcOp::getPredicateAttrName(), + builder->getI64IntegerAttr(static_cast(predicate))); +} + +static void printCmpcOp(OpAsmPrinter &p, fir::CmpcOp op) { printCmpOp(p, op); } + +mlir::ParseResult fir::parseCmpcOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + return parseCmpOp(parser, result); +} + +//===----------------------------------------------------------------------===// +// DispatchOp +//===----------------------------------------------------------------------===// + +mlir::FunctionType fir::DispatchOp::getFunctionType() { + auto attr = getAttr("fn_type").cast(); + return attr.getValue().cast(); +} + +//===----------------------------------------------------------------------===// +// DispatchTableOp +//===----------------------------------------------------------------------===// + +void fir::DispatchTableOp::appendTableEntry(mlir::Operation *op) { + assert(mlir::isa(*op) && "operation must be a DTEntryOp"); + auto &block = getBlock(); + block.getOperations().insert(block.end(), op); +} + +//===----------------------------------------------------------------------===// +// EmboxOp +//===----------------------------------------------------------------------===// + +static mlir::ParseResult parseEmboxOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + mlir::FunctionType type; + llvm::SmallVector operands; + mlir::OpAsmParser::OperandType memref; + if (parser.parseOperand(memref)) + return mlir::failure(); + operands.push_back(memref); + auto &builder = parser.getBuilder(); + if (!parser.parseOptionalLParen()) { + if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None) || + parser.parseRParen()) + return mlir::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(fir::EmboxOp::lenpName(), lens); + } + if (!parser.parseOptionalComma()) { + mlir::OpAsmParser::OperandType dims; + if (parser.parseOperand(dims)) + return mlir::failure(); + operands.push_back(dims); + } else if (!parser.parseOptionalLSquare()) { + mlir::AffineMapAttr map; + if (parser.parseAttribute(map, fir::EmboxOp::layoutName(), + result.attributes) || + parser.parseRSquare()) + return mlir::failure(); + } + if (parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), + result.operands) || + parser.addTypesToList(type.getResults(), result.types)) + return mlir::failure(); + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// GenTypeDescOp +//===----------------------------------------------------------------------===// + +void fir::GenTypeDescOp::build(Builder *, OperationState &result, + mlir::TypeAttr inty) { + result.addAttribute("in_type", inty); + result.addTypes(TypeDescType::get(inty.getValue())); +} + +//===----------------------------------------------------------------------===// +// GlobalOp +//===----------------------------------------------------------------------===// + +void fir::GlobalOp::appendInitialValue(mlir::Operation *op) { + getBlock().getOperations().push_back(op); +} + +//===----------------------------------------------------------------------===// +// LoadOp +//===----------------------------------------------------------------------===// + +/// Get the element type of a reference like type; otherwise null +static mlir::Type elementTypeOf(mlir::Type ref) { + return mlir::TypeSwitch(ref) + .Case( + [](auto type) { return type.getEleTy(); }) + .Default([](mlir::Type) { return mlir::Type{}; }); +} + +mlir::ParseResult fir::LoadOp::getElementOf(mlir::Type &ele, mlir::Type ref) { + if ((ele = elementTypeOf(ref))) + return mlir::success(); + return mlir::failure(); +} + +//===----------------------------------------------------------------------===// +// LoopOp +//===----------------------------------------------------------------------===// + +void fir::LoopOp::build(mlir::Builder *builder, OperationState &result, + mlir::Value lb, mlir::Value ub, ValueRange step, + ArrayRef attributes) { + if (step.empty()) + result.addOperands({lb, ub}); + else + result.addOperands({lb, ub, step[0]}); + mlir::Region *bodyRegion = result.addRegion(); + LoopOp::ensureTerminator(*bodyRegion, *builder, result.location); + bodyRegion->front().addArgument(builder->getIndexType()); + result.addAttributes(attributes); + NamedAttributeList attrs(attributes); + if (!attrs.get(unorderedAttrName())) + result.addTypes(builder->getIndexType()); +} + +static mlir::ParseResult parseLoopOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + auto &builder = parser.getBuilder(); + OpAsmParser::OperandType inductionVariable, lb, ub, step; + // Parse the induction variable followed by '='. + if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) + return mlir::failure(); + + // Parse loop bounds. + mlir::Type indexType = builder.getIndexType(); + if (parser.parseOperand(lb) || + parser.resolveOperand(lb, indexType, result.operands) || + parser.parseKeyword("to") || parser.parseOperand(ub) || + parser.resolveOperand(ub, indexType, result.operands)) + return mlir::failure(); + + if (parser.parseOptionalKeyword(fir::LoopOp::stepAttrName())) { + result.addAttribute(fir::LoopOp::stepAttrName(), + builder.getIntegerAttr(builder.getIndexType(), 1)); + } else if (parser.parseOperand(step) || + parser.resolveOperand(step, indexType, result.operands)) { + return mlir::failure(); + } + + // Parse the optional `unordered` keyword + bool isUnordered = false; + if (!parser.parseOptionalKeyword(LoopOp::unorderedAttrName())) { + result.addAttribute(LoopOp::unorderedAttrName(), builder.getUnitAttr()); + isUnordered = true; + } + + // Parse the body region. + mlir::Region *body = result.addRegion(); + if (parser.parseRegion(*body, inductionVariable, indexType)) + return mlir::failure(); + + fir::LoopOp::ensureTerminator(*body, builder, result.location); + + // Parse the optional attribute list. + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + if (!isUnordered) + result.addTypes(builder.getIndexType()); + return mlir::success(); +} + +fir::LoopOp fir::getForInductionVarOwner(mlir::Value val) { + auto ivArg = val.dyn_cast(); + if (!ivArg) + return {}; + assert(ivArg.getOwner() && "unlinked block argument"); + auto *containingInst = ivArg.getOwner()->getParentOp(); + return dyn_cast_or_null(containingInst); +} + +//===----------------------------------------------------------------------===// +// SelectOp +//===----------------------------------------------------------------------===// + +static constexpr llvm::StringRef getCompareOffsetAttr() { + return "compare_operand_offsets"; +} + +static constexpr llvm::StringRef getTargetOffsetAttr() { + return "target_operand_offsets"; +} + +template +static A getSubOperands(unsigned pos, A allArgs, + mlir::DenseIntElementsAttr ranges) { + unsigned start = 0; + for (unsigned i = 0; i < pos; ++i) + start += (*(ranges.begin() + i)).getZExtValue(); + unsigned end = start + (*(ranges.begin() + pos)).getZExtValue(); + return {std::next(allArgs.begin(), start), std::next(allArgs.begin(), end)}; +} + +llvm::Optional fir::SelectOp::getCompareOperands(unsigned) { + return {}; +} + +llvm::Optional> +fir::SelectOp::getCompareOperands(llvm::ArrayRef, unsigned) { + return {}; +} + +llvm::Optional +fir::SelectOp::getSuccessorOperands(unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + return {getSubOperands(oper, targetArgs(), a)}; +} + +llvm::Optional> +fir::SelectOp::getSuccessorOperands(llvm::ArrayRef operands, + unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + auto segments = + getAttrOfType(getOperandSegmentSizeAttr()); + return {getSubOperands(oper, getSubOperands(2, operands, segments), a)}; +} + +bool fir::SelectOp::canEraseSuccessorOperand() { return true; } + +//===----------------------------------------------------------------------===// +// SelectCaseOp +//===----------------------------------------------------------------------===// + +llvm::Optional +fir::SelectCaseOp::getCompareOperands(unsigned cond) { + auto a = getAttrOfType(getCompareOffsetAttr()); + return {getSubOperands(cond, compareArgs(), a)}; +} + +llvm::Optional> +fir::SelectCaseOp::getCompareOperands(llvm::ArrayRef operands, + unsigned cond) { + auto a = getAttrOfType(getCompareOffsetAttr()); + auto segments = + getAttrOfType(getOperandSegmentSizeAttr()); + return {getSubOperands(cond, getSubOperands(1, operands, segments), a)}; +} + +llvm::Optional +fir::SelectCaseOp::getSuccessorOperands(unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + return {getSubOperands(oper, targetArgs(), a)}; +} + +llvm::Optional> +fir::SelectCaseOp::getSuccessorOperands(llvm::ArrayRef operands, + unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + auto segments = + getAttrOfType(getOperandSegmentSizeAttr()); + return {getSubOperands(oper, getSubOperands(2, operands, segments), a)}; +} + +bool fir::SelectCaseOp::canEraseSuccessorOperand() { return true; } + +// parser for fir.select_case Op +static mlir::ParseResult parseSelectCase(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector opers; + llvm::SmallVector dests; + llvm::SmallVector, 8> destArgs; + llvm::SmallVector argOffs; + int32_t offSize = 0; + while (true) { + mlir::Attribute attr; + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || isValidCaseAttr(attr) || + parser.parseComma()) + return mlir::failure(); + attrs.push_back(attr); + if (attr.dyn_cast_or_null()) { + argOffs.push_back(0); + } else if (attr.dyn_cast_or_null()) { + mlir::OpAsmParser::OperandType oper1; + mlir::OpAsmParser::OperandType oper2; + if (parser.parseOperand(oper1) || parser.parseComma() || + parser.parseOperand(oper2) || parser.parseComma()) + return mlir::failure(); + opers.push_back(oper1); + opers.push_back(oper2); + argOffs.push_back(2); + offSize += 2; + } else { + mlir::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || parser.parseComma()) + return mlir::failure(); + opers.push_back(oper); + argOffs.push_back(1); + ++offSize; + } + if (parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + result.addAttribute(fir::SelectCaseOp::getCasesAttr(), + parser.getBuilder().getArrayAttr(attrs)); + if (parser.resolveOperands(opers, type, result.operands)) + return mlir::failure(); + llvm::SmallVector targOffs; + int32_t toffSize = 0; + const auto count = dests.size(); + for (std::remove_const_t i = 0; i != count; ++i) { + result.addSuccessors(dests[i]); + result.addOperands(destArgs[i]); + auto argSize = destArgs[i].size(); + targOffs.push_back(argSize); + toffSize += argSize; + } + auto &bld = parser.getBuilder(); + result.addAttribute(fir::SelectCaseOp::getOperandSegmentSizeAttr(), + bld.getI32VectorAttr({1, offSize, toffSize})); + result.addAttribute(getCompareOffsetAttr(), bld.getI32VectorAttr(argOffs)); + result.addAttribute(getTargetOffsetAttr(), bld.getI32VectorAttr(targOffs)); + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// SelectRankOp +//===----------------------------------------------------------------------===// + +llvm::Optional +fir::SelectRankOp::getCompareOperands(unsigned) { + return {}; +} + +llvm::Optional> +fir::SelectRankOp::getCompareOperands(llvm::ArrayRef, unsigned) { + return {}; +} + +llvm::Optional +fir::SelectRankOp::getSuccessorOperands(unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + return {getSubOperands(oper, targetArgs(), a)}; +} + +llvm::Optional> +fir::SelectRankOp::getSuccessorOperands(llvm::ArrayRef operands, + unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + auto segments = + getAttrOfType(getOperandSegmentSizeAttr()); + return {getSubOperands(oper, getSubOperands(2, operands, segments), a)}; +} + +bool fir::SelectRankOp::canEraseSuccessorOperand() { return true; } + +//===----------------------------------------------------------------------===// +// SelectTypeOp +//===----------------------------------------------------------------------===// + +llvm::Optional +fir::SelectTypeOp::getCompareOperands(unsigned) { + return {}; +} + +llvm::Optional> +fir::SelectTypeOp::getCompareOperands(llvm::ArrayRef, unsigned) { + return {}; +} + +llvm::Optional +fir::SelectTypeOp::getSuccessorOperands(unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + return {getSubOperands(oper, targetArgs(), a)}; +} + +llvm::Optional> +fir::SelectTypeOp::getSuccessorOperands(llvm::ArrayRef operands, + unsigned oper) { + auto a = getAttrOfType(getTargetOffsetAttr()); + auto segments = + getAttrOfType(getOperandSegmentSizeAttr()); + return {getSubOperands(oper, getSubOperands(2, operands, segments), a)}; +} + +bool fir::SelectTypeOp::canEraseSuccessorOperand() { return true; } + +static ParseResult parseSelectType(OpAsmParser &parser, + OperationState &result) { + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector dests; + llvm::SmallVector, 8> destArgs; + while (true) { + mlir::Attribute attr; + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + attrs.push_back(attr); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + auto &bld = parser.getBuilder(); + result.addAttribute(fir::SelectTypeOp::getCasesAttr(), + bld.getArrayAttr(attrs)); + llvm::SmallVector argOffs; + int32_t offSize = 0; + const auto count = dests.size(); + for (std::remove_const_t i = 0; i != count; ++i) { + result.addSuccessors(dests[i]); + result.addOperands(destArgs[i]); + auto argSize = destArgs[i].size(); + argOffs.push_back(argSize); + offSize += argSize; + } + result.addAttribute(fir::SelectTypeOp::getOperandSegmentSizeAttr(), + bld.getI32VectorAttr({1, 0, offSize})); + result.addAttribute(getTargetOffsetAttr(), bld.getI32VectorAttr(argOffs)); + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// StoreOp +//===----------------------------------------------------------------------===// + +mlir::Type fir::StoreOp::elementType(mlir::Type refType) { + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + return {}; +} + +//===----------------------------------------------------------------------===// +// StringLitOp +//===----------------------------------------------------------------------===// + +bool fir::StringLitOp::isWideValue() { + auto eleTy = getType().cast().getEleTy(); + return eleTy.cast().getFKind() != 1; +} + +//===----------------------------------------------------------------------===// +// WhereOp +//===----------------------------------------------------------------------===// + +void fir::WhereOp::build(mlir::Builder *builder, OperationState &result, + mlir::Value cond, bool withElseRegion) { + result.addOperands(cond); + mlir::Region *thenRegion = result.addRegion(); + mlir::Region *elseRegion = result.addRegion(); + WhereOp::ensureTerminator(*thenRegion, *builder, result.location); + if (withElseRegion) + WhereOp::ensureTerminator(*elseRegion, *builder, result.location); +} + +static mlir::ParseResult parseWhereOp(OpAsmParser &parser, + OperationState &result) { + result.regions.reserve(2); + mlir::Region *thenRegion = result.addRegion(); + mlir::Region *elseRegion = result.addRegion(); + + auto &builder = parser.getBuilder(); + OpAsmParser::OperandType cond; + mlir::Type i1Type = builder.getIntegerType(1); + if (parser.parseOperand(cond) || + parser.resolveOperand(cond, i1Type, result.operands)) + return mlir::failure(); + + if (parser.parseRegion(*thenRegion, {}, {})) + return mlir::failure(); + + WhereOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location); + + if (!parser.parseOptionalKeyword("otherwise")) { + if (parser.parseRegion(*elseRegion, {}, {})) + return mlir::failure(); + WhereOp::ensureTerminator(*elseRegion, parser.getBuilder(), + result.location); + } + + // Parse the optional attribute list. + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + + return mlir::success(); +} + +//===----------------------------------------------------------------------===// + +mlir::ParseResult fir::isValidCaseAttr(mlir::Attribute attr) { + if (attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null()) + return mlir::success(); + return mlir::failure(); +} + +unsigned fir::getCaseArgumentOffset(llvm::ArrayRef cases, + unsigned dest) { + unsigned o = 0; + for (unsigned i = 0; i < dest; ++i) { + auto &attr = cases[i]; + if (!attr.dyn_cast_or_null()) { + ++o; + if (attr.dyn_cast_or_null()) + ++o; + } + } + return o; +} + +mlir::ParseResult fir::parseSelector(mlir::OpAsmParser &parser, + mlir::OperationState &result, + mlir::OpAsmParser::OperandType &selector, + mlir::Type &type) { + if (parser.parseOperand(selector) || parser.parseColonType(type) || + parser.resolveOperand(selector, type, result.operands) || + parser.parseLSquare()) + return mlir::failure(); + return mlir::success(); +} + +/// Generic pretty-printer of a binary operation +static void printBinaryOp(Operation *op, OpAsmPrinter &p) { + assert(op->getNumOperands() == 2 && "binary op must have two operands"); + assert(op->getNumResults() == 1 && "binary op must have one result"); + + p << op->getName() << ' ' << op->getOperand(0) << ", " << op->getOperand(1); + p.printOptionalAttrDict(op->getAttrs()); + p << " : " << op->getResult(0).getType(); +} + +/// Generic pretty-printer of an unary operation +static void printUnaryOp(Operation *op, OpAsmPrinter &p) { + assert(op->getNumOperands() == 1 && "unary op must have one operand"); + assert(op->getNumResults() == 1 && "unary op must have one result"); + + p << op->getName() << ' ' << op->getOperand(0); + p.printOptionalAttrDict(op->getAttrs()); + p << " : " << op->getResult(0).getType(); +} + +bool fir::isReferenceLike(mlir::Type type) { + return type.isa() || type.isa() || + type.isa(); +} + +mlir::FuncOp fir::createFuncOp(mlir::Location loc, mlir::ModuleOp module, + StringRef name, mlir::FunctionType type, + llvm::ArrayRef attrs) { + if (auto f = module.lookupSymbol(name)) + return f; + mlir::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); +} + +fir::GlobalOp fir::createGlobalOp(mlir::Location loc, mlir::ModuleOp module, + StringRef name, mlir::Type type, + llvm::ArrayRef attrs) { + if (auto g = module.lookupSymbol(name)) + return g; + mlir::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); +} + +namespace fir { + +// Tablegen operators + +#define GET_OP_CLASSES +#include "flang/Optimizer/Dialect/FIROps.cpp.inc" + +} // namespace fir diff --git a/lib/Optimizer/Dialect/FIRType.cpp b/lib/Optimizer/Dialect/FIRType.cpp new file mode 100644 index 000000000000..d1fccd7be317 --- /dev/null +++ b/lib/Optimizer/Dialect/FIRType.cpp @@ -0,0 +1,1292 @@ +//===-- FIRType.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "mlir/ADT/TypeSwitch.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Parser.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringSet.h" + +using namespace fir; + +namespace { + +template +TYPE parseIntSingleton(mlir::DialectAsmParser &parser) { + int kind = 0; + if (parser.parseLess() || parser.parseInteger(kind) || + parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "kind value expected"); + return {}; + } + return TYPE::get(parser.getBuilder().getContext(), kind); +} + +template +TYPE parseKindSingleton(mlir::DialectAsmParser &parser) { + return parseIntSingleton(parser); +} + +template +TYPE parseRankSingleton(mlir::DialectAsmParser &parser) { + return parseIntSingleton(parser); +} + +template +TYPE parseTypeSingleton(mlir::DialectAsmParser &parser, mlir::Location) { + mlir::Type ty; + if (parser.parseLess() || parser.parseType(ty) || parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "type expected"); + return {}; + } + return TYPE::get(ty); +} + +// `box` `<` type (',' affine-map)? `>` +BoxType parseBox(mlir::DialectAsmParser &parser, mlir::Location loc) { + mlir::Type ofTy; + if (parser.parseLess() || parser.parseType(ofTy)) { + parser.emitError(parser.getCurrentLocation(), "expected type parameter"); + return {}; + } + + mlir::AffineMapAttr map; + if (!parser.parseOptionalComma()) + if (parser.parseAttribute(map)) { + parser.emitError(parser.getCurrentLocation(), "expected affine map"); + return {}; + } + if (parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "expected '>'"); + return {}; + } + return BoxType::get(ofTy, map); +} + +// `boxchar` `<` kind `>` +BoxCharType parseBoxChar(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `boxproc` `<` return-type `>` +BoxProcType parseBoxProc(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `char` `<` kind `>` +CharacterType parseCharacter(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `complex` `<` kind `>` +CplxType parseComplex(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `dims` `<` rank `>` +DimsType parseDims(mlir::DialectAsmParser &parser) { + return parseRankSingleton(parser); +} + +// `field` +FieldType parseField(mlir::DialectAsmParser &parser) { + return FieldType::get(parser.getBuilder().getContext()); +} + +// `heap` `<` type `>` +HeapType parseHeap(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `int` `<` kind `>` +IntType parseInteger(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `len` +LenType parseLen(mlir::DialectAsmParser &parser) { + return LenType::get(parser.getBuilder().getContext()); +} + +// `logical` `<` kind `>` +LogicalType parseLogical(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `ptr` `<` type `>` +PointerType parsePointer(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `real` `<` kind `>` +RealType parseReal(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `ref` `<` type `>` +ReferenceType parseReference(mlir::DialectAsmParser &parser, + mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `tdesc` `<` type `>` +TypeDescType parseTypeDesc(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `void` +mlir::Type parseVoid(mlir::DialectAsmParser &parser) { + return parser.getBuilder().getNoneType(); +} + +// `array` `<` `*` | bounds (`x` bounds)* `:` type (',' affine-map)? `>` +// bounds ::= `?` | int-lit +SequenceType parseSequence(mlir::DialectAsmParser &parser, mlir::Location) { + if (parser.parseLess()) { + parser.emitError(parser.getNameLoc(), "expecting '<'"); + return {}; + } + SequenceType::Shape shape; + if (parser.parseOptionalStar()) { + if (parser.parseDimensionList(shape, true)) { + parser.emitError(parser.getNameLoc(), "invalid shape"); + return {}; + } + } else if (parser.parseColon()) { + parser.emitError(parser.getNameLoc(), "expected ':'"); + return {}; + } + mlir::Type eleTy; + if (parser.parseType(eleTy) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expecting element type"); + return {}; + } + mlir::AffineMapAttr map; + if (!parser.parseOptionalComma()) + if (parser.parseAttribute(map)) { + parser.emitError(parser.getNameLoc(), "expecting affine map"); + return {}; + } + return SequenceType::get(shape, eleTy, map); +} + +bool verifyIntegerType(mlir::Type ty) { + return ty.isa() || ty.isa(); +} + +bool verifyRecordMemberType(mlir::Type ty) { + return !(ty.isa() || ty.isa() || + ty.isa() || ty.isa() || ty.isa() || + ty.isa() || ty.isa() || + ty.isa()); +} + +bool verifySameLists(llvm::ArrayRef a1, + llvm::ArrayRef a2) { + // FIXME: do we need to allow for any variance here? + return a1 == a2; +} + +RecordType verifyDerived(mlir::DialectAsmParser &parser, RecordType derivedTy, + llvm::ArrayRef lenPList, + llvm::ArrayRef typeList) { + auto loc = parser.getNameLoc(); + if (!verifySameLists(derivedTy.getLenParamList(), lenPList) || + !verifySameLists(derivedTy.getTypeList(), typeList)) { + parser.emitError(loc, "cannot redefine record type members"); + return {}; + } + for (auto &p : lenPList) + if (!verifyIntegerType(p.second)) { + parser.emitError(loc, "LEN parameter must be integral type"); + return {}; + } + for (auto &p : typeList) + if (!verifyRecordMemberType(p.second)) { + parser.emitError(loc, "field parameter has invalid type"); + return {}; + } + llvm::StringSet<> uniq; + for (auto &p : lenPList) + if (!uniq.insert(p.first).second) { + parser.emitError(loc, "LEN parameter cannot have duplicate name"); + return {}; + } + for (auto &p : typeList) + if (!uniq.insert(p.first).second) { + parser.emitError(loc, "field cannot have duplicate name"); + return {}; + } + return derivedTy; +} + +// Fortran derived type +// `type` `<` name +// (`(` id `:` type (`,` id `:` type)* `)`)? +// (`{` id `:` type (`,` id `:` type)* `}`)? '>' +RecordType parseDerived(mlir::DialectAsmParser &parser, mlir::Location) { + llvm::StringRef name; + if (parser.parseLess() || parser.parseKeyword(&name)) { + parser.emitError(parser.getNameLoc(), + "expected a identifier as name of derived type"); + return {}; + } + RecordType result = RecordType::get(parser.getBuilder().getContext(), name); + + RecordType::TypeList lenParamList; + if (!parser.parseOptionalLParen()) { + while (true) { + llvm::StringRef lenparam; + mlir::Type intTy; + if (parser.parseKeyword(&lenparam) || parser.parseColon() || + parser.parseType(intTy)) { + parser.emitError(parser.getNameLoc(), "expected LEN parameter list"); + return {}; + } + lenParamList.emplace_back(lenparam, intTy); + if (parser.parseOptionalComma()) + break; + } + if (parser.parseRParen()) { + parser.emitError(parser.getNameLoc(), "expected ')'"); + return {}; + } + } + + RecordType::TypeList typeList; + if (!parser.parseOptionalLBrace()) { + while (true) { + llvm::StringRef field; + mlir::Type fldTy; + if (parser.parseKeyword(&field) || parser.parseColon() || + parser.parseType(fldTy)) { + parser.emitError(parser.getNameLoc(), "expected field type list"); + return {}; + } + typeList.emplace_back(field, fldTy); + if (parser.parseOptionalComma()) + break; + } + if (parser.parseRBrace()) { + parser.emitError(parser.getNameLoc(), "expected '}'"); + return {}; + } + } + + if (parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected '>' in type type"); + return {}; + } + + if (lenParamList.empty() && typeList.empty()) + return result; + + result.finalize(lenParamList, typeList); + return verifyDerived(parser, result, lenParamList, typeList); +} + +// !fir.ptr and !fir.heap where X is !fir.ptr, !fir.heap, or !fir.ref +// is undefined and disallowed. +inline bool singleIndirectionLevel(mlir::Type ty) { + return !fir::isa_ref_type(ty); +} + +} // namespace + +// Implementation of the thin interface from dialect to type parser + +mlir::Type fir::parseFirType(FIROpsDialect *, mlir::DialectAsmParser &parser) { + llvm::StringRef typeNameLit; + if (mlir::failed(parser.parseKeyword(&typeNameLit))) + return {}; + + auto loc = parser.getEncodedSourceLoc(parser.getNameLoc()); + if (typeNameLit == "array") + return parseSequence(parser, loc); + if (typeNameLit == "box") + return parseBox(parser, loc); + if (typeNameLit == "boxchar") + return parseBoxChar(parser); + if (typeNameLit == "boxproc") + return parseBoxProc(parser, loc); + if (typeNameLit == "char") + return parseCharacter(parser); + if (typeNameLit == "complex") + return parseComplex(parser); + if (typeNameLit == "dims") + return parseDims(parser); + if (typeNameLit == "field") + return parseField(parser); + if (typeNameLit == "heap") + return parseHeap(parser, loc); + if (typeNameLit == "int") + return parseInteger(parser); + if (typeNameLit == "len") + return parseLen(parser); + if (typeNameLit == "logical") + return parseLogical(parser); + if (typeNameLit == "ptr") + return parsePointer(parser, loc); + if (typeNameLit == "real") + return parseReal(parser); + if (typeNameLit == "ref") + return parseReference(parser, loc); + if (typeNameLit == "tdesc") + return parseTypeDesc(parser, loc); + if (typeNameLit == "type") + return parseDerived(parser, loc); + if (typeNameLit == "void") + return parseVoid(parser); + + parser.emitError(parser.getNameLoc(), "unknown FIR type " + typeNameLit); + return {}; +} + +namespace fir { +namespace detail { + +// Type storage classes + +/// `CHARACTER` storage +struct CharacterTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CharacterTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) CharacterTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + CharacterTypeStorage() = delete; + explicit CharacterTypeStorage(KindTy kind) : kind{kind} {} +}; + +struct DimsTypeStorage : public mlir::TypeStorage { + using KeyTy = unsigned; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { + return key == static_cast(getRank()); + } + + static DimsTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + int rank) { + auto *storage = allocator.allocate(); + return new (storage) DimsTypeStorage{rank}; + } + + int getRank() const { return rank; } + +protected: + int rank; + +private: + DimsTypeStorage() = delete; + explicit DimsTypeStorage(int rank) : rank{rank} {} +}; + +/// The type of a derived type part reference +struct FieldTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return llvm::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static FieldTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy) { + auto *storage = allocator.allocate(); + return new (storage) FieldTypeStorage{0}; + } + +private: + FieldTypeStorage() = delete; + explicit FieldTypeStorage(KindTy) {} +}; + +/// The type of a derived type LEN parameter reference +struct LenTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return llvm::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static LenTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy) { + auto *storage = allocator.allocate(); + return new (storage) LenTypeStorage{0}; + } + +private: + LenTypeStorage() = delete; + explicit LenTypeStorage(KindTy) {} +}; + +/// `LOGICAL` storage +struct LogicalTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static LogicalTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) LogicalTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + LogicalTypeStorage() = delete; + explicit LogicalTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `INTEGER` storage +struct IntTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static IntTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) IntTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + IntTypeStorage() = delete; + explicit IntTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `COMPLEX` storage +struct CplxTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CplxTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) CplxTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + CplxTypeStorage() = delete; + explicit CplxTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `REAL` storage (for reals of unsupported sizes) +struct RealTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static RealTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) RealTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + RealTypeStorage() = delete; + explicit RealTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// Boxed object (a Fortran descriptor) +struct BoxTypeStorage : public mlir::TypeStorage { + using KeyTy = std::tuple; + + static unsigned hashKey(const KeyTy &key) { + auto hashVal{llvm::hash_combine(std::get(key))}; + return llvm::hash_combine( + hashVal, llvm::hash_combine(std::get(key))); + } + + bool operator==(const KeyTy &key) const { + return std::get(key) == getElementType() && + std::get(key) == getLayoutMap(); + } + + static BoxTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) BoxTypeStorage{std::get(key), + std::get(key)}; + } + + mlir::Type getElementType() const { return eleTy; } + mlir::AffineMapAttr getLayoutMap() const { return map; } + +protected: + mlir::Type eleTy; + mlir::AffineMapAttr map; + +private: + BoxTypeStorage() = delete; + explicit BoxTypeStorage(mlir::Type eleTy, mlir::AffineMapAttr map) + : eleTy{eleTy}, map{map} {} +}; + +/// Boxed CHARACTER object type +struct BoxCharTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static BoxCharTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) BoxCharTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + + // a !fir.boxchar always wraps a !fir.char + CharacterType getElementType(mlir::MLIRContext *ctxt) const { + return CharacterType::get(ctxt, getFKind()); + } + +protected: + KindTy kind; + +private: + BoxCharTypeStorage() = delete; + explicit BoxCharTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// Boxed PROCEDURE POINTER object type +struct BoxProcTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static BoxProcTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) BoxProcTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + BoxProcTypeStorage() = delete; + explicit BoxProcTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer-like object storage +struct ReferenceTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static ReferenceTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) ReferenceTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + ReferenceTypeStorage() = delete; + explicit ReferenceTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer object storage +struct PointerTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static PointerTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) PointerTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + PointerTypeStorage() = delete; + explicit PointerTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Heap memory reference object storage +struct HeapTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static HeapTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) HeapTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + HeapTypeStorage() = delete; + explicit HeapTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Sequence-like object storage +struct SequenceTypeStorage : public mlir::TypeStorage { + using KeyTy = + std::tuple; + + static unsigned hashKey(const KeyTy &key) { + auto shapeHash{hash_value(std::get(key))}; + shapeHash = llvm::hash_combine(shapeHash, std::get(key)); + return llvm::hash_combine(shapeHash, std::get(key)); + } + + bool operator==(const KeyTy &key) const { + return key == KeyTy{getShape(), getElementType(), getLayoutMap()}; + } + + static SequenceTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) SequenceTypeStorage{ + std::get(key), std::get(key), + std::get(key)}; + } + + SequenceType::Shape getShape() const { return shape; } + mlir::Type getElementType() const { return eleTy; } + mlir::AffineMapAttr getLayoutMap() const { return map; } + +protected: + SequenceType::Shape shape; + mlir::Type eleTy; + mlir::AffineMapAttr map; + +private: + SequenceTypeStorage() = delete; + explicit SequenceTypeStorage(const SequenceType::Shape &shape, + mlir::Type eleTy, mlir::AffineMapAttr map) + : shape{shape}, eleTy{eleTy}, map{map} {} +}; + +/// Derived type storage +struct RecordTypeStorage : public mlir::TypeStorage { + using KeyTy = llvm::StringRef; + + static unsigned hashKey(const KeyTy &key) { + return llvm::hash_combine(key.str()); + } + + bool operator==(const KeyTy &key) const { return key == getName(); } + + static RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) RecordTypeStorage{key}; + } + + llvm::StringRef getName() const { return name; } + + void setLenParamList(llvm::ArrayRef list) { + lens = list; + } + llvm::ArrayRef getLenParamList() const { return lens; } + + void setTypeList(llvm::ArrayRef list) { types = list; } + llvm::ArrayRef getTypeList() const { return types; } + + void finalize(llvm::ArrayRef lenParamList, + llvm::ArrayRef typeList) { + if (finalized) + return; + finalized = true; + setLenParamList(lenParamList); + setTypeList(typeList); + } + +protected: + std::string name; + bool finalized; + std::vector lens; + std::vector types; + +private: + RecordTypeStorage() = delete; + explicit RecordTypeStorage(llvm::StringRef name) + : name{name}, finalized{false} {} +}; + +/// Type descriptor type storage +struct TypeDescTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getOfType(); } + + static TypeDescTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type ofTy) { + assert(ofTy && "descriptor type is null"); + auto *storage = allocator.allocate(); + return new (storage) TypeDescTypeStorage{ofTy}; + } + + // The type described by this type descriptor instance + mlir::Type getOfType() const { return ofTy; } + +protected: + mlir::Type ofTy; + +private: + TypeDescTypeStorage() = delete; + explicit TypeDescTypeStorage(mlir::Type ofTy) : ofTy{ofTy} {} +}; + +} // namespace detail + +template +bool inbounds(A v, B lb, B ub) { + return v >= lb && v < ub; +} + +bool isa_fir_type(mlir::Type t) { + return inbounds(t.getKind(), mlir::Type::FIRST_FIR_TYPE, + mlir::Type::LAST_FIR_TYPE); +} + +bool isa_std_type(mlir::Type t) { + return inbounds(t.getKind(), mlir::Type::FIRST_STANDARD_TYPE, + mlir::Type::LAST_STANDARD_TYPE); +} + +bool isa_fir_or_std_type(mlir::Type t) { + return isa_fir_type(t) || isa_std_type(t); +} + +bool isa_ref_type(mlir::Type t) { + return t.isa() || t.isa() || t.isa(); +} + +bool isa_aggregate(mlir::Type t) { + return t.isa() || t.isa(); +} + +mlir::Type dyn_cast_ptrEleTy(mlir::Type t) { + return mlir::TypeSwitch(t) + .Case( + [](auto p) { return p.getEleTy(); }) + .Default([](mlir::Type) { return mlir::Type{}; }); +} + +} // namespace fir + +// CHARACTER + +CharacterType fir::CharacterType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_CHARACTER, kind); +} + +int fir::CharacterType::getFKind() const { return getImpl()->getFKind(); } + +// Dims + +DimsType fir::DimsType::get(mlir::MLIRContext *ctxt, unsigned rank) { + return Base::get(ctxt, FIR_DIMS, rank); +} + +int fir::DimsType::getRank() const { return getImpl()->getRank(); } + +// Field + +FieldType fir::FieldType::get(mlir::MLIRContext *ctxt) { + return Base::get(ctxt, FIR_FIELD, 0); +} + +// Len + +LenType fir::LenType::get(mlir::MLIRContext *ctxt) { + return Base::get(ctxt, FIR_LEN, 0); +} + +// LOGICAL + +LogicalType fir::LogicalType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_LOGICAL, kind); +} + +int fir::LogicalType::getFKind() const { return getImpl()->getFKind(); } + +// INTEGER + +IntType fir::IntType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_INT, kind); +} + +int fir::IntType::getFKind() const { return getImpl()->getFKind(); } + +// COMPLEX + +CplxType fir::CplxType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_COMPLEX, kind); +} + +KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind(); } + +// REAL + +RealType fir::RealType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_REAL, kind); +} + +int fir::RealType::getFKind() const { return getImpl()->getFKind(); } + +// Box + +BoxType fir::BoxType::get(mlir::Type elementType, mlir::AffineMapAttr map) { + return Base::get(elementType.getContext(), FIR_BOX, elementType, map); +} + +mlir::Type fir::BoxType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::AffineMapAttr fir::BoxType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + +mlir::LogicalResult +fir::BoxType::verifyConstructionInvariants(mlir::Location, mlir::Type eleTy, + mlir::AffineMapAttr map) { + // TODO + return mlir::success(); +} + +// BoxChar + +BoxCharType fir::BoxCharType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_BOXCHAR, kind); +} + +CharacterType fir::BoxCharType::getEleTy() const { + return getImpl()->getElementType(getContext()); +} + +// BoxProc + +BoxProcType fir::BoxProcType::get(mlir::Type elementType) { + return Base::get(elementType.getContext(), FIR_BOXPROC, elementType); +} + +mlir::Type fir::BoxProcType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult +fir::BoxProcType::verifyConstructionInvariants(mlir::Location loc, + mlir::Type eleTy) { + if (eleTy.isa()) + return mlir::success(); + if (auto refTy = eleTy.dyn_cast()) + if (refTy.isa()) + return mlir::success(); + return mlir::emitError(loc, "invalid type for boxproc") << eleTy << '\n'; +} + +// Reference + +ReferenceType fir::ReferenceType::get(mlir::Type elementType) { + return Base::get(elementType.getContext(), FIR_REFERENCE, elementType); +} + +mlir::Type fir::ReferenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult +fir::ReferenceType::verifyConstructionInvariants(mlir::Location loc, + mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) + return mlir::emitError(loc, "cannot build a reference to type: ") + << eleTy << '\n'; + return mlir::success(); +} + +// Pointer + +PointerType fir::PointerType::get(mlir::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + llvm_unreachable("FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_POINTER, elementType); +} + +mlir::Type fir::PointerType::getEleTy() const { + return getImpl()->getElementType(); +} + +static bool canBePointerOrHeapElementType(mlir::Type eleTy) { + return eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa(); +} + +mlir::LogicalResult +fir::PointerType::verifyConstructionInvariants(mlir::Location loc, + mlir::Type eleTy) { + if (canBePointerOrHeapElementType(eleTy)) + return mlir::emitError(loc, "cannot build a pointer to type: ") + << eleTy << '\n'; + return mlir::success(); +} + +// Heap + +HeapType fir::HeapType::get(mlir::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + llvm_unreachable("FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_HEAP, elementType); +} + +mlir::Type fir::HeapType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult +fir::HeapType::verifyConstructionInvariants(mlir::Location loc, + mlir::Type eleTy) { + if (canBePointerOrHeapElementType(eleTy)) + return mlir::emitError(loc, "cannot build a heap pointer to type: ") + << eleTy << '\n'; + return mlir::success(); +} + +// Sequence + +SequenceType fir::SequenceType::get(const Shape &shape, mlir::Type elementType, + mlir::AffineMapAttr map) { + auto *ctxt = elementType.getContext(); + return Base::get(ctxt, FIR_SEQUENCE, shape, elementType, map); +} + +mlir::Type fir::SequenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::AffineMapAttr fir::SequenceType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + +SequenceType::Shape fir::SequenceType::getShape() const { + return getImpl()->getShape(); +} + +mlir::LogicalResult fir::SequenceType::verifyConstructionInvariants( + mlir::Location loc, const SequenceType::Shape &shape, mlir::Type eleTy, + mlir::AffineMapAttr map) { + // DIMENSION attribute can only be applied to an intrinsic or record type + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) + return mlir::emitError(loc, "cannot build an array of this element type: ") + << eleTy << '\n'; + return mlir::success(); +} + +// compare if two shapes are equivalent +bool fir::operator==(const SequenceType::Shape &sh_1, + const SequenceType::Shape &sh_2) { + if (sh_1.size() != sh_2.size()) + return false; + auto e = sh_1.size(); + for (decltype(e) i = 0; i != e; ++i) + if (sh_1[i] != sh_2[i]) + return false; + return true; +} + +// compute the hash of a Shape +llvm::hash_code fir::hash_value(const SequenceType::Shape &sh) { + if (sh.size()) { + return llvm::hash_combine_range(sh.begin(), sh.end()); + } + return llvm::hash_combine(0); +} + +/// RecordType +/// +/// This type captures a Fortran "derived type" + +RecordType fir::RecordType::get(mlir::MLIRContext *ctxt, llvm::StringRef name) { + return Base::get(ctxt, FIR_DERIVED, name); +} + +void fir::RecordType::finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList) { + getImpl()->finalize(lenPList, typeList); +} + +llvm::StringRef fir::RecordType::getName() { return getImpl()->getName(); } + +RecordType::TypeList fir::RecordType::getTypeList() { + return getImpl()->getTypeList(); +} + +RecordType::TypeList fir::RecordType::getLenParamList() { + return getImpl()->getLenParamList(); +} + +detail::RecordTypeStorage const *fir::RecordType::uniqueKey() const { + return getImpl(); +} + +mlir::LogicalResult +fir::RecordType::verifyConstructionInvariants(mlir::Location loc, + llvm::StringRef name) { + if (name.size() == 0) + return mlir::emitError(loc, "record types must have a name"); + return mlir::success(); +} + +mlir::Type fir::RecordType::getType(llvm::StringRef ident) { + for (auto f : getTypeList()) + if (ident == f.first) + return f.second; + llvm_unreachable("query for field not present in record"); + return {}; +} + +/// Type descriptor type +/// +/// This is the type of a type descriptor object (similar to a class instance) + +TypeDescType fir::TypeDescType::get(mlir::Type ofType) { + assert(!ofType.isa()); + return Base::get(ofType.getContext(), FIR_TYPEDESC, ofType); +} + +mlir::Type fir::TypeDescType::getOfTy() const { return getImpl()->getOfType(); } + +mlir::LogicalResult +fir::TypeDescType::verifyConstructionInvariants(mlir::Location loc, + mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) + return mlir::emitError(loc, "cannot build a type descriptor of type: ") + << eleTy << '\n'; + return mlir::success(); +} + +namespace { + +void printBounds(llvm::raw_ostream &os, const SequenceType::Shape &bounds) { + os << '<'; + for (auto &b : bounds) { + if (b >= 0) { + os << b << 'x'; + } else { + os << "?x"; + } + } +} + +llvm::SmallPtrSet recordTypeVisited; + +} // namespace + +void fir::printFirType(FIROpsDialect *, mlir::Type ty, + mlir::DialectAsmPrinter &p) { + auto &os = p.getStream(); + switch (ty.getKind()) { + case fir::FIR_BOX: { + auto type = ty.cast(); + os << "box<"; + p.printType(type.getEleTy()); + if (auto map = type.getLayoutMap()) { + os << ", "; + p.printAttribute(map); + } + os << '>'; + } break; + case fir::FIR_BOXCHAR: { + auto type = ty.cast().getEleTy(); + os << "boxchar<" << type.cast().getFKind() << '>'; + } break; + case fir::FIR_BOXPROC: + os << "boxproc<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_CHARACTER: // intrinsic + os << "char<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_COMPLEX: // intrinsic + os << "complex<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_DERIVED: { // derived + auto type = ty.cast(); + os << "type<" << type.getName(); + if (!recordTypeVisited.count(type.uniqueKey())) { + recordTypeVisited.insert(type.uniqueKey()); + if (type.getLenParamList().size()) { + char ch = '('; + for (auto p : type.getLenParamList()) { + os << ch << p.first << ':'; + p.second.print(os); + ch = ','; + } + os << ')'; + } + if (type.getTypeList().size()) { + char ch = '{'; + for (auto p : type.getTypeList()) { + os << ch << p.first << ':'; + p.second.print(os); + ch = ','; + } + os << '}'; + } + recordTypeVisited.erase(type.uniqueKey()); + } + os << '>'; + } break; + case fir::FIR_DIMS: + os << "dims<" << ty.cast().getRank() << '>'; + break; + case fir::FIR_FIELD: + os << "field"; + break; + case fir::FIR_HEAP: + os << "heap<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_INT: // intrinsic + os << "int<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_LEN: + os << "len"; + break; + case fir::FIR_LOGICAL: // intrinsic + os << "logical<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_POINTER: + os << "ptr<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_REAL: // intrinsic + os << "real<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_REFERENCE: + os << "ref<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_SEQUENCE: { + os << "array"; + auto type = ty.cast(); + auto shape = type.getShape(); + if (shape.size()) { + printBounds(os, shape); + } else { + os << "<*:"; + } + p.printType(ty.cast().getEleTy()); + if (auto map = type.getLayoutMap()) { + os << ", "; + map.print(os); + } + os << '>'; + } break; + case fir::FIR_TYPEDESC: + os << "tdesc<"; + p.printType(ty.cast().getOfTy()); + os << '>'; + break; + } +} diff --git a/lib/Optimizer/Support/CMakeLists.txt b/lib/Optimizer/Support/CMakeLists.txt new file mode 100644 index 000000000000..88a1fc78a5f6 --- /dev/null +++ b/lib/Optimizer/Support/CMakeLists.txt @@ -0,0 +1,10 @@ +add_llvm_library(FIRSupport + KindMapping.cpp +) + +target_link_libraries(FIRSupport FIRDialect) + +install (TARGETS FIRSupport + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/lib/Optimizer/Support/KindMapping.cpp b/lib/Optimizer/Support/KindMapping.cpp new file mode 100644 index 000000000000..8731c0bb087e --- /dev/null +++ b/lib/Optimizer/Support/KindMapping.cpp @@ -0,0 +1,244 @@ +//===-- KindMapping.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" + +/// Allow the user to set the FIR intrinsic type kind value to LLVM type +/// mappings. Note that these are not mappings from kind values to any +/// other MLIR dialect, only to LLVM IR. The default values follow the f18 +/// front-end kind mappings. + +using Bitsize = fir::KindMapping::Bitsize; +using KindTy = fir::KindMapping::KindTy; +using LLVMTypeID = fir::KindMapping::LLVMTypeID; +using MatchResult = fir::KindMapping::MatchResult; + +static llvm::cl::opt clKindMapping( + "kind-mapping", llvm::cl::desc("kind mapping string to set kind precision"), + llvm::cl::value_desc("kind-mapping-string"), llvm::cl::init("")); + +/// Integral types default to the kind value being the size of the value in +/// bytes. The default is to scale from bytes to bits. +static Bitsize defaultScalingKind(KindTy kind) { + const unsigned BITS_IN_BYTE = 8; + return kind * BITS_IN_BYTE; +} + +/// Floating-point types default to the kind value being the size of the value +/// in bytes. The default is to translate kinds of 2, 4, 8, 10, and 16 to a +/// valid llvm::Type::TypeID value. Otherwise, the default is FloatTyID. +static LLVMTypeID defaultRealKind(KindTy kind) { + switch (kind) { + case 2: + return LLVMTypeID::HalfTyID; + case 4: + return LLVMTypeID::FloatTyID; + case 8: + return LLVMTypeID::DoubleTyID; + case 10: + return LLVMTypeID::X86_FP80TyID; + case 16: + return LLVMTypeID::FP128TyID; + default: + return LLVMTypeID::FloatTyID; + } +} + +// lookup the kind-value given the defaults, the mappings, and a KIND key +template +static RT doLookup(std::function def, + const llvm::DenseMap, RT> &map, + KindTy kind) { + std::pair key{KEY, kind}; + auto iter = map.find(key); + if (iter != map.end()) + return iter->second; + return def(kind); +} + +// do a lookup for INTERGER, LOGICAL, or CHARACTER +template +static Bitsize getIntegerLikeBitsize(KindTy kind, const MAP &map) { + return doLookup(defaultScalingKind, map, kind); +} + +// do a lookup for REAL or COMPLEX +template +static LLVMTypeID getFloatLikeTypeID(KindTy kind, const MAP &map) { + return doLookup(defaultRealKind, map, kind); +} + +template +static const llvm::fltSemantics &getFloatSemanticsOfKind(KindTy kind, + const MAP &map) { + switch (doLookup(defaultRealKind, map, kind)) { + case LLVMTypeID::HalfTyID: + return llvm::APFloat::IEEEhalf(); + case LLVMTypeID::FloatTyID: + return llvm::APFloat::IEEEsingle(); + case LLVMTypeID::DoubleTyID: + return llvm::APFloat::IEEEdouble(); + case LLVMTypeID::X86_FP80TyID: + return llvm::APFloat::x87DoubleExtended(); + case LLVMTypeID::FP128TyID: + return llvm::APFloat::IEEEquad(); + case LLVMTypeID::PPC_FP128TyID: + return llvm::APFloat::PPCDoubleDouble(); + default: + llvm_unreachable("Invalid floating type"); + } +} + +static MatchResult parseCode(char &code, const char *&ptr) { + if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r') + return mlir::failure(); + code = *ptr++; + return mlir::success(); +} + +template +static MatchResult parseSingleChar(const char *&ptr) { + if (*ptr != ch) + return mlir::failure(); + ++ptr; + return mlir::success(); +} + +static MatchResult parseColon(const char *&ptr) { + return parseSingleChar<':'>(ptr); +} + +static MatchResult parseComma(const char *&ptr) { + return parseSingleChar<','>(ptr); +} + +static MatchResult parseInt(unsigned &result, const char *&ptr) { + const char *beg = ptr; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + if (beg == ptr) + return mlir::failure(); + llvm::StringRef ref(beg, ptr - beg); + int temp; + if (ref.consumeInteger(10, temp)) + return mlir::failure(); + result = temp; + return mlir::success(); +} + +static mlir::LogicalResult matchString(const char *&ptr, + llvm::StringRef literal) { + llvm::StringRef s(ptr); + if (s.startswith(literal)) { + ptr += literal.size(); + return mlir::success(); + } + return mlir::failure(); +} + +static MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr) { + if (mlir::succeeded(matchString(ptr, "Half"))) { + result = LLVMTypeID::HalfTyID; + return mlir::success(); + } + if (mlir::succeeded(matchString(ptr, "Float"))) { + result = LLVMTypeID::FloatTyID; + return mlir::success(); + } + if (mlir::succeeded(matchString(ptr, "Double"))) { + result = LLVMTypeID::DoubleTyID; + return mlir::success(); + } + if (mlir::succeeded(matchString(ptr, "X86_FP80"))) { + result = LLVMTypeID::X86_FP80TyID; + return mlir::success(); + } + if (mlir::succeeded(matchString(ptr, "FP128"))) { + result = LLVMTypeID::FP128TyID; + return mlir::success(); + } + if (mlir::succeeded(matchString(ptr, "PPC_FP128"))) { + result = LLVMTypeID::PPC_FP128TyID; + return mlir::success(); + } + return mlir::failure(); +} + +fir::KindMapping::KindMapping(mlir::MLIRContext *context, llvm::StringRef map) + : context{context} { + if (mlir::failed(parse(map))) { + intMap.clear(); + floatMap.clear(); + } +} + +fir::KindMapping::KindMapping(mlir::MLIRContext *context) + : KindMapping{context, clKindMapping} {} + +MatchResult fir::KindMapping::badMapString(const llvm::Twine &ptr) { + auto unknown = mlir::UnknownLoc::get(context); + mlir::emitError(unknown, ptr); + return mlir::failure(); +} + +MatchResult fir::KindMapping::parse(llvm::StringRef kindMap) { + if (kindMap.empty()) + return mlir::success(); + const char *srcPtr = kindMap.begin(); + while (true) { + char code = '\0'; + KindTy kind = 0; + if (parseCode(code, srcPtr) || parseInt(kind, srcPtr)) + return badMapString(srcPtr); + if (code == 'a' || code == 'i' || code == 'l') { + Bitsize bits = 0; + if (parseColon(srcPtr) || parseInt(bits, srcPtr)) + return badMapString(srcPtr); + intMap[std::pair{code, kind}] = bits; + } else if (code == 'r' || code == 'c') { + LLVMTypeID id{}; + if (parseColon(srcPtr) || parseTypeID(id, srcPtr)) + return badMapString(srcPtr); + floatMap[std::pair{code, kind}] = id; + } else { + return badMapString(srcPtr); + } + if (parseComma(srcPtr)) + break; + } + if (*srcPtr) + return badMapString(srcPtr); + return mlir::success(); +} + +Bitsize fir::KindMapping::getCharacterBitsize(KindTy kind) { + return getIntegerLikeBitsize<'a'>(kind, intMap); +} + +Bitsize fir::KindMapping::getIntegerBitsize(KindTy kind) { + return getIntegerLikeBitsize<'i'>(kind, intMap); +} + +Bitsize fir::KindMapping::getLogicalBitsize(KindTy kind) { + return getIntegerLikeBitsize<'l'>(kind, intMap); +} + +LLVMTypeID fir::KindMapping::getRealTypeID(KindTy kind) { + return getFloatLikeTypeID<'r'>(kind, floatMap); +} + +LLVMTypeID fir::KindMapping::getComplexTypeID(KindTy kind) { + return getFloatLikeTypeID<'c'>(kind, floatMap); +} + +const llvm::fltSemantics &fir::KindMapping::getFloatSemantics(KindTy kind) { + return getFloatSemanticsOfKind<'r'>(kind, floatMap); +} diff --git a/test-lit/CMakeLists.txt b/test-lit/CMakeLists.txt index 965adff78976..cdc5dd58d3ca 100644 --- a/test-lit/CMakeLists.txt +++ b/test-lit/CMakeLists.txt @@ -16,6 +16,11 @@ set(FLANG_TEST_PARAMS set(FLANG_TEST_DEPENDS f18 ) + +if (LINK_WITH_FIR) + list(APPEND FLANG_TEST_DEPENDS tco) +endif() + add_lit_testsuite(check-all "Running the Flang regression tests" ${CMAKE_CURRENT_BINARY_DIR} PARAMS ${FLANG_TEST_PARAMS} diff --git a/test-lit/Fir/fir-ops.fir b/test-lit/Fir/fir-ops.fir new file mode 100644 index 000000000000..bdadf5cd6f58 --- /dev/null +++ b/test-lit/Fir/fir-ops.fir @@ -0,0 +1,403 @@ +// Test the FIR operations + +// RUN: tco -emit-fir %s | tco -emit-fir | FileCheck %s +// UNSUPPORTED: !fir + +// CHECK-LABEL: func @it1() -> !fir.int<4> +func @it1() -> !fir.int<4> +// CHECK-LABEL: func @box1() -> !fir.boxchar<2> +func @box1() -> !fir.boxchar<2> +// CHECK-LABEL: func @box2() -> !fir.boxproc<(i32, i32) -> i64> +func @box2() -> !fir.boxproc<(i32, i32) -> i64> +// CHECK-LABEL: func @box3() -> !fir.box> +func @box3() -> !fir.box> + +// Fortran SUBROUTINE and FUNCTION +// CHECK-LABEL: func @print_index3(index, index, index) +// CHECK-LABEL: func @user_i64(i64) +// CHECK-LABEL: func @user_tdesc(!fir.tdesc>) +func @print_index3(index, index, index) +func @user_i64(i64) +func @user_tdesc(!fir.tdesc>) + +// expect the void return to be omitted +// CHECK-LABEL: func @store_tuple(tuple>) +func @store_tuple(tuple>) -> () + +// CHECK-LABEL: func @get_method_box() -> !fir.box> +// CHECK-LABEL: func @method_impl(!fir.box>) +func @get_method_box() -> !fir.box> +func @method_impl(!fir.box>) + +// CHECK-LABEL: func @nop() +func @nop() + +// CHECK-LABEL: func @get_func() -> (() -> ()) +func @get_func() -> (() -> ()) + +// CHECK-LABEL: @instructions +func @instructions() { + // CHECK: %[[A0:.*]] = fir.alloca !fir.array<10xi32> + %0 = fir.alloca !fir.array<10xi32> + // CHECK: fir.load %[[A0]] : !fir.ref> + %1 = fir.load %0 : !fir.ref> + %2 = fir.alloca i32 + %3 = constant 22 : i32 + // CHECK: fir.store %{{.*}} to %{{.*}} : !fir.ref + fir.store %3 to %2 : !fir.ref + // CHECK: fir.undefined i32 + %4 = fir.undefined i32 + // CHECK: %[[A5:.*]] = fir.allocmem !fir.array<100xf32> + %5 = fir.allocmem !fir.array<100xf32> + // CHECK: %[[A6:.*]] = fir.embox %[[A5]] : (!fir.heap>) -> !fir.box> + %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> + // CHECK: fir.box_addr %{{.*}} : (!fir.box>) -> !fir.ref> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> + %c0 = constant 0 : index + // CHECK: %[[A8:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}} : (!fir.box>, index) -> (index, index, index) + %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) + // CHECK: fir.call @print_index3(%[[A8]]#0, %[[A8]]#1, %[[A8]]#2) : (index, index, index) + fir.call @print_index3(%d1#0, %d1#1, %d1#2) : (index, index, index) -> () + %8 = fir.call @it1() : () -> !fir.int<4> + // CHECK: fir.box_elesize %[[A6]] : (!fir.box>) -> i64 + %9 = fir.box_elesize %6 : (!fir.box>) -> i64 + // CHECK: fir.box_isalloc %[[A6]] : (!fir.box>) -> i1 + %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 + // CHECK: fir.box_isarray %[[A6]] : (!fir.box>) -> i1 + %11 = fir.box_isarray %6 : (!fir.box>) -> i1 + // CHECK: fir.box_isptr %[[A6]] : (!fir.box>) -> i1 + %12 = fir.box_isptr %6 : (!fir.box>) -> i1 + // CHECK: fir.box_rank %[[A6]] : (!fir.box>) -> i64 + %13 = fir.box_rank %6 : (!fir.box>) -> i64 + // CHECK: fir.box_tdesc %[[A6]] : (!fir.box>) -> !fir.tdesc> + %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> + %15 = fir.call @box1() : () -> !fir.boxchar<2> + // CHECK: fir.boxchar_len %{{.*}} : (!fir.boxchar<2>) -> i32 + %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 + %17 = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> + // CHECK: fir.boxproc_host %{{.*}} : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + %19 = constant 10 : i32 + // CHECK: fir.coordinate_of %{{.*}}, %{{.*}} : (!fir.heap>, i32) -> !fir.ref + %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref + // CHECK: fir.field_index f, !fir.type + %21 = fir.field_index f, !fir.type + // CHECK: fir.undefined !fir.type + %22 = fir.undefined !fir.type + // CHECK: fir.extract_value %{{.*}}, %{{.*}} : (!fir.type, !fir.field) -> f32 + %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 + %c1 = constant 1 : i32 + // CHECK: fir.gendims %{{.*}}, %{{.*}}, %{{.*}} : (i32, i32, i32) -> !fir.dims<1> + %24 = fir.gendims %c1, %19, %c1 : (i32, i32, i32) -> !fir.dims<1> + %cf1 = constant 1.0 : f32 + // CHECK: fir.insert_value %{{.*}}, %{{.*}}, %{{.*}} : (!fir.type, f32, !fir.field) -> !fir.type + %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type + // CHECK: fir.len_param_index f, !fir.type + %26 = fir.len_param_index f, !fir.type + %27 = fir.call @box3() : () -> !fir.box> + // CHECK: fir.dispatch "method"(%{{.*}}) : (!fir.box>) -> i32 + %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 + // CHECK: fir.convert %{{.*}} : (i32) -> i64 + %29 = fir.convert %28 : (i32) -> i64 + // CHECK: fir.gentypedesc !fir.type + %30 = fir.gentypedesc !fir.type + fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () + // CHECK: fir.no_reassoc %{{.*}} : i64 + %31 = fir.no_reassoc %29 : i64 + fir.call @user_i64(%31) : (i64) -> () + // CHECK: fir.freemem %{{.*}} : !fir.heap> + fir.freemem %5 : !fir.heap> + %32 = fir.call @get_func() : () -> (() -> ()) + fir.call %32() : () -> () + // CHECK: fir.address_of(@it1) : !fir.ref<() -> !fir.int<4>> + %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> + return +} + +// CHECK-LABEL: @boxing_match +func @boxing_match() { + %0 = fir.alloca i32 + %d6 = fir.alloca !fir.type + %d3 = fir.alloca !fir.char<1> + %e6 = fir.alloca tuple + %1 = fir.embox %0 : (!fir.ref) -> !fir.box + // CHECK: fir.unbox %{{.*}} : (!fir.box) -> (!fir.ref, i32, i32, !fir.tdesc, i32, !fir.dims<0>) + %2:6 = fir.unbox %1 : (!fir.box) -> (!fir.ref,i32,i32,!fir.tdesc,i32,!fir.dims<0>) + %c8 = constant 8 : i32 + %3 = fir.undefined !fir.char<1> + // CHECK: fir.emboxchar %{{.*}}, %{{.*}} : (!fir.ref>, i32) -> !fir.boxchar<1> + // CHECK: fir.unboxchar %{{.*}} : (!fir.boxchar<1>) -> (!fir.ref>, i32) + %4 = fir.emboxchar %d3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> + %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) + %6 = fir.undefined !fir.type + %z = constant 0 : i32 + %c12 = constant 12 : i32 + %a2 = fir.insert_value %6, %c12, %z : (!fir.type, i32, i32) -> !fir.type + %z1 = constant 1 : i32 + %c42 = constant 42.13 : f64 + %a3 = fir.insert_value %6, %c42, %z1 : (!fir.type, f64, i32) -> !fir.type + fir.store %a3 to %d6 : !fir.ref> + %7 = fir.emboxproc @method_impl, %e6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + %8:2 = fir.unboxproc %7 : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) + // CHECK: fir.emboxproc @method_impl, %{{.*}} : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + // CHECK: fir.unboxproc %{{.*}} : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) + %9 = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> + %10:2 = fir.unboxproc %9 : (!fir.boxproc<(i32, i32) -> i64>) -> ((i32, i32) -> i64, !fir.ref>>) + %11 = fir.load %10#1 : !fir.ref>> + fir.call @store_tuple(%11) : (tuple>) -> () + return +} + +// CHECK-LABEL: @loop +func @loop() { + %c1 = constant 1 : index + %c10 = constant 10 : index + %ct = constant true + // CHECK: fir.loop %{{.*}} = %{{.*}} to %{{.*}} { + %i = fir.loop %i = %c1 to %c10 { + // CHECK: fir.where %{{.*}} { + fir.where %ct { + fir.call @nop() : () -> () + // CHECK: } otherwise { + } otherwise { + fir.call @nop() : () -> () + } + } + // CHECK: fir.unreachable + fir.unreachable +} + +// CHECK-LABEL: @bar_select +func @bar_select(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), -3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), 4, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), -3,^bb3(%arg2,%2:i32,i32), 4,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2 : i32, %b3:i32) : + %4 = addi %b, %b2 : i32 + %5 = addi %4, %b3 : i32 + return %5 : i32 +^bb3(%c:i32, %c2:i32) : + %6 = addi %c, %c2 : i32 + return %6 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_rank +func @bar_select_rank(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_rank %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), 3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), -1, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_rank %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), 3,^bb3(%arg2,%2:i32,i32), -1,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2 : i32, %b3:i32) : + %4 = addi %b, %b2 : i32 + %5 = addi %4, %b3 : i32 + return %5 : i32 +^bb3(%c:i32, %c2:i32) : + %6 = addi %c, %c2 : i32 + return %6 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + %7 = fir.call @get_method_box() : () -> !fir.box> + fir.dispatch method(%7) : (!fir.box>) -> () + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_type +func @bar_select_type(%arg : !fir.box}>>) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_type %{{.*}} : !fir.box}>> [#fir.instance>, ^bb1(%{{.*}} : i32), #fir.instance>, ^bb2(%{{.*}} : i32), #fir.subsumed>, ^bb3(%{{.*}} : i32), #fir.instance>, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_type %arg : !fir.box}>> [ #fir.instance>,^bb1(%0:i32), #fir.instance>,^bb2(%2:i32), #fir.subsumed>,^bb3(%2:i32), #fir.instance>,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32) : + return %b : i32 +^bb3(%c : i32) : + return %c : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_case +func @bar_select_case(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_case %{{.*}} : i32 [#fir.point, %{{.*}}, ^bb1(%{{.*}} : i32), #fir.lower, %{{.*}}, ^bb2(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32, i32), #fir.interval, %{{.*}}, %{{.*}}, ^bb3(%{{.*}}, %{{.*}} : i32, i32), #fir.upper, %{{.*}}, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_case %arg : i32 [#fir.point, %0, ^bb1(%0:i32), #fir.lower, %1, ^bb2(%2,%arg,%arg2,%1:i32,i32,i32,i32), #fir.interval, %2, %3, ^bb3(%2,%arg2:i32,i32), #fir.upper, %arg, ^bb4(%1:i32), unit, ^bb5] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2:i32, %b3:i32, %b4:i32) : + %4 = addi %b, %b2 : i32 + %5 = muli %4, %b3 : i32 + %6 = addi %5, %b4 : i32 + return %6 : i32 +^bb3(%c : i32, %c2 : i32) : + %7 = addi %c, %c2 : i32 + return %7 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @global_var +fir.global @global_var : i32 { + %0 = constant 1 : i32 + fir.has_value %0 : i32 +} + +// CHECK-LABEL: @global_constant +fir.global @global_constant constant : i32 { + %0 = constant 934 : i32 + fir.has_value %0 : i32 +} + +// CHECK-LABEL: @global_derived +fir.global @global_derived : !fir.type { + // CHECK: fir.global_len "f", 1 : i32 + fir.global_len f, 1 : i32 + %0 = fir.undefined !fir.type + fir.has_value %0 : !fir.type +} + +// CHECK-LABEL: @dispatch_tbl +fir.dispatch_table @dispatch_tbl { + // CHECK: fir.dt_entry "method", @method_impl + fir.dt_entry "method", @method_impl +} + +// CHECK-LABEL: @compare_real +func @compare_real(%a : !fir.real<16>, %b : !fir.real<16>) { + // CHECK: fir.cmpf "false", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "oeq", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ogt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "oge", %{{.*}}, %{{.*}} : !fir.real<16> + %d0 = fir.cmpf "false", %a, %b : !fir.real<16> + %d1 = fir.cmpf "oeq", %a, %b : !fir.real<16> + %d2 = fir.cmpf "ogt", %a, %b : !fir.real<16> + %d3 = fir.cmpf "oge", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "olt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ole", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "one", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ord", %{{.*}}, %{{.*}} : !fir.real<16> + %a0 = fir.cmpf "olt", %a, %b : !fir.real<16> + %a1 = fir.cmpf "ole", %a, %b : !fir.real<16> + %a2 = fir.cmpf "one", %a, %b : !fir.real<16> + %a3 = fir.cmpf "ord", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "ueq", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ugt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "uge", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ult", %{{.*}}, %{{.*}} : !fir.real<16> + %b0 = fir.cmpf "ueq", %a, %b : !fir.real<16> + %b1 = fir.cmpf "ugt", %a, %b : !fir.real<16> + %b2 = fir.cmpf "uge", %a, %b : !fir.real<16> + %b3 = fir.cmpf "ult", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "ule", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "une", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "uno", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "true", %{{.*}}, %{{.*}} : !fir.real<16> + %c0 = fir.cmpf "ule", %a, %b : !fir.real<16> + %c1 = fir.cmpf "une", %a, %b : !fir.real<16> + %c2 = fir.cmpf "uno", %a, %b : !fir.real<16> + %c3 = fir.cmpf "true", %a, %b : !fir.real<16> + return +} + +// CHECK-LABEL: @compare_complex +func @compare_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) { + // CHECK: fir.cmpc "false", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "oeq", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ogt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "oge", %{{.*}}, %{{.*}} : !fir.complex<16> + %d0 = fir.cmpc "false", %a, %b : !fir.complex<16> + %d1 = fir.cmpc "oeq", %a, %b : !fir.complex<16> + %d2 = fir.cmpc "ogt", %a, %b : !fir.complex<16> + %d3 = fir.cmpc "oge", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "olt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ole", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "one", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ord", %{{.*}}, %{{.*}} : !fir.complex<16> + %a0 = fir.cmpc "olt", %a, %b : !fir.complex<16> + %a1 = fir.cmpc "ole", %a, %b : !fir.complex<16> + %a2 = fir.cmpc "one", %a, %b : !fir.complex<16> + %a3 = fir.cmpc "ord", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "ueq", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ugt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "uge", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ult", %{{.*}}, %{{.*}} : !fir.complex<16> + %b0 = fir.cmpc "ueq", %a, %b : !fir.complex<16> + %b1 = fir.cmpc "ugt", %a, %b : !fir.complex<16> + %b2 = fir.cmpc "uge", %a, %b : !fir.complex<16> + %b3 = fir.cmpc "ult", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "ule", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "une", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "uno", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "true", %{{.*}}, %{{.*}} : !fir.complex<16> + %c0 = fir.cmpc "ule", %a, %b : !fir.complex<16> + %c1 = fir.cmpc "une", %a, %b : !fir.complex<16> + %c2 = fir.cmpc "uno", %a, %b : !fir.complex<16> + %c3 = fir.cmpc "true", %a, %b : !fir.complex<16> + return +} + +// CHECK-LABEL: @arith_real +func @arith_real(%a : !fir.real<16>, %b : !fir.real<16>) -> !fir.real<16> { + %c1 = constant 1.0 : f32 + %0 = fir.convert %c1 : (f32) -> !fir.real<16> + // CHECK: %[[R1:.*]] = fir.negf %{{.*}} : !fir.real<16> + // CHECK: fir.addf %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: %[[R3:.*]] = fir.subf %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.mulf %[[R1]], %[[R3]] : !fir.real<16> + // CHECK: fir.divf %{{.*}}, %{{.*}} : !fir.real<16> + %1 = fir.negf %a : !fir.real<16> + %2 = fir.addf %0, %1 : !fir.real<16> + %3 = fir.subf %2, %b : !fir.real<16> + %4 = fir.mulf %1, %3 : !fir.real<16> + %5 = fir.divf %4, %a : !fir.real<16> + return %5 : !fir.real<16> +} + +// CHECK-LABEL: @arith_complex +func @arith_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) -> !fir.complex<16> { + // CHECK: fir.negc %{{.*}} : !fir.complex<16> + // CHECK: fir.addc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.subc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.mulc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.divc %{{.*}}, %{{.*}} : !fir.complex<16> + %1 = fir.negc %a : !fir.complex<16> + %2 = fir.addc %b, %1 : !fir.complex<16> + %3 = fir.subc %2, %b : !fir.complex<16> + %4 = fir.mulc %1, %3 : !fir.complex<16> + %5 = fir.divc %4, %a : !fir.complex<16> + return %5 : !fir.complex<16> +} + +// CHECK-LABEL: @character_literal +func @character_literal() -> !fir.array<13 x !fir.char<1>> { + // CHECK: fir.string_lit "Hello, World!"(13) : !fir.char<1> + %0 = fir.string_lit "Hello, World!"(13) : !fir.char<1> + return %0 : !fir.array<13 x !fir.char<1>> +} diff --git a/test-lit/Fir/fir-types.fir b/test-lit/Fir/fir-types.fir new file mode 100644 index 000000000000..19b0286f5b73 --- /dev/null +++ b/test-lit/Fir/fir-types.fir @@ -0,0 +1,78 @@ +// Test the FIR types + +// RUN: tco -emit-fir %s | tco -emit-fir | FileCheck %s +// UNSUPPORTED: !fir + +// Fortran Intrinsic types +// CHECK-LABEL: func @it1() -> !fir.int<4> +// CHECK-LABEL: func @it2() -> !fir.real<8> +// CHECK-LABEL: func @it3() -> !fir.complex<8> +// CHECK-LABEL: func @it4() -> !fir.logical<1> +// CHECK-LABEL: func @it5() -> !fir.char<1> +func @it1() -> !fir.int<4> +func @it2() -> !fir.real<8> +func @it3() -> !fir.complex<8> +func @it4() -> !fir.logical<1> +func @it5() -> !fir.char<1> + +// Fortran Derived types (records) +// CHECK-LABEL: func @dvd1() -> !fir.type +// CHECK-LABEL: func @dvd2() -> !fir.type +// CHECK-LABEL: func @dvd3() -> !fir.type +// CHECK-LABEL: func @dvd4() -> !fir.type +// CHECK-LABEL: func @dvd5() -> !fir.type +// CHECK-LABEL: func @dvd6() -> !fir.type>}> +func @dvd1() -> !fir.type +func @dvd2() -> !fir.type +func @dvd3() -> !fir.type +func @dvd4() -> !fir.type +func @dvd5() -> !fir.type +func @dvd6() -> !fir.type>}> + +// FIR array types +// CHECK-LABEL: func @arr1() -> !fir.array<10xf32> +// CHECK-LABEL: func @arr2() -> !fir.array<10x10xf32> +// CHECK-LABEL: func @arr3() -> !fir.array +// CHECK-LABEL: func @arr4() -> !fir.array<10x?xf32> +// CHECK-LABEL: func @arr5() -> !fir.array +// CHECK-LABEL: func @arr6() -> !fir.array<*:f32> +// CHECK-LABEL: func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9xf32> +func @arr1() -> !fir.array<10xf32> +func @arr2() -> !fir.array<10x10xf32> +func @arr3() -> !fir.array +func @arr4() -> !fir.array<10x?xf32> +func @arr5() -> !fir.array +func @arr6() -> !fir.array<*:f32> +func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9xf32> + +// FIR pointer-like types +// CHECK-LABEL: func @mem1() -> !fir.ref +// CHECK-LABEL: func @mem2() -> !fir.ptr +// CHECK-LABEL: func @mem3() -> !fir.heap +// CHECK-LABEL: func @mem4() -> !fir.ref<() -> ()> +func @mem1() -> !fir.ref +func @mem2() -> !fir.ptr +func @mem3() -> !fir.heap +func @mem4() -> !fir.ref<() -> ()> + +// FIR box types (descriptors) +// CHECK-LABEL: func @box1() -> !fir.box> +// CHECK-LABEL: func @box2() -> !fir.boxchar<2> +// CHECK-LABEL: func @box3() -> !fir.boxproc<(i32, i32) -> i64> +// CHECK-LABEL: func @box4() -> !fir.box +// CHECK-LABEL: func @box5() -> !fir.box> +func @box1() -> !fir.box> +func @box2() -> !fir.boxchar<2> +func @box3() -> !fir.boxproc<(i32, i32) -> i64> +func @box4() -> !fir.box +func @box5() -> !fir.box> + +// FIR misc. types +// CHECK-LABEL: func @oth1() -> !fir.dims<1> +// CHECK-LABEL: func @oth2() -> !fir.field +// CHECK-LABEL: func @oth3() -> !fir.tdesc> +// CHECK-LABEL: func @oth4() -> !fir.dims<15> +func @oth1() -> !fir.dims<1> +func @oth2() -> !fir.field +func @oth3() -> !fir.tdesc> +func @oth4() -> !fir.dims<15> diff --git a/test-lit/lit.cfg.py b/test-lit/lit.cfg.py index 3ca3c8ad23f7..2dce888c33fd 100644 --- a/test-lit/lit.cfg.py +++ b/test-lit/lit.cfg.py @@ -28,7 +28,7 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = ['.f', '.F', '.ff','.FOR', '.for', '.f77', '.f90', '.F90', '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf', - '.CUF', '.f18', '.F18'] + '.CUF', '.f18', '.F18', '.fir' ] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) @@ -56,6 +56,12 @@ llvm_config.with_environment('PATH', config.flang_tools_dir, append_path=True) llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True) +# For builds with FIR, set path for tco and enable related tests +if config.flang_llvm_tools_dir != "" : + config.available_features.add('fir') + if config.llvm_tools_dir != config.flang_llvm_tools_dir : + llvm_config.with_environment('PATH', config.flang_llvm_tools_dir, append_path=True) + # For each occurrence of a flang tool name, replace it with the full path to # the build directory holding that tool. We explicitly specify the directories # to search to ensure that we get the tools just built and not some random diff --git a/test-lit/lit.site.cfg.py.in b/test-lit/lit.site.cfg.py.in index d00f3856fb41..fe428f9bee06 100644 --- a/test-lit/lit.site.cfg.py.in +++ b/test-lit/lit.site.cfg.py.in @@ -7,6 +7,7 @@ config.flang_obj_root = "@FLANG_BINARY_DIR@" config.flang_src_dir = "@FLANG_SOURCE_DIR@" config.flang_tools_dir = "@FLANG_TOOLS_DIR@" config.flang_intrinsic_modules_dir = "@FLANG_INTRINSIC_MODULES_DIR@" +config.flang_llvm_tools_dir = "@LLVM_RUNTIME_OUTPUT_INTDIR@" config.python_executable = "@PYTHON_EXECUTABLE@" # Support substitution of the tools_dir with user parameters. This is diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 41983a7b2e39..a02679436d14 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,5 +6,7 @@ # #===------------------------------------------------------------------------===# - add_subdirectory(f18) +if(LINK_WITH_FIR) + add_subdirectory(tco) +endif() diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt new file mode 100644 index 000000000000..33f410677705 --- /dev/null +++ b/tools/tco/CMakeLists.txt @@ -0,0 +1,24 @@ +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +set(LIBS + FIRDialect + FIRSupport + ${dialect_libs} + MLIRIR + MLIRLLVMIR + MLIRPass + MLIRStandardToLLVM + MLIRTransforms + MLIRAffineToStandard + MLIRAnalysis + MLIRLoopToStandard + MLIREDSC + MLIRParser + MLIRStandardToLLVM + MLIRSupport + MLIRVectorToLLVM +) + +add_llvm_tool(tco tco.cpp) +llvm_update_compile_flags(tco) +target_link_libraries(tco PRIVATE ${LIBS}) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp new file mode 100644 index 000000000000..0e085fa5633f --- /dev/null +++ b/tools/tco/tco.cpp @@ -0,0 +1,113 @@ +//===- tco.cpp - Tilikum Crossing Opt ---------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This is to be like LLVM's opt program, only for FIR. Such a program is +// required for roundtrip testing, etc. +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +static cl::opt + inputFilename(cl::Positional, cl::desc(""), cl::init("-")); + +static cl::opt outputFilename("o", + cl::desc("Specify output filename"), + cl::value_desc("filename"), + cl::init("-")); + +static cl::opt emitFir("emit-fir", + cl::desc("Parse and pretty-print the input"), + cl::init(false)); + +static void printModuleBody(mlir::ModuleOp mod, raw_ostream &output) { + for (auto &op : mod.getBody()->without_terminator()) + output << op << '\n'; +} + +// compile a .fir file +static int compileFIR() { + // check that there is a file to load + ErrorOr> fileOrErr = + MemoryBuffer::getFileOrSTDIN(inputFilename); + + if (std::error_code EC = fileOrErr.getError()) { + errs() << "Could not open file: " << EC.message() << '\n'; + return 1; + } + + // load the file into a module + SourceMgr sourceMgr; + sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc()); + auto context = std::make_unique(); + auto owningRef = mlir::parseSourceFile(sourceMgr, context.get()); + + if (!owningRef) { + errs() << "Error can't load file " << inputFilename << '\n'; + return 2; + } + if (mlir::failed(owningRef->verify())) { + errs() << "Error verifying FIR module\n"; + return 4; + } + + std::error_code ec; + ToolOutputFile out(outputFilename, ec, sys::fs::OF_None); + + // run passes + mlir::PassManager pm{context.get()}; + mlir::applyPassManagerCLOptions(pm); + if (emitFir) { + // parse the input and pretty-print it back out + // -emit-fir intentionally disables all the passes + } else { + // TODO: Actually add passes when added to FIR code base + // add all the passes + // the user can disable them individually + } + + // run the pass manager + if (mlir::succeeded(pm.run(*owningRef))) { + // passes ran successfully, so keep the output + if (emitFir) + printModuleBody(*owningRef, out.os()); + out.keep(); + return 0; + } + + // pass manager failed + printModuleBody(*owningRef, errs()); + errs() << "\n\nFAILED: " << inputFilename << '\n'; + return 8; +} + +int main(int argc, char **argv) { + fir::registerFIR(); + fir::registerFIRPasses(); + [[maybe_unused]] InitLLVM y(argc, argv); + mlir::registerPassManagerCLOptions(); + mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); + cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n"); + return compileFIR(); +} From e5f369d0863899b601079b2d97e9144c1fd0b71e Mon Sep 17 00:00:00 2001 From: RichBarton-Arm <43278683+RichBarton-Arm@users.noreply.github.com> Date: Thu, 12 Mar 2020 15:15:20 +0000 Subject: [PATCH 079/345] Add initial CODE_OWNERS file (#1066) --- CODE_OWNERS.TXT | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 CODE_OWNERS.TXT diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT new file mode 100644 index 000000000000..a828c068d80e --- /dev/null +++ b/CODE_OWNERS.TXT @@ -0,0 +1,18 @@ +This file is a list of the people responsible for ensuring that patches for a +particular part of Flang are reviewed, either by themself or by someone else. +They are also the gatekeepers for their part of Flang, with the final word on +what goes in or not. + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), snail-mail address +(S) and (I) IRC handle. Each entry should contain at least the (N), (E) and +(D) fields. + +N: Steve Scalpone +E: sscalpone@nvidia.com +D: Anything not covered by others + +N: Eric Schweitz +E: eschweitz@nvidia.com +D: FIR (lib/Fir), Fortran lowering (lib/Lower) From 094f55890775362f093bb62c57c13b8d3436c82e Mon Sep 17 00:00:00 2001 From: David Truby Date: Thu, 12 Mar 2020 16:38:59 +0000 Subject: [PATCH 080/345] Added documentation explaining the use of std::list (#988) --- documentation/Parsing.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/documentation/Parsing.md b/documentation/Parsing.md index 4f584ee5aacd..b961cd630ae1 100644 --- a/documentation/Parsing.md +++ b/documentation/Parsing.md @@ -164,7 +164,12 @@ Parse tree entities should be viewed as values, not objects; their addresses should not be abused for purposes of identification. They are assembled with C++ move semantics during parse tree construction. Their default and copy constructors are deliberately deleted in their -declarations. +declarations. + +The std::list<> data type is used in the parse tree to reliably store pointers +to other relevant entries in the tree. Since the tree lists are moved and +spliced at certain points std::list<> provides the necessary guarantee of the +stability of pointers into these lists. There is a general purpose library by means of which parse trees may be traversed. From 4cdc9f75e2bd81889ccc4e30da8c40b2335f10c0 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 12 Mar 2020 10:25:22 -0700 Subject: [PATCH 081/345] fix typo (#1067) --- CODE_OWNERS.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT index a828c068d80e..7f84d357f9c4 100644 --- a/CODE_OWNERS.TXT +++ b/CODE_OWNERS.TXT @@ -15,4 +15,4 @@ D: Anything not covered by others N: Eric Schweitz E: eschweitz@nvidia.com -D: FIR (lib/Fir), Fortran lowering (lib/Lower) +D: FIR (lib/Optimizer), Fortran lowering (lib/Lower) From d0f9ef7742132a5f9f7173b773aaac934484c7c1 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Thu, 12 Mar 2020 12:52:29 -0500 Subject: [PATCH 082/345] Need for std::min and std::max (#1063) --- runtime/connection.cpp | 1 + runtime/edit-input.cpp | 1 + runtime/edit-output.cpp | 1 + runtime/format-implementation.h | 1 + runtime/tools.cpp | 1 + test/Runtime/testing.cpp | 1 + 6 files changed, 6 insertions(+) diff --git a/runtime/connection.cpp b/runtime/connection.cpp index d206b050aee4..e4a716560a45 100644 --- a/runtime/connection.cpp +++ b/runtime/connection.cpp @@ -8,6 +8,7 @@ #include "connection.h" #include "environment.h" +#include namespace Fortran::runtime::io { diff --git a/runtime/edit-input.cpp b/runtime/edit-input.cpp index 5b7884021067..dd7a804da2f8 100644 --- a/runtime/edit-input.cpp +++ b/runtime/edit-input.cpp @@ -9,6 +9,7 @@ #include "edit-input.h" #include "flang/Common/real.h" #include "flang/Common/uint128.h" +#include namespace Fortran::runtime::io { diff --git a/runtime/edit-output.cpp b/runtime/edit-output.cpp index 47e3b2918788..8206b418f3cd 100644 --- a/runtime/edit-output.cpp +++ b/runtime/edit-output.cpp @@ -9,6 +9,7 @@ #include "edit-output.h" #include "flang/Common/uint128.h" #include "flang/Common/unsigned-const-division.h" +#include namespace Fortran::runtime::io { diff --git a/runtime/format-implementation.h b/runtime/format-implementation.h index 43efd6e1f130..86168d0e823d 100644 --- a/runtime/format-implementation.h +++ b/runtime/format-implementation.h @@ -16,6 +16,7 @@ #include "main.h" #include "flang/Common/format.h" #include "flang/Decimal/decimal.h" +#include #include namespace Fortran::runtime::io { diff --git a/runtime/tools.cpp b/runtime/tools.cpp index f4dffb0027f1..12c792ca29cc 100644 --- a/runtime/tools.cpp +++ b/runtime/tools.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "tools.h" +#include #include namespace Fortran::runtime { diff --git a/test/Runtime/testing.cpp b/test/Runtime/testing.cpp index 5eb86495f0cf..50ee686884cf 100644 --- a/test/Runtime/testing.cpp +++ b/test/Runtime/testing.cpp @@ -1,5 +1,6 @@ #include "testing.h" #include "../../runtime/terminator.h" +#include #include #include #include From 45454587c932c23430f0f1eb81df816dd75de86b Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 13 Mar 2020 04:06:31 -0700 Subject: [PATCH 083/345] Support latest LLVM head with FIR - MLIR SideEffects interface change Include new .td after LLVM changes: https://github.com/llvm/llvm-project/commit/0ddba0bd59c337f16b51a00cb205ecfda46f97fa Tested to work with LLVM head ecd3e678bbb11cf899603037ec2c5949b8d7fa6c from 2020-03-13 01:45 am PCT Backwards compatible with previous known compatible heads at least back to fde9d33f7101bac631b26990d17822474d3a34e9 from 2020-03-10, so need to update LLVM builds if they previously work with FIR. --- include/flang/Optimizer/Dialect/FIROps.td | 1 + 1 file changed, 1 insertion(+) diff --git a/include/flang/Optimizer/Dialect/FIROps.td b/include/flang/Optimizer/Dialect/FIROps.td index ab91c10e00fe..9d503928b25d 100644 --- a/include/flang/Optimizer/Dialect/FIROps.td +++ b/include/flang/Optimizer/Dialect/FIROps.td @@ -15,6 +15,7 @@ #define FIR_DIALECT_FIR_OPS include "mlir/Interfaces/ControlFlowInterfaces.td" +include "mlir/Interfaces/SideEffects.td" def fir_Dialect : Dialect { let name = "fir"; From 4de19d7ba2df892ce4361461841c3997de439532 Mon Sep 17 00:00:00 2001 From: Luke Ireland Date: Fri, 21 Feb 2020 12:28:58 +0000 Subject: [PATCH 084/345] Re-enable semantics/altreturn{02,03} tests These tests were disabled due to https://github.com/flang-compiler/f18/issues/407. Previously these tests caused F18 to crash as the feature was not fully implemented. The altreturn feature is now implemented, so these tests can be re-enabled. altreturn03 tested some negative cases which F18 correctly diagnoses. Modified that test to expect these new error messages. Also make the later cases in the test reachable. These tests can now be ported by the script to lit-style tests. Change-Id: Ib336c10d55068d9a26fc2deb43ad052e74e73456 --- test/Semantics/CMakeLists.txt | 5 ++--- test/Semantics/altreturn03.f90 | 15 +++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt index 7c826d82b961..a24108782ae7 100644 --- a/test/Semantics/CMakeLists.txt +++ b/test/Semantics/CMakeLists.txt @@ -131,9 +131,8 @@ set(ERROR_TESTS deallocate05.f90 coarrays01.f90 altreturn01.f90 -# Issue 407 -# altreturn02.f90 -# altreturn03.f90 + altreturn02.f90 + altreturn03.f90 altreturn04.f90 altreturn05.f90 allocate01.f90 diff --git a/test/Semantics/altreturn03.f90 b/test/Semantics/altreturn03.f90 index 9873fb8357c0..15410e716752 100644 --- a/test/Semantics/altreturn03.f90 +++ b/test/Semantics/altreturn03.f90 @@ -8,11 +8,14 @@ SUBROUTINE TEST (N, *, *) IF ( N .EQ. 1 ) RETURN 1 IF ( N .EQ. 2 ) RETURN 2 IF ( N .EQ. 3 ) RETURN 3 - IF ( N .EQ. 3 ) RETURN N - IF ( N .EQ. 3 ) RETURN N * N - IF ( N .EQ. 3 ) RETURN B(N) - IF ( N .EQ. 3 ) RETURN B - IF ( N .EQ. 3 ) RETURN R - IF ( N .EQ. 3 ) RETURN Z + IF ( N .EQ. 4 ) RETURN N + IF ( N .EQ. 5 ) RETURN N * N + IF ( N .EQ. 6 ) RETURN B(N) + !ERROR: Must be a scalar value, but is a rank-1 array + IF ( N .EQ. 7 ) RETURN B + !ERROR: Must have INTEGER type, but is REAL(4) + IF ( N .EQ. 8 ) RETURN R + !ERROR: Must have INTEGER type, but is COMPLEX(4) + IF ( N .EQ. 9 ) RETURN Z RETURN 2 END From 2bfddbe8f8898551a28c3ea07b7ae508018f8634 Mon Sep 17 00:00:00 2001 From: Luke Ireland Date: Wed, 19 Feb 2020 15:49:33 +0000 Subject: [PATCH 085/345] Create a separate directory for unittests Some of the regression tests are C programs that act as test harnesses for the compiler internals as opposed to being Fortran inputs to test the compiler in action. The former style of tests are analog to LLVM's unittests and will not use the lit framework. Change-Id: I0ff10e23f66ff843e8fff4c35cfb6559b9dab762 --- CMakeLists.txt | 2 +- {test => unittests}/CMakeLists.txt | 1 - {test => unittests}/Decimal/CMakeLists.txt | 0 .../Decimal/quick-sanity-test.cpp | 0 {test => unittests}/Decimal/thorough-test.cpp | 0 {test => unittests}/Evaluate/CMakeLists.txt | 25 ------------------- .../Evaluate/ISO-Fortran-binding.cpp | 0 .../Evaluate/bit-population-count.cpp | 0 {test => unittests}/Evaluate/expression.cpp | 0 {test => unittests}/Evaluate/folding.cpp | 0 {test => unittests}/Evaluate/fp-testing.cpp | 0 {test => unittests}/Evaluate/fp-testing.h | 0 {test => unittests}/Evaluate/integer.cpp | 0 {test => unittests}/Evaluate/intrinsics.cpp | 0 .../Evaluate/leading-zero-bit-count.cpp | 0 {test => unittests}/Evaluate/logical.cpp | 0 {test => unittests}/Evaluate/real.cpp | 0 {test => unittests}/Evaluate/reshape.cpp | 0 {test => unittests}/Evaluate/testing.cpp | 0 {test => unittests}/Evaluate/testing.h | 0 {test => unittests}/Evaluate/uint128.cpp | 0 {test => unittests}/Runtime/CMakeLists.txt | 0 .../Runtime/external-hello.cpp | 0 {test => unittests}/Runtime/format.cpp | 0 {test => unittests}/Runtime/hello.cpp | 0 {test => unittests}/Runtime/list-input.cpp | 0 {test => unittests}/Runtime/testing.cpp | 0 {test => unittests}/Runtime/testing.h | 0 28 files changed, 1 insertion(+), 27 deletions(-) rename {test => unittests}/CMakeLists.txt (94%) rename {test => unittests}/Decimal/CMakeLists.txt (100%) rename {test => unittests}/Decimal/quick-sanity-test.cpp (100%) rename {test => unittests}/Decimal/thorough-test.cpp (100%) rename {test => unittests}/Evaluate/CMakeLists.txt (85%) rename {test => unittests}/Evaluate/ISO-Fortran-binding.cpp (100%) rename {test => unittests}/Evaluate/bit-population-count.cpp (100%) rename {test => unittests}/Evaluate/expression.cpp (100%) rename {test => unittests}/Evaluate/folding.cpp (100%) rename {test => unittests}/Evaluate/fp-testing.cpp (100%) rename {test => unittests}/Evaluate/fp-testing.h (100%) rename {test => unittests}/Evaluate/integer.cpp (100%) rename {test => unittests}/Evaluate/intrinsics.cpp (100%) rename {test => unittests}/Evaluate/leading-zero-bit-count.cpp (100%) rename {test => unittests}/Evaluate/logical.cpp (100%) rename {test => unittests}/Evaluate/real.cpp (100%) rename {test => unittests}/Evaluate/reshape.cpp (100%) rename {test => unittests}/Evaluate/testing.cpp (100%) rename {test => unittests}/Evaluate/testing.h (100%) rename {test => unittests}/Evaluate/uint128.cpp (100%) rename {test => unittests}/Runtime/CMakeLists.txt (100%) rename {test => unittests}/Runtime/external-hello.cpp (100%) rename {test => unittests}/Runtime/format.cpp (100%) rename {test => unittests}/Runtime/hello.cpp (100%) rename {test => unittests}/Runtime/list-input.cpp (100%) rename {test => unittests}/Runtime/testing.cpp (100%) rename {test => unittests}/Runtime/testing.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a03ee4a9a3..f19259df77e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,7 @@ enable_testing() add_subdirectory(include/flang) add_subdirectory(lib) add_subdirectory(runtime) -add_subdirectory(test) +add_subdirectory(unittests) add_subdirectory(tools) add_subdirectory(test-lit) diff --git a/test/CMakeLists.txt b/unittests/CMakeLists.txt similarity index 94% rename from test/CMakeLists.txt rename to unittests/CMakeLists.txt index e83f63c8e030..6d49e6c72b57 100644 --- a/test/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -9,4 +9,3 @@ add_subdirectory(Decimal) add_subdirectory(Evaluate) add_subdirectory(Runtime) -add_subdirectory(Semantics) diff --git a/test/Decimal/CMakeLists.txt b/unittests/Decimal/CMakeLists.txt similarity index 100% rename from test/Decimal/CMakeLists.txt rename to unittests/Decimal/CMakeLists.txt diff --git a/test/Decimal/quick-sanity-test.cpp b/unittests/Decimal/quick-sanity-test.cpp similarity index 100% rename from test/Decimal/quick-sanity-test.cpp rename to unittests/Decimal/quick-sanity-test.cpp diff --git a/test/Decimal/thorough-test.cpp b/unittests/Decimal/thorough-test.cpp similarity index 100% rename from test/Decimal/thorough-test.cpp rename to unittests/Decimal/thorough-test.cpp diff --git a/test/Evaluate/CMakeLists.txt b/unittests/Evaluate/CMakeLists.txt similarity index 85% rename from test/Evaluate/CMakeLists.txt rename to unittests/Evaluate/CMakeLists.txt index b08c1431df70..d874fcb39dbc 100644 --- a/test/Evaluate/CMakeLists.txt +++ b/unittests/Evaluate/CMakeLists.txt @@ -132,18 +132,6 @@ target_link_libraries(folding-test FortranSemantics ) -set(FOLDING_TESTS - folding01.f90 - folding02.f90 - folding03.f90 - folding04.f90 - folding05.f90 - folding06.f90 - folding07.f90 - folding08.f90 - folding09.f90 -) - add_test(Expression expression-test) add_test(Integer integer-test) add_test(Intrinsics intrinsics-test) @@ -152,16 +140,3 @@ add_test(Real real-test) add_test(RESHAPE reshape-test) add_test(ISO-binding ISO-Fortran-binding-test) add_test(folding folding-test) - -set(TEST_LIBPGMATH "-pgmath=false") -if (LIBPGMATH_DIR) - find_library(LIBPGMATH pgmath PATHS ${LIBPGMATH_DIR}) - if(LIBPGMATH) - set(TEST_LIBPGMATH "-pgmath=true") - endif() -endif() - -foreach(test ${FOLDING_TESTS}) - add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_folding.sh - ${test} ${TEST_LIBPGMATH}) -endforeach() diff --git a/test/Evaluate/ISO-Fortran-binding.cpp b/unittests/Evaluate/ISO-Fortran-binding.cpp similarity index 100% rename from test/Evaluate/ISO-Fortran-binding.cpp rename to unittests/Evaluate/ISO-Fortran-binding.cpp diff --git a/test/Evaluate/bit-population-count.cpp b/unittests/Evaluate/bit-population-count.cpp similarity index 100% rename from test/Evaluate/bit-population-count.cpp rename to unittests/Evaluate/bit-population-count.cpp diff --git a/test/Evaluate/expression.cpp b/unittests/Evaluate/expression.cpp similarity index 100% rename from test/Evaluate/expression.cpp rename to unittests/Evaluate/expression.cpp diff --git a/test/Evaluate/folding.cpp b/unittests/Evaluate/folding.cpp similarity index 100% rename from test/Evaluate/folding.cpp rename to unittests/Evaluate/folding.cpp diff --git a/test/Evaluate/fp-testing.cpp b/unittests/Evaluate/fp-testing.cpp similarity index 100% rename from test/Evaluate/fp-testing.cpp rename to unittests/Evaluate/fp-testing.cpp diff --git a/test/Evaluate/fp-testing.h b/unittests/Evaluate/fp-testing.h similarity index 100% rename from test/Evaluate/fp-testing.h rename to unittests/Evaluate/fp-testing.h diff --git a/test/Evaluate/integer.cpp b/unittests/Evaluate/integer.cpp similarity index 100% rename from test/Evaluate/integer.cpp rename to unittests/Evaluate/integer.cpp diff --git a/test/Evaluate/intrinsics.cpp b/unittests/Evaluate/intrinsics.cpp similarity index 100% rename from test/Evaluate/intrinsics.cpp rename to unittests/Evaluate/intrinsics.cpp diff --git a/test/Evaluate/leading-zero-bit-count.cpp b/unittests/Evaluate/leading-zero-bit-count.cpp similarity index 100% rename from test/Evaluate/leading-zero-bit-count.cpp rename to unittests/Evaluate/leading-zero-bit-count.cpp diff --git a/test/Evaluate/logical.cpp b/unittests/Evaluate/logical.cpp similarity index 100% rename from test/Evaluate/logical.cpp rename to unittests/Evaluate/logical.cpp diff --git a/test/Evaluate/real.cpp b/unittests/Evaluate/real.cpp similarity index 100% rename from test/Evaluate/real.cpp rename to unittests/Evaluate/real.cpp diff --git a/test/Evaluate/reshape.cpp b/unittests/Evaluate/reshape.cpp similarity index 100% rename from test/Evaluate/reshape.cpp rename to unittests/Evaluate/reshape.cpp diff --git a/test/Evaluate/testing.cpp b/unittests/Evaluate/testing.cpp similarity index 100% rename from test/Evaluate/testing.cpp rename to unittests/Evaluate/testing.cpp diff --git a/test/Evaluate/testing.h b/unittests/Evaluate/testing.h similarity index 100% rename from test/Evaluate/testing.h rename to unittests/Evaluate/testing.h diff --git a/test/Evaluate/uint128.cpp b/unittests/Evaluate/uint128.cpp similarity index 100% rename from test/Evaluate/uint128.cpp rename to unittests/Evaluate/uint128.cpp diff --git a/test/Runtime/CMakeLists.txt b/unittests/Runtime/CMakeLists.txt similarity index 100% rename from test/Runtime/CMakeLists.txt rename to unittests/Runtime/CMakeLists.txt diff --git a/test/Runtime/external-hello.cpp b/unittests/Runtime/external-hello.cpp similarity index 100% rename from test/Runtime/external-hello.cpp rename to unittests/Runtime/external-hello.cpp diff --git a/test/Runtime/format.cpp b/unittests/Runtime/format.cpp similarity index 100% rename from test/Runtime/format.cpp rename to unittests/Runtime/format.cpp diff --git a/test/Runtime/hello.cpp b/unittests/Runtime/hello.cpp similarity index 100% rename from test/Runtime/hello.cpp rename to unittests/Runtime/hello.cpp diff --git a/test/Runtime/list-input.cpp b/unittests/Runtime/list-input.cpp similarity index 100% rename from test/Runtime/list-input.cpp rename to unittests/Runtime/list-input.cpp diff --git a/test/Runtime/testing.cpp b/unittests/Runtime/testing.cpp similarity index 100% rename from test/Runtime/testing.cpp rename to unittests/Runtime/testing.cpp diff --git a/test/Runtime/testing.h b/unittests/Runtime/testing.h similarity index 100% rename from test/Runtime/testing.h rename to unittests/Runtime/testing.h From 63ec0af9f4c8e736a626d1cd1410f71f4e578aa9 Mon Sep 17 00:00:00 2001 From: Luke Ireland Date: Fri, 14 Feb 2020 14:02:29 +0000 Subject: [PATCH 086/345] Port all remaining regression tests to lit We have re-classified a subset of the regression tests as unit tests and now we are porting the remaining ones. Test discovery and running is now performed by lit rather than ctest. The tests continue to use their original scripts with minor modifications. Most of the changes were mechanical and so scripted. A few changes were made by hand. Details Manual: * modfile09-*.f90 tests depend on being run together as some tests have dependencies on modules created by other tests. This will need separating out when porting away from test_modfile.sh, but for now, added modfile09-*.f90 to the Inputs directory and added a single tests modfile09.f90 to hold the run line. * getdefinition03-a.f90 includes a non-test file getdefinition03-b.f90. Manually edited the former to find the latter in Inputs so as to add only one test. * Same pattern for getsymbols03-{a,b}.f90 Auto: * Remaining tests have a lit RUN line added to them based on the type of test they are. * Failing tests also have an XFAIL line added to them. * Generic tests have their pre-existing RUN lines replaced with the word "EXEC" to avoid conflict with the added lit RUN line. --- {test-lit => test}/Driver/version_test.f90 | 0 test/Evaluate/folding01.f90 | 1 + test/Evaluate/folding02.f90 | 1 + test/Evaluate/folding03.f90 | 1 + test/Evaluate/folding04.f90 | 1 + test/Evaluate/folding05.f90 | Bin 9530 -> 9569 bytes test/Evaluate/folding06.f90 | 1 + test/Evaluate/folding07.f90 | 1 + test/Evaluate/folding08.f90 | 1 + test/Evaluate/folding09.f90 | 1 + {test-lit => test}/Fir/fir-ops.fir | 0 {test-lit => test}/Fir/fir-types.fir | 0 {test-lit => test}/Lower/pre-fir-tree01.f90 | 0 {test-lit => test}/Lower/pre-fir-tree02.f90 | 0 {test-lit => test}/Lower/pre-fir-tree03.f90 | 0 {test-lit => test}/Lower/pre-fir-tree04.f90 | 0 .../{ => Inputs}/getdefinition03-b.f90 | 0 .../Semantics/{ => Inputs}/getsymbols02-a.f90 | 2 +- .../Semantics/{ => Inputs}/getsymbols02-b.f90 | 2 +- .../Semantics/{ => Inputs}/getsymbols02-c.f90 | 2 +- .../Semantics/{ => Inputs}/getsymbols03-b.f90 | 0 .../Semantics/Inputs/mod-file-changed.f90 | 0 .../Semantics/Inputs/mod-file-unchanged.f90 | 0 test/Semantics/{ => Inputs}/modfile09-a.f90 | 0 test/Semantics/{ => Inputs}/modfile09-b.f90 | 0 test/Semantics/{ => Inputs}/modfile09-c.f90 | 0 test/Semantics/{ => Inputs}/modfile09-d.f90 | 0 test/Semantics/allocate01.f90 | 1 + test/Semantics/allocate02.f90 | 1 + test/Semantics/allocate03.f90 | 1 + test/Semantics/allocate04.f90 | 1 + test/Semantics/allocate05.f90 | 1 + test/Semantics/allocate06.f90 | 1 + test/Semantics/allocate07.f90 | 1 + test/Semantics/allocate08.f90 | 1 + test/Semantics/allocate09.f90 | 1 + test/Semantics/allocate10.f90 | 1 + test/Semantics/allocate11.f90 | 1 + test/Semantics/allocate12.f90 | 1 + test/Semantics/allocate13.f90 | 1 + test/Semantics/altreturn01.f90 | 1 + test/Semantics/altreturn02.f90 | 1 + test/Semantics/altreturn03.f90 | 1 + test/Semantics/altreturn04.f90 | 1 + test/Semantics/altreturn05.f90 | 1 + test/Semantics/assign01.f90 | 1 + test/Semantics/assign02.f90 | 1 + test/Semantics/assign03.f90 | 1 + test/Semantics/assign04.f90 | 1 + test/Semantics/bad-forward-type.f90 | 1 + test/Semantics/bindings01.f90 | 1 + test/Semantics/block-data01.f90 | 1 + test/Semantics/blockconstruct01.f90 | 1 + test/Semantics/blockconstruct02.f90 | 1 + test/Semantics/blockconstruct03.f90 | 1 + test/Semantics/c_f_pointer.f90 | 1 + test/Semantics/call01.f90 | 1 + test/Semantics/call02.f90 | 1 + test/Semantics/call03.f90 | 1 + test/Semantics/call04.f90 | 1 + test/Semantics/call05.f90 | 1 + test/Semantics/call06.f90 | 1 + test/Semantics/call07.f90 | 1 + test/Semantics/call08.f90 | 1 + test/Semantics/call09.f90 | 1 + test/Semantics/call10.f90 | 1 + test/Semantics/call11.f90 | 1 + test/Semantics/call12.f90 | 1 + test/Semantics/call13.f90 | 1 + test/Semantics/call14.f90 | 1 + test/Semantics/call15.f90 | 1 + test/Semantics/canondo01.f90 | 3 ++- test/Semantics/canondo02.f90 | 3 ++- test/Semantics/canondo03.f90 | 3 ++- test/Semantics/canondo04.f90 | 3 ++- test/Semantics/canondo05.f90 | 5 +++-- test/Semantics/canondo06.f90 | 3 ++- test/Semantics/canondo07.f90 | 3 ++- test/Semantics/canondo08.f90 | 3 ++- test/Semantics/canondo09.f90 | 3 ++- test/Semantics/canondo10.f90 | 3 ++- test/Semantics/canondo11.f90 | 3 ++- test/Semantics/canondo12.f90 | 3 ++- test/Semantics/canondo13.f90 | 3 ++- test/Semantics/canondo14.f90 | 3 ++- test/Semantics/canondo15.f90 | 3 ++- test/Semantics/canondo16.f90 | 3 ++- test/Semantics/canondo17.f90 | 3 ++- test/Semantics/canondo18.f90 | 3 ++- test/Semantics/canondo19.f90 | 3 ++- test/Semantics/coarrays01.f90 | 1 + test/Semantics/complex01.f90 | 1 + test/Semantics/computed-goto01.f90 | 1 + test/Semantics/computed-goto02.f90 | 1 + test/Semantics/critical01.f90 | 1 + test/Semantics/critical02.f90 | 1 + test/Semantics/critical03.f90 | 1 + test/Semantics/critical04.f90 | 3 ++- test/Semantics/data01.f90 | 1 + test/Semantics/data02.f90 | 1 + test/Semantics/deallocate01.f90 | 1 + test/Semantics/deallocate04.f90 | 1 + test/Semantics/deallocate05.f90 | 1 + test/Semantics/doconcurrent01.f90 | 1 + test/Semantics/doconcurrent02.f90 | 3 ++- test/Semantics/doconcurrent03.f90 | 3 ++- test/Semantics/doconcurrent04.f90 | 3 ++- test/Semantics/doconcurrent05.f90 | 1 + test/Semantics/doconcurrent06.f90 | 1 + test/Semantics/doconcurrent07.f90 | 3 ++- test/Semantics/doconcurrent08.f90 | 1 + test/Semantics/dosemantics01.f90 | 1 + test/Semantics/dosemantics02.f90 | 1 + test/Semantics/dosemantics03.f90 | 1 + test/Semantics/dosemantics04.f90 | 1 + test/Semantics/dosemantics05.f90 | 1 + test/Semantics/dosemantics06.f90 | 1 + test/Semantics/dosemantics07.f90 | 1 + test/Semantics/dosemantics08.f90 | 1 + test/Semantics/dosemantics09.f90 | 1 + test/Semantics/dosemantics10.f90 | 1 + test/Semantics/dosemantics11.f90 | 1 + test/Semantics/dosemantics12.f90 | 1 + test/Semantics/equivalence01.f90 | 1 + test/Semantics/expr-errors01.f90 | 1 + test/Semantics/expr-errors02.f90 | 1 + test/Semantics/forall01.f90 | 1 + test/Semantics/getdefinition01.f90 | 14 +++++++------- test/Semantics/getdefinition02.f | 10 +++++----- test/Semantics/getdefinition03-a.f90 | 10 +++++----- test/Semantics/getdefinition04.f90 | 4 ++-- test/Semantics/getdefinition05.f90 | 8 ++++---- test/Semantics/getsymbols01.f90 | 4 ++-- test/Semantics/getsymbols02.f90 | 1 + test/Semantics/getsymbols03-a.f90 | 6 +++--- test/Semantics/getsymbols04.f90 | 4 ++-- test/Semantics/getsymbols05.f90 | 4 ++-- test/Semantics/if_arith01.f90 | 1 + test/Semantics/if_arith02.f90 | 1 + test/Semantics/if_arith03.f90 | 1 + test/Semantics/if_arith04.f90 | 1 + test/Semantics/if_construct01.f90 | 1 + test/Semantics/if_construct02.f90 | 1 + test/Semantics/if_stmt01.f90 | 1 + test/Semantics/if_stmt02.f90 | 1 + test/Semantics/if_stmt03.f90 | 1 + test/Semantics/implicit01.f90 | 1 + test/Semantics/implicit02.f90 | 1 + test/Semantics/implicit03.f90 | 1 + test/Semantics/implicit04.f90 | 1 + test/Semantics/implicit05.f90 | 1 + test/Semantics/implicit06.f90 | 1 + test/Semantics/implicit07.f90 | 1 + test/Semantics/implicit08.f90 | 1 + test/Semantics/init01.f90 | 1 + test/Semantics/int-literals.f90 | 1 + test/Semantics/io01.f90 | 1 + test/Semantics/io02.f90 | 1 + test/Semantics/io03.f90 | 1 + test/Semantics/io04.f90 | 1 + test/Semantics/io05.f90 | 1 + test/Semantics/io06.f90 | 1 + test/Semantics/io07.f90 | 1 + test/Semantics/io08.f90 | 1 + test/Semantics/io09.f90 | 1 + test/Semantics/io10.f90 | 1 + test/Semantics/kinds01.f90 | 1 + test/Semantics/kinds02.f90 | 1 + test/Semantics/kinds03.f90 | 1 + test/Semantics/kinds04.f90 | 1 + test/Semantics/label01.F90 | 3 ++- test/Semantics/label02.f90 | 3 ++- test/Semantics/label03.f90 | 3 ++- test/Semantics/label04.f90 | 3 ++- test/Semantics/label05.f90 | 3 ++- test/Semantics/label06.f90 | 3 ++- test/Semantics/label07.f90 | 3 ++- test/Semantics/label08.f90 | 3 ++- test/Semantics/label09.f90 | 3 ++- test/Semantics/label10.f90 | 3 ++- test/Semantics/label11.f90 | 3 ++- test/Semantics/label12.f90 | 3 ++- test/Semantics/label13.f90 | 3 ++- test/Semantics/label14.f90 | 3 ++- test/Semantics/misc-declarations.f90 | 1 + .../Semantics/mod-file-rewriter.f90 | 0 test/Semantics/modfile01.f90 | 1 + test/Semantics/modfile02.f90 | 1 + test/Semantics/modfile03.f90 | 1 + test/Semantics/modfile04.f90 | 1 + test/Semantics/modfile05.f90 | 1 + test/Semantics/modfile06.f90 | 1 + test/Semantics/modfile07.f90 | 1 + test/Semantics/modfile08.f90 | 1 + test/Semantics/modfile09.f90 | 1 + test/Semantics/modfile10.f90 | 1 + test/Semantics/modfile11.f90 | 1 + test/Semantics/modfile12.f90 | 1 + test/Semantics/modfile13.f90 | 1 + test/Semantics/modfile14.f90 | 1 + test/Semantics/modfile15.f90 | 1 + test/Semantics/modfile16.f90 | 1 + test/Semantics/modfile17.f90 | 1 + test/Semantics/modfile18.f90 | 1 + test/Semantics/modfile19.f90 | 1 + test/Semantics/modfile20.f90 | 1 + test/Semantics/modfile21.f90 | 1 + test/Semantics/modfile22.f90 | 1 + test/Semantics/modfile23.f90 | 1 + test/Semantics/modfile24.f90 | 1 + test/Semantics/modfile25.f90 | 1 + test/Semantics/modfile26.f90 | 1 + test/Semantics/modfile27.f90 | 1 + test/Semantics/modfile28.f90 | 1 + test/Semantics/modfile29.f90 | 1 + test/Semantics/modfile30.f90 | 1 + test/Semantics/modfile31.f90 | 1 + test/Semantics/modfile32.f90 | 1 + test/Semantics/modfile33.f90 | 1 + test/Semantics/modfile34.f90 | 1 + test/Semantics/modfile35.f90 | 1 + test/Semantics/namelist01.f90 | 1 + test/Semantics/null01.f90 | 1 + test/Semantics/nullify01.f90 | 1 + test/Semantics/nullify02.f90 | 1 + test/Semantics/omp-atomic.f90 | 1 + test/Semantics/omp-clause-validity01.f90 | 1 + test/Semantics/omp-declarative-directive.f90 | 1 + test/Semantics/omp-device-constructs.f90 | 1 + test/Semantics/omp-loop-association.f90 | 1 + test/Semantics/omp-nested01.f90 | 2 ++ test/Semantics/omp-resolve01.f90 | 1 + test/Semantics/omp-resolve02.f90 | 1 + test/Semantics/omp-resolve03.f90 | 1 + test/Semantics/omp-resolve04.f90 | 1 + test/Semantics/omp-resolve05.f90 | 1 + test/Semantics/omp-symbol01.f90 | 1 + test/Semantics/omp-symbol02.f90 | 1 + test/Semantics/omp-symbol03.f90 | 1 + test/Semantics/omp-symbol04.f90 | 1 + test/Semantics/omp-symbol05.f90 | 1 + test/Semantics/omp-symbol06.f90 | 1 + test/Semantics/omp-symbol07.f90 | 1 + test/Semantics/omp-symbol08.f90 | 1 + test/Semantics/procinterface01.f90 | 1 + test/Semantics/resolve01.f90 | 1 + test/Semantics/resolve02.f90 | 1 + test/Semantics/resolve03.f90 | 1 + test/Semantics/resolve04.f90 | 1 + test/Semantics/resolve05.f90 | 1 + test/Semantics/resolve06.f90 | 1 + test/Semantics/resolve07.f90 | 1 + test/Semantics/resolve08.f90 | 1 + test/Semantics/resolve09.f90 | 1 + test/Semantics/resolve10.f90 | 1 + test/Semantics/resolve11.f90 | 1 + test/Semantics/resolve12.f90 | 1 + test/Semantics/resolve13.f90 | 1 + test/Semantics/resolve14.f90 | 1 + test/Semantics/resolve15.f90 | 1 + test/Semantics/resolve16.f90 | 1 + test/Semantics/resolve17.f90 | 1 + test/Semantics/resolve18.f90 | 1 + test/Semantics/resolve19.f90 | 1 + test/Semantics/resolve20.f90 | 1 + test/Semantics/resolve21.f90 | 1 + test/Semantics/resolve22.f90 | 1 + test/Semantics/resolve23.f90 | 1 + test/Semantics/resolve24.f90 | 1 + test/Semantics/resolve25.f90 | 1 + test/Semantics/resolve26.f90 | 1 + test/Semantics/resolve27.f90 | 1 + test/Semantics/resolve28.f90 | 1 + test/Semantics/resolve29.f90 | 1 + test/Semantics/resolve30.f90 | 1 + test/Semantics/resolve31.f90 | 1 + test/Semantics/resolve32.f90 | 1 + test/Semantics/resolve33.f90 | 1 + test/Semantics/resolve34.f90 | 1 + test/Semantics/resolve35.f90 | 1 + test/Semantics/resolve36.f90 | 1 + test/Semantics/resolve37.f90 | 1 + test/Semantics/resolve38.f90 | 1 + test/Semantics/resolve39.f90 | 1 + test/Semantics/resolve40.f90 | 1 + test/Semantics/resolve41.f90 | 1 + test/Semantics/resolve42.f90 | 1 + test/Semantics/resolve43.f90 | 1 + test/Semantics/resolve44.f90 | 1 + test/Semantics/resolve45.f90 | 1 + test/Semantics/resolve46.f90 | 1 + test/Semantics/resolve47.f90 | 1 + test/Semantics/resolve48.f90 | 1 + test/Semantics/resolve49.f90 | 1 + test/Semantics/resolve50.f90 | 1 + test/Semantics/resolve51.f90 | 1 + test/Semantics/resolve52.f90 | 1 + test/Semantics/resolve53.f90 | 1 + test/Semantics/resolve54.f90 | 1 + test/Semantics/resolve55.f90 | 1 + test/Semantics/resolve56.f90 | 1 + test/Semantics/resolve57.f90 | 1 + test/Semantics/resolve58.f90 | 1 + test/Semantics/resolve59.f90 | 1 + test/Semantics/resolve60.f90 | 1 + test/Semantics/resolve61.f90 | 1 + test/Semantics/resolve62.f90 | 1 + test/Semantics/resolve63.f90 | 1 + test/Semantics/resolve64.f90 | 1 + test/Semantics/resolve65.f90 | 1 + test/Semantics/resolve66.f90 | 1 + test/Semantics/resolve67.f90 | 1 + test/Semantics/resolve68.f90 | 1 + test/Semantics/resolve69.f90 | 1 + test/Semantics/resolve70.f90 | 1 + test/Semantics/resolve71.f90 | 1 + test/Semantics/resolve72.f90 | 1 + test/Semantics/resolve73.f90 | 1 + test/Semantics/resolve74.f90 | 1 + test/Semantics/resolve75.f90 | 1 + test/Semantics/separate-module-procs.f90 | 1 + test/Semantics/stop01.f90 | 1 + test/Semantics/structconst01.f90 | 1 + test/Semantics/structconst02.f90 | 1 + test/Semantics/structconst03.f90 | 1 + test/Semantics/structconst04.f90 | 1 + test/Semantics/symbol01.f90 | 1 + test/Semantics/symbol02.f90 | 1 + test/Semantics/symbol03.f90 | 1 + test/Semantics/symbol05.f90 | 1 + test/Semantics/symbol06.f90 | 1 + test/Semantics/symbol07.f90 | 1 + test/Semantics/symbol08.f90 | 1 + test/Semantics/symbol09.f90 | 1 + test/Semantics/symbol10.f90 | 1 + test/Semantics/symbol11.f90 | 1 + test/Semantics/symbol12.f90 | 1 + test/Semantics/symbol13.f90 | 1 + test/Semantics/symbol14.f90 | 1 + test/Semantics/symbol15.f90 | 1 + test/Semantics/symbol16.f90 | 1 + test/Semantics/symbol17.f90 | 1 + 342 files changed, 388 insertions(+), 74 deletions(-) rename {test-lit => test}/Driver/version_test.f90 (100%) rename {test-lit => test}/Fir/fir-ops.fir (100%) rename {test-lit => test}/Fir/fir-types.fir (100%) rename {test-lit => test}/Lower/pre-fir-tree01.f90 (100%) rename {test-lit => test}/Lower/pre-fir-tree02.f90 (100%) rename {test-lit => test}/Lower/pre-fir-tree03.f90 (100%) rename {test-lit => test}/Lower/pre-fir-tree04.f90 (100%) rename test/Semantics/{ => Inputs}/getdefinition03-b.f90 (100%) rename test/Semantics/{ => Inputs}/getsymbols02-a.f90 (83%) rename test/Semantics/{ => Inputs}/getsymbols02-b.f90 (86%) rename test/Semantics/{ => Inputs}/getsymbols02-c.f90 (70%) rename test/Semantics/{ => Inputs}/getsymbols03-b.f90 (100%) rename {test-lit => test}/Semantics/Inputs/mod-file-changed.f90 (100%) rename {test-lit => test}/Semantics/Inputs/mod-file-unchanged.f90 (100%) rename test/Semantics/{ => Inputs}/modfile09-a.f90 (100%) rename test/Semantics/{ => Inputs}/modfile09-b.f90 (100%) rename test/Semantics/{ => Inputs}/modfile09-c.f90 (100%) rename test/Semantics/{ => Inputs}/modfile09-d.f90 (100%) create mode 100644 test/Semantics/getsymbols02.f90 rename {test-lit => test}/Semantics/mod-file-rewriter.f90 (100%) create mode 100644 test/Semantics/modfile09.f90 diff --git a/test-lit/Driver/version_test.f90 b/test/Driver/version_test.f90 similarity index 100% rename from test-lit/Driver/version_test.f90 rename to test/Driver/version_test.f90 diff --git a/test/Evaluate/folding01.f90 b/test/Evaluate/folding01.f90 index 81f59c59a277..8a75a819ff81 100644 --- a/test/Evaluate/folding01.f90 +++ b/test/Evaluate/folding01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test intrinsic operation folding diff --git a/test/Evaluate/folding02.f90 b/test/Evaluate/folding02.f90 index 47c7f6373e4f..b69ff87b5c20 100644 --- a/test/Evaluate/folding02.f90 +++ b/test/Evaluate/folding02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Check intrinsic function folding with host runtime library module m diff --git a/test/Evaluate/folding03.f90 b/test/Evaluate/folding03.f90 index 56a6adffb824..c5e26faf8327 100644 --- a/test/Evaluate/folding03.f90 +++ b/test/Evaluate/folding03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test operation folding edge case (both expected value and messages) ! These tests make assumptions regarding real(4) and integer(4) extrema. diff --git a/test/Evaluate/folding04.f90 b/test/Evaluate/folding04.f90 index 3ced207a742c..a0e207b375b7 100644 --- a/test/Evaluate/folding04.f90 +++ b/test/Evaluate/folding04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test intrinsic function folding edge case (both expected value and messages) ! These tests make assumptions regarding real(4) extrema. diff --git a/test/Evaluate/folding05.f90 b/test/Evaluate/folding05.f90 index 5e5e5c576976724256e3c150f56be5df2efaf023..79635e392d7717c1d1a8159c6548e7413f4712ff 100644 GIT binary patch delta 47 zcmdnx_0UUQQ6VVQ&q_fxSidB-xFkL;KPM$KFI}%VLqWAzK{YKWF)v*~wS;Sfl_~&# C&JS<^ delta 8 PcmaFpwaaUxx|J#b61D?p diff --git a/test/Evaluate/folding06.f90 b/test/Evaluate/folding06.f90 index c591989488c3..42dc70d5165e 100644 --- a/test/Evaluate/folding06.f90 +++ b/test/Evaluate/folding06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test transformational intrinsic function folding module m diff --git a/test/Evaluate/folding07.f90 b/test/Evaluate/folding07.f90 index b7e13eb027a3..9c9c0a40ed61 100644 --- a/test/Evaluate/folding07.f90 +++ b/test/Evaluate/folding07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test numeric model inquiry intrinsics module m diff --git a/test/Evaluate/folding08.f90 b/test/Evaluate/folding08.f90 index a5546b9cf2c4..67f435a99f31 100644 --- a/test/Evaluate/folding08.f90 +++ b/test/Evaluate/folding08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test folding of LBOUND and UBOUND module m diff --git a/test/Evaluate/folding09.f90 b/test/Evaluate/folding09.f90 index af89aecf951a..a7510604acca 100644 --- a/test/Evaluate/folding09.f90 +++ b/test/Evaluate/folding09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_folding.sh %s %flang %t ! Test folding of IS_CONTIGUOUS on simply contiguous items (9.5.4) ! When IS_CONTIGUOUS() is constant, it's .TRUE. diff --git a/test-lit/Fir/fir-ops.fir b/test/Fir/fir-ops.fir similarity index 100% rename from test-lit/Fir/fir-ops.fir rename to test/Fir/fir-ops.fir diff --git a/test-lit/Fir/fir-types.fir b/test/Fir/fir-types.fir similarity index 100% rename from test-lit/Fir/fir-types.fir rename to test/Fir/fir-types.fir diff --git a/test-lit/Lower/pre-fir-tree01.f90 b/test/Lower/pre-fir-tree01.f90 similarity index 100% rename from test-lit/Lower/pre-fir-tree01.f90 rename to test/Lower/pre-fir-tree01.f90 diff --git a/test-lit/Lower/pre-fir-tree02.f90 b/test/Lower/pre-fir-tree02.f90 similarity index 100% rename from test-lit/Lower/pre-fir-tree02.f90 rename to test/Lower/pre-fir-tree02.f90 diff --git a/test-lit/Lower/pre-fir-tree03.f90 b/test/Lower/pre-fir-tree03.f90 similarity index 100% rename from test-lit/Lower/pre-fir-tree03.f90 rename to test/Lower/pre-fir-tree03.f90 diff --git a/test-lit/Lower/pre-fir-tree04.f90 b/test/Lower/pre-fir-tree04.f90 similarity index 100% rename from test-lit/Lower/pre-fir-tree04.f90 rename to test/Lower/pre-fir-tree04.f90 diff --git a/test/Semantics/getdefinition03-b.f90 b/test/Semantics/Inputs/getdefinition03-b.f90 similarity index 100% rename from test/Semantics/getdefinition03-b.f90 rename to test/Semantics/Inputs/getdefinition03-b.f90 diff --git a/test/Semantics/getsymbols02-a.f90 b/test/Semantics/Inputs/getsymbols02-a.f90 similarity index 83% rename from test/Semantics/getsymbols02-a.f90 rename to test/Semantics/Inputs/getsymbols02-a.f90 index b9d75fde50af..04786c0bea6d 100644 --- a/test/Semantics/getsymbols02-a.f90 +++ b/test/Semantics/Inputs/getsymbols02-a.f90 @@ -1,4 +1,4 @@ -! RUN: ${F18} -fparse-only %s +! EXEC: ${F18} -fparse-only %s module mm2a implicit none diff --git a/test/Semantics/getsymbols02-b.f90 b/test/Semantics/Inputs/getsymbols02-b.f90 similarity index 86% rename from test/Semantics/getsymbols02-b.f90 rename to test/Semantics/Inputs/getsymbols02-b.f90 index 7ed4cbe0d894..1e6bb8b03a6c 100644 --- a/test/Semantics/getsymbols02-b.f90 +++ b/test/Semantics/Inputs/getsymbols02-b.f90 @@ -1,4 +1,4 @@ -! RUN: ${F18} -fparse-only %s +! EXEC: ${F18} -fparse-only %s module mm2b use mm2a diff --git a/test/Semantics/getsymbols02-c.f90 b/test/Semantics/Inputs/getsymbols02-c.f90 similarity index 70% rename from test/Semantics/getsymbols02-c.f90 rename to test/Semantics/Inputs/getsymbols02-c.f90 index cb66680906bb..52a210b91699 100644 --- a/test/Semantics/getsymbols02-c.f90 +++ b/test/Semantics/Inputs/getsymbols02-c.f90 @@ -7,6 +7,6 @@ PROGRAM helloworld i = callget5() ENDPROGRAM -! RUN: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s ! CHECK:callget5: mm2b ! CHECK:get5: mm2a diff --git a/test/Semantics/getsymbols03-b.f90 b/test/Semantics/Inputs/getsymbols03-b.f90 similarity index 100% rename from test/Semantics/getsymbols03-b.f90 rename to test/Semantics/Inputs/getsymbols03-b.f90 diff --git a/test-lit/Semantics/Inputs/mod-file-changed.f90 b/test/Semantics/Inputs/mod-file-changed.f90 similarity index 100% rename from test-lit/Semantics/Inputs/mod-file-changed.f90 rename to test/Semantics/Inputs/mod-file-changed.f90 diff --git a/test-lit/Semantics/Inputs/mod-file-unchanged.f90 b/test/Semantics/Inputs/mod-file-unchanged.f90 similarity index 100% rename from test-lit/Semantics/Inputs/mod-file-unchanged.f90 rename to test/Semantics/Inputs/mod-file-unchanged.f90 diff --git a/test/Semantics/modfile09-a.f90 b/test/Semantics/Inputs/modfile09-a.f90 similarity index 100% rename from test/Semantics/modfile09-a.f90 rename to test/Semantics/Inputs/modfile09-a.f90 diff --git a/test/Semantics/modfile09-b.f90 b/test/Semantics/Inputs/modfile09-b.f90 similarity index 100% rename from test/Semantics/modfile09-b.f90 rename to test/Semantics/Inputs/modfile09-b.f90 diff --git a/test/Semantics/modfile09-c.f90 b/test/Semantics/Inputs/modfile09-c.f90 similarity index 100% rename from test/Semantics/modfile09-c.f90 rename to test/Semantics/Inputs/modfile09-c.f90 diff --git a/test/Semantics/modfile09-d.f90 b/test/Semantics/Inputs/modfile09-d.f90 similarity index 100% rename from test/Semantics/modfile09-d.f90 rename to test/Semantics/Inputs/modfile09-d.f90 diff --git a/test/Semantics/allocate01.f90 b/test/Semantics/allocate01.f90 index 6944e2b30090..0948230a3ea2 100644 --- a/test/Semantics/allocate01.f90 +++ b/test/Semantics/allocate01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements ! Creating a symbol that allocate should accept diff --git a/test/Semantics/allocate02.f90 b/test/Semantics/allocate02.f90 index 7f1693849b5f..13a68e811a55 100644 --- a/test/Semantics/allocate02.f90 +++ b/test/Semantics/allocate02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate03.f90 b/test/Semantics/allocate03.f90 index f86b44ceca2e..63598f0786df 100644 --- a/test/Semantics/allocate03.f90 +++ b/test/Semantics/allocate03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C933_a(b1, ca3, ca4, cp3, cp3mold, cp4, cp7, cp8, bsrc) diff --git a/test/Semantics/allocate04.f90 b/test/Semantics/allocate04.f90 index 3b7ce25bf00e..40e7562938df 100644 --- a/test/Semantics/allocate04.f90 +++ b/test/Semantics/allocate04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate05.f90 b/test/Semantics/allocate05.f90 index 5d3f2b58160f..84814b674735 100644 --- a/test/Semantics/allocate05.f90 +++ b/test/Semantics/allocate05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate06.f90 b/test/Semantics/allocate06.f90 index 606f9cec32fe..1de258ccfb46 100644 --- a/test/Semantics/allocate06.f90 +++ b/test/Semantics/allocate06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate07.f90 b/test/Semantics/allocate07.f90 index 3641ae62a3c6..14077a24013e 100644 --- a/test/Semantics/allocate07.f90 +++ b/test/Semantics/allocate07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C936(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) diff --git a/test/Semantics/allocate08.f90 b/test/Semantics/allocate08.f90 index 732ce270a78b..3e235fcc9cdc 100644 --- a/test/Semantics/allocate08.f90 +++ b/test/Semantics/allocate08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C945_a(srca, srcb, srcc, src_complex, src_logical, & diff --git a/test/Semantics/allocate09.f90 b/test/Semantics/allocate09.f90 index e47cd8134b49..61046fb13ce2 100644 --- a/test/Semantics/allocate09.f90 +++ b/test/Semantics/allocate09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C946(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) diff --git a/test/Semantics/allocate10.f90 b/test/Semantics/allocate10.f90 index b3e5d77da315..c15dc57b4472 100644 --- a/test/Semantics/allocate10.f90 +++ b/test/Semantics/allocate10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements !TODO: mixing expr and source-expr? diff --git a/test/Semantics/allocate11.f90 b/test/Semantics/allocate11.f90 index 45128ac8d69d..b883edc4980a 100644 --- a/test/Semantics/allocate11.f90 +++ b/test/Semantics/allocate11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements ! TODO: Function Pointer in allocate and derived types! diff --git a/test/Semantics/allocate12.f90 b/test/Semantics/allocate12.f90 index 8e46b6ddf3d0..41de8edc83ed 100644 --- a/test/Semantics/allocate12.f90 +++ b/test/Semantics/allocate12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C941_C942b_C950(xsrc, x1, a2, b2, cx1, ca2, cb1, cb2, c1) diff --git a/test/Semantics/allocate13.f90 b/test/Semantics/allocate13.f90 index 5e01c3853748..b7010f5b0c89 100644 --- a/test/Semantics/allocate13.f90 +++ b/test/Semantics/allocate13.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements module not_iso_fortran_env diff --git a/test/Semantics/altreturn01.f90 b/test/Semantics/altreturn01.f90 index b227d15b4ba1..0449ff774c36 100644 --- a/test/Semantics/altreturn01.f90 +++ b/test/Semantics/altreturn01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check calls with alt returns CALL TEST (N, *100, *200 ) diff --git a/test/Semantics/altreturn02.f90 b/test/Semantics/altreturn02.f90 index ab59a3246588..74ff96933a83 100644 --- a/test/Semantics/altreturn02.f90 +++ b/test/Semantics/altreturn02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check subroutine with alt return SUBROUTINE TEST (N, *, *) diff --git a/test/Semantics/altreturn03.f90 b/test/Semantics/altreturn03.f90 index 15410e716752..73a63860efc7 100644 --- a/test/Semantics/altreturn03.f90 +++ b/test/Semantics/altreturn03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for various alt return error conditions SUBROUTINE TEST (N, *, *) diff --git a/test/Semantics/altreturn04.f90 b/test/Semantics/altreturn04.f90 index 5e930c781c2b..e3714fb92223 100644 --- a/test/Semantics/altreturn04.f90 +++ b/test/Semantics/altreturn04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Functions cannot use alt return REAL FUNCTION altreturn01(X) diff --git a/test/Semantics/altreturn05.f90 b/test/Semantics/altreturn05.f90 index 6669942d00cc..cbd222cba9e7 100644 --- a/test/Semantics/altreturn05.f90 +++ b/test/Semantics/altreturn05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test extension: RETURN from main program return !ok diff --git a/test/Semantics/assign01.f90 b/test/Semantics/assign01.f90 index b125da87ad22..bd41a5b5cc9f 100644 --- a/test/Semantics/assign01.f90 +++ b/test/Semantics/assign01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! 10.2.3.1(2) All masks and LHS of assignments in a WHERE must conform subroutine s1 diff --git a/test/Semantics/assign02.f90 b/test/Semantics/assign02.f90 index 5b3fa4f6da2b..e97be64d6aab 100644 --- a/test/Semantics/assign02.f90 +++ b/test/Semantics/assign02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Pointer assignment constraints 10.2.2.2 module m1 diff --git a/test/Semantics/assign03.f90 b/test/Semantics/assign03.f90 index 6127de22de4f..5b9fe269addc 100644 --- a/test/Semantics/assign03.f90 +++ b/test/Semantics/assign03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Pointer assignment constraints 10.2.2.2 (see also assign02.f90) module m diff --git a/test/Semantics/assign04.f90 b/test/Semantics/assign04.f90 index b4214a4766f2..f8798138c15c 100644 --- a/test/Semantics/assign04.f90 +++ b/test/Semantics/assign04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! 9.4.5 subroutine s1 type :: t(k, l) diff --git a/test/Semantics/bad-forward-type.f90 b/test/Semantics/bad-forward-type.f90 index a8f7a4c64af6..62ad9d4b2b4c 100644 --- a/test/Semantics/bad-forward-type.f90 +++ b/test/Semantics/bad-forward-type.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Forward references to derived types (error cases) !ERROR: The derived type 'undef' was forward-referenced but not defined diff --git a/test/Semantics/bindings01.f90 b/test/Semantics/bindings01.f90 index 72cab0832388..54aaacd2e9f8 100644 --- a/test/Semantics/bindings01.f90 +++ b/test/Semantics/bindings01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Confirm enforcement of constraints and restrictions in 7.5.7.3 ! and C779-C785. diff --git a/test/Semantics/block-data01.f90 b/test/Semantics/block-data01.f90 index 5abd0999c010..164709118f6f 100644 --- a/test/Semantics/block-data01.f90 +++ b/test/Semantics/block-data01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test BLOCK DATA subprogram (14.3) block data foo !ERROR: IMPORT is not allowed in a BLOCK DATA subprogram diff --git a/test/Semantics/blockconstruct01.f90 b/test/Semantics/blockconstruct01.f90 index 727e6ab05eeb..7f7eec5b56c3 100644 --- a/test/Semantics/blockconstruct01.f90 +++ b/test/Semantics/blockconstruct01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1107 -- COMMON, EQUIVALENCE, INTENT, NAMELIST, OPTIONAL, VALUE or ! STATEMENT FUNCTIONS not allow in specification part diff --git a/test/Semantics/blockconstruct02.f90 b/test/Semantics/blockconstruct02.f90 index eb7203052fcf..2a1a95f312bf 100644 --- a/test/Semantics/blockconstruct02.f90 +++ b/test/Semantics/blockconstruct02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1108 -- Save statement in a BLOCK construct shall not conatin a ! saved-entity-list that does not specify a common-block-name diff --git a/test/Semantics/blockconstruct03.f90 b/test/Semantics/blockconstruct03.f90 index cb016bb8080f..df5aff7699ea 100644 --- a/test/Semantics/blockconstruct03.f90 +++ b/test/Semantics/blockconstruct03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Tests implemented for this standard: ! Block Construct ! C1109 diff --git a/test/Semantics/c_f_pointer.f90 b/test/Semantics/c_f_pointer.f90 index 2f48574717b4..1064461c509d 100644 --- a/test/Semantics/c_f_pointer.f90 +++ b/test/Semantics/c_f_pointer.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Enforce 18.2.3.3 program test diff --git a/test/Semantics/call01.f90 b/test/Semantics/call01.f90 index d38fc904cfeb..88274dd42844 100644 --- a/test/Semantics/call01.f90 +++ b/test/Semantics/call01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Confirm enforcement of constraints and restrictions in 15.6.2.1 non_recursive function f01(n) result(res) diff --git a/test/Semantics/call02.f90 b/test/Semantics/call02.f90 index f60eabfd8e1e..2d23274da1b0 100644 --- a/test/Semantics/call02.f90 +++ b/test/Semantics/call02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! 15.5.1 procedure reference constraints and restrictions subroutine s01(elem, subr) diff --git a/test/Semantics/call03.f90 b/test/Semantics/call03.f90 index c994b9fa8519..098106aed45e 100644 --- a/test/Semantics/call03.f90 +++ b/test/Semantics/call03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.4 constraints and restrictions for non-POINTER non-ALLOCATABLE ! dummy arguments. diff --git a/test/Semantics/call04.f90 b/test/Semantics/call04.f90 index a3e727a770c8..3064fee5decc 100644 --- a/test/Semantics/call04.f90 +++ b/test/Semantics/call04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 8.5.10 & 8.5.18 constraints on dummy argument declarations module m diff --git a/test/Semantics/call05.f90 b/test/Semantics/call05.f90 index 368ec59b33b8..80f1874ff2d5 100644 --- a/test/Semantics/call05.f90 +++ b/test/Semantics/call05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.5 constraints and restrictions for POINTER & ALLOCATABLE ! arguments when both sides of the call have the same attributes. diff --git a/test/Semantics/call06.f90 b/test/Semantics/call06.f90 index d9c8a0beee72..eb4bd3755f87 100644 --- a/test/Semantics/call06.f90 +++ b/test/Semantics/call06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.6 constraints and restrictions for ALLOCATABLE ! dummy arguments. diff --git a/test/Semantics/call07.f90 b/test/Semantics/call07.f90 index bd44e43b552c..f596e3600288 100644 --- a/test/Semantics/call07.f90 +++ b/test/Semantics/call07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.7 constraints and restrictions for POINTER dummy arguments. module m diff --git a/test/Semantics/call08.f90 b/test/Semantics/call08.f90 index 7fe42e7bd7ef..88ec7e3b4cca 100644 --- a/test/Semantics/call08.f90 +++ b/test/Semantics/call08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.8 coarray dummy arguments module m diff --git a/test/Semantics/call09.f90 b/test/Semantics/call09.f90 index 06c304af4101..e27c78e4281f 100644 --- a/test/Semantics/call09.f90 +++ b/test/Semantics/call09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.5.2.9(2,3,5) dummy procedure requirements module m diff --git a/test/Semantics/call10.f90 b/test/Semantics/call10.f90 index 00db9cd7319c..52983c9f18a0 100644 --- a/test/Semantics/call10.f90 +++ b/test/Semantics/call10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.7 (C1583-C1590, C1592-C1599) constraints and restrictions ! for pure procedures. ! (C1591 is tested in call11.f90; C1594 in call12.f90.) diff --git a/test/Semantics/call11.f90 b/test/Semantics/call11.f90 index 254566fa38b8..b53b40334e93 100644 --- a/test/Semantics/call11.f90 +++ b/test/Semantics/call11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.7 C1591 & others: contexts requiring pure subprograms module m diff --git a/test/Semantics/call12.f90 b/test/Semantics/call12.f90 index ebcaab6fb903..3ce0812560ac 100644 --- a/test/Semantics/call12.f90 +++ b/test/Semantics/call12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.7 C1594 - prohibited assignments in pure subprograms module used diff --git a/test/Semantics/call13.f90 b/test/Semantics/call13.f90 index 798de8fbf2d9..952a7d0c8b1d 100644 --- a/test/Semantics/call13.f90 +++ b/test/Semantics/call13.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 15.4.2.2 constraints and restrictions for calls to implicit ! interfaces diff --git a/test/Semantics/call14.f90 b/test/Semantics/call14.f90 index d6e94be51996..e25620b2694b 100644 --- a/test/Semantics/call14.f90 +++ b/test/Semantics/call14.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test 8.5.18 constraints on the VALUE attribute module m diff --git a/test/Semantics/call15.f90 b/test/Semantics/call15.f90 index ca935b91530f..08886e4e7c6d 100644 --- a/test/Semantics/call15.f90 +++ b/test/Semantics/call15.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C711 An assumed-type actual argument that corresponds to an assumed-rank ! dummy argument shall be assumed-shape or assumed-rank. subroutine s(arg1, arg2, arg3) diff --git a/test/Semantics/canondo01.f90 b/test/Semantics/canondo01.f90 index 46c82cba9c1d..51060f8a5f1d 100644 --- a/test/Semantics/canondo01.f90 +++ b/test/Semantics/canondo01.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: end do SUBROUTINE sub00(a,b,n,m) diff --git a/test/Semantics/canondo02.f90 b/test/Semantics/canondo02.f90 index 0389df4d52c8..62dbd4b0a024 100644 --- a/test/Semantics/canondo02.f90 +++ b/test/Semantics/canondo02.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: end do SUBROUTINE sub00(a,b,n,m) diff --git a/test/Semantics/canondo03.f90 b/test/Semantics/canondo03.f90 index f72b1ffd0180..4be30775221e 100644 --- a/test/Semantics/canondo03.f90 +++ b/test/Semantics/canondo03.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: 10 continue ! CHECK: end do diff --git a/test/Semantics/canondo04.f90 b/test/Semantics/canondo04.f90 index 763d62674084..452d77d0559e 100644 --- a/test/Semantics/canondo04.f90 +++ b/test/Semantics/canondo04.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: do [1-9] ! Figure out how to also execute this test. diff --git a/test/Semantics/canondo05.f90 b/test/Semantics/canondo05.f90 index f676eff82219..4550e9849fc4 100644 --- a/test/Semantics/canondo05.f90 +++ b/test/Semantics/canondo05.f90 @@ -1,5 +1,6 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s -! XXXRUN: ${F18} -fopenmp -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! XXXEXEC: ${F18} -fopenmp -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: do *[1-9] program P diff --git a/test/Semantics/canondo06.f90 b/test/Semantics/canondo06.f90 index 1e7235b9dc47..0aea3daed4f9 100644 --- a/test/Semantics/canondo06.f90 +++ b/test/Semantics/canondo06.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -fopenmp -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -fopenmp -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: do *[1-9] ! CHECK: omp simd diff --git a/test/Semantics/canondo07.f90 b/test/Semantics/canondo07.f90 index 59a524275efb..f5a0feef93d0 100644 --- a/test/Semantics/canondo07.f90 +++ b/test/Semantics/canondo07.f90 @@ -1,7 +1,8 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1131 -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: A DO loop should terminate with an END DO or CONTINUE program endDo diff --git a/test/Semantics/canondo08.f90 b/test/Semantics/canondo08.f90 index 7e5c158692b1..c5bfb56f1288 100644 --- a/test/Semantics/canondo08.f90 +++ b/test/Semantics/canondo08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 @@ -5,7 +6,7 @@ ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo09.f90 b/test/Semantics/canondo09.f90 index 98c422124c21..99956a03fe3d 100644 --- a/test/Semantics/canondo09.f90 +++ b/test/Semantics/canondo09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 @@ -5,7 +6,7 @@ ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo10.f90 b/test/Semantics/canondo10.f90 index 0827be6de97f..93d060dd9aaa 100644 --- a/test/Semantics/canondo10.f90 +++ b/test/Semantics/canondo10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 @@ -5,7 +6,7 @@ ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo11.f90 b/test/Semantics/canondo11.f90 index 9019f34ae09f..8e98a24bb87f 100644 --- a/test/Semantics/canondo11.f90 +++ b/test/Semantics/canondo11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 @@ -5,7 +6,7 @@ ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo12.f90 b/test/Semantics/canondo12.f90 index 1809afaedac1..48fde32faf99 100644 --- a/test/Semantics/canondo12.f90 +++ b/test/Semantics/canondo12.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo13.f90 b/test/Semantics/canondo13.f90 index 09a3e4c13fd7..b317d7963aa3 100644 --- a/test/Semantics/canondo13.f90 +++ b/test/Semantics/canondo13.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo14.f90 b/test/Semantics/canondo14.f90 index f6d422ab7436..69bd748212be 100644 --- a/test/Semantics/canondo14.f90 +++ b/test/Semantics/canondo14.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo15.f90 b/test/Semantics/canondo15.f90 index 2726cd914aa6..f58959898345 100644 --- a/test/Semantics/canondo15.f90 +++ b/test/Semantics/canondo15.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo16.f90 b/test/Semantics/canondo16.f90 index e1819c00049e..d5c5db464930 100644 --- a/test/Semantics/canondo16.f90 +++ b/test/Semantics/canondo16.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard -I../../tools/f18/include %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard -I../../tools/f18/include %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo17.f90 b/test/Semantics/canondo17.f90 index e9194b0fd592..a687fb2fefac 100644 --- a/test/Semantics/canondo17.f90 +++ b/test/Semantics/canondo17.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo18.f90 b/test/Semantics/canondo18.f90 index 6760d20622db..3e3f18b05174 100644 --- a/test/Semantics/canondo18.f90 +++ b/test/Semantics/canondo18.f90 @@ -1,10 +1,11 @@ +! RUN: %S/test_any.sh %s %flang %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/canondo19.f90 b/test/Semantics/canondo19.f90 index 35da0076422b..82bad39e950f 100644 --- a/test/Semantics/canondo19.f90 +++ b/test/Semantics/canondo19.f90 @@ -1,7 +1,8 @@ +! RUN: %S/test_any.sh %s %flang %t ! Check that if there is a label or a name on an label-do-stmt, ! then it is not lost when rewriting it to an non-label-do-stmt. -! RUN: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard %s 2>&1 | ${FileCheck} %s ! CHECK: end do ! CHECK: 2 do diff --git a/test/Semantics/coarrays01.f90 b/test/Semantics/coarrays01.f90 index 491ebb22cbd8..3e8e1672a47b 100644 --- a/test/Semantics/coarrays01.f90 +++ b/test/Semantics/coarrays01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test selector and team-value in CHANGE TEAM statement ! OK diff --git a/test/Semantics/complex01.f90 b/test/Semantics/complex01.f90 index 4fb46ba56b71..c70f0defad6a 100644 --- a/test/Semantics/complex01.f90 +++ b/test/Semantics/complex01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C718 Each named constant in a complex literal constant shall be of type ! integer or real. subroutine s() diff --git a/test/Semantics/computed-goto01.f90 b/test/Semantics/computed-goto01.f90 index f16838e9ca78..9f24996f41a0 100644 --- a/test/Semantics/computed-goto01.f90 +++ b/test/Semantics/computed-goto01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that a basic computed goto compiles INTEGER, DIMENSION (2) :: B diff --git a/test/Semantics/computed-goto02.f90 b/test/Semantics/computed-goto02.f90 index 7c40c65ec6b0..eea61a827052 100644 --- a/test/Semantics/computed-goto02.f90 +++ b/test/Semantics/computed-goto02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that computed goto express must be a scalar integer expression ! TODO: PGI, for example, accepts a float & converts the value to int. diff --git a/test/Semantics/critical01.f90 b/test/Semantics/critical01.f90 index 89d3337ba536..5ca97ade6998 100644 --- a/test/Semantics/critical01.f90 +++ b/test/Semantics/critical01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !C1117 subroutine test1(a, i) diff --git a/test/Semantics/critical02.f90 b/test/Semantics/critical02.f90 index 2c75ac2ab33e..ba5e0f4c55a7 100644 --- a/test/Semantics/critical02.f90 +++ b/test/Semantics/critical02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !C1118 subroutine test1 diff --git a/test/Semantics/critical03.f90 b/test/Semantics/critical03.f90 index 6bf45531a170..2ab60e5d59a9 100644 --- a/test/Semantics/critical03.f90 +++ b/test/Semantics/critical03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !C1119 subroutine test1(a, i) diff --git a/test/Semantics/critical04.f90 b/test/Semantics/critical04.f90 index 3b5f7e8e1383..136e31baa621 100644 --- a/test/Semantics/critical04.f90 +++ b/test/Semantics/critical04.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: Control flow escapes from CRITICAL subroutine test1(a, i) diff --git a/test/Semantics/data01.f90 b/test/Semantics/data01.f90 index c8af31a50d07..4bdf7ea9dd4a 100644 --- a/test/Semantics/data01.f90 +++ b/test/Semantics/data01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !Test for checking data constraints, C882-C887 module m1 type person diff --git a/test/Semantics/data02.f90 b/test/Semantics/data02.f90 index 4cd593697b23..ac6902622d83 100644 --- a/test/Semantics/data02.f90 +++ b/test/Semantics/data02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that expressions are analyzed in data statements subroutine s1 diff --git a/test/Semantics/deallocate01.f90 b/test/Semantics/deallocate01.f90 index 2bb4236c3b82..8aaf14496d71 100644 --- a/test/Semantics/deallocate01.f90 +++ b/test/Semantics/deallocate01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test that DEALLOCATE works INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/deallocate04.f90 b/test/Semantics/deallocate04.f90 index 7183e2d3ecb8..2a1ad62b9920 100644 --- a/test/Semantics/deallocate04.f90 +++ b/test/Semantics/deallocate04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for type errors in DEALLOCATE statements INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/deallocate05.f90 b/test/Semantics/deallocate05.f90 index 765753ac0c64..fdc66004e2ce 100644 --- a/test/Semantics/deallocate05.f90 +++ b/test/Semantics/deallocate05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in DEALLOCATE statements Module share diff --git a/test/Semantics/doconcurrent01.f90 b/test/Semantics/doconcurrent01.f90 index bba111ad25c2..a4161a5c3073 100644 --- a/test/Semantics/doconcurrent01.f90 +++ b/test/Semantics/doconcurrent01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1141 ! A reference to the procedure IEEE_SET_HALTING_MODE ! from the intrinsic ! module IEEE_EXCEPTIONS, shall not ! appear within a DO CONCURRENT construct. diff --git a/test/Semantics/doconcurrent02.f90 b/test/Semantics/doconcurrent02.f90 index c09977d23228..db120b62bc45 100644 --- a/test/Semantics/doconcurrent02.f90 +++ b/test/Semantics/doconcurrent02.f90 @@ -1,7 +1,8 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative tests: we don't want DO CONCURRENT semantics constraints checked ! when the loops are not DO CONCURRENT -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: image control statement not allowed in DO CONCURRENT ! CHECK-NOT: RETURN not allowed in DO CONCURRENT ! CHECK-NOT: call to impure procedure in DO CONCURRENT not allowed diff --git a/test/Semantics/doconcurrent03.f90 b/test/Semantics/doconcurrent03.f90 index ffaca88b9906..cfefd92cc3b0 100644 --- a/test/Semantics/doconcurrent03.f90 +++ b/test/Semantics/doconcurrent03.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: Control flow escapes from DO CONCURRENT ! CHECK: branch into loop body from outside ! CHECK: the loop branched into diff --git a/test/Semantics/doconcurrent04.f90 b/test/Semantics/doconcurrent04.f90 index 8182477020f0..51ec5737a154 100644 --- a/test/Semantics/doconcurrent04.f90 +++ b/test/Semantics/doconcurrent04.f90 @@ -1,5 +1,6 @@ +! RUN: %S/test_any.sh %s %flang %t ! C1122 The index-name shall be a named scalar variable of type integer. -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: Must have INTEGER type, but is REAL\\(4\\) subroutine do_concurrent_test1(n) diff --git a/test/Semantics/doconcurrent05.f90 b/test/Semantics/doconcurrent05.f90 index 8c46192e915e..d92ef6d18322 100644 --- a/test/Semantics/doconcurrent05.f90 +++ b/test/Semantics/doconcurrent05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if ! it belongs to that construct or an outer construct. diff --git a/test/Semantics/doconcurrent06.f90 b/test/Semantics/doconcurrent06.f90 index 2f181fe9de6b..f178b7a11640 100644 --- a/test/Semantics/doconcurrent06.f90 +++ b/test/Semantics/doconcurrent06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if ! it belongs to that construct or an outer construct. diff --git a/test/Semantics/doconcurrent07.f90 b/test/Semantics/doconcurrent07.f90 index 5cc70c00896a..661d51a71be5 100644 --- a/test/Semantics/doconcurrent07.f90 +++ b/test/Semantics/doconcurrent07.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK-NOT: exit from DO CONCURRENT construct subroutine do_concurrent_test1(n) diff --git a/test/Semantics/doconcurrent08.f90 b/test/Semantics/doconcurrent08.f90 index f6773995e202..91a077fade49 100644 --- a/test/Semantics/doconcurrent08.f90 +++ b/test/Semantics/doconcurrent08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1140 -- A statement that might result in the deallocation of a polymorphic ! entity shall not appear within a DO CONCURRENT construct. module m1 diff --git a/test/Semantics/dosemantics01.f90 b/test/Semantics/dosemantics01.f90 index 6745f1f2740e..2261f184e3cc 100644 --- a/test/Semantics/dosemantics01.f90 +++ b/test/Semantics/dosemantics01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1131 -- check valid and invalid DO loop naming PROGRAM C1131 diff --git a/test/Semantics/dosemantics02.f90 b/test/Semantics/dosemantics02.f90 index 0b3165a88270..96047f0a3678 100644 --- a/test/Semantics/dosemantics02.f90 +++ b/test/Semantics/dosemantics02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1121 -- any procedure referenced in a concurrent header must be pure ! Also, check that the step expressions are not zero. This is prohibited by diff --git a/test/Semantics/dosemantics03.f90 b/test/Semantics/dosemantics03.f90 index 4792ae6572de..c063a7b8c854 100644 --- a/test/Semantics/dosemantics03.f90 +++ b/test/Semantics/dosemantics03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Issue 458 -- semantic checks for a normal DO loop. The DO variable ! and the initial, final, and step expressions must be INTEGER if the ! options for standard conformance and turning warnings into errors diff --git a/test/Semantics/dosemantics04.f90 b/test/Semantics/dosemantics04.f90 index 7c0743517f17..35a3c9493ca2 100644 --- a/test/Semantics/dosemantics04.f90 +++ b/test/Semantics/dosemantics04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1123 -- Expressions in DO CONCURRENT header cannot reference variables ! declared in the same header PROGRAM dosemantics04 diff --git a/test/Semantics/dosemantics05.f90 b/test/Semantics/dosemantics05.f90 index c7e27d53aec4..f565f9b71679 100644 --- a/test/Semantics/dosemantics05.f90 +++ b/test/Semantics/dosemantics05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test DO loop semantics for constraint C1130 -- ! The constraint states that "If the locality-spec DEFAULT ( NONE ) appears in a ! DO CONCURRENT statement; a variable that is a local or construct entity of a diff --git a/test/Semantics/dosemantics06.f90 b/test/Semantics/dosemantics06.f90 index fd5b0a86bab8..41b9598970b5 100644 --- a/test/Semantics/dosemantics06.f90 +++ b/test/Semantics/dosemantics06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1131, C1133 -- check valid and invalid DO loop naming ! C1131 (R1119) If the do-stmt of a do-construct specifies a do-construct-name, ! the corresponding end-do shall be an end-do-stmt specifying the same diff --git a/test/Semantics/dosemantics07.f90 b/test/Semantics/dosemantics07.f90 index 9b871fd6f3e4..f1450dda31eb 100644 --- a/test/Semantics/dosemantics07.f90 +++ b/test/Semantics/dosemantics07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !C1132 ! If the do-stmt is a nonlabel-do-stmt, the corresponding end-do shall be an ! end-do-stmt. diff --git a/test/Semantics/dosemantics08.f90 b/test/Semantics/dosemantics08.f90 index e6e313372ff8..388fb75254f8 100644 --- a/test/Semantics/dosemantics08.f90 +++ b/test/Semantics/dosemantics08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1138 -- ! A branch (11.2) within a DO CONCURRENT construct shall not have a branch ! target that is outside the construct. diff --git a/test/Semantics/dosemantics09.f90 b/test/Semantics/dosemantics09.f90 index 425e71e3db54..46136f29c74e 100644 --- a/test/Semantics/dosemantics09.f90 +++ b/test/Semantics/dosemantics09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !C1129 !A variable that is referenced by the scalar-mask-expr of a !concurrent-header or by any concurrent-limit or concurrent-step in that diff --git a/test/Semantics/dosemantics10.f90 b/test/Semantics/dosemantics10.f90 index 7bd7bbbb7c85..561f9b7fb7ea 100644 --- a/test/Semantics/dosemantics10.f90 +++ b/test/Semantics/dosemantics10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1134 A CYCLE statement must be within a DO construct ! ! C1166 An EXIT statement must be within a DO construct diff --git a/test/Semantics/dosemantics11.f90 b/test/Semantics/dosemantics11.f90 index 50d69608215c..760f9f5f9b60 100644 --- a/test/Semantics/dosemantics11.f90 +++ b/test/Semantics/dosemantics11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1135 A cycle-stmt shall not appear within a CHANGE TEAM, CRITICAL, or DO ! CONCURRENT construct if it belongs to an outer construct. ! diff --git a/test/Semantics/dosemantics12.f90 b/test/Semantics/dosemantics12.f90 index b1ee8a02707d..48ecd14feda5 100644 --- a/test/Semantics/dosemantics12.f90 +++ b/test/Semantics/dosemantics12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. ! ! Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/Semantics/equivalence01.f90 b/test/Semantics/equivalence01.f90 index a6e70e6ab53f..31b561e33b0d 100644 --- a/test/Semantics/equivalence01.f90 +++ b/test/Semantics/equivalence01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 integer i, j real r(2) diff --git a/test/Semantics/expr-errors01.f90 b/test/Semantics/expr-errors01.f90 index 378bd2d2368e..a479e863dcaf 100644 --- a/test/Semantics/expr-errors01.f90 +++ b/test/Semantics/expr-errors01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1003 - can't parenthesize function call returning procedure pointer module m1 type :: dt diff --git a/test/Semantics/expr-errors02.f90 b/test/Semantics/expr-errors02.f90 index 2df05a2cd523..d1aac68bf008 100644 --- a/test/Semantics/expr-errors02.f90 +++ b/test/Semantics/expr-errors02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test specification expressions module m diff --git a/test/Semantics/forall01.f90 b/test/Semantics/forall01.f90 index e90a17f62978..ecb243bc2a09 100644 --- a/test/Semantics/forall01.f90 +++ b/test/Semantics/forall01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine forall1 real :: a(9) !ERROR: 'i' is already declared in this scoping unit diff --git a/test/Semantics/getdefinition01.f90 b/test/Semantics/getdefinition01.f90 index 880e282bca9f..4a2fdd760568 100644 --- a/test/Semantics/getdefinition01.f90 +++ b/test/Semantics/getdefinition01.f90 @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-definition returning source position of symbol definition. - module m1 private :: f contains @@ -16,12 +16,12 @@ recursive pure function f() result(x) end function end module -! RUN: echo %t 1>&2; -! RUN: ${F18} -fget-definition 7 17 18 -fparse-only %s > %t; -! RUN: ${F18} -fget-definition 8 20 23 -fparse-only %s >> %t; -! RUN: ${F18} -fget-definition 15 3 4 -fparse-only %s >> %t; -! RUN: ${F18} -fget-definition -fparse-only %s >> %t 2>&1; -! RUN: cat %t | ${FileCheck} %s +! EXEC: echo %t 1>&2; +! EXEC: ${F18} -fget-definition 7 17 18 -fparse-only %s > %t; +! EXEC: ${F18} -fget-definition 8 20 23 -fparse-only %s >> %t; +! EXEC: ${F18} -fget-definition 15 3 4 -fparse-only %s >> %t; +! EXEC: ${F18} -fget-definition -fparse-only %s >> %t 2>&1; +! EXEC: cat %t | ${FileCheck} %s ! CHECK:x:.*getdefinition01.f90, 6, 21-22 ! CHECK:yyy:.*getdefinition01.f90, 6, 24-27 ! CHECK:x:.*getdefinition01.f90, 14, 24-25 diff --git a/test/Semantics/getdefinition02.f b/test/Semantics/getdefinition02.f index 3f8ac46a1380..58391a27d530 100644 --- a/test/Semantics/getdefinition02.f +++ b/test/Semantics/getdefinition02.f @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-definition with fixed form. - module m2 private :: f contains @@ -17,10 +17,10 @@ recursive pure function f() result(x) end function end module -! RUN: ${F18} -fget-definition 8 9 10 -fparse-only %s > %t; -! RUN: ${F18} -fget-definition 9 26 29 -fparse-only %s >> %t; -! RUN: ${F18} -fget-definition 16 9 10 -fparse-only %s >> %t; -! RUN: cat %t | ${FileCheck} %s +! EXEC: ${F18} -fget-definition 8 9 10 -fparse-only %s > %t; +! EXEC: ${F18} -fget-definition 9 26 29 -fparse-only %s >> %t; +! EXEC: ${F18} -fget-definition 16 9 10 -fparse-only %s >> %t; +! EXEC: cat %t | ${FileCheck} %s ! CHECK:x:.*getdefinition02.f, 6, 27-28 ! CHECK:yyy:.*getdefinition02.f, 6, 30-33 ! CHECK:x:.*getdefinition02.f, 15, 30-31 diff --git a/test/Semantics/getdefinition03-a.f90 b/test/Semantics/getdefinition03-a.f90 index 5b287d28665c..81ad276ec29a 100644 --- a/test/Semantics/getdefinition03-a.f90 +++ b/test/Semantics/getdefinition03-a.f90 @@ -1,6 +1,6 @@ ! Tests -fget-definition with INCLUDE - -INCLUDE "getdefinition03-b.f90" +!RUN: %S/test_any.sh %s %flang %t +INCLUDE "Inputs/getdefinition03-b.f90" program main use m3 @@ -8,8 +8,8 @@ program main x = f end program -! RUN: ${F18} -fget-definition 8 6 7 -fparse-only %s > %t; -! RUN: ${F18} -fget-definition 8 2 3 -fparse-only %s >> %t; -! RUN: cat %t | ${FileCheck} %s; +! EXEC: ${F18} -fget-definition 8 6 7 -fparse-only %s > %t; +! EXEC: ${F18} -fget-definition 8 2 3 -fparse-only %s >> %t; +! EXEC: cat %t | ${FileCheck} %s; ! CHECK:f:.*getdefinition03-b.f90, 2, 12-13 ! CHECK:x:.*getdefinition03-a.f90, 7, 13-14 diff --git a/test/Semantics/getdefinition04.f90 b/test/Semantics/getdefinition04.f90 index 80ace6544386..aa143a161852 100644 --- a/test/Semantics/getdefinition04.f90 +++ b/test/Semantics/getdefinition04.f90 @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-definition with COMMON block with same name as variable. - program main integer :: x integer :: y @@ -7,5 +7,5 @@ program main x = y end program -! RUN: ${F18} -fget-definition 7 3 4 -fparse-only %s | ${FileCheck} %s +! EXEC: ${F18} -fget-definition 7 3 4 -fparse-only %s | ${FileCheck} %s ! CHECK:x:.*getdefinition04.f90, 4, 14-15 diff --git a/test/Semantics/getdefinition05.f90 b/test/Semantics/getdefinition05.f90 index 3ad69778ead0..e1115a245611 100644 --- a/test/Semantics/getdefinition05.f90 +++ b/test/Semantics/getdefinition05.f90 @@ -1,6 +1,6 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-symbols-sources with BLOCK that contains same variable name as ! another in an outer scope. - program main integer :: x integer :: y @@ -13,9 +13,9 @@ program main end program !! Inner x -! RUN: ${F18} -fget-definition 10 5 6 -fparse-only %s > %t; +! EXEC: ${F18} -fget-definition 10 5 6 -fparse-only %s > %t; ! CHECK:x:.*getdefinition05.f90, 8, 16-17 !! Outer y -! RUN: ${F18} -fget-definition 12 7 8 -fparse-only %s >> %t; +! EXEC: ${F18} -fget-definition 12 7 8 -fparse-only %s >> %t; ! CHECK:y:.*getdefinition05.f90, 6, 14-15 -! RUN: cat %t | ${FileCheck} %s; +! EXEC: cat %t | ${FileCheck} %s; diff --git a/test/Semantics/getsymbols01.f90 b/test/Semantics/getsymbols01.f90 index 9f754f9374d0..d102807ed482 100644 --- a/test/Semantics/getsymbols01.f90 +++ b/test/Semantics/getsymbols01.f90 @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-symbols-sources finding all symbols in file. - module mm1 private :: f contains @@ -16,7 +16,7 @@ recursive pure function f() result(x) end function end module -! RUN: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s ! CHECK-ONCE:mm1:.*getsymbols01.f90, 3, 8-11 ! CHECK-ONCE:f:.*getsymbols01.f90, 13, 26-27 ! CHECK-ONCE:s:.*getsymbols01.f90, 6, 18-19 diff --git a/test/Semantics/getsymbols02.f90 b/test/Semantics/getsymbols02.f90 new file mode 100644 index 000000000000..4172b7418f9e --- /dev/null +++ b/test/Semantics/getsymbols02.f90 @@ -0,0 +1 @@ +!RUN: %S/test_any.sh '%S/Inputs/getsymbols02-*' %f18 %t \ No newline at end of file diff --git a/test/Semantics/getsymbols03-a.f90 b/test/Semantics/getsymbols03-a.f90 index 1d6d3b6aaba2..5616f97629ce 100644 --- a/test/Semantics/getsymbols03-a.f90 +++ b/test/Semantics/getsymbols03-a.f90 @@ -1,6 +1,6 @@ ! Tests -fget-symbols with INCLUDE - -INCLUDE "getsymbols03-b.f90" +!RUN: %S/test_any.sh %s %flang %t +INCLUDE "Inputs/getsymbols03-b.f90" program main use mm3 @@ -8,7 +8,7 @@ program main x = f end program -! RUN: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s ! CHECK:mm3:.*getsymbols03-b.f90, 1, 8-11 ! CHECK:f:.*getsymbols03-b.f90, 2, 12-13 ! CHECK:main:.*getsymbols03-a.f90, 5, 9-13 diff --git a/test/Semantics/getsymbols04.f90 b/test/Semantics/getsymbols04.f90 index d4a83aecb5d9..06f739c71137 100644 --- a/test/Semantics/getsymbols04.f90 +++ b/test/Semantics/getsymbols04.f90 @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-symbols-sources with COMMON. - program main integer :: x integer :: y @@ -7,7 +7,7 @@ program main x = y end program -! RUN: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s ! CHECK:x:.*getsymbols04.f90, 4, 14-15 ! CHECK:y:.*getsymbols04.f90, 5, 14-15 ! CHECK:x:.*getsymbols04.f90, 6, 11-12 diff --git a/test/Semantics/getsymbols05.f90 b/test/Semantics/getsymbols05.f90 index c65a2a6f5a99..f905313675cd 100644 --- a/test/Semantics/getsymbols05.f90 +++ b/test/Semantics/getsymbols05.f90 @@ -1,5 +1,5 @@ +!RUN: %S/test_any.sh %s %flang %t ! Tests -fget-symbols-sources with COMMON. - program main integer :: x integer :: y @@ -10,7 +10,7 @@ program main x = y end program -! RUN: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -fget-symbols-sources -fparse-only %s 2>&1 | ${FileCheck} %s ! CHECK:x:.*getsymbols05.f90, 4, 14-15 ! CHECK:y:.*getsymbols05.f90, 5, 14-15 ! CHECK:x:.*getsymbols05.f90, 7, 16-17 diff --git a/test/Semantics/if_arith01.f90 b/test/Semantics/if_arith01.f90 index 43365c64ad3a..5ec06b47485d 100644 --- a/test/Semantics/if_arith01.f90 +++ b/test/Semantics/if_arith01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that a basic arithmetic if compiles. if ( A ) 100, 200, 300 diff --git a/test/Semantics/if_arith02.f90 b/test/Semantics/if_arith02.f90 index fc94e151cf14..f8e24b42dffa 100644 --- a/test/Semantics/if_arith02.f90 +++ b/test/Semantics/if_arith02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that only labels are allowed in arithmetic if statements. ! TODO: Revisit error message "expected 'ASSIGN'" etc. ! TODO: Revisit error message "expected one of '0123456789'" diff --git a/test/Semantics/if_arith03.f90 b/test/Semantics/if_arith03.f90 index fd30eb2ee954..1e5eb67d184c 100644 --- a/test/Semantics/if_arith03.f90 +++ b/test/Semantics/if_arith03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !ERROR: label '600' was not found diff --git a/test/Semantics/if_arith04.f90 b/test/Semantics/if_arith04.f90 index 360d596762b1..9a436cd5eb67 100644 --- a/test/Semantics/if_arith04.f90 +++ b/test/Semantics/if_arith04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Make sure arithmetic if expressions are non-complex numeric exprs. INTEGER I diff --git a/test/Semantics/if_construct01.f90 b/test/Semantics/if_construct01.f90 index 66398def9805..c133b7d8cc9f 100644 --- a/test/Semantics/if_construct01.f90 +++ b/test/Semantics/if_construct01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Simple check that if constructs are ok. if (a < b) then diff --git a/test/Semantics/if_construct02.f90 b/test/Semantics/if_construct02.f90 index 5177f388b493..9ba6caa45355 100644 --- a/test/Semantics/if_construct02.f90 +++ b/test/Semantics/if_construct02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that if constructs only accept scalar logical expressions. ! TODO: expand the test to check this restriction for more types. diff --git a/test/Semantics/if_stmt01.f90 b/test/Semantics/if_stmt01.f90 index e111f6519d26..51454a9d2116 100644 --- a/test/Semantics/if_stmt01.f90 +++ b/test/Semantics/if_stmt01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Simple check that if statements are ok. IF (A > 0.0) A = LOG (A) diff --git a/test/Semantics/if_stmt02.f90 b/test/Semantics/if_stmt02.f90 index 483e92d2c940..71c458381ac2 100644 --- a/test/Semantics/if_stmt02.f90 +++ b/test/Semantics/if_stmt02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !ERROR: IF statement is not allowed in IF statement IF (A > 0.0) IF (B < 0.0) A = LOG (A) END diff --git a/test/Semantics/if_stmt03.f90 b/test/Semantics/if_stmt03.f90 index dd869b2cad0a..2a2595404960 100644 --- a/test/Semantics/if_stmt03.f90 +++ b/test/Semantics/if_stmt03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check that non-logical expressions are not allowed. ! Check that non-scalar expressions are not allowed. ! TODO: Insure all non-logicals are prohibited. diff --git a/test/Semantics/implicit01.f90 b/test/Semantics/implicit01.f90 index 318fe760322c..f0893f7ed33f 100644 --- a/test/Semantics/implicit01.f90 +++ b/test/Semantics/implicit01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 implicit none !ERROR: More than one IMPLICIT NONE statement diff --git a/test/Semantics/implicit02.f90 b/test/Semantics/implicit02.f90 index d77c3f55c6e6..5d2b6e09474f 100644 --- a/test/Semantics/implicit02.f90 +++ b/test/Semantics/implicit02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 implicit none !ERROR: IMPLICIT statement after IMPLICIT NONE or IMPLICIT NONE(TYPE) statement diff --git a/test/Semantics/implicit03.f90 b/test/Semantics/implicit03.f90 index 343471ad5a15..9636743233a3 100644 --- a/test/Semantics/implicit03.f90 +++ b/test/Semantics/implicit03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 implicit integer(a-z) !ERROR: IMPLICIT NONE statement after IMPLICIT statement diff --git a/test/Semantics/implicit04.f90 b/test/Semantics/implicit04.f90 index 004dbe65549d..86adb95f9852 100644 --- a/test/Semantics/implicit04.f90 +++ b/test/Semantics/implicit04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s parameter(a=1.0) !ERROR: IMPLICIT NONE statement after PARAMETER statement diff --git a/test/Semantics/implicit05.f90 b/test/Semantics/implicit05.f90 index 50039a421eaf..7649c228fa44 100644 --- a/test/Semantics/implicit05.f90 +++ b/test/Semantics/implicit05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s !ERROR: 'a' does not follow 'b' alphabetically implicit integer(b-a) diff --git a/test/Semantics/implicit06.f90 b/test/Semantics/implicit06.f90 index 225052cd5e89..3f6672008d53 100644 --- a/test/Semantics/implicit06.f90 +++ b/test/Semantics/implicit06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 implicit integer(a-c) !ERROR: More than one implicit type specified for 'c' diff --git a/test/Semantics/implicit07.f90 b/test/Semantics/implicit07.f90 index 8201c3dcb0e3..68fa37de8ce7 100644 --- a/test/Semantics/implicit07.f90 +++ b/test/Semantics/implicit07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t implicit none(external) external x call x diff --git a/test/Semantics/implicit08.f90 b/test/Semantics/implicit08.f90 index a56382e4154c..44e96d89855e 100644 --- a/test/Semantics/implicit08.f90 +++ b/test/Semantics/implicit08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 block !ERROR: IMPLICIT statement is not allowed in a BLOCK construct diff --git a/test/Semantics/init01.f90 b/test/Semantics/init01.f90 index b160a99dfc2f..1fc1ed877fa3 100644 --- a/test/Semantics/init01.f90 +++ b/test/Semantics/init01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Object pointer initializer error tests subroutine test(j) diff --git a/test/Semantics/int-literals.f90 b/test/Semantics/int-literals.f90 index b3b966996f53..3c48b7e1b7da 100644 --- a/test/Semantics/int-literals.f90 +++ b/test/Semantics/int-literals.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Fortran syntax considers signed int literals in complex literals ! to be a distinct production, not an application of unary +/- to ! an unsigned int literal, so they're used here to test overflow diff --git a/test/Semantics/io01.f90 b/test/Semantics/io01.f90 index c951943a4bc7..81b537d7e4c5 100644 --- a/test/Semantics/io01.f90 +++ b/test/Semantics/io01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t character(len=20) :: access = "direcT" character(len=20) :: access_(2) = (/"direcT", "streaM"/) character(len=20) :: action_(2) = (/"reaD ", "writE"/) diff --git a/test/Semantics/io02.f90 b/test/Semantics/io02.f90 index 65e6b263bb8b..7cb901d34027 100644 --- a/test/Semantics/io02.f90 +++ b/test/Semantics/io02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t integer :: unit10 = 10 integer :: unit11 = 11 diff --git a/test/Semantics/io03.f90 b/test/Semantics/io03.f90 index 71425b8869c5..a6696176b126 100644 --- a/test/Semantics/io03.f90 +++ b/test/Semantics/io03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t character(kind=1,len=50) internal_file character(kind=2,len=50) internal_file2 character(kind=4,len=50) internal_file4 diff --git a/test/Semantics/io04.f90 b/test/Semantics/io04.f90 index 68b217f57a61..09776ef94ab1 100644 --- a/test/Semantics/io04.f90 +++ b/test/Semantics/io04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t character(kind=1,len=50) internal_file character(kind=1,len=100) msg character(20) sign diff --git a/test/Semantics/io05.f90 b/test/Semantics/io05.f90 index 5b36f9ba923e..1df878197237 100644 --- a/test/Semantics/io05.f90 +++ b/test/Semantics/io05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t character*20 c(25), cv character(kind=1,len=59) msg logical*2 v(5), lv diff --git a/test/Semantics/io06.f90 b/test/Semantics/io06.f90 index d4ea73e51154..eba437c86c86 100644 --- a/test/Semantics/io06.f90 +++ b/test/Semantics/io06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t character(kind=1,len=100) msg1 character(kind=2,len=200) msg2 integer(1) stat1 diff --git a/test/Semantics/io07.f90 b/test/Semantics/io07.f90 index 4677be23ec54..9462a099d67e 100644 --- a/test/Semantics/io07.f90 +++ b/test/Semantics/io07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t 1001 format(A) !ERROR: Format statement must be labeled diff --git a/test/Semantics/io08.f90 b/test/Semantics/io08.f90 index db25da188b9f..1b75e8094a9a 100644 --- a/test/Semantics/io08.f90 +++ b/test/Semantics/io08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t write(*,*) write(*,'()') write(*,'(A)') diff --git a/test/Semantics/io09.f90 b/test/Semantics/io09.f90 index dba5ae53692a..5f50e4e0151e 100644 --- a/test/Semantics/io09.f90 +++ b/test/Semantics/io09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !ERROR: String edit descriptor in READ format expression read(*,'("abc")') diff --git a/test/Semantics/io10.f90 b/test/Semantics/io10.f90 index fa38c3d38e3d..90ae8b194330 100644 --- a/test/Semantics/io10.f90 +++ b/test/Semantics/io10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -Mstandard write(*, '(B0)') diff --git a/test/Semantics/kinds01.f90 b/test/Semantics/kinds01.f90 index 3bef1bb39762..388ca2342167 100644 --- a/test/Semantics/kinds01.f90 +++ b/test/Semantics/kinds01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /MainProgram1/jk1 ObjectEntity INTEGER(1) integer(kind=1) jk1 !DEF: /MainProgram1/js1 ObjectEntity INTEGER(1) diff --git a/test/Semantics/kinds02.f90 b/test/Semantics/kinds02.f90 index 9fb921345d85..0983be564738 100644 --- a/test/Semantics/kinds02.f90 +++ b/test/Semantics/kinds02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C712 The value of scalar-int-constant-expr shall be nonnegative and ! shall specify a representation method that exists on the processor. ! C714 The value of kind-param shall be nonnegative. diff --git a/test/Semantics/kinds03.f90 b/test/Semantics/kinds03.f90 index 63239e08d05a..b4ba7e67bb6c 100644 --- a/test/Semantics/kinds03.f90 +++ b/test/Semantics/kinds03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /MainProgram1/ipdt DerivedType !DEF: /MainProgram1/ipdt/k TypeParam INTEGER(4) type :: ipdt(k) diff --git a/test/Semantics/kinds04.f90 b/test/Semantics/kinds04.f90 index ecf3a446cc3d..af6a8965ca65 100644 --- a/test/Semantics/kinds04.f90 +++ b/test/Semantics/kinds04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C716 If both kind-param and exponent-letter appear, exponent-letter ! shall be E. ! C717 The value of kind-param shall specify an approximation method that diff --git a/test/Semantics/label01.F90 b/test/Semantics/label01.F90 index d4fd7331fb5d..e63bd547ee75 100644 --- a/test/Semantics/label01.F90 +++ b/test/Semantics/label01.F90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s -o /dev/null 2>&1 | grep -v 'procedure conflicts' | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s -o /dev/null 2>&1 | grep -v 'procedure conflicts' | ${FileCheck} %s ! CHECK-NOT: error:[[:space:]] ! FIXME: filter out the array/function syntax issues (procedure conflicts) diff --git a/test/Semantics/label02.f90 b/test/Semantics/label02.f90 index f7b61953b630..6aa052d52d66 100644 --- a/test/Semantics/label02.f90 +++ b/test/Semantics/label02.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: label '0' is out of range ! CHECK: label '100000' is out of range ! CHECK: label '123456' is out of range diff --git a/test/Semantics/label03.f90 b/test/Semantics/label03.f90 index 0ee40e95602f..a33b2f33a9b3 100644 --- a/test/Semantics/label03.f90 +++ b/test/Semantics/label03.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: DO loop doesn't properly nest ! CHECK: DO loop conflicts ! CHECK: label '30' cannot be found diff --git a/test/Semantics/label04.f90 b/test/Semantics/label04.f90 index d9de328642cd..a3f3586763ee 100644 --- a/test/Semantics/label04.f90 +++ b/test/Semantics/label04.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: branch into loop body from outside ! CHECK: do 10 i = 1, m ! CHECK: the loop branched into diff --git a/test/Semantics/label05.f90 b/test/Semantics/label05.f90 index 53f99df8fa6b..09bd9fa2b0f0 100644 --- a/test/Semantics/label05.f90 +++ b/test/Semantics/label05.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: label '50' was not found ! CHECK: label '55' is not in scope ! CHECK: '70' not a branch target diff --git a/test/Semantics/label06.f90 b/test/Semantics/label06.f90 index 42f6631f59ab..4e633d3df552 100644 --- a/test/Semantics/label06.f90 +++ b/test/Semantics/label06.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: label '10' is not in scope ! CHECK: label '20' was not found ! CHECK: '30' not a branch target diff --git a/test/Semantics/label07.f90 b/test/Semantics/label07.f90 index 0f6b57c42f5c..62755082e030 100644 --- a/test/Semantics/label07.f90 +++ b/test/Semantics/label07.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: '30' not a branch target ! CHECK: control flow use of '30' ! CHECK: label '10' is not in scope diff --git a/test/Semantics/label08.f90 b/test/Semantics/label08.f90 index db51c6772c34..140ceb33ec68 100644 --- a/test/Semantics/label08.f90 +++ b/test/Semantics/label08.f90 @@ -1,6 +1,7 @@ +! RUN: %S/test_any.sh %s %flang %t ! negative test -- invalid labels, out of range -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: CYCLE construct-name is not in scope ! CHECK: IF construct name unexpected ! CHECK: unnamed IF statement diff --git a/test/Semantics/label09.f90 b/test/Semantics/label09.f90 index 0ec9efabedcb..a74263d58315 100644 --- a/test/Semantics/label09.f90 +++ b/test/Semantics/label09.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: label '60' was not found subroutine s(a) diff --git a/test/Semantics/label10.f90 b/test/Semantics/label10.f90 index 23a0a055cb63..377108c95dd5 100644 --- a/test/Semantics/label10.f90 +++ b/test/Semantics/label10.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: '60' not a FORMAT ! CHECK: data transfer use of '60' diff --git a/test/Semantics/label11.f90 b/test/Semantics/label11.f90 index 5b1866e4b193..924356615e3b 100644 --- a/test/Semantics/label11.f90 +++ b/test/Semantics/label11.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: BLOCK DATA subprogram name mismatch ! CHECK: should be ! CHECK: FUNCTION name mismatch diff --git a/test/Semantics/label12.f90 b/test/Semantics/label12.f90 index bd3455d2fa90..96607bc8e8f0 100644 --- a/test/Semantics/label12.f90 +++ b/test/Semantics/label12.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: expected end of statement subroutine s diff --git a/test/Semantics/label13.f90 b/test/Semantics/label13.f90 index b55ed6d94341..61501804d270 100644 --- a/test/Semantics/label13.f90 +++ b/test/Semantics/label13.f90 @@ -1,4 +1,5 @@ -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s +! RUN: %S/test_any.sh %s %flang %t +! EXEC: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s ! CHECK: branch into loop body from outside ! CHECK: the loop branched into diff --git a/test/Semantics/label14.f90 b/test/Semantics/label14.f90 index 10a91c755b96..e6eb744f50e3 100644 --- a/test/Semantics/label14.f90 +++ b/test/Semantics/label14.f90 @@ -1,8 +1,9 @@ +! RUN: %S/test_any.sh %s %flang %t ! Tests implemented for this standard ! 11.1.4 - 4 It is permissible to branch to and end-block-stmt only withinh its ! Block Construct -! RUN: ${F18} %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} %s 2>&1 | ${FileCheck} %s ! CHECK: label '20' is not in scope subroutine s1 diff --git a/test/Semantics/misc-declarations.f90 b/test/Semantics/misc-declarations.f90 index a25e5ffbfbf4..9103ad7bcf7d 100644 --- a/test/Semantics/misc-declarations.f90 +++ b/test/Semantics/misc-declarations.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Miscellaneous constraint and requirement checking on declarations: ! - 8.5.6.2 & 8.5.6.3 constraints on coarrays ! - 8.5.19 constraints on the VOLATILE attribute diff --git a/test-lit/Semantics/mod-file-rewriter.f90 b/test/Semantics/mod-file-rewriter.f90 similarity index 100% rename from test-lit/Semantics/mod-file-rewriter.f90 rename to test/Semantics/mod-file-rewriter.f90 diff --git a/test/Semantics/modfile01.f90 b/test/Semantics/modfile01.f90 index 79f5e570bfce..d3cd5273f853 100644 --- a/test/Semantics/modfile01.f90 +++ b/test/Semantics/modfile01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check correct modfile generation for type with private component. module m integer :: i diff --git a/test/Semantics/modfile02.f90 b/test/Semantics/modfile02.f90 index 0f9ba86feb4b..9f460004415d 100644 --- a/test/Semantics/modfile02.f90 +++ b/test/Semantics/modfile02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check modfile generation for private type in public API. module m diff --git a/test/Semantics/modfile03.f90 b/test/Semantics/modfile03.f90 index eedde939b068..9beb5308bd38 100644 --- a/test/Semantics/modfile03.f90 +++ b/test/Semantics/modfile03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check modfile generation with use-association. module m1 diff --git a/test/Semantics/modfile04.f90 b/test/Semantics/modfile04.f90 index 0b5800387255..9dbd3adfeede 100644 --- a/test/Semantics/modfile04.f90 +++ b/test/Semantics/modfile04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! modfile with subprograms module m1 diff --git a/test/Semantics/modfile05.f90 b/test/Semantics/modfile05.f90 index e56023d191cb..49e3f47d4a68 100644 --- a/test/Semantics/modfile05.f90 +++ b/test/Semantics/modfile05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Use-association with VOLATILE or ASYNCHRONOUS module m1 diff --git a/test/Semantics/modfile06.f90 b/test/Semantics/modfile06.f90 index 94fe384dd094..5924b67c7daa 100644 --- a/test/Semantics/modfile06.f90 +++ b/test/Semantics/modfile06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check modfile generation for external interface module m interface diff --git a/test/Semantics/modfile07.f90 b/test/Semantics/modfile07.f90 index 58734b360c1b..b4a49d9924e3 100644 --- a/test/Semantics/modfile07.f90 +++ b/test/Semantics/modfile07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check modfile generation for generic interfaces module m1 interface foo diff --git a/test/Semantics/modfile08.f90 b/test/Semantics/modfile08.f90 index e23078b34dcd..7a2e20195f2d 100644 --- a/test/Semantics/modfile08.f90 +++ b/test/Semantics/modfile08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check modfile generation for external declarations module m real, external :: a diff --git a/test/Semantics/modfile09.f90 b/test/Semantics/modfile09.f90 new file mode 100644 index 000000000000..ec5813b48e51 --- /dev/null +++ b/test/Semantics/modfile09.f90 @@ -0,0 +1 @@ +!RUN: %S/test_modfile.sh '%S/Inputs/modfile09-*' %f18 %t diff --git a/test/Semantics/modfile10.f90 b/test/Semantics/modfile10.f90 index 2340842b2843..dc91d8734b19 100644 --- a/test/Semantics/modfile10.f90 +++ b/test/Semantics/modfile10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test writing procedure bindings in a derived type. module m diff --git a/test/Semantics/modfile11.f90 b/test/Semantics/modfile11.f90 index 89df7d7a068b..ec4dd2f88099 100644 --- a/test/Semantics/modfile11.f90 +++ b/test/Semantics/modfile11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m type t1(a, b, c) integer, kind :: a diff --git a/test/Semantics/modfile12.f90 b/test/Semantics/modfile12.f90 index 89f43ad350eb..ca43611984a4 100644 --- a/test/Semantics/modfile12.f90 +++ b/test/Semantics/modfile12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m integer(8), parameter :: a = 1, b = 2_8 parameter(n=3,l=-3,e=1.0/3.0) diff --git a/test/Semantics/modfile13.f90 b/test/Semantics/modfile13.f90 index 9205eabf6189..c4fcfe71751b 100644 --- a/test/Semantics/modfile13.f90 +++ b/test/Semantics/modfile13.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m character(2) :: z character(len=3) :: y diff --git a/test/Semantics/modfile14.f90 b/test/Semantics/modfile14.f90 index 16fbbc08b11c..1c4fa0e92076 100644 --- a/test/Semantics/modfile14.f90 +++ b/test/Semantics/modfile14.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m type t1 contains diff --git a/test/Semantics/modfile15.f90 b/test/Semantics/modfile15.f90 index 480ad8e77ab2..4cc8787f5d45 100644 --- a/test/Semantics/modfile15.f90 +++ b/test/Semantics/modfile15.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m type :: t procedure(a), pointer, pass :: c diff --git a/test/Semantics/modfile16.f90 b/test/Semantics/modfile16.f90 index e60106148504..acc17d54a282 100644 --- a/test/Semantics/modfile16.f90 +++ b/test/Semantics/modfile16.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m character(2), parameter :: prefix = 'c_' integer, bind(c, name='c_a') :: a diff --git a/test/Semantics/modfile17.f90 b/test/Semantics/modfile17.f90 index 0b91801e1081..33767a38028c 100644 --- a/test/Semantics/modfile17.f90 +++ b/test/Semantics/modfile17.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Tests parameterized derived type instantiation with KIND parameters module m diff --git a/test/Semantics/modfile18.f90 b/test/Semantics/modfile18.f90 index 39f719e4878a..032b0491045b 100644 --- a/test/Semantics/modfile18.f90 +++ b/test/Semantics/modfile18.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Tests folding of array constructors module m diff --git a/test/Semantics/modfile19.f90 b/test/Semantics/modfile19.f90 index 50d50ee6b1e2..fcb10b54e9d0 100644 --- a/test/Semantics/modfile19.f90 +++ b/test/Semantics/modfile19.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m implicit complex(8)(z) real :: x diff --git a/test/Semantics/modfile20.f90 b/test/Semantics/modfile20.f90 index 8677e3479ad2..90188c177c44 100644 --- a/test/Semantics/modfile20.f90 +++ b/test/Semantics/modfile20.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test modfiles for entities with initialization module m integer, parameter :: k8 = 8 diff --git a/test/Semantics/modfile21.f90 b/test/Semantics/modfile21.f90 index 3618ad0ab027..03349a32682d 100644 --- a/test/Semantics/modfile21.f90 +++ b/test/Semantics/modfile21.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m logical b bind(C) :: /cb2/ diff --git a/test/Semantics/modfile22.f90 b/test/Semantics/modfile22.f90 index deb365a7606c..6279ad78678a 100644 --- a/test/Semantics/modfile22.f90 +++ b/test/Semantics/modfile22.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test character length conversions in constructors module m diff --git a/test/Semantics/modfile23.f90 b/test/Semantics/modfile23.f90 index 8bf33b542ed7..4b5637867e1d 100644 --- a/test/Semantics/modfile23.f90 +++ b/test/Semantics/modfile23.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test that subprogram interfaces get all of the symbols that they need. module m1 diff --git a/test/Semantics/modfile24.f90 b/test/Semantics/modfile24.f90 index dc9c7d52a8df..ec446f9e8d3c 100644 --- a/test/Semantics/modfile24.f90 +++ b/test/Semantics/modfile24.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test declarations with coarray-spec ! Different ways of declaring the same coarray. diff --git a/test/Semantics/modfile25.f90 b/test/Semantics/modfile25.f90 index 5c16ead42951..210935df2515 100644 --- a/test/Semantics/modfile25.f90 +++ b/test/Semantics/modfile25.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test compile-time analysis of shapes. module m1 diff --git a/test/Semantics/modfile26.f90 b/test/Semantics/modfile26.f90 index 44d43c6ca788..5064122a3740 100644 --- a/test/Semantics/modfile26.f90 +++ b/test/Semantics/modfile26.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Intrinsics SELECTED_INT_KIND, SELECTED_REAL_KIND, PRECISION, RANGE, ! RADIX, DIGITS diff --git a/test/Semantics/modfile27.f90 b/test/Semantics/modfile27.f90 index ae577d84985c..2a6e23f6f464 100644 --- a/test/Semantics/modfile27.f90 +++ b/test/Semantics/modfile27.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test folding of combined array references and structure component ! references. diff --git a/test/Semantics/modfile28.f90 b/test/Semantics/modfile28.f90 index c53ab04dfc2d..18a349de5ba1 100644 --- a/test/Semantics/modfile28.f90 +++ b/test/Semantics/modfile28.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test UTF-8 support in character literals ! Note: Module files are encoded in UTF-8. diff --git a/test/Semantics/modfile29.f90 b/test/Semantics/modfile29.f90 index 7753e22d0f3e..7afa55120be1 100644 --- a/test/Semantics/modfile29.f90 +++ b/test/Semantics/modfile29.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Check that implicitly typed entities get a type in the module file. module m diff --git a/test/Semantics/modfile30.f90 b/test/Semantics/modfile30.f90 index 427025b91635..ef05b9395139 100644 --- a/test/Semantics/modfile30.f90 +++ b/test/Semantics/modfile30.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Verify miscellaneous bugs ! The function result must be declared after the dummy arguments diff --git a/test/Semantics/modfile31.f90 b/test/Semantics/modfile31.f90 index ec00f9f0ccb9..a29256fe46a2 100644 --- a/test/Semantics/modfile31.f90 +++ b/test/Semantics/modfile31.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test 7.6 enum values module m1 diff --git a/test/Semantics/modfile32.f90 b/test/Semantics/modfile32.f90 index 6db201e852c0..ea5b55a94d05 100644 --- a/test/Semantics/modfile32.f90 +++ b/test/Semantics/modfile32.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Resolution of generic names in expressions. ! Test by using generic function in a specification expression that needs ! to be written to a .mod file. diff --git a/test/Semantics/modfile33.f90 b/test/Semantics/modfile33.f90 index 23a510bf4008..d5474c799f77 100644 --- a/test/Semantics/modfile33.f90 +++ b/test/Semantics/modfile33.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Resolution of user-defined operators in expressions. ! Test by using generic function in a specification expression that needs ! to be written to a .mod file. diff --git a/test/Semantics/modfile34.f90 b/test/Semantics/modfile34.f90 index 16bacf7ade03..59b0fd1a447f 100644 --- a/test/Semantics/modfile34.f90 +++ b/test/Semantics/modfile34.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t ! Test resolution of type-bound generics. module m1 diff --git a/test/Semantics/modfile35.f90 b/test/Semantics/modfile35.f90 index c1d1c9541b1f..9ef35747e947 100644 --- a/test/Semantics/modfile35.f90 +++ b/test/Semantics/modfile35.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_modfile.sh %s %f18 %t module m1 type :: t1 contains diff --git a/test/Semantics/namelist01.f90 b/test/Semantics/namelist01.f90 index 81acecbfc725..f659c998c7ef 100644 --- a/test/Semantics/namelist01.f90 +++ b/test/Semantics/namelist01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test for checking namelist constraints, C8103-C8105 module dup diff --git a/test/Semantics/null01.f90 b/test/Semantics/null01.f90 index f6f5fa79975e..09c6dce22c48 100644 --- a/test/Semantics/null01.f90 +++ b/test/Semantics/null01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! NULL() intrinsic function error tests subroutine test diff --git a/test/Semantics/nullify01.f90 b/test/Semantics/nullify01.f90 index a8a4c7d1c2b8..9af635f8f08c 100644 --- a/test/Semantics/nullify01.f90 +++ b/test/Semantics/nullify01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test that NULLIFY works Module share diff --git a/test/Semantics/nullify02.f90 b/test/Semantics/nullify02.f90 index 2d611f3b7859..49bcc9ef5d11 100644 --- a/test/Semantics/nullify02.f90 +++ b/test/Semantics/nullify02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Check for semantic errors in NULLIFY statements INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/omp-atomic.f90 b/test/Semantics/omp-atomic.f90 index 9a9d027f82e3..760d1ee4f619 100644 --- a/test/Semantics/omp-atomic.f90 +++ b/test/Semantics/omp-atomic.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP 2.13.6 atomic Construct diff --git a/test/Semantics/omp-clause-validity01.f90 b/test/Semantics/omp-clause-validity01.f90 index d624564cd20b..523b2eeb6c10 100644 --- a/test/Semantics/omp-clause-validity01.f90 +++ b/test/Semantics/omp-clause-validity01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP clause validity for the following directives: diff --git a/test/Semantics/omp-declarative-directive.f90 b/test/Semantics/omp-declarative-directive.f90 index 3a7933d25cb7..639ed7d4d895 100644 --- a/test/Semantics/omp-declarative-directive.f90 +++ b/test/Semantics/omp-declarative-directive.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP declarative directives diff --git a/test/Semantics/omp-device-constructs.f90 b/test/Semantics/omp-device-constructs.f90 index e87cb119dba4..7973dc2ef77f 100644 --- a/test/Semantics/omp-device-constructs.f90 +++ b/test/Semantics/omp-device-constructs.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP clause validity for the following directives: ! 2.10 Device constructs diff --git a/test/Semantics/omp-loop-association.f90 b/test/Semantics/omp-loop-association.f90 index 65b79fd5b476..22e9365b2f3f 100644 --- a/test/Semantics/omp-loop-association.f90 +++ b/test/Semantics/omp-loop-association.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check the association between OpenMPLoopConstruct and DoConstruct diff --git a/test/Semantics/omp-nested01.f90 b/test/Semantics/omp-nested01.f90 index 15b1713f6707..0e7220222217 100644 --- a/test/Semantics/omp-nested01.f90 +++ b/test/Semantics/omp-nested01.f90 @@ -1,3 +1,5 @@ +! RUN: %S/test_errors.sh %s %flang %t +!XFAIL: * ! OPTIONS: -fopenmp ! Check OpenMP 2.17 Nesting of Regions diff --git a/test/Semantics/omp-resolve01.f90 b/test/Semantics/omp-resolve01.f90 index 003de6eae171..528915e88f8d 100644 --- a/test/Semantics/omp-resolve01.f90 +++ b/test/Semantics/omp-resolve01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.4 An array section designates a subset of the elements in an array. Although diff --git a/test/Semantics/omp-resolve02.f90 b/test/Semantics/omp-resolve02.f90 index 3703c74cfa96..3d341662b2da 100644 --- a/test/Semantics/omp-resolve02.f90 +++ b/test/Semantics/omp-resolve02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! Test the effect to name resolution from illegal clause diff --git a/test/Semantics/omp-resolve03.f90 b/test/Semantics/omp-resolve03.f90 index 165bfc35773b..a896ef30c9f4 100644 --- a/test/Semantics/omp-resolve03.f90 +++ b/test/Semantics/omp-resolve03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Although variables in common blocks can be accessed by use association diff --git a/test/Semantics/omp-resolve04.f90 b/test/Semantics/omp-resolve04.f90 index d9ea847cb1b8..234013898b87 100644 --- a/test/Semantics/omp-resolve04.f90 +++ b/test/Semantics/omp-resolve04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/omp-resolve05.f90 b/test/Semantics/omp-resolve05.f90 index 0ba4fd816d92..ebc50476b499 100644 --- a/test/Semantics/omp-resolve05.f90 +++ b/test/Semantics/omp-resolve05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/omp-symbol01.f90 b/test/Semantics/omp-symbol01.f90 index bec8e0450dd5..70782f3adf41 100644 --- a/test/Semantics/omp-symbol01.f90 +++ b/test/Semantics/omp-symbol01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! Test clauses that accept list. diff --git a/test/Semantics/omp-symbol02.f90 b/test/Semantics/omp-symbol02.f90 index 3419c61e13db..eddb6865e88c 100644 --- a/test/Semantics/omp-symbol02.f90 +++ b/test/Semantics/omp-symbol02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 1.4.1 Structure of the OpenMP Memory Model diff --git a/test/Semantics/omp-symbol03.f90 b/test/Semantics/omp-symbol03.f90 index a158ee87a425..54072a1e1049 100644 --- a/test/Semantics/omp-symbol03.f90 +++ b/test/Semantics/omp-symbol03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 1.4.1 Structure of the OpenMP Memory Model diff --git a/test/Semantics/omp-symbol04.f90 b/test/Semantics/omp-symbol04.f90 index 4824c78dc92b..052fa859cd32 100644 --- a/test/Semantics/omp-symbol04.f90 +++ b/test/Semantics/omp-symbol04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/omp-symbol05.f90 b/test/Semantics/omp-symbol05.f90 index 7e4e691c5b96..1a4b42e1ce32 100644 --- a/test/Semantics/omp-symbol05.f90 +++ b/test/Semantics/omp-symbol05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.2 threadprivate Directive diff --git a/test/Semantics/omp-symbol06.f90 b/test/Semantics/omp-symbol06.f90 index c1d7581db8be..b8ac0fc06115 100644 --- a/test/Semantics/omp-symbol06.f90 +++ b/test/Semantics/omp-symbol06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/omp-symbol07.f90 b/test/Semantics/omp-symbol07.f90 index 170452959e01..c6cf500b41da 100644 --- a/test/Semantics/omp-symbol07.f90 +++ b/test/Semantics/omp-symbol07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! Generic tests diff --git a/test/Semantics/omp-symbol08.f90 b/test/Semantics/omp-symbol08.f90 index ac09e1690677..3a11933ac023 100644 --- a/test/Semantics/omp-symbol08.f90 +++ b/test/Semantics/omp-symbol08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.1.1 Predetermined rules for associated do-loops index variable diff --git a/test/Semantics/procinterface01.f90 b/test/Semantics/procinterface01.f90 index 5ab53d530ef3..b66206e24134 100644 --- a/test/Semantics/procinterface01.f90 +++ b/test/Semantics/procinterface01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Tests for "proc-interface" semantics. ! These cases are all valid. diff --git a/test/Semantics/resolve01.f90 b/test/Semantics/resolve01.f90 index 0c257fe1be9f..eee8d662517f 100644 --- a/test/Semantics/resolve01.f90 +++ b/test/Semantics/resolve01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t integer :: x !ERROR: The type of 'x' has already been declared real :: x diff --git a/test/Semantics/resolve02.f90 b/test/Semantics/resolve02.f90 index ddc419b392c3..0d8e83b0ed29 100644 --- a/test/Semantics/resolve02.f90 +++ b/test/Semantics/resolve02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s !ERROR: Declaration of 'x' conflicts with its use as internal procedure real :: x diff --git a/test/Semantics/resolve03.f90 b/test/Semantics/resolve03.f90 index 63a88f143adc..773aaab3d453 100644 --- a/test/Semantics/resolve03.f90 +++ b/test/Semantics/resolve03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t implicit none integer :: x !ERROR: No explicit type declared for 'y' diff --git a/test/Semantics/resolve04.f90 b/test/Semantics/resolve04.f90 index 8998acdca244..5132b9f780f6 100644 --- a/test/Semantics/resolve04.f90 +++ b/test/Semantics/resolve04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !ERROR: No explicit type declared for 'f' function f() implicit none diff --git a/test/Semantics/resolve05.f90 b/test/Semantics/resolve05.f90 index d485a34b6532..d1960e1808b1 100644 --- a/test/Semantics/resolve05.f90 +++ b/test/Semantics/resolve05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t program p integer :: p ! this is ok end diff --git a/test/Semantics/resolve06.f90 b/test/Semantics/resolve06.f90 index 12e0e2d4b126..276feb3b4ee4 100644 --- a/test/Semantics/resolve06.f90 +++ b/test/Semantics/resolve06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t implicit none allocatable :: x integer :: x diff --git a/test/Semantics/resolve07.f90 b/test/Semantics/resolve07.f90 index 585bf633b2ad..f2e46f42a9d1 100644 --- a/test/Semantics/resolve07.f90 +++ b/test/Semantics/resolve07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 integer :: x(2) !ERROR: The dimensions of 'x' have already been declared diff --git a/test/Semantics/resolve08.f90 b/test/Semantics/resolve08.f90 index 32274ce49df1..7252c79ef033 100644 --- a/test/Semantics/resolve08.f90 +++ b/test/Semantics/resolve08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t integer :: g(10) f(i) = i + 1 ! statement function g(i) = i + 2 ! mis-parsed array assignment diff --git a/test/Semantics/resolve09.f90 b/test/Semantics/resolve09.f90 index f288dad1a965..5104a371a639 100644 --- a/test/Semantics/resolve09.f90 +++ b/test/Semantics/resolve09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t integer :: y procedure() :: a procedure(real) :: b diff --git a/test/Semantics/resolve10.f90 b/test/Semantics/resolve10.f90 index 75a44a4f5e57..9990935899fa 100644 --- a/test/Semantics/resolve10.f90 +++ b/test/Semantics/resolve10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m public type t diff --git a/test/Semantics/resolve11.f90 b/test/Semantics/resolve11.f90 index 1114339a1bdb..d94c0f8c87d1 100644 --- a/test/Semantics/resolve11.f90 +++ b/test/Semantics/resolve11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m public i integer, private :: j diff --git a/test/Semantics/resolve12.f90 b/test/Semantics/resolve12.f90 index 1d2e1c398642..03bad9f5616f 100644 --- a/test/Semantics/resolve12.f90 +++ b/test/Semantics/resolve12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 end diff --git a/test/Semantics/resolve13.f90 b/test/Semantics/resolve13.f90 index c67c59287ac3..6fc03b1e8be0 100644 --- a/test/Semantics/resolve13.f90 +++ b/test/Semantics/resolve13.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 integer :: x integer, private :: y diff --git a/test/Semantics/resolve14.f90 b/test/Semantics/resolve14.f90 index d9693e3a1ffd..326fe8e94894 100644 --- a/test/Semantics/resolve14.f90 +++ b/test/Semantics/resolve14.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 integer :: x integer :: y diff --git a/test/Semantics/resolve15.f90 b/test/Semantics/resolve15.f90 index 6ad7b2534797..1cca8ce3dd7b 100644 --- a/test/Semantics/resolve15.f90 +++ b/test/Semantics/resolve15.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m real :: var interface i diff --git a/test/Semantics/resolve16.f90 b/test/Semantics/resolve16.f90 index 798b88bd8b2d..8ce084a26fe9 100644 --- a/test/Semantics/resolve16.f90 +++ b/test/Semantics/resolve16.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m interface subroutine sub0 diff --git a/test/Semantics/resolve17.f90 b/test/Semantics/resolve17.f90 index 360115333235..f9c9451dcfe2 100644 --- a/test/Semantics/resolve17.f90 +++ b/test/Semantics/resolve17.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m integer :: foo !Note: PGI, Intel, and GNU allow this; NAG and Sun do not diff --git a/test/Semantics/resolve18.f90 b/test/Semantics/resolve18.f90 index ed9d301106eb..dff395f4bc9b 100644 --- a/test/Semantics/resolve18.f90 +++ b/test/Semantics/resolve18.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 implicit none contains diff --git a/test/Semantics/resolve19.f90 b/test/Semantics/resolve19.f90 index 15f902a2ba46..f28f2b45abdf 100644 --- a/test/Semantics/resolve19.f90 +++ b/test/Semantics/resolve19.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m interface a subroutine s(x) diff --git a/test/Semantics/resolve20.f90 b/test/Semantics/resolve20.f90 index 33c67dd24923..38dbd2367fe4 100644 --- a/test/Semantics/resolve20.f90 +++ b/test/Semantics/resolve20.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m abstract interface subroutine foo diff --git a/test/Semantics/resolve21.f90 b/test/Semantics/resolve21.f90 index 38fc699ca018..764537a565f5 100644 --- a/test/Semantics/resolve21.f90 +++ b/test/Semantics/resolve21.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 type :: t integer :: i diff --git a/test/Semantics/resolve22.f90 b/test/Semantics/resolve22.f90 index cc8c9ed75dad..3549ec76e777 100644 --- a/test/Semantics/resolve22.f90 +++ b/test/Semantics/resolve22.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 !OK: interface followed by type with same name interface t diff --git a/test/Semantics/resolve23.f90 b/test/Semantics/resolve23.f90 index 504363b458e1..41644843bf1f 100644 --- a/test/Semantics/resolve23.f90 +++ b/test/Semantics/resolve23.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m type :: t real :: y diff --git a/test/Semantics/resolve24.f90 b/test/Semantics/resolve24.f90 index 87917ba09fbc..c2ce595d9054 100644 --- a/test/Semantics/resolve24.f90 +++ b/test/Semantics/resolve24.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine test1 !ERROR: Generic interface 'foo' has both a function and a subroutine interface foo diff --git a/test/Semantics/resolve25.f90 b/test/Semantics/resolve25.f90 index 62e0ba6ff2d0..4d3ec8c81495 100644 --- a/test/Semantics/resolve25.f90 +++ b/test/Semantics/resolve25.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m interface foo subroutine s1(x) diff --git a/test/Semantics/resolve26.f90 b/test/Semantics/resolve26.f90 index 343ee1eb9160..f39366faaef0 100644 --- a/test/Semantics/resolve26.f90 +++ b/test/Semantics/resolve26.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 interface module subroutine s() diff --git a/test/Semantics/resolve27.f90 b/test/Semantics/resolve27.f90 index 3f04c1aa64fa..b10105ed9e7d 100644 --- a/test/Semantics/resolve27.f90 +++ b/test/Semantics/resolve27.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m interface module subroutine s() diff --git a/test/Semantics/resolve28.f90 b/test/Semantics/resolve28.f90 index 2843c2cbb071..0fd81807c97f 100644 --- a/test/Semantics/resolve28.f90 +++ b/test/Semantics/resolve28.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s type t end type diff --git a/test/Semantics/resolve29.f90 b/test/Semantics/resolve29.f90 index f692b0c0e91d..d328eba594e7 100644 --- a/test/Semantics/resolve29.f90 +++ b/test/Semantics/resolve29.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m type t1 end type diff --git a/test/Semantics/resolve30.f90 b/test/Semantics/resolve30.f90 index 69121e03ec1e..98777124b134 100644 --- a/test/Semantics/resolve30.f90 +++ b/test/Semantics/resolve30.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 integer x block diff --git a/test/Semantics/resolve31.f90 b/test/Semantics/resolve31.f90 index 982cb56ad564..3c61cd0bb9dc 100644 --- a/test/Semantics/resolve31.f90 +++ b/test/Semantics/resolve31.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 integer :: t0 !ERROR: 't0' is not a derived type diff --git a/test/Semantics/resolve32.f90 b/test/Semantics/resolve32.f90 index 6f6ed8fb0bf9..317a0ad9ed12 100644 --- a/test/Semantics/resolve32.f90 +++ b/test/Semantics/resolve32.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m2 public s2, s4 private s3 diff --git a/test/Semantics/resolve33.f90 b/test/Semantics/resolve33.f90 index 214a678eb567..4a37c5fb57aa 100644 --- a/test/Semantics/resolve33.f90 +++ b/test/Semantics/resolve33.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Derived type parameters module m diff --git a/test/Semantics/resolve34.f90 b/test/Semantics/resolve34.f90 index d9a2a233e8d4..9d148ff43046 100644 --- a/test/Semantics/resolve34.f90 +++ b/test/Semantics/resolve34.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Extended derived types module m1 diff --git a/test/Semantics/resolve35.f90 b/test/Semantics/resolve35.f90 index 6acd24f49b5e..7f6a8ea9492b 100644 --- a/test/Semantics/resolve35.f90 +++ b/test/Semantics/resolve35.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Construct names subroutine s1 diff --git a/test/Semantics/resolve36.f90 b/test/Semantics/resolve36.f90 index e74d6fb62cbf..438ad1aeca92 100644 --- a/test/Semantics/resolve36.f90 +++ b/test/Semantics/resolve36.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 interface module subroutine sub1(arg1) diff --git a/test/Semantics/resolve37.f90 b/test/Semantics/resolve37.f90 index ccc05f3d1715..a07ebbc6625b 100644 --- a/test/Semantics/resolve37.f90 +++ b/test/Semantics/resolve37.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C701 The type-param-value for a kind type parameter shall be a constant ! expression. This constraint looks like a mistake in the standard. integer, parameter :: k = 8 diff --git a/test/Semantics/resolve38.f90 b/test/Semantics/resolve38.f90 index ebc29b7c8ed8..53e8db813380 100644 --- a/test/Semantics/resolve38.f90 +++ b/test/Semantics/resolve38.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C772 module m1 type t1 diff --git a/test/Semantics/resolve39.f90 b/test/Semantics/resolve39.f90 index a5b50afe2062..d0052f16f863 100644 --- a/test/Semantics/resolve39.f90 +++ b/test/Semantics/resolve39.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 implicit none real(8) :: x = 2.0 diff --git a/test/Semantics/resolve40.f90 b/test/Semantics/resolve40.f90 index 1137126740af..95c2c9e8034c 100644 --- a/test/Semantics/resolve40.f90 +++ b/test/Semantics/resolve40.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 namelist /nl/x block diff --git a/test/Semantics/resolve41.f90 b/test/Semantics/resolve41.f90 index 2f618675de60..e2bf877016ed 100644 --- a/test/Semantics/resolve41.f90 +++ b/test/Semantics/resolve41.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m implicit none real, parameter :: a = 8.0 diff --git a/test/Semantics/resolve42.f90 b/test/Semantics/resolve42.f90 index e71e4c881712..5b6ac9f88b2b 100644 --- a/test/Semantics/resolve42.f90 +++ b/test/Semantics/resolve42.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1 !ERROR: Array 'z' without ALLOCATABLE or POINTER attribute must have explicit shape common x, y(4), z(:) diff --git a/test/Semantics/resolve43.f90 b/test/Semantics/resolve43.f90 index ed2454a535ec..385dfedc34bd 100644 --- a/test/Semantics/resolve43.f90 +++ b/test/Semantics/resolve43.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for structure constructors. ! Errors caught by expression resolution are tested elsewhere; these are the ! errors meant to be caught by name resolution, as well as acceptable use diff --git a/test/Semantics/resolve44.f90 b/test/Semantics/resolve44.f90 index f6e7a89ba5c3..dd082adc89df 100644 --- a/test/Semantics/resolve44.f90 +++ b/test/Semantics/resolve44.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for recursive use of derived types. program main diff --git a/test/Semantics/resolve45.f90 b/test/Semantics/resolve45.f90 index ebc9e21b5137..e28dc33c4e72 100644 --- a/test/Semantics/resolve45.f90 +++ b/test/Semantics/resolve45.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t function f1(x, y) integer x !ERROR: SAVE attribute may not be applied to dummy argument 'x' diff --git a/test/Semantics/resolve46.f90 b/test/Semantics/resolve46.f90 index 8a0385ae28b7..181ccfb5c280 100644 --- a/test/Semantics/resolve46.f90 +++ b/test/Semantics/resolve46.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C1030 - pointers to intrinsic procedures program main intrinsic :: cos ! a specific & generic intrinsic name diff --git a/test/Semantics/resolve47.f90 b/test/Semantics/resolve47.f90 index 2c5f8141b967..04dab5616855 100644 --- a/test/Semantics/resolve47.f90 +++ b/test/Semantics/resolve47.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t module m1 !ERROR: Logical constant '.true.' may not be used as a defined operator interface operator(.TRUE.) diff --git a/test/Semantics/resolve48.f90 b/test/Semantics/resolve48.f90 index ba3dea3c41f1..887505d16442 100644 --- a/test/Semantics/resolve48.f90 +++ b/test/Semantics/resolve48.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test correct use-association of a derived type. module m1 implicit none diff --git a/test/Semantics/resolve49.f90 b/test/Semantics/resolve49.f90 index ac470834ff91..97d2cbdb1267 100644 --- a/test/Semantics/resolve49.f90 +++ b/test/Semantics/resolve49.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test section subscript program p1 real :: a(10,10) diff --git a/test/Semantics/resolve50.f90 b/test/Semantics/resolve50.f90 index 7d3ad7e105a3..34d6f1c1d5d5 100644 --- a/test/Semantics/resolve50.f90 +++ b/test/Semantics/resolve50.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test coarray association in CHANGE TEAM statement subroutine s1 diff --git a/test/Semantics/resolve51.f90 b/test/Semantics/resolve51.f90 index 73dafaa406b5..de763ef49911 100644 --- a/test/Semantics/resolve51.f90 +++ b/test/Semantics/resolve51.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test SELECT TYPE errors: C1157 subroutine s1() diff --git a/test/Semantics/resolve52.f90 b/test/Semantics/resolve52.f90 index 3ee41dd3503f..846b412f05ca 100644 --- a/test/Semantics/resolve52.f90 +++ b/test/Semantics/resolve52.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Tests for C760: ! The passed-object dummy argument shall be a scalar, nonpointer, nonallocatable ! dummy data object with the same declared type as the type being defined; diff --git a/test/Semantics/resolve53.f90 b/test/Semantics/resolve53.f90 index 5cfe16410500..1aee5e79bcc9 100644 --- a/test/Semantics/resolve53.f90 +++ b/test/Semantics/resolve53.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! 15.4.3.4.5 Restrictions on generic declarations ! Specific procedures of generic interfaces must be distinguishable. diff --git a/test/Semantics/resolve54.f90 b/test/Semantics/resolve54.f90 index aed15410ddbc..f9f895fa7f05 100644 --- a/test/Semantics/resolve54.f90 +++ b/test/Semantics/resolve54.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Tests based on examples in C.10.6 ! C.10.6(10) diff --git a/test/Semantics/resolve55.f90 b/test/Semantics/resolve55.f90 index 59f0027d9aef..98006bc0a07b 100644 --- a/test/Semantics/resolve55.f90 +++ b/test/Semantics/resolve55.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Tests for C1128: ! A variable-name that appears in a LOCAL or LOCAL_INIT locality-spec shall not ! have the ALLOCATABLE; INTENT (IN); or OPTIONAL attribute; shall not be of diff --git a/test/Semantics/resolve56.f90 b/test/Semantics/resolve56.f90 index 65d5fa2c84b8..1efa535bd434 100644 --- a/test/Semantics/resolve56.f90 +++ b/test/Semantics/resolve56.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test that associations constructs can be correctly combined. The intrinsic ! functions are not what is tested here, they are only use to reveal the types ! of local variables. diff --git a/test/Semantics/resolve57.f90 b/test/Semantics/resolve57.f90 index c5e8661206ad..265decd3bcde 100644 --- a/test/Semantics/resolve57.f90 +++ b/test/Semantics/resolve57.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Tests for the last sentence of C1128: !A variable-name that is not permitted to appear in a variable definition !context shall not appear in a LOCAL or LOCAL_INIT locality-spec. diff --git a/test/Semantics/resolve58.f90 b/test/Semantics/resolve58.f90 index 00232dc9d843..db11e6779335 100644 --- a/test/Semantics/resolve58.f90 +++ b/test/Semantics/resolve58.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1(x, y) !ERROR: Array pointer 'x' must have deferred shape or assumed rank real, pointer :: x(1:) ! C832 diff --git a/test/Semantics/resolve59.f90 b/test/Semantics/resolve59.f90 index e34fcaea01d2..0e6965a5d165 100644 --- a/test/Semantics/resolve59.f90 +++ b/test/Semantics/resolve59.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Testing 15.6.2.2 point 4 (What function-name refers to depending on the ! presence of RESULT). diff --git a/test/Semantics/resolve60.f90 b/test/Semantics/resolve60.f90 index 843057d758c8..3232bc0fb87a 100644 --- a/test/Semantics/resolve60.f90 +++ b/test/Semantics/resolve60.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Testing 7.6 enum ! OK diff --git a/test/Semantics/resolve61.f90 b/test/Semantics/resolve61.f90 index 727b2643ca5c..eb5ba13a07a3 100644 --- a/test/Semantics/resolve61.f90 +++ b/test/Semantics/resolve61.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t program p1 integer(8) :: a, b, c, d pointer(a, b) diff --git a/test/Semantics/resolve62.f90 b/test/Semantics/resolve62.f90 index 06c3ed1afe63..5de3a45e900f 100644 --- a/test/Semantics/resolve62.f90 +++ b/test/Semantics/resolve62.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Resolve generic based on number of arguments subroutine s1 interface f diff --git a/test/Semantics/resolve63.f90 b/test/Semantics/resolve63.f90 index 49b4e7b0d20d..07ae767d676b 100644 --- a/test/Semantics/resolve63.f90 +++ b/test/Semantics/resolve63.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Invalid operand types when user-defined operator is available module m1 type :: t diff --git a/test/Semantics/resolve64.f90 b/test/Semantics/resolve64.f90 index 360605a000ec..3be2ae14fd5d 100644 --- a/test/Semantics/resolve64.f90 +++ b/test/Semantics/resolve64.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !OPTIONS: -flogical-abbreviations -fxor-operator ! Like m4 in resolve63 but compiled with different options. diff --git a/test/Semantics/resolve65.f90 b/test/Semantics/resolve65.f90 index 8c3264cc36f9..9e1278b66dd5 100644 --- a/test/Semantics/resolve65.f90 +++ b/test/Semantics/resolve65.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test restrictions on what subprograms can be used for defined assignment. module m1 diff --git a/test/Semantics/resolve66.f90 b/test/Semantics/resolve66.f90 index 2b82b5f0ec13..d54fd2bfe66c 100644 --- a/test/Semantics/resolve66.f90 +++ b/test/Semantics/resolve66.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test that user-defined assignment is used in the right places module m1 diff --git a/test/Semantics/resolve67.f90 b/test/Semantics/resolve67.f90 index 3f2b2572ffc0..7a8537a0a65e 100644 --- a/test/Semantics/resolve67.f90 +++ b/test/Semantics/resolve67.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test restrictions on what subprograms can be used for defined operators. ! See: 15.4.3.4.2 diff --git a/test/Semantics/resolve68.f90 b/test/Semantics/resolve68.f90 index 06cd13716d43..6accdafd5263 100644 --- a/test/Semantics/resolve68.f90 +++ b/test/Semantics/resolve68.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Test resolution of type-bound generics. module m1 diff --git a/test/Semantics/resolve69.f90 b/test/Semantics/resolve69.f90 index bf08c3a706b0..3bbc37e3f7aa 100644 --- a/test/Semantics/resolve69.f90 +++ b/test/Semantics/resolve69.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t subroutine s1() ! C701 (R701) The type-param-value for a kind type parameter shall be a ! constant expression. diff --git a/test/Semantics/resolve70.f90 b/test/Semantics/resolve70.f90 index 8824ea4249af..31f33c345b63 100644 --- a/test/Semantics/resolve70.f90 +++ b/test/Semantics/resolve70.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C703 (R702) The derived-type-spec shall not specify an abstract type (7.5.7). ! This constraint refers to the derived-type-spec in a type-spec. A type-spec ! can appear in an ALLOCATE statement, an ac-spec for an array constructor, and diff --git a/test/Semantics/resolve71.f90 b/test/Semantics/resolve71.f90 index d570233d4633..8c1c56fd9b0e 100644 --- a/test/Semantics/resolve71.f90 +++ b/test/Semantics/resolve71.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C708 An entity declared with the CLASS keyword shall be a dummy argument ! or have the ALLOCATABLE or POINTER attribute. subroutine s() diff --git a/test/Semantics/resolve72.f90 b/test/Semantics/resolve72.f90 index 6ff2603b2129..284fb2fc2055 100644 --- a/test/Semantics/resolve72.f90 +++ b/test/Semantics/resolve72.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C709 An assumed-type entity shall be a dummy data object that does not have ! the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE attribute and ! is not an explicit-shape array. diff --git a/test/Semantics/resolve73.f90 b/test/Semantics/resolve73.f90 index 191be316b620..35f8429aeacf 100644 --- a/test/Semantics/resolve73.f90 +++ b/test/Semantics/resolve73.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C721 A type-param-value of * shall be used only ! * to declare a dummy argument, ! * to declare a named constant, diff --git a/test/Semantics/resolve74.f90 b/test/Semantics/resolve74.f90 index a674b1f37ac2..60927b198769 100644 --- a/test/Semantics/resolve74.f90 +++ b/test/Semantics/resolve74.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C722 A function name shall not be declared with an asterisk type-param-value ! unless it is of type CHARACTER and is the name of a dummy function or the ! name of the result of an external function. diff --git a/test/Semantics/resolve75.f90 b/test/Semantics/resolve75.f90 index 2c63a36fe523..708ce8ffaeec 100644 --- a/test/Semantics/resolve75.f90 +++ b/test/Semantics/resolve75.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! C726 The length specified for a character statement function or for a ! statement function dummy argument of type character shall be a constant ! expression. diff --git a/test/Semantics/separate-module-procs.f90 b/test/Semantics/separate-module-procs.f90 index ba3b1abcc991..33dfcd557fde 100644 --- a/test/Semantics/separate-module-procs.f90 +++ b/test/Semantics/separate-module-procs.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t !===--- separate-module-procs.f90 - Test separate module procedure ---------=== ! ! Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. diff --git a/test/Semantics/stop01.f90 b/test/Semantics/stop01.f90 index 91112b11a801..2ae8d65a84bb 100644 --- a/test/Semantics/stop01.f90 +++ b/test/Semantics/stop01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t program main implicit none integer :: i = -1 diff --git a/test/Semantics/structconst01.f90 b/test/Semantics/structconst01.f90 index a83286c422ab..68f0261cd85d 100644 --- a/test/Semantics/structconst01.f90 +++ b/test/Semantics/structconst01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for structure constructors. ! Errors caught by name resolution are tested elsewhere; these are the ! errors meant to be caught by expression semantic analysis, as well as diff --git a/test/Semantics/structconst02.f90 b/test/Semantics/structconst02.f90 index 923aa6071b09..22428651fa1c 100644 --- a/test/Semantics/structconst02.f90 +++ b/test/Semantics/structconst02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for structure constructors: per-component type ! (in)compatibility. diff --git a/test/Semantics/structconst03.f90 b/test/Semantics/structconst03.f90 index e637bc08d3e3..776b4d082309 100644 --- a/test/Semantics/structconst03.f90 +++ b/test/Semantics/structconst03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for structure constructors: C1594 violations ! from assigning globally-visible data to POINTER components. ! test/Semantics/structconst04.f90 is this same test without type diff --git a/test/Semantics/structconst04.f90 b/test/Semantics/structconst04.f90 index a2d7421945f4..07a9d69df868 100644 --- a/test/Semantics/structconst04.f90 +++ b/test/Semantics/structconst04.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_errors.sh %s %flang %t ! Error tests for structure constructors: C1594 violations ! from assigning globally-visible data to POINTER components. ! This test is structconst03.f90 with the type parameters removed. diff --git a/test/Semantics/symbol01.f90 b/test/Semantics/symbol01.f90 index 7a6476dddded..9d8cacd3d6b8 100644 --- a/test/Semantics/symbol01.f90 +++ b/test/Semantics/symbol01.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Test that intent-stmt and subprogram prefix and suffix are resolved. !DEF: /m Module diff --git a/test/Semantics/symbol02.f90 b/test/Semantics/symbol02.f90 index ba048a20ef91..8f53c50580ed 100644 --- a/test/Semantics/symbol02.f90 +++ b/test/Semantics/symbol02.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Test host association in module subroutine and internal subroutine. !DEF: /m Module diff --git a/test/Semantics/symbol03.f90 b/test/Semantics/symbol03.f90 index 778794c1a14d..41a7cc26e694 100644 --- a/test/Semantics/symbol03.f90 +++ b/test/Semantics/symbol03.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Test host association in internal subroutine of main program. !DEF: /main MainProgram diff --git a/test/Semantics/symbol05.f90 b/test/Semantics/symbol05.f90 index 4bc42aca6296..678b8f19f55d 100644 --- a/test/Semantics/symbol05.f90 +++ b/test/Semantics/symbol05.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Explicit and implicit entities in blocks !DEF: /s1 (Subroutine) Subprogram diff --git a/test/Semantics/symbol06.f90 b/test/Semantics/symbol06.f90 index 804017bbdf13..b3b3e17b10da 100644 --- a/test/Semantics/symbol06.f90 +++ b/test/Semantics/symbol06.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /main MainProgram program main !DEF: /main/t1 DerivedType diff --git a/test/Semantics/symbol07.f90 b/test/Semantics/symbol07.f90 index 787dfc5b0ec7..b387ec6c673b 100644 --- a/test/Semantics/symbol07.f90 +++ b/test/Semantics/symbol07.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /main MainProgram program main implicit complex(z) diff --git a/test/Semantics/symbol08.f90 b/test/Semantics/symbol08.f90 index e0a65b84e7bb..801f7f449b20 100644 --- a/test/Semantics/symbol08.f90 +++ b/test/Semantics/symbol08.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /main MainProgram program main !DEF: /main/x POINTER ObjectEntity REAL(4) diff --git a/test/Semantics/symbol09.f90 b/test/Semantics/symbol09.f90 index 8dca1332a538..77d4a3416df3 100644 --- a/test/Semantics/symbol09.f90 +++ b/test/Semantics/symbol09.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /s1 (Subroutine) Subprogram subroutine s1 !DEF: /s1/a ObjectEntity REAL(4) diff --git a/test/Semantics/symbol10.f90 b/test/Semantics/symbol10.f90 index c9cf1ce6148a..e487764fa5a2 100644 --- a/test/Semantics/symbol10.f90 +++ b/test/Semantics/symbol10.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /m1 Module module m1 contains diff --git a/test/Semantics/symbol11.f90 b/test/Semantics/symbol11.f90 index d3312eaa293b..e759310c8dcb 100644 --- a/test/Semantics/symbol11.f90 +++ b/test/Semantics/symbol11.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t !DEF: /s1 (Subroutine) Subprogram subroutine s1 implicit none diff --git a/test/Semantics/symbol12.f90 b/test/Semantics/symbol12.f90 index e13c09542720..22350f6c25e2 100644 --- a/test/Semantics/symbol12.f90 +++ b/test/Semantics/symbol12.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Verify that SAVE attribute is propagated by EQUIVALENCE !DEF: /s1 (Subroutine) Subprogram diff --git a/test/Semantics/symbol13.f90 b/test/Semantics/symbol13.f90 index 76235db206da..640066ed76ea 100644 --- a/test/Semantics/symbol13.f90 +++ b/test/Semantics/symbol13.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Old-style "*length" specifiers (R723) !DEF: /f1 (Function) Subprogram CHARACTER(1_8,1) diff --git a/test/Semantics/symbol14.f90 b/test/Semantics/symbol14.f90 index c990665e8d6e..d523e8d6f480 100644 --- a/test/Semantics/symbol14.f90 +++ b/test/Semantics/symbol14.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! "Bare" uses of type parameters and components !DEF: /MainProgram1/t1 DerivedType diff --git a/test/Semantics/symbol15.f90 b/test/Semantics/symbol15.f90 index 4ad09b395ffc..00298cfa1d84 100644 --- a/test/Semantics/symbol15.f90 +++ b/test/Semantics/symbol15.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Forward references in pointer initializers and TBP bindings. !DEF: /m Module diff --git a/test/Semantics/symbol16.f90 b/test/Semantics/symbol16.f90 index a90ab83d2ac1..0650222e0833 100644 --- a/test/Semantics/symbol16.f90 +++ b/test/Semantics/symbol16.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Statement functions !DEF: /p1 MainProgram diff --git a/test/Semantics/symbol17.f90 b/test/Semantics/symbol17.f90 index a861e2f6f260..a99c8245f6d7 100644 --- a/test/Semantics/symbol17.f90 +++ b/test/Semantics/symbol17.f90 @@ -1,3 +1,4 @@ +! RUN: %S/test_symbols.sh %s %flang %t ! Forward references to derived types (non-error cases) !DEF: /main MainProgram From 3f99d35f3d4f57f704fd91a0d6ba8af308e900b3 Mon Sep 17 00:00:00 2001 From: Luke Ireland Date: Fri, 6 Mar 2020 11:21:36 +0000 Subject: [PATCH 087/345] Added CMakeLists changes, moved config and made test scripts compatible. All Fortran tests are now run in lit, except Preprocessing tests #1052 Preprocessing tests are a separate kind of test, so will be sorted out later. --- CMakeLists.txt | 2 +- {test-lit => test}/CMakeLists.txt | 0 test/Evaluate/test_folding.sh | 21 +- test/Preprocessing/lit.local.cfg.py | 7 + test/Semantics/CMakeLists.txt | 346 -------------------------- test/Semantics/common.sh | 17 +- test/Semantics/test_any.sh | 4 +- {test-lit => test}/lit.cfg.py | 14 +- {test-lit => test}/lit.site.cfg.py.in | 2 +- 9 files changed, 33 insertions(+), 380 deletions(-) rename {test-lit => test}/CMakeLists.txt (100%) create mode 100644 test/Preprocessing/lit.local.cfg.py delete mode 100644 test/Semantics/CMakeLists.txt rename {test-lit => test}/lit.cfg.py (91%) rename {test-lit => test}/lit.site.cfg.py.in (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f19259df77e1..8883fb7e7597 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,7 +156,7 @@ add_subdirectory(lib) add_subdirectory(runtime) add_subdirectory(unittests) add_subdirectory(tools) -add_subdirectory(test-lit) +add_subdirectory(test) configure_file( ${FLANG_SOURCE_DIR}/include/flang/Config/config.h.cmake diff --git a/test-lit/CMakeLists.txt b/test/CMakeLists.txt similarity index 100% rename from test-lit/CMakeLists.txt rename to test/CMakeLists.txt diff --git a/test/Evaluate/test_folding.sh b/test/Evaluate/test_folding.sh index 84b10eb23609..3834c21edad7 100755 --- a/test/Evaluate/test_folding.sh +++ b/test/Evaluate/test_folding.sh @@ -19,28 +19,25 @@ # - test_x is not folded (it is neither .true. nor .false.). This means the # compiler could not fold the expression. -PATH=/usr/bin:/bin -srcdir=$(dirname $0) -F18CC=${F18:-../../../tools/f18/bin/f18} -CMD="$F18CC -fdebug-dump-symbols -fparse-only" +CMD="$2 -fdebug-dump-symbols -fparse-only" -if [[ $# < 1 ]]; then - echo "Usage: $0 [-pgmath=]" +if [[ $# < 3 ]]; then + echo "Usage: $0 " exit 1 fi -src=$srcdir/$1 +src=$1 [[ ! -f $src ]] && echo "File not found: $src" && exit 1 -temp=temp-$1 -rm -rf $temp -mkdir $temp -[[ $KEEP ]] || trap "rm -rf $temp" EXIT +temp=$3 +mkdir -p $temp # Check if tests should assume folding is using libpgmath -if [[ $# > 1 && "$2" = "-pgmath=true" ]]; then +if [[ $LIBPGMATH ]]; then CMD="$CMD -DTEST_LIBPGMATH" echo "Assuming libpgmath support" +else + echo "Not assuming libpgmath support" fi src1=$temp/symbols.log diff --git a/test/Preprocessing/lit.local.cfg.py b/test/Preprocessing/lit.local.cfg.py new file mode 100644 index 000000000000..a7cf401d8c66 --- /dev/null +++ b/test/Preprocessing/lit.local.cfg.py @@ -0,0 +1,7 @@ +# -*- Python -*- + +from lit.llvm import llvm_config + +# Added this line file to prevent lit from discovering these tests +# See Issue #1052 +config.suffixes = [] diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt deleted file mode 100644 index a24108782ae7..000000000000 --- a/test/Semantics/CMakeLists.txt +++ /dev/null @@ -1,346 +0,0 @@ -#===-- test/Semantics/CMakeLists.txt ---------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - -# Run tests with test_errors.sh. It compiles the test with f18 and compares -# actual errors produced with expected ones listed in the source. - -# These test files have expected errors in the source -set(ERROR_TESTS - implicit01.f90 - implicit02.f90 - implicit03.f90 - implicit04.f90 - implicit05.f90 - implicit06.f90 - implicit07.f90 - implicit08.f90 - int-literals.f90 - io01.f90 - io02.f90 - io03.f90 - io04.f90 - io05.f90 - io06.f90 - io07.f90 - io08.f90 - io09.f90 - io10.f90 - kinds02.f90 - kinds04.f90 - resolve01.f90 - resolve02.f90 - resolve03.f90 - resolve04.f90 - resolve05.f90 - resolve06.f90 - resolve07.f90 - resolve08.f90 - resolve09.f90 - resolve10.f90 - resolve11.f90 - resolve12.f90 - resolve13.f90 - resolve14.f90 - resolve15.f90 - resolve16.f90 - resolve17.f90 - resolve18.f90 - resolve19.f90 - resolve20.f90 - resolve21.f90 - resolve22.f90 - resolve23.f90 - resolve24.f90 - resolve25.f90 - resolve26.f90 - resolve27.f90 - resolve28.f90 - resolve29.f90 - resolve30.f90 - resolve31.f90 - resolve32.f90 - resolve33.f90 - resolve34.f90 - resolve35.f90 - resolve36.f90 - resolve37.f90 - resolve38.f90 - resolve39.f90 - resolve40.f90 - resolve41.f90 - resolve42.f90 - resolve43.f90 - resolve44.f90 - resolve45.f90 - resolve46.f90 - resolve47.f90 - resolve48.f90 - resolve49.f90 - resolve50.f90 - resolve51.f90 - resolve52.f90 - resolve53.f90 - resolve54.f90 - resolve55.f90 - resolve56.f90 - resolve57.f90 - resolve58.f90 - resolve59.f90 - resolve60.f90 - resolve61.f90 - resolve62.f90 - resolve63.f90 - resolve64.f90 - resolve65.f90 - resolve66.f90 - resolve67.f90 - resolve68.f90 - resolve69.f90 - resolve70.f90 - resolve71.f90 - resolve72.f90 - resolve73.f90 - resolve74.f90 - resolve75.f90 - stop01.f90 - structconst01.f90 - structconst02.f90 - structconst03.f90 - structconst04.f90 - assign01.f90 - assign02.f90 - assign03.f90 - assign04.f90 - if_arith02.f90 - if_arith03.f90 - if_arith04.f90 - if_construct02.f90 - if_stmt02.f90 - if_stmt03.f90 - computed-goto01.f90 - computed-goto02.f90 - nullify01.f90 - nullify02.f90 - deallocate01.f90 - deallocate04.f90 - deallocate05.f90 - coarrays01.f90 - altreturn01.f90 - altreturn02.f90 - altreturn03.f90 - altreturn04.f90 - altreturn05.f90 - allocate01.f90 - allocate02.f90 - allocate03.f90 - allocate04.f90 - allocate05.f90 - allocate06.f90 - allocate07.f90 - allocate08.f90 - allocate09.f90 - allocate10.f90 - allocate11.f90 - allocate12.f90 - allocate13.f90 - doconcurrent01.f90 - doconcurrent05.f90 - doconcurrent06.f90 - doconcurrent08.f90 - dosemantics01.f90 - dosemantics02.f90 - dosemantics03.f90 - dosemantics04.f90 - dosemantics05.f90 - dosemantics06.f90 - dosemantics07.f90 - dosemantics08.f90 - dosemantics09.f90 - dosemantics10.f90 - dosemantics11.f90 - dosemantics12.f90 - expr-errors01.f90 - expr-errors02.f90 - null01.f90 - omp-resolve01.f90 - omp-resolve02.f90 - omp-resolve03.f90 - omp-resolve04.f90 - omp-resolve05.f90 - omp-clause-validity01.f90 - omp-loop-association.f90 -# omp-nested01.f90 - omp-declarative-directive.f90 - omp-atomic.f90 - omp-device-constructs.f90 - equivalence01.f90 - init01.f90 - if_arith01.f90 - if_construct01.f90 - if_stmt01.f90 - blockconstruct01.f90 - blockconstruct02.f90 - blockconstruct03.f90 - call01.f90 - call02.f90 - call03.f90 - call04.f90 - call05.f90 - call06.f90 - call07.f90 - call08.f90 - call09.f90 - call10.f90 - call11.f90 - call12.f90 - call13.f90 - call14.f90 - call15.f90 - forall01.f90 - misc-declarations.f90 - separate-module-procs.f90 - bindings01.f90 - bad-forward-type.f90 - c_f_pointer.f90 - critical01.f90 - critical02.f90 - critical03.f90 - block-data01.f90 - complex01.f90 - data01.f90 - data02.f90 - namelist01.f90 -) - -# These test files have expected symbols in the source -set(SYMBOL_TESTS - symbol01.f90 - symbol02.f90 - symbol03.f90 - symbol05.f90 - symbol06.f90 - symbol07.f90 - symbol08.f90 - symbol09.f90 - symbol10.f90 - symbol11.f90 - symbol12.f90 - symbol13.f90 - symbol14.f90 - symbol15.f90 - symbol16.f90 - symbol17.f90 - omp-symbol01.f90 - omp-symbol02.f90 - omp-symbol03.f90 - omp-symbol04.f90 - omp-symbol05.f90 - omp-symbol06.f90 - omp-symbol07.f90 - omp-symbol08.f90 - kinds01.f90 - kinds03.f90 - procinterface01.f90 -) - -# These test files have expected .mod file contents in the source -set(MODFILE_TESTS - modfile01.f90 - modfile02.f90 - modfile03.f90 - modfile04.f90 - modfile05.f90 - modfile06.f90 - modfile07.f90 - modfile08.f90 - modfile09-*.f90 - modfile10.f90 - modfile11.f90 - modfile12.f90 - modfile13.f90 - modfile14.f90 - modfile15.f90 - modfile16.f90 - modfile17.f90 - modfile18.f90 - modfile19.f90 - modfile20.f90 - modfile21.f90 - modfile22.f90 - modfile23.f90 - modfile24.f90 - modfile25.f90 - modfile26.f90 - modfile27.f90 - modfile28.f90 - modfile29.f90 - modfile30.f90 - modfile31.f90 - modfile32.f90 - modfile33.f90 - modfile34.f90 - modfile35.f90 -) - -set(LABEL_TESTS - label*.[Ff]90 -) - -set(DOCONCURRENT_TESTS - doconcurrent02.f90 - doconcurrent03.f90 - doconcurrent04.f90 - doconcurrent07.f90 -) - -set(CANONDO_TESTS - canondo*.[Ff]90 -) - -set(CRITICAL_TESTS - critical04.f90 -) - -set(GETSYMBOLS_TESTS - getsymbols01.f90 - getsymbols02-*.f90 - getsymbols03-a.f90 - getsymbols04.f90 - getsymbols05.f90 -) - -set(GETDEFINITION_TESTS - getdefinition01.f90 - getdefinition02.f - getdefinition03-a.f90 - getdefinition04.f90 - getdefinition05.f90 -) - -set(F18 $) - -foreach(test ${ERROR_TESTS}) - add_test(NAME ${test} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_errors.sh ${test} ${F18}) -endforeach() - -foreach(test ${SYMBOL_TESTS}) - add_test(NAME ${test} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_symbols.sh ${test} ${F18}) -endforeach() - -foreach(test ${MODFILE_TESTS}) - add_test(NAME ${test} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_modfile.sh ${test} ${F18}) -endforeach() - -foreach(test ${LABEL_TESTS} ${CANONDO_TESTS} ${DOCONCURRENT_TESTS} - ${CRITICAL_TESTS} ${GETSYMBOLS_TESTS} ${GETDEFINITION_TESTS}) - add_test(NAME ${test} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_any.sh ${test} ${F18}) -endforeach() diff --git a/test/Semantics/common.sh b/test/Semantics/common.sh index e84d9b933631..02ccb32fd5f0 100644 --- a/test/Semantics/common.sh +++ b/test/Semantics/common.sh @@ -10,19 +10,18 @@ function die { echo "$(basename $0): $*" >&2 exit 1 } +if [[ $# < 3 ]]; then + echo "Usage: $(basename $0) " + exit 1 +fi -case $# in - (1) ;; - (2) F18=$2 ;; - (*) echo "Usage: $(basename $0) []"; exit 1 -esac -[[ -z ${F18+x} ]] && die "Path to f18 must be second argument or in F18 environment variable" -[[ ! -f $F18 ]] && die "f18 executable not found: $F18" case $1 in (/*) src=$1 ;; (*) src=$(dirname $0)/$1 ;; esac USER_OPTIONS=`sed -n 's/^ *! *OPTIONS: *//p' $src` echo $USER_OPTIONS -temp=`mktemp -d ./tmp.XXXXXX` -[[ $KEEP ]] || trap "rm -rf $temp" EXIT +F18=$2 +[[ ! -f $F18 ]] && die "f18 executable not found: $F18" +temp=$3 +mkdir -p $temp diff --git a/test/Semantics/test_any.sh b/test/Semantics/test_any.sh index 71f42889911a..b0735935928f 100755 --- a/test/Semantics/test_any.sh +++ b/test/Semantics/test_any.sh @@ -48,9 +48,9 @@ function internal_check() { } gr=0 -for input in ${srcdir}/$*; do +for input in $1; do [[ ! -f $input ]] && die "File not found: $input" - CMD=$(cat ${input} | egrep '^[[:space:]]*![[:space:]]*RUN:[[:space:]]*' | sed -e 's/^[[:space:]]*![[:space:]]*RUN:[[:space:]]*//') + CMD=$(cat ${input} | egrep '^[[:space:]]*![[:space:]]*EXEC:[[:space:]]*' | sed -e 's/^[[:space:]]*![[:space:]]*EXEC:[[:space:]]*//') CMD=$(echo ${CMD} | sed -e "s:%s:${input}:g") if egrep -q -e '%t' <<< ${CMD} ; then CMD=$(echo ${CMD} | sed -e "s:%t:$temp/t:g") diff --git a/test-lit/lit.cfg.py b/test/lit.cfg.py similarity index 91% rename from test-lit/lit.cfg.py rename to test/lit.cfg.py index 2dce888c33fd..57dc7383d88b 100644 --- a/test-lit/lit.cfg.py +++ b/test/lit.cfg.py @@ -24,19 +24,11 @@ # the test runner updated. config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) - # suffixes: A list of file extensions to treat as test files. config.suffixes = ['.f', '.F', '.ff','.FOR', '.for', '.f77', '.f90', '.F90', '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf', '.CUF', '.f18', '.F18', '.fir' ] -# test_source_root: The root path where tests are located. -config.test_source_root = os.path.dirname(__file__) - - -# test_exec_root: The root path where tests should be run. -config.test_exec_root = os.path.join(config.flang_obj_root, 'test-lit') - config.substitutions.append(('%PATH%', config.environment['PATH'])) llvm_config.use_default_substitutions() @@ -50,7 +42,7 @@ config.test_source_root = os.path.dirname(__file__) # test_exec_root: The root path where tests should be run. -config.test_exec_root = os.path.join(config.flang_obj_root, 'test-lit') +config.test_exec_root = os.path.join(config.flang_obj_root, 'test') # Tweak the PATH to include the tools dir. llvm_config.with_environment('PATH', config.flang_tools_dir, append_path=True) @@ -76,3 +68,7 @@ llvm_config.add_tool_substitutions(tools, tool_dirs) +# Enable libpgmath testing +result = lit_config.params.get("LIBPGMATH") +if result: + config.environment["LIBPGMATH"] = True \ No newline at end of file diff --git a/test-lit/lit.site.cfg.py.in b/test/lit.site.cfg.py.in similarity index 92% rename from test-lit/lit.site.cfg.py.in rename to test/lit.site.cfg.py.in index fe428f9bee06..92bd926ab5ca 100644 --- a/test-lit/lit.site.cfg.py.in +++ b/test/lit.site.cfg.py.in @@ -24,4 +24,4 @@ import lit.llvm lit.llvm.initialize(lit_config, config) # Let the main config do the real work. -lit_config.load_config(config, "@FLANG_SOURCE_DIR@/test-lit/lit.cfg.py") +lit_config.load_config(config, "@FLANG_SOURCE_DIR@/test/lit.cfg.py") From 9625317ee8aed40a3827fe551cecf08aa34ee7a2 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 13 Mar 2020 09:52:15 -0700 Subject: [PATCH 088/345] Complete formatting of pointer assignments, move to formatting.cpp with rest of AsFortran --- lib/Evaluate/expression.cpp | 36 ------------------------------------ lib/Evaluate/formatting.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/lib/Evaluate/expression.cpp b/lib/Evaluate/expression.cpp index 11ba35dbb8ec..18646e4f19e6 100644 --- a/lib/Evaluate/expression.cpp +++ b/lib/Evaluate/expression.cpp @@ -222,42 +222,6 @@ StructureConstructor &StructureConstructor::Add( GenericExprWrapper::~GenericExprWrapper() {} -std::ostream &Assignment::AsFortran(std::ostream &o) const { - std::visit( - common::visitors{ - [&](const Assignment::Intrinsic &) { - rhs.AsFortran(lhs.AsFortran(o) << '='); - }, - [&](const ProcedureRef &proc) { proc.AsFortran(o << "CALL "); }, - [&](const BoundsSpec &bounds) { - lhs.AsFortran(o); - if (!bounds.empty()) { - char sep{'('}; - for (const auto &bound : bounds) { - bound.AsFortran(o << sep) << ':'; - sep = ','; - } - o << ')'; - } - }, - [&](const BoundsRemapping &bounds) { - lhs.AsFortran(o); - if (!bounds.empty()) { - char sep{'('}; - for (const auto &bound : bounds) { - bound.first.AsFortran(o << sep) << ':'; - bound.second.AsFortran(o); - sep = ','; - } - o << ')'; - } - rhs.AsFortran(o << " => "); - }, - }, - u); - return o; -} - GenericAssignmentWrapper::~GenericAssignmentWrapper() {} template int Expr>::GetKind() const { diff --git a/lib/Evaluate/formatting.cpp b/lib/Evaluate/formatting.cpp index 0b397c92590b..560bfc532973 100644 --- a/lib/Evaluate/formatting.cpp +++ b/lib/Evaluate/formatting.cpp @@ -718,6 +718,43 @@ std::ostream &DescriptorInquiry::AsFortran(std::ostream &o) const { } } +std::ostream &Assignment::AsFortran(std::ostream &o) const { + std::visit( + common::visitors{ + [&](const Assignment::Intrinsic &) { + rhs.AsFortran(lhs.AsFortran(o) << '='); + }, + [&](const ProcedureRef &proc) { proc.AsFortran(o << "CALL "); }, + [&](const BoundsSpec &bounds) { + lhs.AsFortran(o); + if (!bounds.empty()) { + char sep{'('}; + for (const auto &bound : bounds) { + bound.AsFortran(o << sep) << ':'; + sep = ','; + } + o << ')'; + } + rhs.AsFortran(o << " => "); + }, + [&](const BoundsRemapping &bounds) { + lhs.AsFortran(o); + if (!bounds.empty()) { + char sep{'('}; + for (const auto &bound : bounds) { + bound.first.AsFortran(o << sep) << ':'; + bound.second.AsFortran(o); + sep = ','; + } + o << ')'; + } + rhs.AsFortran(o << " => "); + }, + }, + u); + return o; +} + INSTANTIATE_CONSTANT_TEMPLATES INSTANTIATE_EXPRESSION_TEMPLATES INSTANTIATE_VARIABLE_TEMPLATES From 688512ddc671fa1ed54224b7cfef49a1c2284c99 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 12 Mar 2020 17:07:16 -0700 Subject: [PATCH 089/345] more edits Remove AllocateDefaultCharacter Refinements in code review --- runtime/allocatable.h | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 runtime/allocatable.h diff --git a/runtime/allocatable.h b/runtime/allocatable.h new file mode 100644 index 000000000000..bcb70356f322 --- /dev/null +++ b/runtime/allocatable.h @@ -0,0 +1,115 @@ +//===-- runtime/allocatable.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Defines APIs for Fortran runtime library support of code generated +// to manipulate and query allocatable variables, dummy arguments, & components. +#ifndef FORTRAN_RUNTIME_ALLOCATABLE_H_ +#define FORTRAN_RUNTIME_ALLOCATABLE_H_ +#include "descriptor.h" +#include "entry-names.h" + +namespace Fortran::runtime { +extern "C" { + +// Initializes the descriptor for an allocatable of intrinsic or derived type. +// The incoming descriptor is treated as (and can be) uninitialized garbage. +// Must be called for each allocatable variable as its scope comes into being. +// The storage for the allocatable's descriptor must have already been +// allocated to a size sufficient for the rank, corank, and type. +// A descriptor must be initialized before being used for any purpose, +// but needs reinitialization in a deallocated state only when there is +// a change of type, rank, or corank. +void RTNAME(AllocatableInitIntrinsic)( + Descriptor &, TypeCategory, int kind, int rank = 0, int corank = 0); +void RTNAME(AllocatableInitCharacter)(Descriptor &, SubscriptValue length, + int kind = 1, int rank = 0, int corank = 0); +void RTNAME(AllocatableInitDerived)( + Descriptor &, const DerivedType &, int rank = 0, int corank = 0); + +// Checks that an allocatable is not already allocated in statements +// with STAT=. Use this on a value descriptor before setting bounds or +// type parameters. Not necessary on a freshly initialized descriptor. +// (If there's no STAT=, the error will be caught later anyway, but +// this API allows the error to be caught before descriptor is modified.) +// Return 0 on success (deallocated state), else the STAT= value. +int RTNAME(AllocatableCheckAllocated)(Descriptor &, + Descriptor *errMsg = nullptr, const char *sourceFile = nullptr, + int sourceLine = 0); + +// For MOLD= allocation; sets bounds, cobounds, and length type +// parameters from another descriptor. The destination descriptor must +// be initialized and deallocated. +void RTNAME(AllocatableApplyMold)(Descriptor &, const Descriptor &mold); + +// Explicitly sets the bounds and length type parameters of an initialized +// deallocated allocatable. +void RTNAME(AllocatableSetBounds)( + Descriptor &, int zeroBasedDim, SubscriptValue lower, SubscriptValue upper); + +// The upper bound is ignored for the last codimension. +void RTNAME(AllocatableSetCoBounds)(Descriptor &, int zeroBasedCoDim, + SubscriptValue lower, SubscriptValue upper = 0); + +// Length type parameters are indexed in declaration order; i.e., 0 is the +// first length type parameter in the deepest base type. (Not for use +// with CHARACTER; see above.) +void RTNAME(AllocatableSetDerivedLength)( + Descriptor &, int which, SubscriptValue); + +// When an explicit type-spec appears in an ALLOCATE statement for an +// allocatable with an explicit (non-deferred) length type paramater for +// a derived type or CHARACTER value, the explicit value has to match +// the length type parameter's value. This API checks that requirement. +// Returns 0 for success, or the STAT= value on failure with hasStat==true. +int RTNAME(AllocatableCheckLengthParameter)(Descriptor &, + int which /* 0 for CHARACTER length */, SubscriptValue other, + bool hasStat = false, Descriptor *errMsg = nullptr, + const char *sourceFile = nullptr, int sourceLine = 0); + +// Allocates an allocatable. The allocatable descriptor must have been +// initialized and its bounds and length type parameters set and must be +// in a deallocated state. +// On failure, if hasStat is true, returns a nonzero error code for +// STAT= and (if present) fills in errMsg; if hasStat is false, the +// image is terminated. On success, leaves errMsg alone and returns zero. +// Successfully allocated memory is initialized if the allocatable has a +// derived type, and is always initialized by AllocatableAllocateSource(). +// Performs all necessary coarray synchronization and validation actions. +int RTNAME(AllocatableAllocate)(Descriptor &, bool hasStat = false, + Descriptor *errMsg = nullptr, const char *sourceFile = nullptr, + int sourceLine = 0); +int RTNAME(AllocatableAllocateSource)(Descriptor &, const Descriptor &source, + bool hasStat = false, Descriptor *errMsg = nullptr, + const char *sourceFile = nullptr, int sourceLine = 0); + +// Assigns to a whole allocatable, with automatic (re)allocation when the +// destination is unallocated or nonconforming (Fortran 2003 semantics). +// The descriptor must be initialized. +// Recursively assigns components with (re)allocation as necessary. +// TODO: Consider renaming to a more general name that will work for +// assignments to pointers, dummy arguments, and anything else with a +// descriptor. +void RTNAME(AllocatableAssignment)(Descriptor &to, const Descriptor &from); + +// Implements the intrinsic subroutine MOVE_ALLOC (16.9.137 in F'2018, +// but note the order of first two arguments is reversed for consistency +// with the other APIs for allocatables.) The destination descriptor +// must be initialized. +int RTNAME(MoveAlloc)(Descriptor &to, const Descriptor &from, + bool hasStat = false, Descriptor *errMsg = nullptr, + const char *sourceFile = nullptr, int sourceLine = 0); + +// Deallocates an allocatable. Finalizes elements &/or components as needed. +// The allocatable is left in an initialized state suitable for reallocation +// with the same bounds, cobounds, and length type parameters. +int RTNAME(AllocatableDeallocate)(Descriptor &, bool hasStat = false, + Descriptor *errMsg = nullptr, const char *sourceFile = nullptr, + int sourceLine = 0); +} +} +#endif // FORTRAN_RUNTIME_ALLOCATABLE_H_ From c351181cabb234f73cb09c23e45dfe0bfc2d7219 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Mon, 16 Mar 2020 10:46:17 -0700 Subject: [PATCH 090/345] Test cleanup Fix omp-nested01.f90 so that it is not an expected failure. The test was never enabled but I'm guessing this is what it's supposed to do. Fix the instructions to include "make test" as part of running tests. --- README.md | 2 +- test/Semantics/omp-nested01.f90 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9df131d7f325..d8e2cbdef63b 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ To run all tests: ``` cd ~/f18/build cmake -DLLVM_DIR=$LLVM ~/f18/src -make check-all +make test check-all ``` To run individual regression tests llvm-lit needs to know the lit diff --git a/test/Semantics/omp-nested01.f90 b/test/Semantics/omp-nested01.f90 index 0e7220222217..1c0e84ab8fd9 100644 --- a/test/Semantics/omp-nested01.f90 +++ b/test/Semantics/omp-nested01.f90 @@ -1,5 +1,4 @@ ! RUN: %S/test_errors.sh %s %flang %t -!XFAIL: * ! OPTIONS: -fopenmp ! Check OpenMP 2.17 Nesting of Regions @@ -9,7 +8,7 @@ do i = 1, N !ERROR: A worksharing region may not be closely nested inside a worksharing, explicit task, taskloop, critical, ordered, atomic, or master region !$omp do - do i = 1, N + do j = 1, N a = 3.14 enddo enddo From 4a3db55727a93805d3f047713e5d253543ee51fa Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 17 Mar 2020 12:35:31 -0700 Subject: [PATCH 091/345] Do not emit a prefix for a default-kind character constant in AsFortran --- include/flang/Evaluate/formatting.h | 2 -- lib/Evaluate/formatting.cpp | 4 +--- test/Semantics/modfile16.f90 | 14 +++++++------- test/Semantics/modfile20.f90 | 2 +- test/Semantics/modfile21.f90 | 2 +- test/Semantics/modfile22.f90 | 4 ++-- test/Semantics/modfile28.f90 | 2 +- test/Semantics/modfile30.f90 | 4 ++-- tools/f18/f18.cpp | 2 -- 9 files changed, 15 insertions(+), 21 deletions(-) diff --git a/include/flang/Evaluate/formatting.h b/include/flang/Evaluate/formatting.h index 0a1f9517d2d0..c2e5316e39b1 100644 --- a/include/flang/Evaluate/formatting.h +++ b/include/flang/Evaluate/formatting.h @@ -26,8 +26,6 @@ namespace Fortran::evaluate { -extern bool formatForPGF90; - template auto operator<<(std::ostream &o, const A &x) -> decltype(x.AsFortran(o)) { return x.AsFortran(o); diff --git a/lib/Evaluate/formatting.cpp b/lib/Evaluate/formatting.cpp index 560bfc532973..4c28d66a1d66 100644 --- a/lib/Evaluate/formatting.cpp +++ b/lib/Evaluate/formatting.cpp @@ -18,8 +18,6 @@ namespace Fortran::evaluate { -bool formatForPGF90{false}; - static void ShapeAsFortran(std::ostream &o, const ConstantSubscripts &shape) { if (GetRank(shape) > 1) { o << ",shape="; @@ -87,7 +85,7 @@ std::ostream &Constant>::AsFortran( if (j > 0) { o << ','; } - if (Result::kind != 1 || !formatForPGF90) { + if (Result::kind != 1) { o << Result::kind << '_'; } o << parser::QuoteCharacterLiteral(value); diff --git a/test/Semantics/modfile16.f90 b/test/Semantics/modfile16.f90 index acc17d54a282..48a302792878 100644 --- a/test/Semantics/modfile16.f90 +++ b/test/Semantics/modfile16.f90 @@ -22,16 +22,16 @@ subroutine sub() bind(c, name='sub') !Expect: m.mod !module m -! character(2_4,1),parameter::prefix=1_"c_" -! integer(4),bind(c, name=1_"c_a")::a -! procedure(sub),bind(c, name=1_"c_b"),pointer::b +! character(2_4,1),parameter::prefix="c_" +! integer(4),bind(c, name="c_a")::a +! procedure(sub),bind(c, name="c_b"),pointer::b ! type,bind(c)::t ! real(4)::c ! end type -! procedure(real(4)),bind(c, name=1_"dd")::d -! procedure(real(4)),bind(c, name=1_"ee")::e -! procedure(real(4)),bind(c, name=1_"ff")::f +! procedure(real(4)),bind(c, name="dd")::d +! procedure(real(4)),bind(c, name="ee")::e +! procedure(real(4)),bind(c, name="ff")::f !contains -! subroutine sub() bind(c, name=1_"sub") +! subroutine sub() bind(c, name="sub") ! end !end diff --git a/test/Semantics/modfile20.f90 b/test/Semantics/modfile20.f90 index 90188c177c44..a09c4422be23 100644 --- a/test/Semantics/modfile20.f90 +++ b/test/Semantics/modfile20.f90 @@ -26,7 +26,7 @@ module m ! integer(8),parameter::i=2_8 ! real(4)::r ! character(10_4,1)::c -! character(10_4,1),parameter::c2=1_"qwer " +! character(10_4,1),parameter::c2="qwer " ! complex(8),parameter::z=(1._8,2._8) ! complex(8),parameter::zn=(-1._8,2._8) ! type::t diff --git a/test/Semantics/modfile21.f90 b/test/Semantics/modfile21.f90 index 03349a32682d..64dd95f868d3 100644 --- a/test/Semantics/modfile21.f90 +++ b/test/Semantics/modfile21.f90 @@ -30,6 +30,6 @@ module m ! bind(c)::/cb2/ ! common//t,w,u,v ! common/cb/x,y,z -! bind(c, name=1_"CB")::/cb/ +! bind(c, name="CB")::/cb/ ! common/b/cb !end diff --git a/test/Semantics/modfile22.f90 b/test/Semantics/modfile22.f90 index 6279ad78678a..ea2637d4b1d4 100644 --- a/test/Semantics/modfile22.f90 +++ b/test/Semantics/modfile22.f90 @@ -18,6 +18,6 @@ end module m !character(1_4,int(k,kind=8))::a !character(3_4,int(k,kind=8))::b !end type -!type(t(k=1_4)),parameter::p=t(k=1_4)(a=1_"x",b=1_"xx ") -!character(2_4,1),parameter::c2(1_8:3_8)=[CHARACTER(KIND=1,LEN=2)::1_"x ",1_"xx",1_"xx"] +!type(t(k=1_4)),parameter::p=t(k=1_4)(a="x",b="xx ") +!character(2_4,1),parameter::c2(1_8:3_8)=[CHARACTER(KIND=1,LEN=2)::"x ","xx","xx"] !end diff --git a/test/Semantics/modfile28.f90 b/test/Semantics/modfile28.f90 index 18a349de5ba1..b06826ced22f 100644 --- a/test/Semantics/modfile28.f90 +++ b/test/Semantics/modfile28.f90 @@ -16,7 +16,7 @@ end module m !Expect: m.mod !module m !character(*,4),parameter::c4=4_"Hi! \344\275\240\345\245\275!" -!character(*,1),parameter::c1=1_"Hi! \344\275\240\345\245\275!" +!character(*,1),parameter::c1="Hi! \344\275\240\345\245\275!" !character(*,4),parameter::c4a(1_8:*)=[CHARACTER(KIND=4,LEN=1)::4_"\344\270\200",4_"\344\272\214",4_"\344\270\211",4_"\345\233\233",4_"\344\272\224"] !integer(4),parameter::lc4=7_4 !intrinsic::len diff --git a/test/Semantics/modfile30.f90 b/test/Semantics/modfile30.f90 index ef05b9395139..a3b42629e148 100644 --- a/test/Semantics/modfile30.f90 +++ b/test/Semantics/modfile30.f90 @@ -75,7 +75,7 @@ module m4b !Expect: m4a.mod !module m4a -! character(1_4,1),parameter::a=1_"\001" +! character(1_4,1),parameter::a="\001" ! intrinsic::achar !end @@ -83,6 +83,6 @@ module m4b !module m4b ! use m4a,only:a ! use m4a,only:achar -! character(1_4,1),parameter::b=1_"\001" +! character(1_4,1),parameter::b="\001" !end diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index fe7bd8a1946f..edcd0ef66b2a 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -333,13 +333,11 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, { std::ofstream tmpSource; tmpSource.open(tmpSourcePath); - Fortran::evaluate::formatForPGF90 = true; Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes), nullptr /* action before each statement */, driver.unparseTypedExprsToPGF90 ? &asFortran : nullptr); - Fortran::evaluate::formatForPGF90 = false; } if (ParentProcess()) { From 514c0f2c9477040f830fbf0495e4d38129648615 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 17 Mar 2020 13:02:17 -0700 Subject: [PATCH 092/345] Fix location of name of Symbol for ProcEntity When we encounter a ProcDecl and a symbol for it has already been created, replace the CharBlock for the name with the one in the ProcDecl as it is the "main" declaration of that name. This matches what is done for an EntityDecl. This moves the location of some error messages to a better source location so update the affected tests. --- lib/Semantics/resolve-names.cpp | 1 + test/Semantics/call02.f90 | 2 +- test/Semantics/call09.f90 | 2 +- test/Semantics/call10.f90 | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index a37bfd64470f..6f94d06c7a58 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -3603,6 +3603,7 @@ void DeclarationVisitor::Post(const parser::ProcDecl &x) { attrs.set(Attr::EXTERNAL); } Symbol &symbol{DeclareProcEntity(name, attrs, interface)}; + symbol.ReplaceName(name.source); if (dtDetails) { dtDetails->add_component(symbol); } diff --git a/test/Semantics/call02.f90 b/test/Semantics/call02.f90 index 2d23274da1b0..5d9bdf1cd5a2 100644 --- a/test/Semantics/call02.f90 +++ b/test/Semantics/call02.f90 @@ -9,9 +9,9 @@ elemental real function elem(x) subroutine subr(dummy) procedure(sin) :: dummy end subroutine - !ERROR: A dummy procedure may not be ELEMENTAL subroutine badsubr(dummy) import :: elem + !ERROR: A dummy procedure may not be ELEMENTAL procedure(elem) :: dummy end subroutine end interface diff --git a/test/Semantics/call09.f90 b/test/Semantics/call09.f90 index e27c78e4281f..02224477a28f 100644 --- a/test/Semantics/call09.f90 +++ b/test/Semantics/call09.f90 @@ -28,8 +28,8 @@ real elemental function elemfunc(x) real, intent(in) :: x elemfunc = x end function - !ERROR: A dummy procedure may not be ELEMENTAL subroutine selemental2(p) + !ERROR: A dummy procedure may not be ELEMENTAL procedure(elemfunc) :: p end subroutine diff --git a/test/Semantics/call10.f90 b/test/Semantics/call10.f90 index 52983c9f18a0..567d85d5d0e0 100644 --- a/test/Semantics/call10.f90 +++ b/test/Semantics/call10.f90 @@ -109,8 +109,8 @@ pure subroutine s06 ! C1589 real, volatile :: v2 end block end subroutine - !ERROR: A dummy procedure of a pure subprogram must be pure pure subroutine s07(p) ! C1590 + !ERROR: A dummy procedure of a pure subprogram must be pure procedure(impure) :: p end subroutine ! C1591 is tested in call11.f90. From 4d3c4bac843dcc6351fbe7538c6ce23bb9c1a215 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 17 Mar 2020 14:28:08 -0700 Subject: [PATCH 093/345] Create symbols for args of separate-module-subprogram A separate-module-subprogram is declared as `module procedure ...` and gets its characteristics from the declaration of that name as a separate module procedure. When we encounter one, we need to create symbols in the new subprogram scope for the dummy arguments and function return (if any). The failure to create these symbols led to the bug in issue #1054: when a dummy argument was referenced, the compiler interpreted it as an implicit declaration because there was no symbol for the argument. Fixes #1054. --- include/flang/Semantics/scope.h | 2 ++ lib/Semantics/resolve-names.cpp | 21 ++++++++++++++++++--- lib/Semantics/scope.cpp | 12 ++++++++++++ test/Semantics/resolve76.f90 | 30 ++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 test/Semantics/resolve76.f90 diff --git a/include/flang/Semantics/scope.h b/include/flang/Semantics/scope.h index 7c12dc14563d..b6945e6da045 100644 --- a/include/flang/Semantics/scope.h +++ b/include/flang/Semantics/scope.h @@ -134,6 +134,8 @@ class Scope { Symbol &symbol{MakeSymbol(name, attrs, std::move(details))}; return symbols_.emplace(name, symbol); } + // Make a copy of a symbol in this scope; nullptr if one is already there + Symbol *CopySymbol(const Symbol &); const std::list &equivalenceSets() const; void add_equivalenceSet(EquivalenceSet &&); diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 6f94d06c7a58..d3d3c7067a54 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -2729,10 +2729,25 @@ bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) { Say(name, "'%s' was not declared a separate module procedure"_err_en_US); return false; } - if (symbol->owner() != currScope()) { - symbol = &MakeSymbol(name, SubprogramDetails{}); + if (symbol->owner() == currScope()) { + PushScope(Scope::Kind::Subprogram, symbol); + } else { + Symbol &newSymbol{MakeSymbol(name, SubprogramDetails{})}; + PushScope(Scope::Kind::Subprogram, &newSymbol); + const auto &details{symbol->get()}; + auto &newDetails{newSymbol.get()}; + for (const Symbol *dummyArg : details.dummyArgs()) { + if (!dummyArg) { + newDetails.add_alternateReturn(); + } else if (Symbol * copy{currScope().CopySymbol(*dummyArg)}) { + newDetails.add_dummyArg(*copy); + } + } + if (details.isFunction()) { + currScope().erase(symbol->name()); + newDetails.set_result(*currScope().CopySymbol(details.result())); + } } - PushScope(Scope::Kind::Subprogram, symbol); return true; } diff --git a/lib/Semantics/scope.cpp b/lib/Semantics/scope.cpp index b345c6189849..edb3c64382b7 100644 --- a/lib/Semantics/scope.cpp +++ b/lib/Semantics/scope.cpp @@ -110,6 +110,18 @@ bool Scope::Contains(const Scope &that) const { } } +Symbol *Scope::CopySymbol(const Symbol &symbol) { + auto pair{try_emplace(symbol.name(), symbol.attrs())}; + if (!pair.second) { + return nullptr; // already exists + } else { + Symbol &result{*pair.first->second}; + result.flags() = symbol.flags(); + result.set_details(common::Clone(symbol.details())); + return &result; + } +} + const std::list &Scope::equivalenceSets() const { return equivalenceSets_; } diff --git a/test/Semantics/resolve76.f90 b/test/Semantics/resolve76.f90 new file mode 100644 index 000000000000..e68c81f36fb2 --- /dev/null +++ b/test/Semantics/resolve76.f90 @@ -0,0 +1,30 @@ +! RUN: %S/test_errors.sh %s %flang %t + +! 15.6.2.5(3) + +module m1 + implicit logical(a-b) + interface + module subroutine sub1(a, b) + real, intent(in) :: a + real, intent(out) :: b + end + logical module function f() + end + end interface +end +submodule(m1) sm1 +contains + module procedure sub1 + !ERROR: Left-hand side of assignment is not modifiable + a = 1.0 + b = 2.0 + !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types REAL(4) and LOGICAL(4) + b = .false. + end + module procedure f + f = .true. + !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types LOGICAL(4) and REAL(4) + f = 1.0 + end +end From d121578af17109de3cea23617e4b8239971b5527 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Tue, 17 Mar 2020 14:48:36 -0700 Subject: [PATCH 094/345] Check module subprogram against separate module procedure When a module subprogram has the MODULE prefix the following must match with the corresponding separate module procedure interface body: - C1549: characteristics and dummy argument names - C1550: binding label - C1551: NON_RECURSIVE prefix SubprogramMatchHelper performs all of these checks. Rename separate-module-procs.f90 to separate-mp01.f90 so we can have separate-mp02.f90 (etc). Make ShapesAreCompatible public in characteristics.h. Add Scope::IsSubmodule. --- include/flang/Evaluate/characteristics.h | 5 + include/flang/Semantics/scope.h | 1 + lib/Evaluate/characteristics.cpp | 9 +- lib/Semantics/check-declarations.cpp | 263 +++++++++++++++- lib/Semantics/scope.cpp | 3 + lib/Semantics/symbol.cpp | 38 +-- test/Semantics/resolve36.f90 | 4 + ...ate-module-procs.f90 => separate-mp01.f90} | 7 - test/Semantics/separate-mp02.f90 | 285 ++++++++++++++++++ 9 files changed, 577 insertions(+), 38 deletions(-) rename test/Semantics/{separate-module-procs.f90 => separate-mp01.f90} (87%) create mode 100644 test/Semantics/separate-mp02.f90 diff --git a/include/flang/Evaluate/characteristics.h b/include/flang/Evaluate/characteristics.h index 52ac7384815b..d0890465b7c1 100644 --- a/include/flang/Evaluate/characteristics.h +++ b/include/flang/Evaluate/characteristics.h @@ -47,6 +47,11 @@ bool Distinguishable(const Procedure &, const Procedure &); // Are these procedures distinguishable for a generic operator or assignment? bool DistinguishableOpOrAssign(const Procedure &, const Procedure &); +// Shapes of function results and dummy arguments have to have +// the same rank, the same deferred dimensions, and the same +// values for explicit dimensions when constant. +bool ShapesAreCompatible(const Shape &, const Shape &); + class TypeAndShape { public: ENUM_CLASS( diff --git a/include/flang/Semantics/scope.h b/include/flang/Semantics/scope.h index b6945e6da045..6a645ad502b9 100644 --- a/include/flang/Semantics/scope.h +++ b/include/flang/Semantics/scope.h @@ -78,6 +78,7 @@ class Scope { Kind kind() const { return kind_; } bool IsGlobal() const { return kind_ == Kind::Global; } bool IsModule() const; // only module, not submodule + bool IsSubmodule() const; bool IsDerivedType() const { return kind_ == Kind::DerivedType; } bool IsParameterizedDerivedType() const; Symbol *symbol() { return symbol_; } diff --git a/lib/Evaluate/characteristics.cpp b/lib/Evaluate/characteristics.cpp index dffcf3232936..d87ea198ae75 100644 --- a/lib/Evaluate/characteristics.cpp +++ b/lib/Evaluate/characteristics.cpp @@ -37,7 +37,7 @@ static void CopyAttrs(const semantics::Symbol &src, A &dst, // Shapes of function results and dummy arguments have to have // the same rank, the same deferred dimensions, and the same // values for explicit dimensions when constant. -static bool ShapesAreCompatible(const Shape &x, const Shape &y) { +bool ShapesAreCompatible(const Shape &x, const Shape &y) { if (x.size() != y.size()) { return false; } @@ -158,10 +158,9 @@ void TypeAndShape::AcquireShape(const semantics::ObjectEntityDetails &object) { for (const semantics::ShapeSpec &dim : object.shape()) { if (dim.ubound().GetExplicit()) { Expr extent{*dim.ubound().GetExplicit()}; - if (dim.lbound().GetExplicit()) { - extent = std::move(extent) + - common::Clone(*dim.lbound().GetExplicit()) - - Expr{1}; + if (auto lbound{dim.lbound().GetExplicit()}) { + extent = + std::move(extent) + Expr{1} - std::move(*lbound); } shape_.emplace_back(std::move(extent)); } else { diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index 63d36e342cdb..43fe3435ae07 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -23,6 +23,7 @@ namespace Fortran::semantics { using evaluate::characteristics::DummyArgument; using evaluate::characteristics::DummyDataObject; +using evaluate::characteristics::DummyProcedure; using evaluate::characteristics::Procedure; class CheckHelper { @@ -59,6 +60,7 @@ class CheckHelper { void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &); void CheckArraySpec(const Symbol &, const ArraySpec &); void CheckProcEntity(const Symbol &, const ProcEntityDetails &); + void CheckSubprogram(const Symbol &, const SubprogramDetails &); void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); void CheckGeneric(const Symbol &, const GenericDetails &); @@ -88,7 +90,7 @@ class CheckHelper { template void SayWithDeclaration(const Symbol &symbol, A &&... x) { if (parser::Message * msg{messages_.Say(std::forward(x)...)}) { - if (messages_.at() != symbol.name()) { + if (messages_.at().begin() != symbol.name().begin()) { evaluate::AttachDeclaration(*msg, symbol); } } @@ -156,6 +158,7 @@ void CheckHelper::Check(const Symbol &symbol) { [&](const ProcBindingDetails &x) { CheckProcBinding(symbol, x); }, [&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); }, [&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); }, + [&](const SubprogramDetails &x) { CheckSubprogram(symbol, x); }, [&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); }, [&](const GenericDetails &x) { CheckGeneric(symbol, x); }, [](const auto &) {}, @@ -541,6 +544,54 @@ void CheckHelper::CheckProcEntity( } } +// When a module subprogram has the MODULE prefix the following must match +// with the corresponding separate module procedure interface body: +// - C1549: characteristics and dummy argument names +// - C1550: binding label +// - C1551: NON_RECURSIVE prefix +class SubprogramMatchHelper { +public: + explicit SubprogramMatchHelper(SemanticsContext &context) + : context{context} {} + + void Check(const Symbol &, const Symbol &); + +private: + void CheckDummyArg(const Symbol &, const Symbol &, const DummyArgument &, + const DummyArgument &); + void CheckDummyDataObject(const Symbol &, const Symbol &, + const DummyDataObject &, const DummyDataObject &); + void CheckDummyProcedure(const Symbol &, const Symbol &, + const DummyProcedure &, const DummyProcedure &); + bool CheckSameIntent( + const Symbol &, const Symbol &, common::Intent, common::Intent); + template + void Say( + const Symbol &, const Symbol &, parser::MessageFixedText &&, A &&...); + template + bool CheckSameAttrs(const Symbol &, const Symbol &, ATTRS, ATTRS); + bool ShapesAreCompatible(const DummyDataObject &, const DummyDataObject &); + evaluate::Shape FoldShape(const evaluate::Shape &); + std::string AsFortran(DummyDataObject::Attr attr) { + return parser::ToUpperCaseLetters(DummyDataObject::EnumToString(attr)); + } + std::string AsFortran(DummyProcedure::Attr attr) { + return parser::ToUpperCaseLetters(DummyProcedure::EnumToString(attr)); + } + + SemanticsContext &context; +}; + +void CheckHelper::CheckSubprogram( + const Symbol &symbol, const SubprogramDetails &) { + const Scope &scope{symbol.owner()}; + if (symbol.attrs().test(Attr::MODULE) && scope.IsSubmodule()) { + if (const Symbol * iface{scope.parent().FindSymbol(symbol.name())}) { + SubprogramMatchHelper{context_}.Check(symbol, *iface); + } + } +} + void CheckHelper::CheckDerivedType( const Symbol &symbol, const DerivedTypeDetails &details) { if (!symbol.scope()) { @@ -1158,7 +1209,217 @@ void CheckHelper::CheckBlockData(const Scope &scope) { } } +void SubprogramMatchHelper::Check( + const Symbol &symbol1, const Symbol &symbol2) { + const auto details1{symbol1.get()}; + const auto details2{symbol2.get()}; + if (details1.isFunction() != details2.isFunction()) { + Say(symbol1, symbol2, + details1.isFunction() + ? "Module function '%s' was declared as a subroutine in the" + " corresponding interface body"_err_en_US + : "Module subroutine '%s' was declared as a function in the" + " corresponding interface body"_err_en_US); + return; + } + const auto &args1{details1.dummyArgs()}; + const auto &args2{details2.dummyArgs()}; + int nargs1{static_cast(args1.size())}; + int nargs2{static_cast(args2.size())}; + if (nargs1 != nargs2) { + Say(symbol1, symbol2, + "Module subprogram '%s' has %d args but the corresponding interface" + " body has %d"_err_en_US, + nargs1, nargs2); + return; + } + bool nonRecursive1{symbol1.attrs().test(Attr::NON_RECURSIVE)}; + if (nonRecursive1 != symbol2.attrs().test(Attr::NON_RECURSIVE)) { // C1551 + Say(symbol1, symbol2, + nonRecursive1 + ? "Module subprogram '%s' has NON_RECURSIVE prefix but" + " the corresponding interface body does not"_err_en_US + : "Module subprogram '%s' does not have NON_RECURSIVE prefix but " + "the corresponding interface body does"_err_en_US); + } + MaybeExpr bindName1{details1.bindName()}; + MaybeExpr bindName2{details2.bindName()}; + if (bindName1.has_value() != bindName2.has_value()) { + Say(symbol1, symbol2, + bindName1.has_value() + ? "Module subprogram '%s' has a binding label but the corresponding" + " interface body does not"_err_en_US + : "Module subprogram '%s' does not have a binding label but the" + " corresponding interface body does"_err_en_US); + } else if (bindName1) { + std::string string1{bindName1->AsFortran()}; + std::string string2{bindName2->AsFortran()}; + if (string1 != string2) { + Say(symbol1, symbol2, + "Module subprogram '%s' has binding label %s but the corresponding" + " interface body has %s"_err_en_US, + string1, string2); + } + } + auto proc1{Procedure::Characterize(symbol1, context.intrinsics())}; + auto proc2{Procedure::Characterize(symbol2, context.intrinsics())}; + if (!proc1 || !proc2) { + return; + } + if (proc1->functionResult && proc2->functionResult && + *proc1->functionResult != *proc2->functionResult) { + Say(symbol1, symbol2, + "Return type of function '%s' does not match return type of" + " the corresponding interface body"_err_en_US); + } + for (int i{0}; i < nargs1; ++i) { + const Symbol *arg1{args1[i]}; + const Symbol *arg2{args2[i]}; + if (arg1 && !arg2) { + Say(symbol1, symbol2, + "Dummy argument %2$d of '%1$s' is not an alternate return indicator" + " but the corresponding argument in the interface body is"_err_en_US, + i + 1); + } else if (!arg1 && arg2) { + Say(symbol1, symbol2, + "Dummy argument %2$d of '%1$s' is an alternate return indicator but" + " the corresponding argument in the interface body is not"_err_en_US, + i + 1); + } else if (arg1 && arg2) { + SourceName name1{arg1->name()}; + SourceName name2{arg2->name()}; + if (name1 != name2) { + Say(*arg1, *arg2, + "Dummy argument name '%s' does not match corresponding name '%s'" + " in interface body"_err_en_US, + name2); + } else { + CheckDummyArg( + *arg1, *arg2, proc1->dummyArguments[i], proc2->dummyArguments[i]); + } + } + } +} + +void SubprogramMatchHelper::CheckDummyArg(const Symbol &symbol1, + const Symbol &symbol2, const DummyArgument &arg1, + const DummyArgument &arg2) { + std::visit( + common::visitors{ + [&](const DummyDataObject &obj1, const DummyDataObject &obj2) { + CheckDummyDataObject(symbol1, symbol2, obj1, obj2); + }, + [&](const DummyProcedure &proc1, const DummyProcedure &proc2) { + CheckDummyProcedure(symbol1, symbol2, proc1, proc2); + }, + [&](const DummyDataObject &, const auto &) { + Say(symbol1, symbol2, + "Dummy argument '%s' is a data object; the corresponding" + " argument in the interface body is not"_err_en_US); + }, + [&](const DummyProcedure &, const auto &) { + Say(symbol1, symbol2, + "Dummy argument '%s' is a procedure; the corresponding" + " argument in the interface body is not"_err_en_US); + }, + [&](const auto &, const auto &) { DIE("can't happen"); }, + }, + arg1.u, arg2.u); +} + +void SubprogramMatchHelper::CheckDummyDataObject(const Symbol &symbol1, + const Symbol &symbol2, const DummyDataObject &obj1, + const DummyDataObject &obj2) { + if (!CheckSameIntent(symbol1, symbol2, obj1.intent, obj2.intent)) { + } else if (!CheckSameAttrs(symbol1, symbol2, obj1.attrs, obj2.attrs)) { + } else if (obj1.type.type() != obj2.type.type()) { + Say(symbol1, symbol2, + "Dummy argument '%s' has type %s; the corresponding argument in the" + " interface body has type %s"_err_en_US, + obj1.type.type().AsFortran(), obj2.type.type().AsFortran()); + } else if (!ShapesAreCompatible(obj1, obj2)) { + Say(symbol1, symbol2, + "The shape of dummy argument '%s' does not match the shape of the" + " corresponding argument in the interface body"_err_en_US); + } + // TODO: coshape +} + +void SubprogramMatchHelper::CheckDummyProcedure(const Symbol &symbol1, + const Symbol &symbol2, const DummyProcedure &proc1, + const DummyProcedure &proc2) { + if (!CheckSameIntent(symbol1, symbol2, proc1.intent, proc2.intent)) { + } else if (!CheckSameAttrs(symbol1, symbol2, proc1.attrs, proc2.attrs)) { + } else if (proc1 != proc2) { + Say(symbol1, symbol2, + "Dummy procedure '%s' does not match the corresponding argument in" + " the interface body"_err_en_US); + } +} + +bool SubprogramMatchHelper::CheckSameIntent(const Symbol &symbol1, + const Symbol &symbol2, common::Intent intent1, common::Intent intent2) { + if (intent1 == intent2) { + return true; + } else { + Say(symbol1, symbol2, + "The intent of dummy argument '%s' does not match the intent" + " of the corresponding argument in the interface body"_err_en_US); + return false; + } +} + +// Report an error referring to first symbol with declaration of second symbol +template +void SubprogramMatchHelper::Say(const Symbol &symbol1, const Symbol &symbol2, + parser::MessageFixedText &&text, A &&... args) { + auto &message{context.Say(symbol1.name(), std::move(text), symbol1.name(), + std::forward(args)...)}; + evaluate::AttachDeclaration(message, symbol2); +} + +template +bool SubprogramMatchHelper::CheckSameAttrs( + const Symbol &symbol1, const Symbol &symbol2, ATTRS attrs1, ATTRS attrs2) { + if (attrs1 == attrs2) { + return true; + } + attrs1.IterateOverMembers([&](auto attr) { + if (!attrs2.test(attr)) { + Say(symbol1, symbol2, + "Dummy argument '%s' has the %s attribute; the corresponding" + " argument in the interface body does not"_err_en_US, + AsFortran(attr)); + } + }); + attrs2.IterateOverMembers([&](auto attr) { + if (!attrs1.test(attr)) { + Say(symbol1, symbol2, + "Dummy argument '%s' does not have the %s attribute; the" + " corresponding argument in the interface body does"_err_en_US, + AsFortran(attr)); + } + }); + return false; +} + +bool SubprogramMatchHelper::ShapesAreCompatible( + const DummyDataObject &obj1, const DummyDataObject &obj2) { + return evaluate::characteristics::ShapesAreCompatible( + FoldShape(obj1.type.shape()), FoldShape(obj2.type.shape())); +} + +evaluate::Shape SubprogramMatchHelper::FoldShape(const evaluate::Shape &shape) { + evaluate::Shape result; + for (const auto &extent : shape) { + result.emplace_back( + evaluate::Fold(context.foldingContext(), common::Clone(extent))); + } + return result; +} + void CheckDeclarations(SemanticsContext &context) { CheckHelper{context}.Check(); } + } diff --git a/lib/Semantics/scope.cpp b/lib/Semantics/scope.cpp index edb3c64382b7..b63082f2c410 100644 --- a/lib/Semantics/scope.cpp +++ b/lib/Semantics/scope.cpp @@ -51,6 +51,9 @@ std::string EquivalenceObject::AsFortran() const { bool Scope::IsModule() const { return kind_ == Kind::Module && !symbol_->get().isSubmodule(); } +bool Scope::IsSubmodule() const { + return kind_ == Kind::Module && symbol_->get().isSubmodule(); +} Scope &Scope::MakeScope(Kind kind, Symbol *symbol) { return children_.emplace_back(*this, kind, symbol); diff --git a/lib/Semantics/symbol.cpp b/lib/Semantics/symbol.cpp index 92e2f5fdf7af..0017d891643b 100644 --- a/lib/Semantics/symbol.cpp +++ b/lib/Semantics/symbol.cpp @@ -85,20 +85,25 @@ std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) { DumpBool(os, "isInterface", x.isInterface_); DumpExpr(os, "bindName", x.bindName_); if (x.result_) { - os << " result:" << x.result_->name(); + DumpType(os << " result:", x.result()); + os << x.result_->name(); if (!x.result_->attrs().empty()) { os << ", " << x.result_->attrs(); } } - if (x.dummyArgs_.empty()) { - char sep{'('}; - os << ' '; - for (const auto *arg : x.dummyArgs_) { - os << sep << arg->name(); - sep = ','; + char sep{'('}; + os << ' '; + for (const Symbol *arg : x.dummyArgs_) { + os << sep; + sep = ','; + if (arg) { + DumpType(os, *arg); + os << arg->name(); + } else { + os << '*'; } - os << (sep == '(' ? "()" : ")"); } + os << (sep == '(' ? "()" : ")"); return os; } @@ -399,23 +404,6 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { os << ")"; } }, - [&](const SubprogramDetails &x) { - os << " ("; - int n = 0; - for (const auto &dummy : x.dummyArgs()) { - if (n++ > 0) os << ", "; - DumpType(os, *dummy); - os << dummy->name(); - } - os << ')'; - DumpExpr(os, "bindName", x.bindName()); - if (x.isFunction()) { - os << " result("; - DumpType(os, x.result()); - os << x.result().name() << ')'; - } - DumpBool(os, "interface", x.isInterface()); - }, [&](const SubprogramNameDetails &x) { os << ' ' << EnumToString(x.kind()); }, diff --git a/test/Semantics/resolve36.f90 b/test/Semantics/resolve36.f90 index 438ad1aeca92..7ed9391c2f9e 100644 --- a/test/Semantics/resolve36.f90 +++ b/test/Semantics/resolve36.f90 @@ -1,4 +1,8 @@ ! RUN: %S/test_errors.sh %s %flang %t + +! C1568 The procedure-name shall have been declared to be a separate module +! procedure in the containing program unit or an ancestor of that program unit. + module m1 interface module subroutine sub1(arg1) diff --git a/test/Semantics/separate-module-procs.f90 b/test/Semantics/separate-mp01.f90 similarity index 87% rename from test/Semantics/separate-module-procs.f90 rename to test/Semantics/separate-mp01.f90 index 33dfcd557fde..305c147e66c9 100644 --- a/test/Semantics/separate-module-procs.f90 +++ b/test/Semantics/separate-mp01.f90 @@ -1,11 +1,4 @@ ! RUN: %S/test_errors.sh %s %flang %t -!===--- separate-module-procs.f90 - Test separate module procedure ---------=== -! -! 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 -! -!===------------------------------------------------------------------------=== ! case 1: ma_create_new_fun' was not declared a separate module procedure module m1 diff --git a/test/Semantics/separate-mp02.f90 b/test/Semantics/separate-mp02.f90 new file mode 100644 index 000000000000..1f514c2ccd37 --- /dev/null +++ b/test/Semantics/separate-mp02.f90 @@ -0,0 +1,285 @@ +! RUN: %S/test_errors.sh %s %flang %t + +! When a module subprogram has the MODULE prefix the following must match +! with the corresponding separate module procedure interface body: +! - C1549: characteristics and dummy argument names +! - C1550: binding label +! - C1551: NON_RECURSIVE prefix + +module m1 + interface + module subroutine s4(x) + real, intent(in) :: x + end + module subroutine s5(x, y) + real, pointer :: x + real, value :: y + end + module subroutine s6(x, y) + real :: x + real :: y + end + module subroutine s7(x, y, z) + real :: x(8) + real :: y(8) + real :: z(8) + end + module subroutine s8(x, y, z) + real :: x(8) + real :: y(*) + real :: z(*) + end + module subroutine s9(x, y, z, w) + character(len=4) :: x + character(len=4) :: y + character(len=*) :: z + character(len=*) :: w + end + end interface +end + +submodule(m1) sm1 +contains + module subroutine s4(x) + !ERROR: The intent of dummy argument 'x' does not match the intent of the corresponding argument in the interface body + real, intent(out) :: x + end + module subroutine s5(x, y) + !ERROR: Dummy argument 'x' has the OPTIONAL attribute; the corresponding argument in the interface body does not + real, pointer, optional :: x + !ERROR: Dummy argument 'y' does not have the VALUE attribute; the corresponding argument in the interface body does + real :: y + end + module subroutine s6(x, y) + !ERROR: Dummy argument 'x' has type INTEGER(4); the corresponding argument in the interface body has type REAL(4) + integer :: x + !ERROR: Dummy argument 'y' has type REAL(8); the corresponding argument in the interface body has type REAL(4) + real(8) :: y + end + module subroutine s7(x, y, z) + integer, parameter :: n = 8 + real :: x(n) + real :: y(2:n+1) + !ERROR: The shape of dummy argument 'z' does not match the shape of the corresponding argument in the interface body + real :: z(n+1) + end + module subroutine s8(x, y, z) + !ERROR: The shape of dummy argument 'x' does not match the shape of the corresponding argument in the interface body + real :: x(*) + real :: y(*) + !ERROR: The shape of dummy argument 'z' does not match the shape of the corresponding argument in the interface body + real :: z(8) + end + module subroutine s9(x, y, z, w) + character(len=4) :: x + !ERROR: Dummy argument 'y' has type CHARACTER(KIND=1,LEN=5_4); the corresponding argument in the interface body has type CHARACTER(KIND=1,LEN=4_4) + character(len=5) :: y + character(len=*) :: z + !ERROR: Dummy argument 'w' has type CHARACTER(KIND=1,LEN=4_4); the corresponding argument in the interface body has type CHARACTER(KIND=1,LEN=*) + character(len=4) :: w + end +end + +module m2 + interface + module subroutine s1(x, y) + real, intent(in) :: x + real, intent(out) :: y + end + module subroutine s2(x, y) + real, intent(in) :: x + real, intent(out) :: y + end + module subroutine s3(x, y) + real(4) :: x + procedure(real) :: y + end + module subroutine s4() + end + non_recursive module subroutine s5() + end + end interface +end + +submodule(m2) sm2 +contains + !ERROR: Module subprogram 's1' has 3 args but the corresponding interface body has 2 + module subroutine s1(x, y, z) + real, intent(in) :: x + real, intent(out) :: y + real :: z + end + !ERROR: Dummy argument name 'z' does not match corresponding name 'y' in interface body + module subroutine s2(x, z) + real, intent(in) :: x + real, intent(out) :: y + end + module subroutine s3(x, y) + !ERROR: Dummy argument 'x' is a procedure; the corresponding argument in the interface body is not + procedure(real) :: x + !ERROR: Dummy argument 'y' is a data object; the corresponding argument in the interface body is not + real :: y + end + !ERROR: Module subprogram 's4' has NON_RECURSIVE prefix but the corresponding interface body does not + non_recursive module subroutine s4() + end + !ERROR: Module subprogram 's5' does not have NON_RECURSIVE prefix but the corresponding interface body does + module subroutine s5() + end +end + +module m2b + interface + module subroutine s1() + end + module subroutine s2() bind(c, name="s2") + end + module subroutine s3() bind(c, name="s3") + end + end interface +end + +submodule(m2b) sm2b + character(*), parameter :: suffix = "_xxx" +contains + !ERROR: Module subprogram 's1' has a binding label but the corresponding interface body does not + module subroutine s1() bind(c, name="s1") + end + !ERROR: Module subprogram 's2' does not have a binding label but the corresponding interface body does + module subroutine s2() + end + !ERROR: Module subprogram 's3' has binding label "s3_xxx" but the corresponding interface body has "s3" + module subroutine s3() bind(c, name="s3" // suffix) + end +end + + +module m3 + interface + module subroutine s1(x, y, z) + procedure(real), intent(in) :: x + procedure(real), intent(out) :: y + procedure(real), intent(out) :: z + end + module subroutine s2(x, y) + procedure(real), pointer :: x + procedure(real) :: y + end + end interface +end + +submodule(m3) sm3 +contains + module subroutine s1(x, y, z) + procedure(real), intent(in) :: x + !ERROR: The intent of dummy argument 'y' does not match the intent of the corresponding argument in the interface body + procedure(real), intent(inout) :: y + !ERROR: The intent of dummy argument 'z' does not match the intent of the corresponding argument in the interface body + procedure(real) :: z + end + module subroutine s2(x, y) + !ERROR: Dummy argument 'x' has the OPTIONAL attribute; the corresponding argument in the interface body does not + !ERROR: Dummy argument 'x' does not have the POINTER attribute; the corresponding argument in the interface body does + procedure(real), optional :: x + !ERROR: Dummy argument 'y' has the POINTER attribute; the corresponding argument in the interface body does not + procedure(real), pointer :: y + end +end + +module m4 + interface + subroutine s_real(x) + real :: x + end + subroutine s_real2(x) + real :: x + end + subroutine s_integer(x) + integer :: x + end + module subroutine s1(x) + procedure(s_real) :: x + end + module subroutine s2(x) + procedure(s_real) :: x + end + end interface +end + +submodule(m4) sm4 +contains + module subroutine s1(x) + !OK + procedure(s_real2) :: x + end + module subroutine s2(x) + !ERROR: Dummy procedure 'x' does not match the corresponding argument in the interface body + procedure(s_integer) :: x + end +end + +module m5 + interface + module function f1() + real :: f1 + end + module subroutine s2() + end + end interface +end + +submodule(m5) sm5 +contains + !ERROR: Module subroutine 'f1' was declared as a function in the corresponding interface body + module subroutine f1() + end + !ERROR: Module function 's2' was declared as a subroutine in the corresponding interface body + module function s2() + end +end + +module m6 + interface + module function f1() + real :: f1 + end + module function f2() + real :: f2 + end + module function f3() + real :: f3 + end + end interface +end + +submodule(m6) ms6 +contains + !OK + real module function f1() + end + !ERROR: Return type of function 'f2' does not match return type of the corresponding interface body + integer module function f2() + end + !ERROR: Return type of function 'f3' does not match return type of the corresponding interface body + module function f3() + real :: f3 + pointer :: f3 + end +end + +module m7 + interface + module subroutine s1(x, *) + real :: x + end + end interface +end + +submodule(m7) sm7 +contains + !ERROR: Dummy argument 1 of 's1' is an alternate return indicator but the corresponding argument in the interface body is not + !ERROR: Dummy argument 2 of 's1' is not an alternate return indicator but the corresponding argument in the interface body is + module subroutine s1(*, x) + real :: x + end +end From 234bb519cd38d0b9234fc0b4a8d11cfcb9935e6a Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 13 Mar 2020 12:19:44 -0700 Subject: [PATCH 095/345] Improve error message for procedure passed as invalid argument to an intrinsic Support forward references to sibling module procedures Add tests, handle corner cases Rename new test --- include/flang/Evaluate/check-expression.h | 8 +++ include/flang/Semantics/expression.h | 1 + include/flang/Semantics/symbol.h | 6 +- lib/Evaluate/check-expression.cpp | 7 +++ lib/Evaluate/intrinsics.cpp | 22 +++++--- lib/Semantics/check-declarations.cpp | 26 +++++++-- lib/Semantics/expression.cpp | 66 ++++++++++++++-------- lib/Semantics/program-tree.h | 14 ++++- lib/Semantics/resolve-names.cpp | 67 +++++++++++++++++------ lib/Semantics/resolve-names.h | 2 + lib/Semantics/tools.cpp | 3 +- test/Semantics/expr-errors02.f90 | 3 +- test/Semantics/resolve59.f90 | 18 +++--- test/Semantics/resolve77.f90 | 52 ++++++++++++++++++ 14 files changed, 227 insertions(+), 68 deletions(-) create mode 100644 test/Semantics/resolve77.f90 diff --git a/include/flang/Evaluate/check-expression.h b/include/flang/Evaluate/check-expression.h index afd730924baf..2285881c329a 100644 --- a/include/flang/Evaluate/check-expression.h +++ b/include/flang/Evaluate/check-expression.h @@ -31,6 +31,7 @@ class IntrinsicProcTable; template bool IsConstantExpr(const A &); extern template bool IsConstantExpr(const Expr &); extern template bool IsConstantExpr(const Expr &); +extern template bool IsConstantExpr(const Expr &); // Checks whether an expression is an object designator with // constant addressing and no vector-valued subscript. @@ -44,6 +45,13 @@ void CheckSpecificationExpr( const A &, parser::ContextualMessages &, const semantics::Scope &); extern template void CheckSpecificationExpr(const Expr &x, parser::ContextualMessages &, const semantics::Scope &); +extern template void CheckSpecificationExpr(const Expr &x, + parser::ContextualMessages &, const semantics::Scope &); +extern template void CheckSpecificationExpr(const Expr &x, + parser::ContextualMessages &, const semantics::Scope &); +extern template void CheckSpecificationExpr( + const std::optional> &x, parser::ContextualMessages &, + const semantics::Scope &); extern template void CheckSpecificationExpr( const std::optional> &x, parser::ContextualMessages &, const semantics::Scope &); diff --git a/include/flang/Semantics/expression.h b/include/flang/Semantics/expression.h index bf04275d6004..77ead117f345 100644 --- a/include/flang/Semantics/expression.h +++ b/include/flang/Semantics/expression.h @@ -353,6 +353,7 @@ class ExpressionAnalyzer { parser::CharBlock, const ProcedureDesignator &, ActualArguments &); using AdjustActuals = std::optional>; + bool ResolveForward(const Symbol &); const Symbol *ResolveGeneric(const Symbol &, const ActualArguments &, const AdjustActuals &, bool mightBeStructureConstructor = false); void EmitGenericResolutionError(const Symbol &); diff --git a/include/flang/Semantics/symbol.h b/include/flang/Semantics/symbol.h index 80f702a98307..b97cdf0f41e7 100644 --- a/include/flang/Semantics/symbol.h +++ b/include/flang/Semantics/symbol.h @@ -27,6 +27,7 @@ namespace Fortran::semantics { class Scope; class Symbol; +class ProgramTree; using SymbolRef = common::Reference; using SymbolVector = std::vector; @@ -91,12 +92,15 @@ ENUM_CLASS(SubprogramKind, Module, Internal) // type information. class SubprogramNameDetails { public: - SubprogramNameDetails(SubprogramKind kind) : kind_{kind} {} + SubprogramNameDetails(SubprogramKind kind, ProgramTree &node) + : kind_{kind}, node_{node} {} SubprogramNameDetails() = delete; SubprogramKind kind() const { return kind_; } + ProgramTree &node() const { return *node_; } private: SubprogramKind kind_; + common::Reference node_; }; // A name from an entity-decl -- could be object or function. diff --git a/lib/Evaluate/check-expression.cpp b/lib/Evaluate/check-expression.cpp index fede3aec6e9a..07b9065dcb29 100644 --- a/lib/Evaluate/check-expression.cpp +++ b/lib/Evaluate/check-expression.cpp @@ -63,6 +63,7 @@ template bool IsConstantExpr(const A &x) { } template bool IsConstantExpr(const Expr &); template bool IsConstantExpr(const Expr &); +template bool IsConstantExpr(const Expr &); // Object pointer initialization checking predicate IsInitialDataTarget(). // This code determines whether an expression is allowable as the static @@ -244,6 +245,12 @@ void CheckSpecificationExpr(const A &x, parser::ContextualMessages &messages, template void CheckSpecificationExpr(const Expr &, parser::ContextualMessages &, const semantics::Scope &); +template void CheckSpecificationExpr(const Expr &, + parser::ContextualMessages &, const semantics::Scope &); +template void CheckSpecificationExpr(const Expr &, + parser::ContextualMessages &, const semantics::Scope &); +template void CheckSpecificationExpr(const std::optional> &, + parser::ContextualMessages &, const semantics::Scope &); template void CheckSpecificationExpr(const std::optional> &, parser::ContextualMessages &, const semantics::Scope &); template void CheckSpecificationExpr( diff --git a/lib/Evaluate/intrinsics.cpp b/lib/Evaluate/intrinsics.cpp index b5eacb9415ff..93dcfe28f5ae 100644 --- a/lib/Evaluate/intrinsics.cpp +++ b/lib/Evaluate/intrinsics.cpp @@ -42,6 +42,10 @@ class FoldingContext; // optionality and defaults. The kind and rank patterns are represented // here with code values that are significant to the matching/validation engine. +// An actual argument to an intrinsic procedure may be a procedure itself +// only if the dummy argument is Rank::reduceOperation, +// KindCode::addressable, or the special case of NULL(MOLD=procedurePointer). + // These are small bit-sets of type category enumerators. // Note that typeless (BOZ literal) values don't have a distinct type category. // These typeless arguments are represented in the tables as if they were @@ -1085,9 +1089,8 @@ std::optional IntrinsicInterface::Match( std::optional type{arg->GetType()}; if (!type) { CHECK(arg->Rank() == 0); - const Expr *expr{arg->UnwrapExpr()}; - CHECK(expr); - if (std::holds_alternative(expr->u)) { + const Expr &expr{DEREF(arg->UnwrapExpr())}; + if (std::holds_alternative(expr.u)) { if (d.typePattern.kindCode == KindCode::typeless || d.rank == Rank::elementalOrBOZ) { continue; @@ -1097,11 +1100,14 @@ std::optional IntrinsicInterface::Match( d.keyword); } } else { - // NULL(), pointer to subroutine, &c. - if (d.typePattern.kindCode == KindCode::addressable) { + // NULL(), procedure, or procedure pointer + CHECK(IsProcedurePointer(expr)); + if (d.typePattern.kindCode == KindCode::addressable || + d.rank == Rank::reduceOperation) { continue; } else { - messages.Say("Typeless item not allowed for '%s=' argument"_err_en_US, + messages.Say( + "Actual argument for '%s=' may not be a procedure"_err_en_US, d.keyword); } } @@ -1249,8 +1255,8 @@ std::optional IntrinsicInterface::Match( argOk = rank == 0 || rank + 1 == arrayArg->Rank(); break; case Rank::reduceOperation: - // TODO: Confirm that the argument is a pure function - // of two arguments with several constraints + // TODO: validate the reduction operation -- it must be a pure + // function of two arguments with special constraints. CHECK(arrayArg); argOk = rank == 0; break; diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index 43fe3435ae07..c1cd33c75f33 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -43,12 +43,26 @@ class CheckHelper { void Check(const Scope &); private: + template void CheckSpecExpr(const A &x) { + if (symbolBeingChecked_ && IsSaved(*symbolBeingChecked_)) { + if (!evaluate::IsConstantExpr(x)) { + messages_.Say( + "Specification expression must be constant in declaration of '%s' with the SAVE attribute"_err_en_US, + symbolBeingChecked_->name()); + } + } else { + evaluate::CheckSpecificationExpr(x, messages_, DEREF(scope_)); + } + } + template void CheckSpecExpr(const std::optional &x) { + if (x) { + CheckSpecExpr(*x); + } + } template void CheckSpecExpr(A &x) { x = Fold(foldingContext_, std::move(x)); - evaluate::CheckSpecificationExpr(x, messages_, DEREF(scope_)); - } - template void CheckSpecExpr(const A &x) { - evaluate::CheckSpecificationExpr(x, messages_, DEREF(scope_)); + const A &constx{x}; + CheckSpecExpr(constx); } void CheckValue(const Symbol &, const DerivedTypeSpec *); void CheckVolatile( @@ -103,6 +117,7 @@ class CheckHelper { // This symbol is the one attached to the innermost enclosing scope // that has a symbol. const Symbol *innermostSymbol_{nullptr}; + const Symbol *symbolBeingChecked_{nullptr}; }; void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) { @@ -348,10 +363,13 @@ void CheckHelper::CheckAssumedTypeEntity( // C709 void CheckHelper::CheckObjectEntity( const Symbol &symbol, const ObjectEntityDetails &details) { + CHECK(!symbolBeingChecked_); + symbolBeingChecked_ = &symbol; // for specification expr checks CheckArraySpec(symbol, details.shape()); Check(details.shape()); Check(details.coshape()); CheckAssumedTypeEntity(symbol, details); + symbolBeingChecked_ = nullptr; if (!details.coshape().empty()) { if (IsAllocatable(symbol)) { if (!details.coshape().IsDeferredShape()) { // C827 diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 302ef7fdcaf8..84ec0a7386fe 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -9,6 +9,7 @@ #include "flang/Semantics/expression.h" #include "check-call.h" #include "pointer-assignment.h" +#include "resolve-names.h" #include "flang/Common/idioms.h" #include "flang/Evaluate/common.h" #include "flang/Evaluate/fold.h" @@ -1718,6 +1719,41 @@ static bool CheckCompatibleArguments( return true; } +// Handles a forward reference to a module function from what must +// be a specification expression. Return false if the symbol is +// an invalid forward reference. +bool ExpressionAnalyzer::ResolveForward(const Symbol &symbol) { + if (context_.HasError(symbol)) { + return false; + } + if (const auto *details{ + symbol.detailsIf()}) { + if (details->kind() == semantics::SubprogramKind::Module) { + // If this symbol is still a SubprogramNameDetails, we must be + // checking a specification expression in a sibling module + // procedure. Resolve its names now so that its interface + // is known. + semantics::ResolveSpecificationParts(context_, symbol); + if (symbol.has()) { + // When the symbol hasn't had its details updated, we must have + // already been in the process of resolving the function's + // specification part; but recursive function calls are not + // allowed in specification parts (10.1.11 para 5). + Say("The module function '%s' may not be referenced recursively in a specification expression"_err_en_US, + symbol.name()); + context_.SetError(const_cast(symbol)); + return false; + } + } else { // 10.1.11 para 4 + Say("The internal function '%s' may not be referenced in a specification expression"_err_en_US, + symbol.name()); + context_.SetError(const_cast(symbol)); + return false; + } + } + return true; +} + // Resolve a call to a generic procedure with given actual arguments. // adjustActuals is called on procedure bindings to handle pass arg. const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol, @@ -1726,6 +1762,9 @@ const Symbol *ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol, const Symbol *elemental{nullptr}; // matching elemental specific proc const auto &details{symbol.GetUltimate().get()}; for (const Symbol &specific : details.specificProcs()) { + if (!ResolveForward(specific)) { + continue; + } if (std::optional procedure{ characteristics::Procedure::Characterize( ProcedureDesignator{specific}, context_.intrinsics())}) { @@ -2533,6 +2572,11 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite, return Expr{NullPointer{}}; } } + if (const Symbol * symbol{proc.GetSymbol()}) { + if (!ResolveForward(*symbol)) { + return std::nullopt; + } + } if (auto chars{CheckCall(callSite, proc, arguments)}) { if (chars->functionResult) { const auto &result{*chars->functionResult}; @@ -2547,28 +2591,6 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite, } } } - if (const Symbol * symbol{proc.GetSymbol()}) { - if (const auto *details{ - symbol->detailsIf()}) { - // If this symbol is still a SubprogramNameDetails, we must be - // checking a specification expression in a sibling module or internal - // procedure. Since recursion is disallowed in specification - // expressions, we should handle such references by processing the - // sibling procedure's specification part right now (recursively), - // but until we can do so, just complain about the forward reference. - // TODO: recursively process sibling's specification part. - if (details->kind() == semantics::SubprogramKind::Module) { - Say("The module function '%s' must have been previously defined " - "when referenced in a specification expression"_err_en_US, - symbol->name()); - } else { - Say("The internal function '%s' cannot be referenced in " - "a specification expression"_err_en_US, - symbol->name()); - } - return std::nullopt; - } - } return std::nullopt; } diff --git a/lib/Semantics/program-tree.h b/lib/Semantics/program-tree.h index 84e33ba7738d..43d986be7544 100644 --- a/lib/Semantics/program-tree.h +++ b/lib/Semantics/program-tree.h @@ -11,6 +11,7 @@ #include "flang/Parser/parse-tree.h" #include "flang/Semantics/symbol.h" +#include #include // A ProgramTree represents a tree of program units and their contained @@ -56,11 +57,17 @@ class ProgramTree { const parser::Name &name() const { return name_; } Kind GetKind() const; const Stmt &stmt() const { return stmt_; } + bool isSpecificationPartResolved() const { + return isSpecificationPartResolved_; + } + void set_isSpecificationPartResolved(bool yes = true) { + isSpecificationPartResolved_ = yes; + } const parser::ParentIdentifier &GetParentId() const; // only for Submodule const parser::SpecificationPart &spec() const { return spec_; } const parser::ExecutionPart *exec() const { return exec_; } - std::vector &children() { return children_; } - const std::vector &children() const { return children_; } + std::list &children() { return children_; } + const std::list &children() const { return children_; } Symbol::Flag GetSubpFlag() const; bool IsModule() const; // Module or Submodule bool HasModulePrefix() const; // in function or subroutine stmt @@ -84,9 +91,10 @@ class ProgramTree { static_cast *>(nullptr)}; const parser::SpecificationPart &spec_; const parser::ExecutionPart *exec_{nullptr}; - std::vector children_; + std::list children_; Scope *scope_{nullptr}; const parser::CharBlock *endStmt_{nullptr}; + bool isSpecificationPartResolved_{false}; }; } diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index d3d3c7067a54..42766f738e07 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -90,6 +90,9 @@ class ImplicitRules { friend void ShowImplicitRule(std::ostream &, const ImplicitRules &, char); }; +// scope -> implicit rules for that scope +using ImplicitRulesMap = std::map; + // Track statement source locations and save messages. class MessageHandler { public: @@ -135,8 +138,9 @@ class MessageHandler { class BaseVisitor { public: BaseVisitor() { DIE("BaseVisitor: default-constructed"); } - BaseVisitor(SemanticsContext &c, ResolveNamesVisitor &v) - : this_{&v}, context_{&c}, messageHandler_{c} {} + BaseVisitor( + SemanticsContext &c, ResolveNamesVisitor &v, ImplicitRulesMap &rules) + : implicitRulesMap_{&rules}, this_{&v}, context_{&c}, messageHandler_{c} {} template void Walk(const T &); MessageHandler &messageHandler() { return messageHandler_; } @@ -215,6 +219,9 @@ class BaseVisitor { return messageHandler_.Say(name.source, std::move(text), args...); } +protected: + ImplicitRulesMap *implicitRulesMap_{nullptr}; + private: ResolveNamesVisitor *this_; SemanticsContext *context_; @@ -377,8 +384,6 @@ class ImplicitRulesVisitor : public DeclTypeSpecVisitor { void SetScope(const Scope &); private: - // scope -> implicit rules for that scope - std::map implicitRulesMap_; // implicit rules in effect for current scope ImplicitRules *implicitRules_{nullptr}; std::optional prevImplicit_; @@ -1330,7 +1335,8 @@ class ResolveNamesVisitor : public virtual ScopeHandler, using SubprogramVisitor::Post; using SubprogramVisitor::Pre; - ResolveNamesVisitor(SemanticsContext &context) : BaseVisitor{context, *this} { + ResolveNamesVisitor(SemanticsContext &context, ImplicitRulesMap &rules) + : BaseVisitor{context, *this, rules} { PushScope(context.globalScope()); } @@ -1370,6 +1376,8 @@ class ResolveNamesVisitor : public virtual ScopeHandler, void NoteExecutablePartCall(Symbol::Flag, const parser::Call &); + friend void ResolveSpecificationParts(SemanticsContext &, const Symbol &); + private: // Kind of procedure we are expecting to see in a ProcedureDesignator std::optional expectedProcFlag_; @@ -1384,8 +1392,8 @@ class ResolveNamesVisitor : public virtual ScopeHandler, void HandleProcedureName(Symbol::Flag, const parser::Name &); bool SetProcFlag(const parser::Name &, Symbol &, Symbol::Flag); void ResolveSpecificationParts(ProgramTree &); - void AddSubpNames(const ProgramTree &); - bool BeginScope(const ProgramTree &); + void AddSubpNames(ProgramTree &); + bool BeginScopeForNode(const ProgramTree &); void FinishSpecificationParts(const ProgramTree &); void FinishDerivedTypeInstantiation(Scope &); void ResolveExecutionParts(const ProgramTree &); @@ -1712,7 +1720,7 @@ void ImplicitRulesVisitor::Post(const parser::ImplicitSpec &) { } void ImplicitRulesVisitor::SetScope(const Scope &scope) { - implicitRules_ = &implicitRulesMap_.at(&scope); + implicitRules_ = &DEREF(implicitRulesMap_).at(&scope); prevImplicit_ = std::nullopt; prevImplicitNone_ = std::nullopt; prevImplicitNoneType_ = std::nullopt; @@ -1720,7 +1728,7 @@ void ImplicitRulesVisitor::SetScope(const Scope &scope) { } void ImplicitRulesVisitor::BeginScope(const Scope &scope) { // find or create implicit rules for this scope - implicitRulesMap_.try_emplace(&scope, context(), implicitRules_); + DEREF(implicitRulesMap_).try_emplace(&scope, context(), implicitRules_); SetScope(scope); } @@ -1910,7 +1918,7 @@ void ScopeHandler::PushScope(Scope &scope) { currScope_ = &scope; auto kind{currScope_->kind()}; if (kind != Scope::Kind::Block) { - ImplicitRulesVisitor::BeginScope(scope); + BeginScope(scope); } // The name of a module or submodule cannot be "used" in its scope, // as we read 19.3.1(2), so we allow the name to be used as a local @@ -5818,7 +5826,11 @@ class ExecutionPartSkimmer { // Build the scope tree and resolve names in the specification parts of this // node and its children void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) { - if (!BeginScope(node)) { + if (node.isSpecificationPartResolved()) { + return; // been here already + } + node.set_isSpecificationPartResolved(); + if (!BeginScopeForNode(node)) { return; // an error prevented scope from being created } Scope &scope{currScope()}; @@ -5861,18 +5873,18 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) { } } -// Add SubprogramNameDetails symbols for contained subprograms -void ResolveNamesVisitor::AddSubpNames(const ProgramTree &node) { +// Add SubprogramNameDetails symbols for module and internal subprograms +void ResolveNamesVisitor::AddSubpNames(ProgramTree &node) { auto kind{ node.IsModule() ? SubprogramKind::Module : SubprogramKind::Internal}; - for (const auto &child : node.children()) { - auto &symbol{MakeSymbol(child.name(), SubprogramNameDetails{kind})}; + for (auto &child : node.children()) { + auto &symbol{MakeSymbol(child.name(), SubprogramNameDetails{kind, child})}; symbol.set(child.GetSubpFlag()); } } // Push a new scope for this node or return false on error. -bool ResolveNamesVisitor::BeginScope(const ProgramTree &node) { +bool ResolveNamesVisitor::BeginScopeForNode(const ProgramTree &node) { switch (node.GetKind()) { SWITCH_COVERS_ALL_CASES case ProgramTree::Kind::Program: @@ -6539,8 +6551,29 @@ void ResolveNamesVisitor::Post(const parser::Program &) { CHECK(!GetDeclTypeSpec()); } +// A singleton instance of the scope -> IMPLICIT rules mapping is +// shared by all instances of ResolveNamesVisitor and accessed by this +// pointer when the visitors (other than the top-level original) are +// constructed. +static ImplicitRulesMap *sharedImplicitRulesMap{nullptr}; + bool ResolveNames(SemanticsContext &context, const parser::Program &program) { - ResolveNamesVisitor{context}.Walk(program); + ImplicitRulesMap implicitRulesMap; + auto restorer{common::ScopedSet(sharedImplicitRulesMap, &implicitRulesMap)}; + ResolveNamesVisitor{context, implicitRulesMap}.Walk(program); return !context.AnyFatalError(); } + +// Processes a module (but not internal) function when it is referenced +// in a specification expression in a sibling procedure. +void ResolveSpecificationParts( + SemanticsContext &context, const Symbol &subprogram) { + auto originalLocation{context.location()}; + ResolveNamesVisitor visitor{context, DEREF(sharedImplicitRulesMap)}; + ProgramTree &node{subprogram.get().node()}; + const Scope &moduleScope{subprogram.owner()}; + visitor.SetScope(const_cast(moduleScope)); + visitor.ResolveSpecificationParts(node); + context.set_location(std::move(originalLocation)); +} } diff --git a/lib/Semantics/resolve-names.h b/lib/Semantics/resolve-names.h index 8f233adc5ec3..240f315bb715 100644 --- a/lib/Semantics/resolve-names.h +++ b/lib/Semantics/resolve-names.h @@ -20,8 +20,10 @@ struct Program; namespace Fortran::semantics { class SemanticsContext; +class Symbol; bool ResolveNames(SemanticsContext &, const parser::Program &); +void ResolveSpecificationParts(SemanticsContext &, const Symbol &); void DumpSymbols(std::ostream &); } diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index f77a5cc8aaf1..cc3b9084af34 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -607,8 +607,7 @@ bool IsOrContainsEventOrLockComponent(const Symbol &symbol) { bool IsSaved(const Symbol &symbol) { auto scopeKind{symbol.owner().kind()}; - if (scopeKind == Scope::Kind::MainProgram || - scopeKind == Scope::Kind::Module) { + if (scopeKind == Scope::Kind::Module || scopeKind == Scope::Kind::BlockData) { return true; } else if (scopeKind == Scope::Kind::DerivedType) { return false; // this is a component diff --git a/test/Semantics/expr-errors02.f90 b/test/Semantics/expr-errors02.f90 index d1aac68bf008..4b0d6d4118f3 100644 --- a/test/Semantics/expr-errors02.f90 +++ b/test/Semantics/expr-errors02.f90 @@ -29,7 +29,7 @@ subroutine test(out, optional) integer :: local !ERROR: Invalid specification expression: reference to local entity 'local' type(t(local)) :: x2 - !ERROR: The internal function 'internal' cannot be referenced in a specification expression + !ERROR: The internal function 'internal' may not be referenced in a specification expression type(t(internal(0))) :: x3 integer, intent(out) :: out !ERROR: Invalid specification expression: reference to INTENT(OUT) dummy argument 'out' @@ -43,7 +43,6 @@ subroutine test(out, optional) type(t(coarray[1])) :: x7 type(t(kind(foo()))) :: x101 ! ok type(t(modulefunc1(0))) :: x102 ! ok - !ERROR: The module function 'modulefunc2' must have been previously defined when referenced in a specification expression type(t(modulefunc2(0))) :: x103 ! ok contains pure integer function internal(n) diff --git a/test/Semantics/resolve59.f90 b/test/Semantics/resolve59.f90 index 0e6965a5d165..fdc437030971 100644 --- a/test/Semantics/resolve59.f90 +++ b/test/Semantics/resolve59.f90 @@ -41,7 +41,7 @@ function f4() f4 => rf ! OK call to f4 pointer (rf) x = acos(f4()) - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f4) end function function f5(x) @@ -55,7 +55,7 @@ real function rfunc(x) f5 => rfunc ! OK call to f5 pointer x = acos(f5(x+1)) - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f5) end function ! Sanity test: f18 handles C1560 violation by ignoring RESULT @@ -78,21 +78,21 @@ module m_with_result function f1() result(r) real :: r r = acos(f1()) !OK, recursive call - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f1) end function function f2(i) result(r) integer i real :: r r = acos(f2(i+1)) ! OK, recursive call - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure r = acos(f2) end function function f3(i) result(r) integer i real :: r(1) r = acos(f3(i+1)) !OK recursive call - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure r = sum(acos(f3)) end function @@ -104,9 +104,9 @@ function f4() result(r) real :: x procedure(rf), pointer :: r r => rf - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f4()) ! recursive call - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f4) x = acos(r()) ! OK end function @@ -114,9 +114,9 @@ function f5(x) result(r) real :: x procedure(acos), pointer :: r r => acos - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f5(x+1)) ! recursive call - !ERROR: Typeless item not allowed for 'x=' argument + !ERROR: Actual argument for 'x=' may not be a procedure x = acos(f5) x = acos(r(x+1)) ! OK end function diff --git a/test/Semantics/resolve77.f90 b/test/Semantics/resolve77.f90 new file mode 100644 index 000000000000..4d34ce3b8b48 --- /dev/null +++ b/test/Semantics/resolve77.f90 @@ -0,0 +1,52 @@ +! RUN: %S/test_errors.sh %s %flang %t +! Tests valid and invalid usage of forward references to procedures +! in specification expressions. +module m + interface ifn2 + module procedure if2 + end interface + interface ifn3 + module procedure if3 + end interface + !ERROR: Specification expression must be constant in declaration of 'a' with the SAVE attribute + real :: a(if1(1)) + !ERROR: No specific procedure of generic 'ifn2' matches the actual arguments + real :: b(ifn2(1)) + contains + subroutine t1(n) + integer :: iarr(if1(n)) + end subroutine + pure integer function if1(n) + integer, intent(in) :: n + if1 = n + end function + subroutine t2(n) + integer :: iarr(ifn3(n)) ! should resolve to if3 + end subroutine + pure integer function if2(n) + integer, intent(in) :: n + if2 = n + end function + pure integer function if3(n) + integer, intent(in) :: n + if3 = n + end function +end module + +subroutine nester + !ERROR: The internal function 'if1' may not be referenced in a specification expression + real :: a(if1(1)) + contains + subroutine t1(n) + !ERROR: The internal function 'if2' may not be referenced in a specification expression + integer :: iarr(if2(n)) + end subroutine + pure integer function if1(n) + integer, intent(in) :: n + if1 = n + end function + pure integer function if2(n) + integer, intent(in) :: n + if2 = n + end function +end subroutine From a3507d44b8911e6024033aa583c1dc54e0eb89fd Mon Sep 17 00:00:00 2001 From: Caroline Concatto Date: Fri, 28 Feb 2020 15:11:03 +0000 Subject: [PATCH 096/345] [LLVMify F18] Replace the use std::ostream with LLVM streams llvm::ostream This patch replaces the occurrence of std::ostream by llvm::raw_ostream. In LLVM Coding Standards[1] "All new code should use raw_ostream instead of ostream".[1] As a consequence, this patch also replaces the use of: std::stringstream by llvm::raw_string_ostream or llvm::raw_ostream* std::ofstream by llvm::raw_fd_ostream std::endl by '\n' and flush()[2] std::cout by llvm::outs() and std::cerr by llvm::errs() It also replaces std::strerro by llvm::sys::StrError** , but NOT in Fortran runtime libraries *std::stringstream were replaced by llvm::raw_ostream in all methods that used std::stringstream as a parameter. Moreover, it removes the pointers to these streams. [1]https://llvm.org/docs/CodingStandards.html [2]https://releases.llvm.org/2.5/docs/CodingStandards.html#ll_avoidendl Signed-off-by: Caroline Concatto Running clang-format-7 Signed-off-by: Caroline Concatto Removing residue of ostream library Signed-off-by: Caroline Concatto --- documentation/ParserCombinators.md | 2 +- include/flang/Common/enum-set.h | 6 +- include/flang/Evaluate/call.h | 15 ++- include/flang/Evaluate/characteristics.h | 19 +-- include/flang/Evaluate/complex.h | 6 +- include/flang/Evaluate/constant.h | 9 +- include/flang/Evaluate/expression.h | 23 ++-- include/flang/Evaluate/formatting.h | 14 +-- include/flang/Evaluate/intrinsics.h | 7 +- include/flang/Evaluate/real.h | 6 +- include/flang/Evaluate/static-data.h | 7 +- include/flang/Evaluate/variable.h | 33 ++--- include/flang/Lower/PFTBuilder.h | 5 +- include/flang/Parser/char-block.h | 6 +- include/flang/Parser/dump-parse-tree.h | 14 +-- include/flang/Parser/instrumented-parser.h | 7 +- include/flang/Parser/message.h | 5 +- include/flang/Parser/parsing.h | 14 +-- include/flang/Parser/provenance.h | 17 ++- include/flang/Parser/source.h | 11 +- include/flang/Parser/unparse.h | 14 ++- include/flang/Parser/user-state.h | 10 +- include/flang/Semantics/attr.h | 10 +- include/flang/Semantics/scope.h | 6 +- include/flang/Semantics/semantics.h | 10 +- include/flang/Semantics/symbol.h | 22 ++-- include/flang/Semantics/type.h | 20 +-- .../flang/Semantics/unparse-with-symbols.h | 6 +- lib/Evaluate/characteristics.cpp | 18 +-- lib/Evaluate/complex.cpp | 3 +- lib/Evaluate/expression.cpp | 1 + lib/Evaluate/formatting.cpp | 119 ++++++++++-------- lib/Evaluate/host.cpp | 13 +- lib/Evaluate/intrinsics.cpp | 22 ++-- lib/Evaluate/real.cpp | 5 +- lib/Evaluate/static-data.cpp | 2 +- lib/Evaluate/type.cpp | 1 - lib/Evaluate/variable.cpp | 1 - lib/Parser/CMakeLists.txt | 1 + lib/Parser/char-block.cpp | 4 +- lib/Parser/debug-parser.cpp | 1 - lib/Parser/instrumented-parser.cpp | 4 +- lib/Parser/message.cpp | 11 +- lib/Parser/parse-tree.cpp | 3 +- lib/Parser/parsing.cpp | 24 ++-- lib/Parser/preprocessor.cpp | 10 +- lib/Parser/prescan.cpp | 7 +- lib/Parser/provenance.cpp | 22 ++-- lib/Parser/source.cpp | 20 +-- lib/Parser/token-sequence.cpp | 5 +- lib/Parser/token-sequence.h | 9 +- lib/Parser/unparse.cpp | 11 +- lib/Semantics/attr.cpp | 6 +- lib/Semantics/expression.cpp | 11 +- lib/Semantics/mod-file.cpp | 96 +++++++------- lib/Semantics/mod-file.h | 21 +++- lib/Semantics/pointer-assignment.cpp | 4 +- lib/Semantics/resolve-names-utils.cpp | 1 - lib/Semantics/resolve-names.cpp | 13 +- lib/Semantics/resolve-names.h | 6 +- lib/Semantics/scope.cpp | 7 +- lib/Semantics/semantics.cpp | 15 +-- lib/Semantics/symbol.cpp | 48 +++---- lib/Semantics/tools.cpp | 5 +- lib/Semantics/type.cpp | 31 +++-- lib/Semantics/unparse-with-symbols.cpp | 17 ++- tools/f18/dump.cpp | 14 +-- tools/f18/f18-parse-demo.cpp | 53 ++++---- tools/f18/f18.cpp | 100 ++++++++------- unittests/Decimal/CMakeLists.txt | 2 + unittests/Decimal/quick-sanity-test.cpp | 19 ++- unittests/Decimal/thorough-test.cpp | 24 ++-- unittests/Evaluate/CMakeLists.txt | 11 ++ unittests/Evaluate/ISO-Fortran-binding.cpp | 18 ++- unittests/Evaluate/fp-testing.cpp | 13 +- unittests/Evaluate/intrinsics.cpp | 19 +-- unittests/Evaluate/real.cpp | 9 +- unittests/Evaluate/testing.cpp | 14 +-- unittests/Evaluate/uint128.cpp | 6 +- unittests/Runtime/CMakeLists.txt | 4 + unittests/Runtime/format.cpp | 10 +- unittests/Runtime/hello.cpp | 6 +- unittests/Runtime/list-input.cpp | 2 +- unittests/Runtime/testing.cpp | 10 +- unittests/Runtime/testing.h | 6 +- 85 files changed, 722 insertions(+), 540 deletions(-) diff --git a/documentation/ParserCombinators.md b/documentation/ParserCombinators.md index 383367144b2b..b05d281590e9 100644 --- a/documentation/ParserCombinators.md +++ b/documentation/ParserCombinators.md @@ -160,5 +160,5 @@ is built. All of the following parsers consume characters acquired from ### Debugging Parser Last, a string literal `"..."_debug` denotes a parser that emits the string to -`std::cerr` and succeeds. It is useful for tracing while debugging a parser but should +`llvm::errs` and succeeds. It is useful for tracing while debugging a parser but should obviously not be committed for production code. diff --git a/include/flang/Common/enum-set.h b/include/flang/Common/enum-set.h index 4b255c38cc5a..ca8bc0cbd838 100644 --- a/include/flang/Common/enum-set.h +++ b/include/flang/Common/enum-set.h @@ -16,11 +16,11 @@ #include "constexpr-bitset.h" #include "idioms.h" +#include "llvm/Support/raw_ostream.h" #include #include #include #include -#include #include #include @@ -199,8 +199,8 @@ template class EnumSet { } } - std::ostream &Dump( - std::ostream &o, std::string EnumToString(enumerationType)) const { + llvm::raw_ostream &Dump( + llvm::raw_ostream &o, std::string EnumToString(enumerationType)) const { char sep{'{'}; IterateOverMembers([&](auto e) { o << sep << EnumToString(e); diff --git a/include/flang/Evaluate/call.h b/include/flang/Evaluate/call.h index a792ff4188ec..e083c75708b9 100644 --- a/include/flang/Evaluate/call.h +++ b/include/flang/Evaluate/call.h @@ -18,9 +18,12 @@ #include "flang/Parser/char-block.h" #include "flang/Semantics/attr.h" #include -#include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { class Symbol; } @@ -60,7 +63,7 @@ class ActualArgument { bool operator==(const AssumedType &that) const { return &*symbol_ == &*that.symbol_; } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: SymbolRef symbol_; @@ -101,7 +104,7 @@ class ActualArgument { std::optional GetType() const; int Rank() const; bool operator==(const ActualArgument &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; std::optional keyword() const { return keyword_; } void set_keyword(parser::CharBlock x) { keyword_ = x; } @@ -146,7 +149,7 @@ struct SpecificIntrinsic { DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(SpecificIntrinsic) ~SpecificIntrinsic(); bool operator==(const SpecificIntrinsic &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; IntrinsicProcedure name; bool isRestrictedSpecific{false}; // if true, can only call it, not pass it @@ -177,7 +180,7 @@ struct ProcedureDesignator { int Rank() const; bool IsElemental() const; std::optional> LEN() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; std::variant> @@ -200,7 +203,7 @@ class ProcedureRef { int Rank() const; bool IsElemental() const { return proc_.IsElemental(); } bool operator==(const ProcedureRef &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; protected: ProcedureDesignator proc_; diff --git a/include/flang/Evaluate/characteristics.h b/include/flang/Evaluate/characteristics.h index d0890465b7c1..50717f04c515 100644 --- a/include/flang/Evaluate/characteristics.h +++ b/include/flang/Evaluate/characteristics.h @@ -24,11 +24,14 @@ #include "flang/Parser/char-block.h" #include "flang/Semantics/symbol.h" #include -#include #include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate { class IntrinsicProcTable; } @@ -131,7 +134,7 @@ class TypeAndShape { const char *thisIs = "POINTER", const char *thatIs = "TARGET", bool isElemental = false) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: void AcquireShape(const semantics::ObjectEntityDetails &); @@ -160,7 +163,7 @@ struct DummyDataObject { } static std::optional Characterize(const semantics::Symbol &); bool CanBePassedViaImplicitInterface() const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; TypeAndShape type; std::vector> coshape; common::Intent intent{common::Intent::Default}; @@ -177,7 +180,7 @@ struct DummyProcedure { bool operator!=(const DummyProcedure &that) const { return !(*this == that); } static std::optional Characterize( const semantics::Symbol &, const IntrinsicProcTable &); - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; CopyableIndirection procedure; common::Intent intent{common::Intent::Default}; Attrs attrs; @@ -187,7 +190,7 @@ struct DummyProcedure { struct AlternateReturn { bool operator==(const AlternateReturn &) const { return true; } bool operator!=(const AlternateReturn &) const { return false; } - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; }; // 15.3.2.1 @@ -208,7 +211,7 @@ struct DummyArgument { bool IsOptional() const; void SetOptional(bool = true); bool CanBePassedViaImplicitInterface() const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; // name and pass are not characteristics and so does not participate in // operator== but are needed to determine if procedures are distinguishable std::string name; @@ -247,7 +250,7 @@ struct FunctionResult { void SetType(DynamicType t) { std::get(u).set_type(t); } bool CanBeReturnedViaImplicitInterface() const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; Attrs attrs; std::variant> u; @@ -288,7 +291,7 @@ struct Procedure { int FindPassIndex(std::optional) const; bool CanBeCalledViaImplicitInterface() const; bool CanOverride(const Procedure &, std::optional passIndex) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; std::optional functionResult; DummyArguments dummyArguments; diff --git a/include/flang/Evaluate/complex.h b/include/flang/Evaluate/complex.h index 370bcfbb5bce..6c404638ab4f 100644 --- a/include/flang/Evaluate/complex.h +++ b/include/flang/Evaluate/complex.h @@ -13,6 +13,10 @@ #include "real.h" #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate::value { template class Complex { @@ -82,7 +86,7 @@ template class Complex { } std::string DumpHexadecimal() const; - std::ostream &AsFortran(std::ostream &, int kind) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &, int kind) const; // TODO: (C)ABS once Real::HYPOT is done // TODO: unit testing diff --git a/include/flang/Evaluate/constant.h b/include/flang/Evaluate/constant.h index 2a4a8109283f..e57563a6ee8c 100644 --- a/include/flang/Evaluate/constant.h +++ b/include/flang/Evaluate/constant.h @@ -14,9 +14,12 @@ #include "flang/Common/default-kinds.h" #include "flang/Common/reference.h" #include -#include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { class Symbol; } @@ -110,7 +113,7 @@ class ConstantBase : public ConstantBounds { constexpr Result result() const { return result_; } constexpr DynamicType GetType() const { return result_.GetType(); } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; protected: std::vector Reshape(const ConstantSubscripts &) const; @@ -178,7 +181,7 @@ class Constant> : public ConstantBounds { Scalar At(const ConstantSubscripts &) const; Constant Reshape(ConstantSubscripts &&) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; static constexpr DynamicType GetType() { return {TypeCategory::Character, KIND}; } diff --git a/include/flang/Evaluate/expression.h b/include/flang/Evaluate/expression.h index e4bd57f2ae1c..b63fb1ede37b 100644 --- a/include/flang/Evaluate/expression.h +++ b/include/flang/Evaluate/expression.h @@ -28,11 +28,14 @@ #include "flang/Parser/char-block.h" #include #include -#include #include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate { using common::LogicalOperator; @@ -90,7 +93,7 @@ template class ExpressionBase { std::optional GetType() const; int Rank() const; std::string AsFortran() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; static Derived Rewrite(FoldingContext &, Derived &&); }; @@ -185,7 +188,7 @@ class Operation { return operand_ == that.operand_; } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: Container operand_; @@ -212,7 +215,7 @@ struct Convert : public Operation, TO, SomeKind> { using Operand = SomeKind; using Base = Operation; using Base::Base; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; }; template @@ -446,7 +449,7 @@ class ArrayConstructor : public ArrayConstructorValues { template explicit ArrayConstructor(const Expr &) {} static constexpr Result result() { return Result{}; } static constexpr DynamicType GetType() { return Result::GetType(); } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; }; template @@ -464,7 +467,7 @@ class ArrayConstructor> bool operator==(const ArrayConstructor &) const; static constexpr Result result() { return Result{}; } static constexpr DynamicType GetType() { return Result::GetType(); } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; const Expr &LEN() const { return length_.value(); } private: @@ -488,7 +491,7 @@ class ArrayConstructor bool operator==(const ArrayConstructor &) const; constexpr Result result() const { return result_; } constexpr DynamicType GetType() const { return result_.GetType(); } - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: Result result_; @@ -629,7 +632,7 @@ template<> class Relational { int Rank() const { return std::visit([](const auto &x) { return x.Rank(); }, u); } - std::ostream &AsFortran(std::ostream &o) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &o) const; common::MapTemplate u; }; @@ -715,7 +718,7 @@ class StructureConstructor { StructureConstructor &Add(const semantics::Symbol &, Expr &&); int Rank() const { return 0; } DynamicType GetType() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: Result result_; @@ -823,7 +826,7 @@ class Assignment { using BoundsSpec = std::vector>; using BoundsRemapping = std::vector, Expr>>; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; Expr lhs; Expr rhs; diff --git a/include/flang/Evaluate/formatting.h b/include/flang/Evaluate/formatting.h index c2e5316e39b1..b9f9c2f2e15a 100644 --- a/include/flang/Evaluate/formatting.h +++ b/include/flang/Evaluate/formatting.h @@ -9,10 +9,10 @@ #ifndef FORTRAN_EVALUATE_FORMATTING_H_ #define FORTRAN_EVALUATE_FORMATTING_H_ -// It is inconvenient in C++ to have std::ostream::operator<<() as a direct +// It is inconvenient in C++ to have llvm::raw_ostream::operator<<() as a direct // friend function of a class template with many instantiations, so the // various representational class templates in lib/Evaluate format themselves -// via AsFortran(std::ostream &) member functions, which the operator<<() +// via AsFortran(llvm::raw_ostream &) member functions, which the operator<<() // overload below will call. Others have AsFortran() member functions that // return strings. // @@ -20,31 +20,31 @@ // representational class templates that need it, not by external clients. #include "flang/Common/indirection.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include namespace Fortran::evaluate { template -auto operator<<(std::ostream &o, const A &x) -> decltype(x.AsFortran(o)) { +auto operator<<(llvm::raw_ostream &o, const A &x) -> decltype(x.AsFortran(o)) { return x.AsFortran(o); } template -auto operator<<(std::ostream &o, const A &x) -> decltype(o << x.AsFortran()) { +auto operator<<(llvm::raw_ostream &o, const A &x) -> decltype(o << x.AsFortran()) { return o << x.AsFortran(); } template auto operator<<( - std::ostream &o, const Fortran::common::Indirection &x) + llvm::raw_ostream &o, const Fortran::common::Indirection &x) -> decltype(o << x.value()) { return o << x.value(); } template -auto operator<<(std::ostream &o, const std::optional &x) +auto operator<<(llvm::raw_ostream &o, const std::optional &x) -> decltype(o << *x) { if (x) { o << *x; diff --git a/include/flang/Evaluate/intrinsics.h b/include/flang/Evaluate/intrinsics.h index dce5162cb944..2cc523b037e7 100644 --- a/include/flang/Evaluate/intrinsics.h +++ b/include/flang/Evaluate/intrinsics.h @@ -16,9 +16,12 @@ #include "flang/Parser/char-block.h" #include "flang/Parser/message.h" #include -#include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate { class FoldingContext; @@ -76,7 +79,7 @@ class IntrinsicProcTable { std::optional IsSpecificIntrinsicFunction( const std::string &) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: Implementation *impl_{nullptr}; // owning pointer diff --git a/include/flang/Evaluate/real.h b/include/flang/Evaluate/real.h index fbb52086f0cb..43b3aebbac84 100644 --- a/include/flang/Evaluate/real.h +++ b/include/flang/Evaluate/real.h @@ -16,13 +16,15 @@ #include "flang/Evaluate/common.h" #include #include -#include #include // Some environments, viz. clang on Darwin, allow the macro HUGE // to leak out of even when it is never directly included. #undef HUGE +namespace llvm { +class raw_ostream; +} namespace Fortran::evaluate::value { // LOG10(2.)*1E12 @@ -310,7 +312,7 @@ class Real : public common::RealDetails { // Emits a character representation for an equivalent Fortran constant // or parenthesized constant expression that produces this value. - std::ostream &AsFortran(std::ostream &, int kind, bool minimal = false) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &, int kind, bool minimal = false) const; private: using Significand = Integer; // no implicit bit diff --git a/include/flang/Evaluate/static-data.h b/include/flang/Evaluate/static-data.h index 5a708aa25390..c588a77beee2 100644 --- a/include/flang/Evaluate/static-data.h +++ b/include/flang/Evaluate/static-data.h @@ -17,10 +17,13 @@ #include #include #include -#include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate { class StaticDataObject { @@ -63,7 +66,7 @@ class StaticDataObject { std::optional AsString() const; std::optional AsU16String() const; std::optional AsU32String() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; static bool bigEndian; diff --git a/include/flang/Evaluate/variable.h b/include/flang/Evaluate/variable.h index 62effec2c636..0004b0a9c636 100644 --- a/include/flang/Evaluate/variable.h +++ b/include/flang/Evaluate/variable.h @@ -25,10 +25,13 @@ #include "flang/Common/template.h" #include "flang/Parser/char-block.h" #include -#include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { class Symbol; } @@ -49,7 +52,7 @@ struct BaseObject { EVALUATE_UNION_CLASS_BOILERPLATE(BaseObject) int Rank() const; std::optional> LEN() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; const Symbol *symbol() const { if (const auto *result{std::get_if(&u)}) { return &result->get(); @@ -82,7 +85,7 @@ class Component { const Symbol &GetLastSymbol() const { return symbol_; } std::optional> LEN() const; bool operator==(const Component &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: common::CopyableIndirection base_; @@ -110,7 +113,7 @@ class NamedEntity { int Rank() const; std::optional> LEN() const; bool operator==(const NamedEntity &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: std::variant u_; @@ -140,7 +143,7 @@ template class TypeParamInquiry { static constexpr int Rank() { return 0; } // always scalar bool operator==(const TypeParamInquiry &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: std::optional base_; @@ -168,7 +171,7 @@ class Triplet { bool operator==(const Triplet &) const; bool IsStrideOne() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: std::optional lower_, upper_; @@ -181,7 +184,7 @@ struct Subscript { explicit Subscript(Expr &&s) : u{IndirectSubscriptIntegerExpr::Make(std::move(s))} {} int Rank() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; std::variant u; }; @@ -217,7 +220,7 @@ class ArrayRef { const Symbol &GetLastSymbol() const; std::optional> LEN() const; bool operator==(const ArrayRef &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: NamedEntity base_; @@ -265,7 +268,7 @@ class CoarrayRef { NamedEntity GetBase() const; std::optional> LEN() const; bool operator==(const CoarrayRef &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: SymbolVector base_; @@ -286,7 +289,7 @@ struct DataRef { const Symbol &GetFirstSymbol() const; const Symbol &GetLastSymbol() const; std::optional> LEN() const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; std::variant u; }; @@ -327,7 +330,7 @@ class Substring { const Symbol *GetLastSymbol() const; std::optional> LEN() const; bool operator==(const Substring &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; std::optional> Fold(FoldingContext &); @@ -352,7 +355,7 @@ class ComplexPart { const Symbol &GetFirstSymbol() const { return complex_.GetFirstSymbol(); } const Symbol &GetLastSymbol() const { return complex_.GetLastSymbol(); } bool operator==(const ComplexPart &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: DataRef complex_; @@ -387,7 +390,7 @@ template class Designator { BaseObject GetBaseObject() const; const Symbol *GetLastSymbol() const; std::optional> LEN() const; - std::ostream &AsFortran(std::ostream &o) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &o) const; Variant u; }; @@ -405,7 +408,7 @@ template struct Variable { int Rank() const { return std::visit([](const auto &x) { return x.Rank(); }, u); } - std::ostream &AsFortran(std::ostream &o) const { + llvm::raw_ostream &AsFortran(llvm::raw_ostream &o) const { std::visit([&](const auto &x) { x.AsFortran(o); }, u); return o; } @@ -428,7 +431,7 @@ class DescriptorInquiry { static constexpr int Rank() { return 0; } // always scalar bool operator==(const DescriptorInquiry &) const; - std::ostream &AsFortran(std::ostream &) const; + llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; private: NamedEntity base_; diff --git a/include/flang/Lower/PFTBuilder.h b/include/flang/Lower/PFTBuilder.h index bef98b519038..733027cc425d 100644 --- a/include/flang/Lower/PFTBuilder.h +++ b/include/flang/Lower/PFTBuilder.h @@ -11,7 +11,6 @@ #include "flang/Common/template.h" #include "flang/Parser/parse-tree.h" -#include "llvm/Support/raw_ostream.h" #include /// Build a light-weight tree over the parse-tree to help with lowering to FIR. @@ -26,6 +25,10 @@ /// are either statements or constructs, where a construct contains a list of /// evaluations. The resulting PFT structure can then be used to create FIR. +namespace llvm { +class raw_ostream; +} + namespace Fortran::lower { namespace pft { diff --git a/include/flang/Parser/char-block.h b/include/flang/Parser/char-block.h index 421bff9dc0d7..fd41e14821ce 100644 --- a/include/flang/Parser/char-block.h +++ b/include/flang/Parser/char-block.h @@ -19,6 +19,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { class CharBlock { @@ -134,7 +138,7 @@ inline bool operator>(const char *left, const CharBlock &right) { return right < left; } -std::ostream &operator<<(std::ostream &os, const CharBlock &x); +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const CharBlock &x); } diff --git a/include/flang/Parser/dump-parse-tree.h b/include/flang/Parser/dump-parse-tree.h index 818dfbd625bc..ab678e76af76 100644 --- a/include/flang/Parser/dump-parse-tree.h +++ b/include/flang/Parser/dump-parse-tree.h @@ -15,8 +15,7 @@ #include "unparse.h" #include "flang/Common/idioms.h" #include "flang/Common/indirection.h" -#include -#include +#include "llvm/Support/raw_ostream.h" #include #include @@ -37,7 +36,7 @@ struct HasSource : std::true_type {}; class ParseTreeDumper { public: explicit ParseTreeDumper( - std::ostream &out, const AnalyzedObjectsAsFortran *asFortran = nullptr) + llvm::raw_ostream &out, const AnalyzedObjectsAsFortran *asFortran = nullptr) : out_(out), asFortran_{asFortran} {} static constexpr const char *GetNodeName(const char *) { return "char *"; } @@ -761,7 +760,8 @@ class ParseTreeDumper { protected: // Return a Fortran representation of this node to include in the dump template std::string AsFortran(const T &x) { - std::ostringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; if constexpr (std::is_same_v) { if (asFortran_ && x.typedExpr) { asFortran_->expr(ss, *x.typedExpr); @@ -784,7 +784,7 @@ class ParseTreeDumper { std::is_same_v || std::is_same_v) { ss << x; } - if (ss.tellp()) { + if (ss.tell()) { return ss.str(); } if constexpr (std::is_same_v || HasSource::value) { @@ -830,13 +830,13 @@ class ParseTreeDumper { private: int indent_{0}; - std::ostream &out_; + llvm::raw_ostream &out_; const AnalyzedObjectsAsFortran *const asFortran_; bool emptyline_{false}; }; template -void DumpTree(std::ostream &out, const T &x, +void DumpTree(llvm::raw_ostream &out, const T &x, const AnalyzedObjectsAsFortran *asFortran = nullptr) { ParseTreeDumper dumper{out, asFortran}; Walk(x, dumper); diff --git a/include/flang/Parser/instrumented-parser.h b/include/flang/Parser/instrumented-parser.h index 0369a5f363fb..007edbf56d4e 100644 --- a/include/flang/Parser/instrumented-parser.h +++ b/include/flang/Parser/instrumented-parser.h @@ -15,7 +15,10 @@ #include "flang/Parser/provenance.h" #include #include -#include + +namespace llvm { +class raw_ostream; +} namespace Fortran::parser { @@ -28,7 +31,7 @@ class ParsingLog { bool Fails(const char *at, const MessageFixedText &tag, ParseState &); void Note(const char *at, const MessageFixedText &tag, bool pass, const ParseState &); - void Dump(std::ostream &, const CookedSource &) const; + void Dump(llvm::raw_ostream &, const CookedSource &) const; private: struct LogForPosition { diff --git a/include/flang/Parser/message.h b/include/flang/Parser/message.h index 19b94bb70388..e38145a7b04b 100644 --- a/include/flang/Parser/message.h +++ b/include/flang/Parser/message.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -187,7 +186,7 @@ class Message : public common::ReferenceCounted { std::string ToString() const; std::optional GetProvenanceRange(const CookedSource &) const; void Emit( - std::ostream &, const CookedSource &, bool echoSourceLine = true) const; + llvm::raw_ostream &, const CookedSource &, bool echoSourceLine = true) const; // If this Message or any of its attachments locates itself via a CharBlock // within a particular CookedSource, replace its location with the @@ -255,7 +254,7 @@ class Messages { void Merge(Messages &&); void Copy(const Messages &); void ResolveProvenances(const CookedSource &); - void Emit(std::ostream &, const CookedSource &cooked, + void Emit(llvm::raw_ostream &, const CookedSource &cooked, bool echoSourceLines = true) const; void AttachTo(Message &); bool AnyFatalError() const; diff --git a/include/flang/Parser/parsing.h b/include/flang/Parser/parsing.h index cff2f57b8185..0d32afee38c2 100644 --- a/include/flang/Parser/parsing.h +++ b/include/flang/Parser/parsing.h @@ -15,8 +15,8 @@ #include "parse-tree.h" #include "provenance.h" #include "flang/Common/Fortran-features.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include #include #include @@ -50,19 +50,19 @@ class Parsing { std::optional &parseTree() { return parseTree_; } const SourceFile *Prescan(const std::string &path, Options); - void DumpCookedChars(std::ostream &) const; - void DumpProvenance(std::ostream &) const; - void DumpParsingLog(std::ostream &) const; - void Parse(std::ostream *debugOutput = nullptr); + void DumpCookedChars(llvm::raw_ostream &) const; + void DumpProvenance(llvm::raw_ostream &) const; + void DumpParsingLog(llvm::raw_ostream &) const; + void Parse(llvm::raw_ostream &debugOutput); void ClearLog(); - void EmitMessage(std::ostream &o, const char *at, const std::string &message, + void EmitMessage(llvm::raw_ostream &o, const char *at, const std::string &message, bool echoSourceLine = false) const { cooked_.allSources().EmitMessage( o, cooked_.GetProvenanceRange(CharBlock(at)), message, echoSourceLine); } - bool ForTesting(std::string path, std::ostream &); + bool ForTesting(std::string path, llvm::raw_ostream &); private: Options options_; diff --git a/include/flang/Parser/provenance.h b/include/flang/Parser/provenance.h index f1f48c07715d..2ed00d416b64 100644 --- a/include/flang/Parser/provenance.h +++ b/include/flang/Parser/provenance.h @@ -15,12 +15,11 @@ #include "source.h" #include "flang/Common/idioms.h" #include "flang/Common/interval.h" +#include "llvm/Support/raw_ostream.h" #include #include #include #include -#include -#include #include #include #include @@ -91,7 +90,7 @@ class ProvenanceRangeToOffsetMappings { bool empty() const { return map_.empty(); } void Put(ProvenanceRange, std::size_t offset); std::optional Map(ProvenanceRange) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: // A comparison function object for use in std::multimap. @@ -120,7 +119,7 @@ class OffsetToProvenanceMappings { ProvenanceRange Map(std::size_t at) const; void RemoveLastBytes(std::size_t); ProvenanceRangeToOffsetMappings Invert(const AllSources &) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: struct ContiguousProvenanceMapping { @@ -150,8 +149,8 @@ class AllSources { void PushSearchPathDirectory(std::string); std::string PopSearchPathDirectory(); - const SourceFile *Open(std::string path, std::stringstream *error); - const SourceFile *ReadStandardInput(std::stringstream *error); + const SourceFile *Open(std::string path, llvm::raw_ostream &error); + const SourceFile *ReadStandardInput(llvm::raw_ostream &error); ProvenanceRange AddIncludedFile( const SourceFile &, ProvenanceRange, bool isModule = false); @@ -163,7 +162,7 @@ class AllSources { bool IsValid(ProvenanceRange range) const { return range.size() > 0 && range_.Contains(range); } - void EmitMessage(std::ostream &, const std::optional &, + void EmitMessage(llvm::raw_ostream &, const std::optional &, const std::string &message, bool echoSourceLine = false) const; const SourceFile *GetSourceFile( Provenance, std::size_t *offset = nullptr) const; @@ -174,7 +173,7 @@ class AllSources { Provenance CompilerInsertionProvenance(char ch); Provenance CompilerInsertionProvenance(const char *, std::size_t); ProvenanceRange IntersectionWithSourceFiles(ProvenanceRange) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: struct Inclusion { @@ -260,7 +259,7 @@ class CookedSource { void Marshal(); // marshals text into one contiguous block void CompileProvenanceRangeToOffsetMappings(); std::string AcquireData() { return std::move(data_); } - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: AllSources &allSources_; diff --git a/include/flang/Parser/source.h b/include/flang/Parser/source.h index 08ce1514c07c..cc7dc9219a88 100644 --- a/include/flang/Parser/source.h +++ b/include/flang/Parser/source.h @@ -16,11 +16,14 @@ #include "characters.h" #include -#include #include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { std::string DirectoryName(std::string path); @@ -44,8 +47,8 @@ class SourceFile { std::size_t lines() const { return lineStart_.size(); } Encoding encoding() const { return encoding_; } - bool Open(std::string path, std::stringstream *error); - bool ReadStandardInput(std::stringstream *error); + bool Open(std::string path, llvm::raw_ostream &error); + bool ReadStandardInput(llvm::raw_ostream &error); void Close(); SourcePosition FindOffsetLineAndColumn(std::size_t) const; std::size_t GetLineStartOffset(int lineNumber) const { @@ -53,7 +56,7 @@ class SourceFile { } private: - bool ReadFile(std::string errorPath, std::stringstream *error); + bool ReadFile(std::string errorPath, llvm::raw_ostream &error); void IdentifyPayload(); void RecordLineStarts(); diff --git a/include/flang/Parser/unparse.h b/include/flang/Parser/unparse.h index d6bca8d2133c..0c7745809f39 100644 --- a/include/flang/Parser/unparse.h +++ b/include/flang/Parser/unparse.h @@ -14,6 +14,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::evaluate { struct GenericExprWrapper; struct GenericAssignmentWrapper; @@ -26,21 +30,21 @@ struct Program; // A function called before each Statement is unparsed. using preStatementType = - std::function; + std::function; // Functions to handle unparsing of analyzed expressions and related // objects rather than their original parse trees. struct AnalyzedObjectsAsFortran { - std::function + std::function expr; std::function + llvm::raw_ostream &, const evaluate::GenericAssignmentWrapper &)> assignment; - std::function call; + std::function call; }; // Converts parsed program to out as Fortran. -void Unparse(std::ostream &out, const Program &program, +void Unparse(llvm::raw_ostream &out, const Program &program, Encoding encoding = Encoding::UTF_8, bool capitalizeKeywords = true, bool backslashEscapes = true, preStatementType *preStatement = nullptr, AnalyzedObjectsAsFortran * = nullptr); diff --git a/include/flang/Parser/user-state.h b/include/flang/Parser/user-state.h index 60a85d1fed6b..cc5466568ecb 100644 --- a/include/flang/Parser/user-state.h +++ b/include/flang/Parser/user-state.h @@ -18,9 +18,9 @@ #include "flang/Common/idioms.h" #include "flang/Parser/char-block.h" #include "flang/Parser/parse-tree.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include #include #include @@ -40,9 +40,9 @@ class UserState { const CookedSource &cooked() const { return cooked_; } const common::LanguageFeatureControl &features() const { return features_; } - std::ostream *debugOutput() const { return debugOutput_; } - UserState &set_debugOutput(std::ostream *out) { - debugOutput_ = out; + llvm::raw_ostream *debugOutput() const { return debugOutput_; } + UserState &set_debugOutput(llvm::raw_ostream &out) { + debugOutput_ = &out; return *this; } @@ -91,7 +91,7 @@ class UserState { private: const CookedSource &cooked_; - std::ostream *debugOutput_{nullptr}; + llvm::raw_ostream *debugOutput_{nullptr}; ParsingLog *log_{nullptr}; bool instrumentedParse_{false}; diff --git a/include/flang/Semantics/attr.h b/include/flang/Semantics/attr.h index 48fec0441cd3..9aa828da1f80 100644 --- a/include/flang/Semantics/attr.h +++ b/include/flang/Semantics/attr.h @@ -14,6 +14,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { // All available attributes. @@ -38,13 +42,13 @@ class Attrs : public common::EnumSet { void CheckValid(const Attrs &allowed) const; private: - friend std::ostream &operator<<(std::ostream &, const Attrs &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Attrs &); }; // Return string representation of attr that matches Fortran source. std::string AttrToString(Attr attr); -std::ostream &operator<<(std::ostream &o, Attr attr); -std::ostream &operator<<(std::ostream &o, const Attrs &attrs); +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Attr attr); +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Attrs &attrs); } #endif // FORTRAN_SEMANTICS_ATTR_H_ diff --git a/include/flang/Semantics/scope.h b/include/flang/Semantics/scope.h index 6a645ad502b9..f7cef58bdcd5 100644 --- a/include/flang/Semantics/scope.h +++ b/include/flang/Semantics/scope.h @@ -22,6 +22,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { using namespace parser::literals; @@ -234,7 +238,7 @@ class Scope { bool CanImport(const SourceName &) const; const DeclTypeSpec &MakeLengthlessType(DeclTypeSpec &&); - friend std::ostream &operator<<(std::ostream &, const Scope &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Scope &); }; } #endif // FORTRAN_SEMANTICS_SCOPE_H_ diff --git a/include/flang/Semantics/semantics.h b/include/flang/Semantics/semantics.h index b88bcc4563ed..8f789e977c1d 100644 --- a/include/flang/Semantics/semantics.h +++ b/include/flang/Semantics/semantics.h @@ -19,6 +19,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::common { class IntrinsicTypeDefaultKinds; } @@ -209,9 +213,9 @@ class Semantics { return context_.FindScope(where); } bool AnyFatalError() const { return context_.AnyFatalError(); } - void EmitMessages(std::ostream &) const; - void DumpSymbols(std::ostream &); - void DumpSymbolsSources(std::ostream &) const; + void EmitMessages(llvm::raw_ostream &) const; + void DumpSymbols(llvm::raw_ostream &); + void DumpSymbolsSources(llvm::raw_ostream &) const; private: SemanticsContext &context_; diff --git a/include/flang/Semantics/symbol.h b/include/flang/Semantics/symbol.h index b97cdf0f41e7..a9f4dd6bc185 100644 --- a/include/flang/Semantics/symbol.h +++ b/include/flang/Semantics/symbol.h @@ -19,6 +19,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { /// A Symbol consists of common information (name, owner, and attributes) @@ -79,7 +83,7 @@ class SubprogramDetails { std::vector dummyArgs_; // nullptr -> alternate return indicator Symbol *result_{nullptr}; MaybeExpr stmtFunction_; - friend std::ostream &operator<<(std::ostream &, const SubprogramDetails &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SubprogramDetails &); }; // For SubprogramNameDetails, the kind indicates whether it is the name @@ -121,7 +125,7 @@ class EntityDetails { bool isFuncResult_{false}; const DeclTypeSpec *type_{nullptr}; MaybeExpr bindName_; - friend std::ostream &operator<<(std::ostream &, const EntityDetails &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const EntityDetails &); }; // Symbol is associated with a name or expression in a SELECT TYPE or ASSOCIATE. @@ -176,7 +180,7 @@ class ObjectEntityDetails : public EntityDetails { ArraySpec shape_; ArraySpec coshape_; const Symbol *commonBlock_{nullptr}; // common block this object is in - friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ObjectEntityDetails &); }; // Mixin for details with passed-object dummy argument. @@ -213,7 +217,7 @@ class ProcEntityDetails : public EntityDetails, public WithPassArg { private: ProcInterface interface_; std::optional init_; - friend std::ostream &operator<<(std::ostream &, const ProcEntityDetails &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ProcEntityDetails &); }; // These derived type details represent the characteristics of a derived @@ -259,7 +263,7 @@ class DerivedTypeDetails { std::list componentNames_; bool sequence_{false}; bool isForwardReferenced_{false}; - friend std::ostream &operator<<(std::ostream &, const DerivedTypeDetails &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DerivedTypeDetails &); }; class ProcBindingDetails : public WithPassArg { @@ -443,7 +447,7 @@ using Details = std::variant; -std::ostream &operator<<(std::ostream &, const Details &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Details &); std::string DetailsToString(const Details &); class Symbol { @@ -657,8 +661,8 @@ class Symbol { Symbol() {} // only created in class Symbols const std::string GetDetailsName() const; - friend std::ostream &operator<<(std::ostream &, const Symbol &); - friend std::ostream &DumpForUnparse(std::ostream &, const Symbol &, bool); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &); + friend llvm::raw_ostream &DumpForUnparse(llvm::raw_ostream &, const Symbol &, bool); // If a derived type's symbol refers to an extended derived type, // return the parent component's symbol. The scope of the derived type @@ -669,7 +673,7 @@ class Symbol { template friend struct std::array; }; -std::ostream &operator<<(std::ostream &, Symbol::Flag); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, Symbol::Flag); // Manage memory for all symbols. BLOCK_SIZE symbols at a time are allocated. // Make() returns a reference to the next available one. They are never diff --git a/include/flang/Semantics/type.h b/include/flang/Semantics/type.h index 85930d334ebb..f18114b1f380 100644 --- a/include/flang/Semantics/type.h +++ b/include/flang/Semantics/type.h @@ -21,6 +21,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { struct Keyword; } @@ -71,7 +75,7 @@ class Bound { : category_{category}, expr_{std::move(expr)} {} Category category_{Category::Explicit}; MaybeSubscriptIntExpr expr_; - friend std::ostream &operator<<(std::ostream &, const Bound &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Bound &); }; // A type parameter value: integer expression or assumed or deferred. @@ -107,7 +111,7 @@ class ParamValue { Category category_{Category::Explicit}; common::TypeParamAttr attr_{common::TypeParamAttr::Kind}; MaybeIntExpr expr_; - friend std::ostream &operator<<(std::ostream &, const ParamValue &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ParamValue &); }; class IntrinsicTypeSpec { @@ -126,7 +130,7 @@ class IntrinsicTypeSpec { private: TypeCategory category_; KindExpr kind_; - friend std::ostream &operator<<(std::ostream &os, const IntrinsicTypeSpec &x); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const IntrinsicTypeSpec &x); }; class NumericTypeSpec : public IntrinsicTypeSpec { @@ -153,7 +157,7 @@ class CharacterTypeSpec : public IntrinsicTypeSpec { private: ParamValue length_; - friend std::ostream &operator<<(std::ostream &os, const CharacterTypeSpec &x); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const CharacterTypeSpec &x); }; class ShapeSpec { @@ -205,7 +209,7 @@ class ShapeSpec { ShapeSpec(Bound &&lb, Bound &&ub) : lb_{std::move(lb)}, ub_{std::move(ub)} {} Bound lb_; Bound ub_; - friend std::ostream &operator<<(std::ostream &, const ShapeSpec &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ShapeSpec &); }; struct ArraySpec : public std::vector { @@ -224,7 +228,7 @@ struct ArraySpec : public std::vector { return !empty() && std::all_of(begin(), end(), predicate); } }; -std::ostream &operator<<(std::ostream &, const ArraySpec &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ArraySpec &); // Each DerivedTypeSpec has a typeSymbol that has DerivedTypeDetails. // The name may not match the symbol's name in case of a USE rename. @@ -289,7 +293,7 @@ class DerivedTypeSpec { bool instantiated_{false}; RawParameters rawParameters_; ParameterMapType parameters_; - friend std::ostream &operator<<(std::ostream &, const DerivedTypeSpec &); + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DerivedTypeSpec &); }; class DeclTypeSpec { @@ -370,7 +374,7 @@ class DeclTypeSpec { CharacterTypeSpec, DerivedTypeSpec> typeSpec_; }; -std::ostream &operator<<(std::ostream &, const DeclTypeSpec &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DeclTypeSpec &); // This represents a proc-interface in the declaration of a procedure or // procedure component. It comprises a symbol that represents the specific diff --git a/include/flang/Semantics/unparse-with-symbols.h b/include/flang/Semantics/unparse-with-symbols.h index 6553b7b34de0..9bea94223e8a 100644 --- a/include/flang/Semantics/unparse-with-symbols.h +++ b/include/flang/Semantics/unparse-with-symbols.h @@ -12,12 +12,16 @@ #include "flang/Parser/characters.h" #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { struct Program; } namespace Fortran::semantics { -void UnparseWithSymbols(std::ostream &, const parser::Program &, +void UnparseWithSymbols(llvm::raw_ostream &, const parser::Program &, parser::Encoding encoding = parser::Encoding::UTF_8); } diff --git a/lib/Evaluate/characteristics.cpp b/lib/Evaluate/characteristics.cpp index d87ea198ae75..867b8116702f 100644 --- a/lib/Evaluate/characteristics.cpp +++ b/lib/Evaluate/characteristics.cpp @@ -16,8 +16,8 @@ #include "flang/Parser/message.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" +#include "llvm/Support/raw_ostream.h" #include -#include using namespace Fortran::parser::literals; @@ -179,7 +179,7 @@ void TypeAndShape::AcquireLEN() { } } -std::ostream &TypeAndShape::Dump(std::ostream &o) const { +llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const { o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""); attrs_.Dump(o, EnumToString); if (!shape_.empty()) { @@ -261,7 +261,7 @@ bool DummyDataObject::CanBePassedViaImplicitInterface() const { } } -std::ostream &DummyDataObject::Dump(std::ostream &o) const { +llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const { attrs.Dump(o, EnumToString); if (intent != common::Intent::Default) { o << "INTENT(" << common::EnumToString(intent) << ')'; @@ -306,7 +306,7 @@ std::optional DummyProcedure::Characterize( } } -std::ostream &DummyProcedure::Dump(std::ostream &o) const { +llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const { attrs.Dump(o, EnumToString); if (intent != common::Intent::Default) { o << "INTENT(" << common::EnumToString(intent) << ')'; @@ -315,7 +315,9 @@ std::ostream &DummyProcedure::Dump(std::ostream &o) const { return o; } -std::ostream &AlternateReturn::Dump(std::ostream &o) const { return o << '*'; } +llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const { + return o << '*'; +} DummyArgument::~DummyArgument() {} @@ -417,7 +419,7 @@ bool DummyArgument::CanBePassedViaImplicitInterface() const { } } -std::ostream &DummyArgument::Dump(std::ostream &o) const { +llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const { if (!name.empty()) { o << name << '='; } @@ -503,7 +505,7 @@ bool FunctionResult::CanBeReturnedViaImplicitInterface() const { } } -std::ostream &FunctionResult::Dump(std::ostream &o) const { +llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const { attrs.Dump(o, EnumToString); std::visit( common::visitors{ @@ -698,7 +700,7 @@ bool Procedure::CanBeCalledViaImplicitInterface() const { } } -std::ostream &Procedure::Dump(std::ostream &o) const { +llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const { attrs.Dump(o, EnumToString); if (functionResult) { functionResult->Dump(o << "TYPE(") << ") FUNCTION"; diff --git a/lib/Evaluate/complex.cpp b/lib/Evaluate/complex.cpp index ebb3898dee90..298c14a07540 100644 --- a/lib/Evaluate/complex.cpp +++ b/lib/Evaluate/complex.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "flang/Evaluate/complex.h" +#include "llvm/Support/raw_ostream.h" namespace Fortran::evaluate::value { @@ -90,7 +91,7 @@ template std::string Complex::DumpHexadecimal() const { } template -std::ostream &Complex::AsFortran(std::ostream &o, int kind) const { +llvm::raw_ostream &Complex::AsFortran(llvm::raw_ostream &o, int kind) const { re_.AsFortran(o << '(', kind); im_.AsFortran(o << ',', kind); return o << ')'; diff --git a/lib/Evaluate/expression.cpp b/lib/Evaluate/expression.cpp index 18646e4f19e6..a3bb9280648f 100644 --- a/lib/Evaluate/expression.cpp +++ b/lib/Evaluate/expression.cpp @@ -13,6 +13,7 @@ #include "flang/Evaluate/tools.h" #include "flang/Evaluate/variable.h" #include "flang/Parser/message.h" +#include "llvm/Support/raw_ostream.h" #include #include diff --git a/lib/Evaluate/formatting.cpp b/lib/Evaluate/formatting.cpp index 4c28d66a1d66..adcc22865762 100644 --- a/lib/Evaluate/formatting.cpp +++ b/lib/Evaluate/formatting.cpp @@ -14,11 +14,12 @@ #include "flang/Evaluate/tools.h" #include "flang/Parser/characters.h" #include "flang/Semantics/symbol.h" -#include +#include "llvm/Support/raw_ostream.h" namespace Fortran::evaluate { -static void ShapeAsFortran(std::ostream &o, const ConstantSubscripts &shape) { +static void ShapeAsFortran( + llvm::raw_ostream &o, const ConstantSubscripts &shape) { if (GetRank(shape) > 1) { o << ",shape="; char ch{'['}; @@ -31,7 +32,8 @@ static void ShapeAsFortran(std::ostream &o, const ConstantSubscripts &shape) { } template -std::ostream &ConstantBase::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ConstantBase::AsFortran( + llvm::raw_ostream &o) const { if (Rank() > 1) { o << "reshape("; } @@ -71,8 +73,8 @@ std::ostream &ConstantBase::AsFortran(std::ostream &o) const { } template -std::ostream &Constant>::AsFortran( - std::ostream &o) const { +llvm::raw_ostream &Constant>::AsFortran( + llvm::raw_ostream &o) const { if (Rank() > 1) { o << "reshape("; } @@ -97,11 +99,12 @@ std::ostream &Constant>::AsFortran( return o; } -std::ostream &ActualArgument::AssumedType::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ActualArgument::AssumedType::AsFortran( + llvm::raw_ostream &o) const { return o << symbol_->name().ToString(); } -std::ostream &ActualArgument::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const { if (keyword_) { o << keyword_->ToString() << '='; } @@ -115,11 +118,11 @@ std::ostream &ActualArgument::AsFortran(std::ostream &o) const { } } -std::ostream &SpecificIntrinsic::AsFortran(std::ostream &o) const { +llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const { return o << name; } -std::ostream &ProcedureRef::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ProcedureRef::AsFortran(llvm::raw_ostream &o) const { for (const auto &arg : arguments_) { if (arg && arg->isPassedObject()) { arg->AsFortran(o) << '%'; @@ -306,7 +309,8 @@ static OperatorSpelling SpellOperator(const Relational &x) { } template -std::ostream &Operation::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Operation::AsFortran( + llvm::raw_ostream &o) const { Precedence lhsPrec{ToPrecedence(left())}; OperatorSpelling spelling{SpellOperator(derived())}; o << spelling.prefix; @@ -337,7 +341,7 @@ std::ostream &Operation::AsFortran(std::ostream &o) const { } template -std::ostream &Convert::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Convert::AsFortran(llvm::raw_ostream &o) const { static_assert(TO::category == TypeCategory::Integer || TO::category == TypeCategory::Real || TO::category == TypeCategory::Character || @@ -355,21 +359,22 @@ std::ostream &Convert::AsFortran(std::ostream &o) const { return o << ",kind=" << TO::kind << ')'; } -std::ostream &Relational::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Relational::AsFortran(llvm::raw_ostream &o) const { std::visit([&](const auto &rel) { rel.AsFortran(o); }, u); return o; } template -std::ostream &EmitArray(std::ostream &o, const Expr &expr) { +llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const Expr &expr) { return expr.AsFortran(o); } template -std::ostream &EmitArray(std::ostream &, const ArrayConstructorValues &); +llvm::raw_ostream &EmitArray( + llvm::raw_ostream &, const ArrayConstructorValues &); template -std::ostream &EmitArray(std::ostream &o, const ImpliedDo &implDo) { +llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const ImpliedDo &implDo) { o << '('; EmitArray(o, implDo.values()); o << ',' << ImpliedDoIndex::Result::AsFortran() @@ -381,8 +386,8 @@ std::ostream &EmitArray(std::ostream &o, const ImpliedDo &implDo) { } template -std::ostream &EmitArray( - std::ostream &o, const ArrayConstructorValues &values) { +llvm::raw_ostream &EmitArray( + llvm::raw_ostream &o, const ArrayConstructorValues &values) { const char *sep{""}; for (const auto &value : values) { o << sep; @@ -393,21 +398,23 @@ std::ostream &EmitArray( } template -std::ostream &ArrayConstructor::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ArrayConstructor::AsFortran(llvm::raw_ostream &o) const { o << '[' << GetType().AsFortran() << "::"; EmitArray(o, *this); return o << ']'; } template -std::ostream &ArrayConstructor>::AsFortran( - std::ostream &o) const { +llvm::raw_ostream & +ArrayConstructor>::AsFortran( + llvm::raw_ostream &o) const { o << '[' << GetType().AsFortran(LEN().AsFortran()) << "::"; EmitArray(o, *this); return o << ']'; } -std::ostream &ArrayConstructor::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ArrayConstructor::AsFortran( + llvm::raw_ostream &o) const { o << '[' << GetType().AsFortran() << "::"; EmitArray(o, *this); return o << ']'; @@ -415,13 +422,15 @@ std::ostream &ArrayConstructor::AsFortran(std::ostream &o) const { template std::string ExpressionBase::AsFortran() const { - std::ostringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; AsFortran(ss); return ss.str(); } template -std::ostream &ExpressionBase::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ExpressionBase::AsFortran( + llvm::raw_ostream &o) const { std::visit( common::visitors{ [&](const BOZLiteralConstant &x) { @@ -438,7 +447,7 @@ std::ostream &ExpressionBase::AsFortran(std::ostream &o) const { return o; } -std::ostream &StructureConstructor::AsFortran(std::ostream &o) const { +llvm::raw_ostream &StructureConstructor::AsFortran(llvm::raw_ostream &o) const { o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec()); if (values_.empty()) { o << '('; @@ -496,7 +505,8 @@ std::string SomeDerived::AsFortran() const { } std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; ss << spec.name().ToString(); char ch{'('}; for (const auto &[name, value] : spec.parameters()) { @@ -516,33 +526,35 @@ std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) { return ss.str(); } -std::ostream &EmitVar(std::ostream &o, const Symbol &symbol) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const Symbol &symbol) { return o << symbol.name().ToString(); } -std::ostream &EmitVar(std::ostream &o, const std::string &lit) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::string &lit) { return o << parser::QuoteCharacterLiteral(lit); } -std::ostream &EmitVar(std::ostream &o, const std::u16string &lit) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u16string &lit) { return o << parser::QuoteCharacterLiteral(lit); } -std::ostream &EmitVar(std::ostream &o, const std::u32string &lit) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u32string &lit) { return o << parser::QuoteCharacterLiteral(lit); } -template std::ostream &EmitVar(std::ostream &o, const A &x) { +template +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const A &x) { return x.AsFortran(o); } template -std::ostream &EmitVar(std::ostream &o, common::Reference x) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, common::Reference x) { return EmitVar(o, *x); } template -std::ostream &EmitVar(std::ostream &o, const A *p, const char *kw = nullptr) { +llvm::raw_ostream &EmitVar( + llvm::raw_ostream &o, const A *p, const char *kw = nullptr) { if (p) { if (kw) { o << kw; @@ -553,8 +565,8 @@ std::ostream &EmitVar(std::ostream &o, const A *p, const char *kw = nullptr) { } template -std::ostream &EmitVar( - std::ostream &o, const std::optional &x, const char *kw = nullptr) { +llvm::raw_ostream &EmitVar( + llvm::raw_ostream &o, const std::optional &x, const char *kw = nullptr) { if (x) { if (kw) { o << kw; @@ -565,8 +577,8 @@ std::ostream &EmitVar( } template -std::ostream &EmitVar(std::ostream &o, const common::Indirection &p, - const char *kw = nullptr) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, + const common::Indirection &p, const char *kw = nullptr) { if (kw) { o << kw; } @@ -575,35 +587,36 @@ std::ostream &EmitVar(std::ostream &o, const common::Indirection &p, } template -std::ostream &EmitVar(std::ostream &o, const std::shared_ptr &p) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::shared_ptr &p) { CHECK(p); return EmitVar(o, *p); } template -std::ostream &EmitVar(std::ostream &o, const std::variant &u) { +llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::variant &u) { std::visit([&](const auto &x) { EmitVar(o, x); }, u); return o; } -std::ostream &BaseObject::AsFortran(std::ostream &o) const { +llvm::raw_ostream &BaseObject::AsFortran(llvm::raw_ostream &o) const { return EmitVar(o, u); } template -std::ostream &TypeParamInquiry::AsFortran(std::ostream &o) const { +llvm::raw_ostream &TypeParamInquiry::AsFortran( + llvm::raw_ostream &o) const { if (base_) { return base_->AsFortran(o) << '%'; } return EmitVar(o, parameter_); } -std::ostream &Component::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Component::AsFortran(llvm::raw_ostream &o) const { base_.value().AsFortran(o); return EmitVar(o << '%', symbol_); } -std::ostream &NamedEntity::AsFortran(std::ostream &o) const { +llvm::raw_ostream &NamedEntity::AsFortran(llvm::raw_ostream &o) const { std::visit( common::visitors{ [&](SymbolRef s) { EmitVar(o, s); }, @@ -613,18 +626,18 @@ std::ostream &NamedEntity::AsFortran(std::ostream &o) const { return o; } -std::ostream &Triplet::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Triplet::AsFortran(llvm::raw_ostream &o) const { EmitVar(o, lower_) << ':'; EmitVar(o, upper_); EmitVar(o << ':', stride_.value()); return o; } -std::ostream &Subscript::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Subscript::AsFortran(llvm::raw_ostream &o) const { return EmitVar(o, u); } -std::ostream &ArrayRef::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ArrayRef::AsFortran(llvm::raw_ostream &o) const { base_.AsFortran(o); char separator{'('}; for (const Subscript &ss : subscript_) { @@ -634,7 +647,7 @@ std::ostream &ArrayRef::AsFortran(std::ostream &o) const { return o << ')'; } -std::ostream &CoarrayRef::AsFortran(std::ostream &o) const { +llvm::raw_ostream &CoarrayRef::AsFortran(llvm::raw_ostream &o) const { bool first{true}; for (const Symbol &part : base_) { if (first) { @@ -668,26 +681,26 @@ std::ostream &CoarrayRef::AsFortran(std::ostream &o) const { return o << ']'; } -std::ostream &DataRef::AsFortran(std::ostream &o) const { +llvm::raw_ostream &DataRef::AsFortran(llvm::raw_ostream &o) const { return EmitVar(o, u); } -std::ostream &Substring::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Substring::AsFortran(llvm::raw_ostream &o) const { EmitVar(o, parent_) << '('; EmitVar(o, lower_) << ':'; return EmitVar(o, upper_) << ')'; } -std::ostream &ComplexPart::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ComplexPart::AsFortran(llvm::raw_ostream &o) const { return complex_.AsFortran(o) << '%' << EnumToString(part_); } -std::ostream &ProcedureDesignator::AsFortran(std::ostream &o) const { +llvm::raw_ostream &ProcedureDesignator::AsFortran(llvm::raw_ostream &o) const { return EmitVar(o, u); } template -std::ostream &Designator::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Designator::AsFortran(llvm::raw_ostream &o) const { std::visit( common::visitors{ [&](SymbolRef symbol) { EmitVar(o, symbol); }, @@ -697,7 +710,7 @@ std::ostream &Designator::AsFortran(std::ostream &o) const { return o; } -std::ostream &DescriptorInquiry::AsFortran(std::ostream &o) const { +llvm::raw_ostream &DescriptorInquiry::AsFortran(llvm::raw_ostream &o) const { switch (field_) { case Field::LowerBound: o << "lbound("; break; case Field::Extent: o << "size("; break; @@ -716,7 +729,7 @@ std::ostream &DescriptorInquiry::AsFortran(std::ostream &o) const { } } -std::ostream &Assignment::AsFortran(std::ostream &o) const { +llvm::raw_ostream &Assignment::AsFortran(llvm::raw_ostream &o) const { std::visit( common::visitors{ [&](const Assignment::Intrinsic &) { diff --git a/lib/Evaluate/host.cpp b/lib/Evaluate/host.cpp index c9f48789e218..bc55d53fc2e3 100644 --- a/lib/Evaluate/host.cpp +++ b/lib/Evaluate/host.cpp @@ -9,7 +9,7 @@ #include "host.h" #include "flang/Common/idioms.h" -#include +#include "llvm/Support/Errno.h" #include namespace Fortran::evaluate::host { @@ -20,12 +20,12 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment( errno = 0; if (feholdexcept(&originalFenv_) != 0) { common::die("Folding with host runtime: feholdexcept() failed: %s", - std::strerror(errno)); + llvm::sys::StrError(errno).c_str()); return; } if (fegetenv(¤tFenv_) != 0) { common::die("Folding with host runtime: fegetenv() failed: %s", - std::strerror(errno)); + llvm::sys::StrError(errno).c_str()); return; } #if __x86_64__ @@ -72,7 +72,7 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment( errno = 0; if (fesetenv(¤tFenv_) != 0) { common::die("Folding with host runtime: fesetenv() failed: %s", - std::strerror(errno)); + llvm::sys::StrError(errno).c_str()); return; } switch (context.rounding().mode) { @@ -127,10 +127,11 @@ void HostFloatingPointEnvironment::CheckAndRestoreFloatingPointEnvironment( } errno = 0; if (fesetenv(&originalFenv_) != 0) { - std::fprintf(stderr, "fesetenv() failed: %s\n", std::strerror(errno)); + std::fprintf( + stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); common::die( "Folding with host runtime: fesetenv() failed while restoring fenv: %s", - std::strerror(errno)); + llvm::sys::StrError(errno).c_str()); } errno = 0; } diff --git a/lib/Evaluate/intrinsics.cpp b/lib/Evaluate/intrinsics.cpp index 93dcfe28f5ae..1936ca2cebbf 100644 --- a/lib/Evaluate/intrinsics.cpp +++ b/lib/Evaluate/intrinsics.cpp @@ -16,10 +16,9 @@ #include "flang/Evaluate/shape.h" #include "flang/Evaluate/tools.h" #include "flang/Evaluate/type.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include -#include #include #include @@ -90,7 +89,7 @@ ENUM_CLASS(KindCode, none, defaultIntegerKind, struct TypePattern { CategorySet categorySet; KindCode kindCode{KindCode::none}; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; }; // Abbreviations for argument and result patterns in the intrinsic prototypes: @@ -195,7 +194,7 @@ struct IntrinsicDummyArgument { TypePattern typePattern; Rank rank{Rank::elemental}; Optionality optionality{Optionality::required}; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; }; // constexpr abbreviations for popular arguments: @@ -234,7 +233,7 @@ struct IntrinsicInterface { const common::IntrinsicTypeDefaultKinds &, ActualArguments &, FoldingContext &context) const; int CountArguments() const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; }; int IntrinsicInterface::CountArguments() const { @@ -1517,7 +1516,7 @@ class IntrinsicProcTable::Implementation { std::optional IsSpecificIntrinsicFunction( const std::string &) const; - std::ostream &Dump(std::ostream &) const; + llvm::raw_ostream &Dump(llvm::raw_ostream &) const; private: DynamicType GetSpecificType(const TypePattern &) const; @@ -2000,7 +1999,7 @@ IntrinsicProcTable::IsSpecificIntrinsicFunction(const std::string &name) const { return DEREF(impl_).IsSpecificIntrinsicFunction(name); } -std::ostream &TypePattern::Dump(std::ostream &o) const { +llvm::raw_ostream &TypePattern::Dump(llvm::raw_ostream &o) const { if (categorySet == AnyType) { o << "any type"; } else { @@ -2016,7 +2015,7 @@ std::ostream &TypePattern::Dump(std::ostream &o) const { return o; } -std::ostream &IntrinsicDummyArgument::Dump(std::ostream &o) const { +llvm::raw_ostream &IntrinsicDummyArgument::Dump(llvm::raw_ostream &o) const { if (keyword) { o << keyword << '='; } @@ -2024,7 +2023,7 @@ std::ostream &IntrinsicDummyArgument::Dump(std::ostream &o) const { << ' ' << EnumToString(rank) << ' ' << EnumToString(optionality); } -std::ostream &IntrinsicInterface::Dump(std::ostream &o) const { +llvm::raw_ostream &IntrinsicInterface::Dump(llvm::raw_ostream &o) const { o << name; char sep{'('}; for (const auto &d : dummy) { @@ -2040,7 +2039,8 @@ std::ostream &IntrinsicInterface::Dump(std::ostream &o) const { return result.Dump(o << " -> ") << ' ' << EnumToString(rank); } -std::ostream &IntrinsicProcTable::Implementation::Dump(std::ostream &o) const { +llvm::raw_ostream &IntrinsicProcTable::Implementation::Dump( + llvm::raw_ostream &o) const { o << "generic intrinsic functions:\n"; for (const auto &iter : genericFuncs_) { iter.second->Dump(o << iter.first << ": ") << '\n'; @@ -2060,7 +2060,7 @@ std::ostream &IntrinsicProcTable::Implementation::Dump(std::ostream &o) const { return o; } -std::ostream &IntrinsicProcTable::Dump(std::ostream &o) const { +llvm::raw_ostream &IntrinsicProcTable::Dump(llvm::raw_ostream &o) const { return impl_->Dump(o); } } diff --git a/lib/Evaluate/real.cpp b/lib/Evaluate/real.cpp index fe3b5821b676..18309d9adb64 100644 --- a/lib/Evaluate/real.cpp +++ b/lib/Evaluate/real.cpp @@ -11,6 +11,7 @@ #include "flang/Common/idioms.h" #include "flang/Decimal/decimal.h" #include "flang/Parser/characters.h" +#include "llvm/Support/raw_ostream.h" #include namespace Fortran::evaluate::value { @@ -478,8 +479,8 @@ template std::string Real::DumpHexadecimal() const { } template -std::ostream &Real::AsFortran( - std::ostream &o, int kind, bool minimal) const { +llvm::raw_ostream &Real::AsFortran( + llvm::raw_ostream &o, int kind, bool minimal) const { if (IsNotANumber()) { o << "(0._" << kind << "/0.)"; } else if (IsInfinite()) { diff --git a/lib/Evaluate/static-data.cpp b/lib/Evaluate/static-data.cpp index 8de4f3119684..bd1a729c4996 100644 --- a/lib/Evaluate/static-data.cpp +++ b/lib/Evaluate/static-data.cpp @@ -13,7 +13,7 @@ namespace Fortran::evaluate { bool StaticDataObject::bigEndian{false}; -std::ostream &StaticDataObject::AsFortran(std::ostream &o) const { +llvm::raw_ostream &StaticDataObject::AsFortran(llvm::raw_ostream &o) const { if (auto string{AsString()}) { o << parser::QuoteCharacterLiteral(*string); } else if (auto string{AsU16String()}) { diff --git a/lib/Evaluate/type.cpp b/lib/Evaluate/type.cpp index 79f40d0481aa..d8d30264b397 100644 --- a/lib/Evaluate/type.cpp +++ b/lib/Evaluate/type.cpp @@ -18,7 +18,6 @@ #include "flang/Semantics/type.h" #include #include -#include #include // IsDescriptor() predicate diff --git a/lib/Evaluate/variable.cpp b/lib/Evaluate/variable.cpp index 030f8866a42b..10362c583831 100644 --- a/lib/Evaluate/variable.cpp +++ b/lib/Evaluate/variable.cpp @@ -14,7 +14,6 @@ #include "flang/Parser/characters.h" #include "flang/Parser/message.h" #include "flang/Semantics/symbol.h" -#include #include using namespace Fortran::parser::literals; diff --git a/lib/Parser/CMakeLists.txt b/lib/Parser/CMakeLists.txt index 5f7ba14b0cc8..a04f37c71aec 100644 --- a/lib/Parser/CMakeLists.txt +++ b/lib/Parser/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(FortranParser target_link_libraries(FortranParser FortranCommon + LLVMSupport ) install (TARGETS FortranParser diff --git a/lib/Parser/char-block.cpp b/lib/Parser/char-block.cpp index b68be8a146ef..f74b08268c6d 100644 --- a/lib/Parser/char-block.cpp +++ b/lib/Parser/char-block.cpp @@ -7,11 +7,11 @@ //----------------------------------------------------------------------------// #include "flang/Parser/char-block.h" -#include +#include "llvm/Support/raw_ostream.h" namespace Fortran::parser { -std::ostream &operator<<(std::ostream &os, const CharBlock &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const CharBlock &x) { return os << x.ToString(); } diff --git a/lib/Parser/debug-parser.cpp b/lib/Parser/debug-parser.cpp index b0db22ff143c..75578ec51840 100644 --- a/lib/Parser/debug-parser.cpp +++ b/lib/Parser/debug-parser.cpp @@ -8,7 +8,6 @@ #include "debug-parser.h" #include "flang/Parser/user-state.h" -#include #include namespace Fortran::parser { diff --git a/lib/Parser/instrumented-parser.cpp b/lib/Parser/instrumented-parser.cpp index 47ff042b168d..7419c53874ca 100644 --- a/lib/Parser/instrumented-parser.cpp +++ b/lib/Parser/instrumented-parser.cpp @@ -9,8 +9,8 @@ #include "flang/Parser/instrumented-parser.h" #include "flang/Parser/message.h" #include "flang/Parser/provenance.h" +#include "llvm/Support/raw_ostream.h" #include -#include namespace Fortran::parser { @@ -63,7 +63,7 @@ void ParsingLog::Note(const char *at, const MessageFixedText &tag, bool pass, } } -void ParsingLog::Dump(std::ostream &o, const CookedSource &cooked) const { +void ParsingLog::Dump(llvm::raw_ostream &o, const CookedSource &cooked) const { for (const auto &posLog : perPos_) { const char *at{reinterpret_cast(posLog.first)}; for (const auto &tagLog : posLog.second.perTag) { diff --git a/lib/Parser/message.cpp b/lib/Parser/message.cpp index feb93a76c53e..1af00ad401b9 100644 --- a/lib/Parser/message.cpp +++ b/lib/Parser/message.cpp @@ -9,6 +9,7 @@ #include "flang/Parser/message.h" #include "flang/Common/idioms.h" #include "flang/Parser/char-set.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -19,7 +20,7 @@ namespace Fortran::parser { -std::ostream &operator<<(std::ostream &o, const MessageFixedText &t) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const MessageFixedText &t) { std::size_t n{t.text().size()}; for (std::size_t j{0}; j < n; ++j) { o << t.text()[j]; @@ -187,8 +188,8 @@ std::optional Message::GetProvenanceRange( location_); } -void Message::Emit( - std::ostream &o, const CookedSource &cooked, bool echoSourceLine) const { +void Message::Emit(llvm::raw_ostream &o, const CookedSource &cooked, + bool echoSourceLine) const { std::optional provenanceRange{GetProvenanceRange(cooked)}; std::string text; if (IsFatal()) { @@ -306,8 +307,8 @@ void Messages::ResolveProvenances(const CookedSource &cooked) { } } -void Messages::Emit( - std::ostream &o, const CookedSource &cooked, bool echoSourceLines) const { +void Messages::Emit(llvm::raw_ostream &o, const CookedSource &cooked, + bool echoSourceLines) const { std::vector sorted; for (const auto &msg : messages_) { sorted.push_back(&msg); diff --git a/lib/Parser/parse-tree.cpp b/lib/Parser/parse-tree.cpp index 181bf066c587..819e4abfc831 100644 --- a/lib/Parser/parse-tree.cpp +++ b/lib/Parser/parse-tree.cpp @@ -11,6 +11,7 @@ #include "flang/Common/indirection.h" #include "flang/Parser/tools.h" #include "flang/Parser/user-state.h" +#include "llvm/Support/raw_ostream.h" #include // So "delete Expr;" calls an external destructor for its typedExpr. @@ -252,7 +253,7 @@ CharBlock Variable::GetSource() const { u); } -std::ostream &operator<<(std::ostream &os, const Name &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Name &x) { return os << x.ToString(); } diff --git a/lib/Parser/parsing.cpp b/lib/Parser/parsing.cpp index 9b77b9f0ada5..1b8256a00a81 100644 --- a/lib/Parser/parsing.cpp +++ b/lib/Parser/parsing.cpp @@ -13,7 +13,7 @@ #include "flang/Parser/message.h" #include "flang/Parser/provenance.h" #include "flang/Parser/source.h" -#include +#include "llvm/Support/raw_ostream.h" namespace Fortran::parser { @@ -29,12 +29,13 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) { } } - std::stringstream fileError; + std::string buf; + llvm::raw_string_ostream fileError{buf}; const SourceFile *sourceFile; if (path == "-") { - sourceFile = allSources.ReadStandardInput(&fileError); + sourceFile = allSources.ReadStandardInput(fileError); } else { - sourceFile = allSources.Open(path, &fileError); + sourceFile = allSources.Open(path, fileError); } if (!fileError.str().empty()) { ProvenanceRange range{allSources.AddCompilerInsertion(path)}; @@ -85,7 +86,7 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) { return sourceFile; } -void Parsing::DumpCookedChars(std::ostream &out) const { +void Parsing::DumpCookedChars(llvm::raw_ostream &out) const { UserState userState{cooked_, common::LanguageFeatureControl{}}; ParseState parseState{cooked_}; parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState); @@ -94,13 +95,15 @@ void Parsing::DumpCookedChars(std::ostream &out) const { } } -void Parsing::DumpProvenance(std::ostream &out) const { cooked_.Dump(out); } +void Parsing::DumpProvenance(llvm::raw_ostream &out) const { + cooked_.Dump(out); +} -void Parsing::DumpParsingLog(std::ostream &out) const { +void Parsing::DumpParsingLog(llvm::raw_ostream &out) const { log_.Dump(out, cooked_); } -void Parsing::Parse(std::ostream *out) { +void Parsing::Parse(llvm::raw_ostream &out) { UserState userState{cooked_, options_.features}; userState.set_debugOutput(out) .set_instrumentedParse(options_.instrumentedParse) @@ -117,14 +120,15 @@ void Parsing::Parse(std::ostream *out) { void Parsing::ClearLog() { log_.clear(); } -bool Parsing::ForTesting(std::string path, std::ostream &err) { +bool Parsing::ForTesting(std::string path, llvm::raw_ostream &err) { + llvm::raw_null_ostream NullStream; Prescan(path, Options{}); if (messages_.AnyFatalError()) { messages_.Emit(err, cooked_); err << "could not scan " << path << '\n'; return false; } - Parse(); + Parse(NullStream); messages_.Emit(err, cooked_); if (!consumedWholeFile_) { EmitMessage(err, finalRestingPlace_, "parser FAIL; final position"); diff --git a/lib/Parser/preprocessor.cpp b/lib/Parser/preprocessor.cpp index f825f561e32b..d2fd07268ee5 100644 --- a/lib/Parser/preprocessor.cpp +++ b/lib/Parser/preprocessor.cpp @@ -11,6 +11,7 @@ #include "flang/Common/idioms.h" #include "flang/Parser/characters.h" #include "flang/Parser/message.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include namespace Fortran::parser { @@ -257,7 +257,8 @@ std::optional Preprocessor::MacroReplacement( repl = "\""s + allSources_.GetPath(prescanner.GetCurrentProvenance()) + '"'; } else if (name == "__LINE__") { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; ss << allSources_.GetLineNumber(prescanner.GetCurrentProvenance()); repl = ss.str(); } @@ -574,8 +575,9 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { "#include: empty include file name"_err_en_US); return; } - std::stringstream error; - const SourceFile *included{allSources_.Open(include, &error)}; + std::string buf; + llvm::raw_string_ostream error{buf}; + const SourceFile *included{allSources_.Open(include, error)}; if (!included) { prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), "#include: %s"_err_en_US, error.str()); diff --git a/lib/Parser/prescan.cpp b/lib/Parser/prescan.cpp index c28fc1535e38..67d5cdfbfb1d 100644 --- a/lib/Parser/prescan.cpp +++ b/lib/Parser/prescan.cpp @@ -13,9 +13,9 @@ #include "flang/Parser/characters.h" #include "flang/Parser/message.h" #include "flang/Parser/source.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include #include #include @@ -736,14 +736,15 @@ void Prescanner::FortranInclude(const char *firstQuote) { Say(GetProvenanceRange(garbage, p), "excess characters after path name"_en_US); } - std::stringstream error; + std::string buf; + llvm::raw_string_ostream error{buf}; Provenance provenance{GetProvenance(nextLine_)}; AllSources &allSources{cooked_.allSources()}; const SourceFile *currentFile{allSources.GetSourceFile(provenance)}; if (currentFile) { allSources.PushSearchPathDirectory(DirectoryName(currentFile->path())); } - const SourceFile *included{allSources.Open(path, &error)}; + const SourceFile *included{allSources.Open(path, error)}; if (currentFile) { allSources.PopSearchPathDirectory(); } diff --git a/lib/Parser/provenance.cpp b/lib/Parser/provenance.cpp index db61e4e44f2e..fb43496b53ef 100644 --- a/lib/Parser/provenance.cpp +++ b/lib/Parser/provenance.cpp @@ -8,6 +8,7 @@ #include "flang/Parser/provenance.h" #include "flang/Common/idioms.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -166,7 +167,7 @@ std::string AllSources::PopSearchPathDirectory() { return directory; } -const SourceFile *AllSources::Open(std::string path, std::stringstream *error) { +const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error) { std::unique_ptr source{std::make_unique(encoding_)}; if (source->Open(LocateSourceFile(path, searchPath_), error)) { return ownedSourceFiles_.emplace_back(std::move(source)).get(); @@ -175,7 +176,7 @@ const SourceFile *AllSources::Open(std::string path, std::stringstream *error) { } } -const SourceFile *AllSources::ReadStandardInput(std::stringstream *error) { +const SourceFile *AllSources::ReadStandardInput(llvm::raw_ostream &error) { std::unique_ptr source{std::make_unique(encoding_)}; if (source->ReadStandardInput(error)) { return ownedSourceFiles_.emplace_back(std::move(source)).get(); @@ -209,7 +210,7 @@ ProvenanceRange AllSources::AddCompilerInsertion(std::string text) { return covers; } -void AllSources::EmitMessage(std::ostream &o, +void AllSources::EmitMessage(llvm::raw_ostream &o, const std::optional &range, const std::string &message, bool echoSourceLine) const { if (!range) { @@ -470,12 +471,13 @@ void CookedSource::CompileProvenanceRangeToOffsetMappings() { } } -static void DumpRange(std::ostream &o, const ProvenanceRange &r) { +static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) { o << "[" << r.start().offset() << ".." << r.Last().offset() << "] (" << r.size() << " bytes)"; } -std::ostream &ProvenanceRangeToOffsetMappings::Dump(std::ostream &o) const { +llvm::raw_ostream &ProvenanceRangeToOffsetMappings::Dump( + llvm::raw_ostream &o) const { for (const auto &m : map_) { o << "provenances "; DumpRange(o, m.first); @@ -485,7 +487,8 @@ std::ostream &ProvenanceRangeToOffsetMappings::Dump(std::ostream &o) const { return o; } -std::ostream &OffsetToProvenanceMappings::Dump(std::ostream &o) const { +llvm::raw_ostream &OffsetToProvenanceMappings::Dump( + llvm::raw_ostream &o) const { for (const ContiguousProvenanceMapping &m : provenanceMap_) { std::size_t n{m.range.size()}; o << "offsets [" << m.start << ".." << (m.start + n - 1) @@ -496,7 +499,7 @@ std::ostream &OffsetToProvenanceMappings::Dump(std::ostream &o) const { return o; } -std::ostream &AllSources::Dump(std::ostream &o) const { +llvm::raw_ostream &AllSources::Dump(llvm::raw_ostream &o) const { o << "AllSources range_ "; DumpRange(o, range_); o << '\n'; @@ -517,7 +520,8 @@ std::ostream &AllSources::Dump(std::ostream &o) const { o << "compiler '" << ins.text << '\''; if (ins.text.length() == 1) { int ch = ins.text[0]; - o << " (0x" << std::hex << (ch & 0xff) << std::dec << ")"; + o << "(0x"; + o.write_hex(ch & 0xff) << ")"; } }, }, @@ -531,7 +535,7 @@ std::ostream &AllSources::Dump(std::ostream &o) const { return o; } -std::ostream &CookedSource::Dump(std::ostream &o) const { +llvm::raw_ostream &CookedSource::Dump(llvm::raw_ostream &o) const { o << "CookedSource:\n"; allSources_.Dump(o); o << "CookedSource::provenanceMap_:\n"; diff --git a/lib/Parser/source.cpp b/lib/Parser/source.cpp index e1bbfcac765c..4f8a08aa5271 100644 --- a/lib/Parser/source.cpp +++ b/lib/Parser/source.cpp @@ -9,8 +9,9 @@ #include "flang/Parser/source.h" #include "flang/Common/idioms.h" #include "flang/Parser/char-buffer.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include #include #include @@ -111,36 +112,38 @@ static std::size_t RemoveCarriageReturns(char *buffer, std::size_t bytes) { return wrote; } -bool SourceFile::Open(std::string path, std::stringstream *error) { +bool SourceFile::Open(std::string path, llvm::raw_ostream &error) { Close(); path_ = path; std::string errorPath{"'"s + path + "'"}; errno = 0; fileDescriptor_ = open(path.c_str(), O_RDONLY); if (fileDescriptor_ < 0) { - *error << "Could not open " << errorPath << ": " << std::strerror(errno); + error << "Could not open " << errorPath << ": " + << llvm::sys::StrError(errno); return false; } ++openFileDescriptors; return ReadFile(errorPath, error); } -bool SourceFile::ReadStandardInput(std::stringstream *error) { +bool SourceFile::ReadStandardInput(llvm::raw_ostream &error) { Close(); path_ = "standard input"; fileDescriptor_ = 0; return ReadFile(path_, error); } -bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) { +bool SourceFile::ReadFile(std::string errorPath, llvm::raw_ostream &error) { struct stat statbuf; if (fstat(fileDescriptor_, &statbuf) != 0) { - *error << "fstat failed on " << errorPath << ": " << std::strerror(errno); + error << "fstat failed on " << errorPath << ": " + << llvm::sys::StrError(errno); Close(); return false; } if (S_ISDIR(statbuf.st_mode)) { - *error << errorPath << " is a directory"; + error << errorPath << " is a directory"; Close(); return false; } @@ -203,7 +206,8 @@ bool SourceFile::ReadFile(std::string errorPath, std::stringstream *error) { char *to{buffer.FreeSpace(count)}; ssize_t got{read(fileDescriptor_, to, count)}; if (got < 0) { - *error << "could not read " << errorPath << ": " << std::strerror(errno); + error << "could not read " << errorPath << ": " + << llvm::sys::StrError(errno); Close(); return false; } diff --git a/lib/Parser/token-sequence.cpp b/lib/Parser/token-sequence.cpp index 23aa450ceb41..235a4a4d303f 100644 --- a/lib/Parser/token-sequence.cpp +++ b/lib/Parser/token-sequence.cpp @@ -8,6 +8,7 @@ #include "token-sequence.h" #include "flang/Parser/characters.h" +#include "llvm/Support/raw_ostream.h" namespace Fortran::parser { @@ -123,7 +124,7 @@ void TokenSequence::Put(const std::string &s, Provenance provenance) { Put(s.data(), s.size(), provenance); } -void TokenSequence::Put(const std::stringstream &ss, Provenance provenance) { +void TokenSequence::Put(llvm::raw_string_ostream &ss, Provenance provenance) { Put(ss.str(), provenance); } @@ -248,7 +249,7 @@ void TokenSequence::Emit(CookedSource &cooked) const { cooked.PutProvenanceMappings(provenances_); } -void TokenSequence::Dump(std::ostream &o) const { +void TokenSequence::Dump(llvm::raw_ostream &o) const { o << "TokenSequence has " << char_.size() << " chars; nextStart_ " << nextStart_ << '\n'; for (std::size_t j{0}; j < start_.size(); ++j) { diff --git a/lib/Parser/token-sequence.h b/lib/Parser/token-sequence.h index d0ef0750e2fe..b70134bd4b20 100644 --- a/lib/Parser/token-sequence.h +++ b/lib/Parser/token-sequence.h @@ -17,11 +17,14 @@ #include "flang/Parser/provenance.h" #include #include -#include #include #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { // Buffers a contiguous sequence of characters that has been partitioned into @@ -91,7 +94,7 @@ class TokenSequence { void Put(const char *, std::size_t, Provenance); void Put(const CharBlock &, Provenance); void Put(const std::string &, Provenance); - void Put(const std::stringstream &, Provenance); + void Put(llvm::raw_string_ostream &, Provenance); Provenance GetTokenProvenance( std::size_t token, std::size_t offset = 0) const; @@ -109,7 +112,7 @@ class TokenSequence { TokenSequence &RemoveRedundantBlanks(std::size_t firstChar = 0); TokenSequence &ClipComment(bool skipFirst = false); void Emit(CookedSource &) const; - void Dump(std::ostream &) const; + void Dump(llvm::raw_ostream &) const; private: std::size_t TokenBytes(std::size_t token) const { diff --git a/lib/Parser/unparse.cpp b/lib/Parser/unparse.cpp index 45bb54fd58fa..5a67bac118e0 100644 --- a/lib/Parser/unparse.cpp +++ b/lib/Parser/unparse.cpp @@ -16,6 +16,7 @@ #include "flang/Parser/characters.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Parser/parse-tree.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -25,9 +26,9 @@ namespace Fortran::parser { class UnparseVisitor { public: - UnparseVisitor(std::ostream &out, int indentationAmount, Encoding encoding, - bool capitalize, bool backslashEscapes, preStatementType *preStatement, - AnalyzedObjectsAsFortran *asFortran) + UnparseVisitor(llvm::raw_ostream &out, int indentationAmount, + Encoding encoding, bool capitalize, bool backslashEscapes, + preStatementType *preStatement, AnalyzedObjectsAsFortran *asFortran) : out_{out}, indentationAmount_{indentationAmount}, encoding_{encoding}, capitalizeKeywords_{capitalize}, backslashEscapes_{backslashEscapes}, preStatement_{preStatement}, asFortran_{asFortran} {} @@ -2511,7 +2512,7 @@ class UnparseVisitor { structureComponents_.clear(); } - std::ostream &out_; + llvm::raw_ostream &out_; int indent_{0}; const int indentationAmount_{1}; int column_{1}; @@ -2593,7 +2594,7 @@ void UnparseVisitor::Word(const char *str) { void UnparseVisitor::Word(const std::string &str) { Word(str.c_str()); } -void Unparse(std::ostream &out, const Program &program, Encoding encoding, +void Unparse(llvm::raw_ostream &out, const Program &program, Encoding encoding, bool capitalizeKeywords, bool backslashEscapes, preStatementType *preStatement, AnalyzedObjectsAsFortran *asFortran) { UnparseVisitor visitor{out, 1, encoding, capitalizeKeywords, backslashEscapes, diff --git a/lib/Semantics/attr.cpp b/lib/Semantics/attr.cpp index 341fe5e67f89..e041a8f511c3 100644 --- a/lib/Semantics/attr.cpp +++ b/lib/Semantics/attr.cpp @@ -8,7 +8,7 @@ #include "flang/Semantics/attr.h" #include "flang/Common/idioms.h" -#include +#include "llvm/Support/raw_ostream.h" #include namespace Fortran::semantics { @@ -29,11 +29,11 @@ std::string AttrToString(Attr attr) { } } -std::ostream &operator<<(std::ostream &o, Attr attr) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Attr attr) { return o << AttrToString(attr); } -std::ostream &operator<<(std::ostream &o, const Attrs &attrs) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Attrs &attrs) { std::size_t n{attrs.count()}; std::size_t seen{0}; for (std::size_t j{0}; seen < n; ++j) { diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 84ec0a7386fe..fa8f96e4c79a 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -22,11 +22,11 @@ #include "flang/Semantics/semantics.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" +#include "llvm/Support/raw_ostream.h" #include #include #include #include -#include // Typedef for optional generic expressions (ubiquitous in this file) using MaybeExpr = @@ -174,7 +174,7 @@ class ArgumentAnalyzer { // Find and return a user-defined assignment std::optional TryDefinedAssignment(); std::optional GetDefinedAssignmentProc(); - void Dump(std::ostream &); + void Dump(llvm::raw_ostream &); private: MaybeExpr TryDefinedOp( @@ -2426,7 +2426,8 @@ MaybeExpr ExpressionAnalyzer::ExprOrVariable(const PARSED &x) { x.typedExpr.reset(new GenericExprWrapper{std::move(result)}); if (!x.typedExpr->v) { if (!context_.AnyFatalError()) { - std::stringstream dump; + std::string buf; + llvm::raw_string_ostream dump{buf}; parser::DumpTree(dump, x); Say("Internal error: Expression analysis failed on: %s"_err_en_US, dump.str()); @@ -2542,7 +2543,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { // C885 + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); @@ -2848,7 +2849,7 @@ std::optional ArgumentAnalyzer::GetDefinedAssignmentProc() { } } -void ArgumentAnalyzer::Dump(std::ostream &os) { +void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) { os << "source_: " << source_.ToString() << " fatalErrors_ = " << fatalErrors_ << '\n'; for (const auto &actual : actuals_) { diff --git a/lib/Semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp index 7251e3fa285a..bbf62f9c372f 100644 --- a/lib/Semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -20,7 +20,6 @@ #include "llvm/Support/raw_ostream.h" #include #include -#include #include #include #include @@ -44,22 +43,23 @@ struct ModHeader { static std::optional GetSubmoduleParent(const parser::Program &); static SymbolVector CollectSymbols(const Scope &); -static void PutEntity(std::ostream &, const Symbol &); -static void PutObjectEntity(std::ostream &, const Symbol &); -static void PutProcEntity(std::ostream &, const Symbol &); -static void PutPassName(std::ostream &, const std::optional &); -static void PutTypeParam(std::ostream &, const Symbol &); +static void PutEntity(llvm::raw_ostream &, const Symbol &); +static void PutObjectEntity(llvm::raw_ostream &, const Symbol &); +static void PutProcEntity(llvm::raw_ostream &, const Symbol &); +static void PutPassName(llvm::raw_ostream &, const std::optional &); +static void PutTypeParam(llvm::raw_ostream &, const Symbol &); static void PutEntity( - std::ostream &, const Symbol &, std::function, Attrs); -static void PutInit(std::ostream &, const Symbol &, const MaybeExpr &); -static void PutInit(std::ostream &, const MaybeIntExpr &); -static void PutBound(std::ostream &, const Bound &); -static std::ostream &PutAttrs(std::ostream &, Attrs, + llvm::raw_ostream &, const Symbol &, std::function, Attrs); +static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &); +static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &); +static void PutBound(llvm::raw_ostream &, const Bound &); +static llvm::raw_ostream &PutAttrs(llvm::raw_ostream &, Attrs, const MaybeExpr & = std::nullopt, std::string before = ","s, std::string after = ""s); -static std::ostream &PutAttr(std::ostream &, Attr); -static std::ostream &PutType(std::ostream &, const DeclTypeSpec &); -static std::ostream &PutLower(std::ostream &, const std::string &); + +static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr); +static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &); +static llvm::raw_ostream &PutLower(llvm::raw_ostream &, const std::string &); static std::error_code WriteFile( const std::string &, const std::string &, bool = true); static bool FileContentsMatch( @@ -143,7 +143,8 @@ void ModFileWriter::Write(const Symbol &symbol) { // Return the entire body of the module file // and clear saved uses, decls, and contains. std::string ModFileWriter::GetAsString(const Symbol &symbol) { - std::stringstream all; + std::string buf; + llvm::raw_string_ostream all{buf}; auto &details{symbol.get()}; if (!details.isSubmodule()) { all << "module " << symbol.name(); @@ -157,13 +158,13 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) { all << ") " << symbol.name(); } all << '\n' << uses_.str(); - uses_.str(""s); + uses_.str().clear(); all << useExtraAttrs_.str(); - useExtraAttrs_.str(""s); + useExtraAttrs_.str().clear(); all << decls_.str(); - decls_.str(""s); + decls_.str().clear(); auto str{contains_.str()}; - contains_.str(""s); + contains_.str().clear(); if (!str.empty()) { all << "contains\n" << str; } @@ -173,7 +174,9 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) { // Put out the visible symbols from scope. void ModFileWriter::PutSymbols(const Scope &scope) { - std::stringstream typeBindings; // stuff after CONTAINS in derived type + std::string buf; + llvm::raw_string_ostream typeBindings{ + buf}; // stuff after CONTAINS in derived type for (const Symbol &symbol : CollectSymbols(scope)) { PutSymbol(typeBindings, symbol); } @@ -186,7 +189,7 @@ void ModFileWriter::PutSymbols(const Scope &scope) { // Emit a symbol to decls_, except for bindings in a derived type (type-bound // procedures, type-bound generics, final procedures) which go to typeBindings. void ModFileWriter::PutSymbol( - std::stringstream &typeBindings, const Symbol &symbol) { + llvm::raw_ostream &typeBindings, const Symbol &symbol) { std::visit( common::visitors{ [&](const ModuleDetails &) { /* should be current module */ }, @@ -301,13 +304,14 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) { Attrs prefixAttrs{subprogramPrefixAttrs & attrs}; // emit any non-prefix attributes in an attribute statement attrs &= ~subprogramPrefixAttrs; - std::stringstream ss; + std::string ssBuf; + llvm::raw_string_ostream ss{ssBuf}; PutAttrs(ss, attrs); if (!ss.str().empty()) { decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n'; } bool isInterface{details.isInterface()}; - std::ostream &os{isInterface ? decls_ : contains_}; + llvm::raw_ostream &os{isInterface ? decls_ : contains_}; if (isInterface) { os << "interface\n"; } @@ -333,7 +337,8 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) { // walk symbols, collect ones needed ModFileWriter writer{context_}; - std::stringstream typeBindings; + std::string typeBindingsBuf; + llvm::raw_string_ostream typeBindings{typeBindingsBuf}; SubprogramSymbolCollector collector{symbol}; collector.Collect(); for (const Symbol &need : collector.symbols()) { @@ -359,7 +364,8 @@ static bool IsIntrinsicOp(const Symbol &symbol) { } } -static std::ostream &PutGenericName(std::ostream &os, const Symbol &symbol) { +static llvm::raw_ostream &PutGenericName( + llvm::raw_ostream &os, const Symbol &symbol) { if (IsGenericDefinedOp(symbol)) { return os << "operator(" << symbol.name() << ')'; } else { @@ -440,7 +446,7 @@ SymbolVector CollectSymbols(const Scope &scope) { return sorted; } -void PutEntity(std::ostream &os, const Symbol &symbol) { +void PutEntity(llvm::raw_ostream &os, const Symbol &symbol) { std::visit( common::visitors{ [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); }, @@ -454,7 +460,7 @@ void PutEntity(std::ostream &os, const Symbol &symbol) { symbol.details()); } -void PutShapeSpec(std::ostream &os, const ShapeSpec &x) { +void PutShapeSpec(llvm::raw_ostream &os, const ShapeSpec &x) { if (x.lbound().isAssumed()) { CHECK(x.ubound().isAssumed()); os << ".."; @@ -468,7 +474,8 @@ void PutShapeSpec(std::ostream &os, const ShapeSpec &x) { } } } -void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) { +void PutShape( + llvm::raw_ostream &os, const ArraySpec &shape, char open, char close) { if (!shape.empty()) { os << open; bool first{true}; @@ -484,7 +491,7 @@ void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) { } } -void PutObjectEntity(std::ostream &os, const Symbol &symbol) { +void PutObjectEntity(llvm::raw_ostream &os, const Symbol &symbol) { auto &details{symbol.get()}; PutEntity(os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); }, symbol.attrs()); @@ -494,7 +501,7 @@ void PutObjectEntity(std::ostream &os, const Symbol &symbol) { os << '\n'; } -void PutProcEntity(std::ostream &os, const Symbol &symbol) { +void PutProcEntity(llvm::raw_ostream &os, const Symbol &symbol) { if (symbol.attrs().test(Attr::INTRINSIC)) { os << "intrinsic::" << symbol.name() << '\n'; return; @@ -520,13 +527,13 @@ void PutProcEntity(std::ostream &os, const Symbol &symbol) { os << '\n'; } -void PutPassName(std::ostream &os, const std::optional &passName) { +void PutPassName( + llvm::raw_ostream &os, const std::optional &passName) { if (passName) { os << ",pass(" << *passName << ')'; } } - -void PutTypeParam(std::ostream &os, const Symbol &symbol) { +void PutTypeParam(llvm::raw_ostream &os, const Symbol &symbol) { auto &details{symbol.get()}; PutEntity(os, symbol, [&]() { @@ -538,7 +545,8 @@ void PutTypeParam(std::ostream &os, const Symbol &symbol) { os << '\n'; } -void PutInit(std::ostream &os, const Symbol &symbol, const MaybeExpr &init) { +void PutInit( + llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init) { if (init) { if (symbol.attrs().test(Attr::PARAMETER) || symbol.owner().IsDerivedType()) { @@ -548,13 +556,13 @@ void PutInit(std::ostream &os, const Symbol &symbol, const MaybeExpr &init) { } } -void PutInit(std::ostream &os, const MaybeIntExpr &init) { +void PutInit(llvm::raw_ostream &os, const MaybeIntExpr &init) { if (init) { init->AsFortran(os << '='); } } -void PutBound(std::ostream &os, const Bound &x) { +void PutBound(llvm::raw_ostream &os, const Bound &x) { if (x.isAssumed()) { os << '*'; } else if (x.isDeferred()) { @@ -566,7 +574,7 @@ void PutBound(std::ostream &os, const Bound &x) { // Write an entity (object or procedure) declaration. // writeType is called to write out the type. -void PutEntity(std::ostream &os, const Symbol &symbol, +void PutEntity(llvm::raw_ostream &os, const Symbol &symbol, std::function writeType, Attrs attrs) { writeType(); MaybeExpr bindName; @@ -584,8 +592,8 @@ void PutEntity(std::ostream &os, const Symbol &symbol, // Put out each attribute to os, surrounded by `before` and `after` and // mapped to lower case. -std::ostream &PutAttrs(std::ostream &os, Attrs attrs, const MaybeExpr &bindName, - std::string before, std::string after) { +llvm::raw_ostream &PutAttrs(llvm::raw_ostream &os, Attrs attrs, + const MaybeExpr &bindName, std::string before, std::string after) { attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL if (bindName) { @@ -601,15 +609,15 @@ std::ostream &PutAttrs(std::ostream &os, Attrs attrs, const MaybeExpr &bindName, return os; } -std::ostream &PutAttr(std::ostream &os, Attr attr) { +llvm::raw_ostream &PutAttr(llvm::raw_ostream &os, Attr attr) { return PutLower(os, AttrToString(attr)); } -std::ostream &PutType(std::ostream &os, const DeclTypeSpec &type) { +llvm::raw_ostream &PutType(llvm::raw_ostream &os, const DeclTypeSpec &type) { return PutLower(os, type.AsFortran()); } -std::ostream &PutLower(std::ostream &os, const std::string &str) { +llvm::raw_ostream &PutLower(llvm::raw_ostream &os, const std::string &str) { for (char c : str) { os << parser::ToLowerCaseLetter(c); } @@ -764,8 +772,8 @@ Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { sourceFile->path()); return nullptr; } - - parsing.Parse(nullptr); + llvm::raw_null_ostream NullStream; + parsing.Parse(NullStream); auto &parseTree{parsing.parseTree()}; if (!parsing.messages().empty() || !parsing.consumedWholeFile() || !parseTree) { diff --git a/lib/Semantics/mod-file.h b/lib/Semantics/mod-file.h index 1bc356ad1bc1..d71a0f599af8 100644 --- a/lib/Semantics/mod-file.h +++ b/lib/Semantics/mod-file.h @@ -10,7 +10,6 @@ #define FORTRAN_SEMANTICS_MOD_FILE_H_ #include "flang/Semantics/attr.h" -#include #include namespace Fortran::parser { @@ -19,6 +18,10 @@ class Message; class MessageFixedText; } +namespace llvm { +class raw_ostream; +} + namespace Fortran::semantics { using SourceName = parser::CharBlock; @@ -33,17 +36,23 @@ class ModFileWriter { private: SemanticsContext &context_; - std::stringstream uses_; - std::stringstream useExtraAttrs_; // attrs added to used entity - std::stringstream decls_; - std::stringstream contains_; + // Buffer to use with raw_string_ostream + std::string usesBuf_; + std::string useExtraAttrsBuf_; + std::string declsBuf_; + std::string containsBuf_; + + llvm::raw_string_ostream uses_{usesBuf_}; + llvm::raw_string_ostream useExtraAttrs_{useExtraAttrsBuf_}; // attrs added to used entity + llvm::raw_string_ostream decls_{declsBuf_}; + llvm::raw_string_ostream contains_{containsBuf_}; void WriteAll(const Scope &); void WriteOne(const Scope &); void Write(const Symbol &); std::string GetAsString(const Symbol &); void PutSymbols(const Scope &); - void PutSymbol(std::stringstream &, const Symbol &); + void PutSymbol(llvm::raw_ostream &, const Symbol &); void PutDerivedType(const Symbol &); void PutSubprogram(const Symbol &); void PutGeneric(const Symbol &); diff --git a/lib/Semantics/pointer-assignment.cpp b/lib/Semantics/pointer-assignment.cpp index b59dc8169aff..fdb6addc20de 100644 --- a/lib/Semantics/pointer-assignment.cpp +++ b/lib/Semantics/pointer-assignment.cpp @@ -19,6 +19,7 @@ #include "flang/Semantics/expression.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -232,7 +233,8 @@ void PointerAssignmentChecker::Check(const evaluate::Designator &d) { if (msg) { auto restorer{common::ScopedSet(lhs_, last)}; if (auto *m{std::get_if(&*msg)}) { - std::ostringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; d.AsFortran(ss); Say(*m, description_, ss.str()); } else { diff --git a/lib/Semantics/resolve-names-utils.cpp b/lib/Semantics/resolve-names-utils.cpp index afc0aac5a315..4c0e70e8caf4 100644 --- a/lib/Semantics/resolve-names-utils.cpp +++ b/lib/Semantics/resolve-names-utils.cpp @@ -19,7 +19,6 @@ #include "flang/Semantics/semantics.h" #include "flang/Semantics/tools.h" #include -#include #include namespace Fortran::semantics { diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index 42766f738e07..c696d5e4fe82 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -33,9 +33,9 @@ #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" #include "flang/Semantics/type.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include #include #include @@ -86,8 +86,10 @@ class ImplicitRules { // the default Fortran mappings nor the mapping defined in parents. std::map> map_; - friend std::ostream &operator<<(std::ostream &, const ImplicitRules &); - friend void ShowImplicitRule(std::ostream &, const ImplicitRules &, char); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const ImplicitRules &); + friend void ShowImplicitRule( + llvm::raw_ostream &, const ImplicitRules &, char); }; // scope -> implicit rules for that scope @@ -1463,7 +1465,8 @@ char ImplicitRules::Incr(char ch) { } } -std::ostream &operator<<(std::ostream &o, const ImplicitRules &implicitRules) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &o, const ImplicitRules &implicitRules) { o << "ImplicitRules:\n"; for (char ch = 'a'; ch; ch = ImplicitRules::Incr(ch)) { ShowImplicitRule(o, implicitRules, ch); @@ -1474,7 +1477,7 @@ std::ostream &operator<<(std::ostream &o, const ImplicitRules &implicitRules) { return o; } void ShowImplicitRule( - std::ostream &o, const ImplicitRules &implicitRules, char ch) { + llvm::raw_ostream &o, const ImplicitRules &implicitRules, char ch) { auto it{implicitRules.map_.find(ch)}; if (it != implicitRules.map_.end()) { o << " " << ch << ": " << *it->second << '\n'; diff --git a/lib/Semantics/resolve-names.h b/lib/Semantics/resolve-names.h index 240f315bb715..218dc9c63eae 100644 --- a/lib/Semantics/resolve-names.h +++ b/lib/Semantics/resolve-names.h @@ -13,6 +13,10 @@ #include #include +namespace llvm { +class raw_ostream; +} + namespace Fortran::parser { struct Program; } @@ -24,7 +28,7 @@ class Symbol; bool ResolveNames(SemanticsContext &, const parser::Program &); void ResolveSpecificationParts(SemanticsContext &, const Symbol &); -void DumpSymbols(std::ostream &); +void DumpSymbols(llvm::raw_ostream &); } diff --git a/lib/Semantics/scope.cpp b/lib/Semantics/scope.cpp index b63082f2c410..c25ce1114ddc 100644 --- a/lib/Semantics/scope.cpp +++ b/lib/Semantics/scope.cpp @@ -10,9 +10,9 @@ #include "flang/Parser/characters.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/type.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include namespace Fortran::semantics { @@ -32,7 +32,8 @@ bool EquivalenceObject::operator<(const EquivalenceObject &that) const { } std::string EquivalenceObject::AsFortran() const { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; ss << symbol.name().ToString(); if (!subscripts.empty()) { char sep{'('}; @@ -283,7 +284,7 @@ void Scope::AddSourceRange(const parser::CharBlock &source) { } } -std::ostream &operator<<(std::ostream &os, const Scope &scope) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) { os << Scope::EnumToString(scope.kind()) << " scope: "; if (auto *symbol{scope.symbol()}) { os << *symbol << ' '; diff --git a/lib/Semantics/semantics.cpp b/lib/Semantics/semantics.cpp index 745143267d6d..53be760b7741 100644 --- a/lib/Semantics/semantics.cpp +++ b/lib/Semantics/semantics.cpp @@ -35,12 +35,13 @@ #include "flang/Semantics/expression.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" +#include "llvm/Support/raw_ostream.h" namespace Fortran::semantics { using NameToSymbolMap = std::map; -static void DoDumpSymbols(std::ostream &, const Scope &, int indent = 0); -static void PutIndent(std::ostream &, int indent); +static void DoDumpSymbols(llvm::raw_ostream &, const Scope &, int indent = 0); +static void PutIndent(llvm::raw_ostream &, int indent); static void GetSymbolNames(const Scope &scope, NameToSymbolMap &symbols) { // Finds all symbol names in the scope without collecting duplicates. @@ -282,15 +283,15 @@ bool Semantics::Perform() { ModFileWriter{context_}.WriteAll(); } -void Semantics::EmitMessages(std::ostream &os) const { +void Semantics::EmitMessages(llvm::raw_ostream &os) const { context_.messages().Emit(os, cooked_); } -void Semantics::DumpSymbols(std::ostream &os) { +void Semantics::DumpSymbols(llvm::raw_ostream &os) { DoDumpSymbols(os, context_.globalScope()); } -void Semantics::DumpSymbolsSources(std::ostream &os) const { +void Semantics::DumpSymbolsSources(llvm::raw_ostream &os) const { NameToSymbolMap symbols; GetSymbolNames(context_.globalScope(), symbols); for (const auto &pair : symbols) { @@ -306,7 +307,7 @@ void Semantics::DumpSymbolsSources(std::ostream &os) const { } } -void DoDumpSymbols(std::ostream &os, const Scope &scope, int indent) { +void DoDumpSymbols(llvm::raw_ostream &os, const Scope &scope, int indent) { PutIndent(os, indent); os << Scope::EnumToString(scope.kind()) << " scope:"; if (const auto *symbol{scope.symbol()}) { @@ -357,7 +358,7 @@ void DoDumpSymbols(std::ostream &os, const Scope &scope, int indent) { --indent; } -static void PutIndent(std::ostream &os, int indent) { +static void PutIndent(llvm::raw_ostream &os, int indent) { for (int i = 0; i < indent; ++i) { os << " "; } diff --git a/lib/Semantics/symbol.cpp b/lib/Semantics/symbol.cpp index 0017d891643b..a13f2c0d3779 100644 --- a/lib/Semantics/symbol.cpp +++ b/lib/Semantics/symbol.cpp @@ -12,32 +12,32 @@ #include "flang/Semantics/scope.h" #include "flang/Semantics/semantics.h" #include "flang/Semantics/tools.h" -#include +#include "llvm/Support/raw_ostream.h" #include namespace Fortran::semantics { template -static void DumpOptional(std::ostream &os, const char *label, const T &x) { +static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) { if (x) { os << ' ' << label << ':' << *x; } } template -static void DumpExpr(std::ostream &os, const char *label, +static void DumpExpr(llvm::raw_ostream &os, const char *label, const std::optional> &x) { if (x) { x->AsFortran(os << ' ' << label << ':'); } } -static void DumpBool(std::ostream &os, const char *label, bool x) { +static void DumpBool(llvm::raw_ostream &os, const char *label, bool x) { if (x) { os << ' ' << label; } } -static void DumpSymbolVector(std::ostream &os, const SymbolVector &list) { +static void DumpSymbolVector(llvm::raw_ostream &os, const SymbolVector &list) { char sep{' '}; for (const Symbol &elem : list) { os << sep << elem.name(); @@ -45,19 +45,19 @@ static void DumpSymbolVector(std::ostream &os, const SymbolVector &list) { } } -static void DumpType(std::ostream &os, const Symbol &symbol) { +static void DumpType(llvm::raw_ostream &os, const Symbol &symbol) { if (const auto *type{symbol.GetType()}) { os << *type << ' '; } } -static void DumpType(std::ostream &os, const DeclTypeSpec *type) { +static void DumpType(llvm::raw_ostream &os, const DeclTypeSpec *type) { if (type) { os << ' ' << *type; } } template -static void DumpList(std::ostream &os, const char *label, const T &list) { +static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) { if (!list.empty()) { os << ' ' << label << ':'; char sep{' '}; @@ -81,7 +81,8 @@ void ModuleDetails::set_scope(const Scope *scope) { scope_ = scope; } -std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const SubprogramDetails &x) { DumpBool(os, "isInterface", x.isInterface_); DumpExpr(os, "bindName", x.bindName_); if (x.result_) { @@ -334,7 +335,7 @@ bool Symbol::IsFromModFile() const { ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d) : EntityDetails(d) {} -std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) { DumpBool(os, "dummy", x.isDummy()); DumpBool(os, "funcResult", x.isFuncResult()); if (x.type()) { @@ -344,7 +345,8 @@ std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { return os; } -std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const ObjectEntityDetails &x) { os << *static_cast(&x); DumpList(os, "shape", x.shape()); DumpList(os, "coshape", x.coshape()); @@ -352,13 +354,15 @@ std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { return os; } -std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const AssocEntityDetails &x) { os << *static_cast(&x); DumpExpr(os, "expr", x.expr()); return os; } -std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const ProcEntityDetails &x) { if (auto *symbol{x.interface_.symbol()}) { os << ' ' << symbol->name(); } else { @@ -376,13 +380,14 @@ std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { return os; } -std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const DerivedTypeDetails &x) { DumpBool(os, "sequence", x.sequence_); DumpList(os, "components", x.componentNames_); return os; } -std::ostream &operator<<(std::ostream &os, const Details &details) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { os << DetailsToString(details); std::visit( common::visitors{ @@ -453,11 +458,12 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { return os; } -std::ostream &operator<<(std::ostream &o, Symbol::Flag flag) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) { return o << Symbol::EnumToString(flag); } -std::ostream &operator<<(std::ostream &o, const Symbol::Flags &flags) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &o, const Symbol::Flags &flags) { std::size_t n{flags.count()}; std::size_t seen{0}; for (std::size_t j{0}; seen < n; ++j) { @@ -472,7 +478,7 @@ std::ostream &operator<<(std::ostream &o, const Symbol::Flags &flags) { return o; } -std::ostream &operator<<(std::ostream &os, const Symbol &symbol) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) { os << symbol.name(); if (!symbol.attrs().empty()) { os << ", " << symbol.attrs(); @@ -487,7 +493,7 @@ std::ostream &operator<<(std::ostream &os, const Symbol &symbol) { // Output a unique name for a scope by qualifying it with the names of // parent scopes. For scopes without corresponding symbols, use the kind // with an index (e.g. Block1, Block2, etc.). -static void DumpUniqueName(std::ostream &os, const Scope &scope) { +static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) { if (!scope.IsGlobal()) { DumpUniqueName(os, scope.parent()); os << '/'; @@ -511,8 +517,8 @@ static void DumpUniqueName(std::ostream &os, const Scope &scope) { // Dump a symbol for UnparseWithSymbols. This will be used for tests so the // format should be reasonably stable. -std::ostream &DumpForUnparse( - std::ostream &os, const Symbol &symbol, bool isDef) { +llvm::raw_ostream &DumpForUnparse( + llvm::raw_ostream &os, const Symbol &symbol, bool isDef) { DumpUniqueName(os, symbol.owner()); os << '/' << symbol.name(); if (isDef) { diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index cc3b9084af34..f6a4e39e511d 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -17,9 +17,9 @@ #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" #include "flang/Semantics/type.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include #include namespace Fortran::semantics { @@ -414,7 +414,8 @@ bool ExprTypeKindIsDefault( // If an analyzed expr or assignment is missing, dump the node and die. template static void CheckMissingAnalysis(bool absent, const T &x) { if (absent) { - std::ostringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; ss << "node has not been analyzed:\n"; parser::DumpTree(ss, x); common::die(ss.str().c_str()); diff --git a/lib/Semantics/type.cpp b/lib/Semantics/type.cpp index 49bd618e3637..cba4d3ce9b0c 100644 --- a/lib/Semantics/type.cpp +++ b/lib/Semantics/type.cpp @@ -12,8 +12,7 @@ #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" -#include -#include +#include "llvm/Support/raw_ostream.h" namespace Fortran::semantics { @@ -272,7 +271,8 @@ void DerivedTypeSpec::Instantiate( } std::string DerivedTypeSpec::AsFortran() const { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; ss << name_; if (!rawParameters_.empty()) { CHECK(parameters_.empty()); @@ -306,13 +306,13 @@ std::string DerivedTypeSpec::AsFortran() const { return ss.str(); } -std::ostream &operator<<(std::ostream &o, const DerivedTypeSpec &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DerivedTypeSpec &x) { return o << x.AsFortran(); } Bound::Bound(int bound) : expr_{bound} {} -std::ostream &operator<<(std::ostream &o, const Bound &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Bound &x) { if (x.isAssumed()) { o << '*'; } else if (x.isDeferred()) { @@ -325,7 +325,7 @@ std::ostream &operator<<(std::ostream &o, const Bound &x) { return o; } -std::ostream &operator<<(std::ostream &o, const ShapeSpec &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ShapeSpec &x) { if (x.lb_.isAssumed()) { CHECK(x.ub_.isAssumed()); o << ".."; @@ -365,7 +365,8 @@ bool ArraySpec::IsAssumedRank() const { return Rank() == 1 && front().lbound().isAssumed(); } -std::ostream &operator<<(std::ostream &os, const ArraySpec &arraySpec) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const ArraySpec &arraySpec) { char sep{'('}; for (auto &shape : arraySpec) { os << sep << shape; @@ -398,7 +399,8 @@ std::string ParamValue::AsFortran() const { case Category::Deferred: return ":"; case Category::Explicit: if (expr_) { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; expr_->AsFortran(ss); return ss.str(); } else { @@ -407,7 +409,7 @@ std::string ParamValue::AsFortran() const { } } -std::ostream &operator<<(std::ostream &o, const ParamValue &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ParamValue &x) { return o << x.AsFortran(); } @@ -417,7 +419,8 @@ IntrinsicTypeSpec::IntrinsicTypeSpec(TypeCategory category, KindExpr &&kind) } static std::string KindAsFortran(const KindExpr &kind) { - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; if (auto k{evaluate::ToInt64(kind)}) { ss << *k; // emit unsuffixed kind code } else { @@ -431,7 +434,8 @@ std::string IntrinsicTypeSpec::AsFortran() const { KindAsFortran(kind_) + ')'; } -std::ostream &operator<<(std::ostream &os, const IntrinsicTypeSpec &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const IntrinsicTypeSpec &x) { return os << x.AsFortran(); } @@ -439,7 +443,8 @@ std::string CharacterTypeSpec::AsFortran() const { return "CHARACTER(" + length_.AsFortran() + ',' + KindAsFortran(kind()) + ')'; } -std::ostream &operator<<(std::ostream &os, const CharacterTypeSpec &x) { +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const CharacterTypeSpec &x) { return os << x.AsFortran(); } @@ -494,7 +499,7 @@ std::string DeclTypeSpec::AsFortran() const { } } -std::ostream &operator<<(std::ostream &o, const DeclTypeSpec &x) { +llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DeclTypeSpec &x) { return o << x.AsFortran(); } diff --git a/lib/Semantics/unparse-with-symbols.cpp b/lib/Semantics/unparse-with-symbols.cpp index b4f89198b347..b8e5e8837b3d 100644 --- a/lib/Semantics/unparse-with-symbols.cpp +++ b/lib/Semantics/unparse-with-symbols.cpp @@ -11,8 +11,8 @@ #include "flang/Parser/parse-tree.h" #include "flang/Parser/unparse.h" #include "flang/Semantics/symbol.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include namespace Fortran::semantics { @@ -24,7 +24,7 @@ namespace Fortran::semantics { class SymbolDumpVisitor { public: // Write out symbols referenced at this statement. - void PrintSymbols(const parser::CharBlock &, std::ostream &, int); + void PrintSymbols(const parser::CharBlock &, llvm::raw_ostream &, int); template bool Pre(const T &) { return true; } template void Post(const T &) {} @@ -51,11 +51,11 @@ class SymbolDumpVisitor { std::optional currStmt_; // current statement we are processing std::multimap symbols_; // location to symbol std::set symbolsDefined_; // symbols that have been processed - void Indent(std::ostream &, int) const; + void Indent(llvm::raw_ostream &, int) const; }; void SymbolDumpVisitor::PrintSymbols( - const parser::CharBlock &location, std::ostream &out, int indent) { + const parser::CharBlock &location, llvm::raw_ostream &out, int indent) { std::set done; // prevent duplicates on this line auto range{symbols_.equal_range(location.begin())}; for (auto it{range.first}; it != range.second; ++it) { @@ -70,7 +70,7 @@ void SymbolDumpVisitor::PrintSymbols( } } -void SymbolDumpVisitor::Indent(std::ostream &out, int indent) const { +void SymbolDumpVisitor::Indent(llvm::raw_ostream &out, int indent) const { for (int i{0}; i < indent; ++i) { out << ' '; } @@ -84,14 +84,13 @@ void SymbolDumpVisitor::Post(const parser::Name &name) { } } -void UnparseWithSymbols(std::ostream &out, const parser::Program &program, +void UnparseWithSymbols(llvm::raw_ostream &out, const parser::Program &program, parser::Encoding encoding) { SymbolDumpVisitor visitor; parser::Walk(program, visitor); parser::preStatementType preStatement{ - [&](const parser::CharBlock &location, std::ostream &out, int indent) { - visitor.PrintSymbols(location, out, indent); - }}; + [&](const parser::CharBlock &location, llvm::raw_ostream &out, + int indent) { visitor.PrintSymbols(location, out, indent); }}; parser::Unparse(out, program, encoding, false, true, &preStatement); } } diff --git a/tools/f18/dump.cpp b/tools/f18/dump.cpp index 26f4d730f959..d04b8e094a23 100644 --- a/tools/f18/dump.cpp +++ b/tools/f18/dump.cpp @@ -8,26 +8,26 @@ // This file defines Dump routines available for calling from the debugger. // Each is based on operator<< for that type. There are overloadings for -// reference and pointer, and for dumping to a provided ostream or cerr. +// reference and pointer, and for dumping to a provided raw_ostream or errs(). #ifdef DEBUGF18 -#include +#include "llvm/Support/raw_ostream.h" #define DEFINE_DUMP(ns, name) \ namespace ns { \ class name; \ - std::ostream &operator<<(std::ostream &, const name &); \ + llvm::raw_ostream &operator<<(llvm::raw_ostream &, const name &); \ } \ - void Dump(std::ostream &os, const ns::name &x) { os << x << '\n'; } \ - void Dump(std::ostream &os, const ns::name *x) { \ + void Dump(llvm::raw_ostream &os, const ns::name &x) { os << x << '\n'; } \ + void Dump(llvm::raw_ostream &os, const ns::name *x) { \ if (x == nullptr) \ os << "null\n"; \ else \ Dump(os, *x); \ } \ - void Dump(const ns::name &x) { Dump(std::cerr, x); } \ - void Dump(const ns::name *x) { Dump(std::cerr, *x); } + void Dump(const ns::name &x) { Dump(llvm::errs(), x); } \ + void Dump(const ns::name *x) { Dump(llvm::errs(), *x); } namespace Fortran { DEFINE_DUMP(parser, Name) diff --git a/tools/f18/f18-parse-demo.cpp b/tools/f18/f18-parse-demo.cpp index 67bc45eb499a..ebb35615263d 100644 --- a/tools/f18/f18-parse-demo.cpp +++ b/tools/f18/f18-parse-demo.cpp @@ -31,11 +31,12 @@ #include "flang/Parser/parsing.h" #include "flang/Parser/provenance.h" #include "flang/Parser/unparse.h" -#include +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" #include #include #include -#include #include #include #include @@ -112,14 +113,14 @@ bool ParentProcess() { void Exec(std::vector &argv, bool verbose = false) { if (verbose) { for (size_t j{0}; j < argv.size(); ++j) { - std::cerr << (j > 0 ? " " : "") << argv[j]; + llvm::errs() << (j > 0 ? " " : "") << argv[j]; } - std::cerr << '\n'; + llvm::errs() << '\n'; } argv.push_back(nullptr); execvp(argv[0], &argv[0]); - std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno) - << '\n'; + llvm::errs() << "execvp(" << argv[0] + << ") failed: " << llvm::sys::StrError(errno) << '\n'; exit(EXIT_FAILURE); } @@ -173,52 +174,52 @@ std::string CompileFortran( parsing.Prescan(path, options); if (!parsing.messages().empty() && (driver.warningsAreErrors || parsing.messages().AnyFatalError())) { - std::cerr << driver.prefix << "could not scan " << path << '\n'; - parsing.messages().Emit(std::cerr, parsing.cooked()); + llvm::errs() << driver.prefix << "could not scan " << path << '\n'; + parsing.messages().Emit(llvm::errs(), parsing.cooked()); exitStatus = EXIT_FAILURE; return {}; } if (driver.dumpProvenance) { - parsing.DumpProvenance(std::cout); + parsing.DumpProvenance(llvm::outs()); return {}; } if (driver.dumpCookedChars) { - parsing.DumpCookedChars(std::cout); + parsing.DumpCookedChars(llvm::outs()); return {}; } - parsing.Parse(&std::cout); + parsing.Parse(llvm::outs()); auto stop{CPUseconds()}; if (driver.timeParse) { if (canTime) { - std::cout << "parse time for " << path << ": " << (stop - start) - << " CPU seconds\n"; + llvm::outs() << "parse time for " << path << ": " << (stop - start) + << " CPU seconds\n"; } else { - std::cout << "no timing information due to lack of clock_gettime()\n"; + llvm::outs() << "no timing information due to lack of clock_gettime()\n"; } } parsing.ClearLog(); - parsing.messages().Emit(std::cerr, parsing.cooked()); + parsing.messages().Emit(llvm::errs(), parsing.cooked()); if (!parsing.consumedWholeFile()) { - parsing.EmitMessage( - std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)"); + parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), + "parser FAIL (final position)"); exitStatus = EXIT_FAILURE; return {}; } if ((!parsing.messages().empty() && (driver.warningsAreErrors || parsing.messages().AnyFatalError())) || !parsing.parseTree()) { - std::cerr << driver.prefix << "could not parse " << path << '\n'; + llvm::errs() << driver.prefix << "could not parse " << path << '\n'; exitStatus = EXIT_FAILURE; return {}; } auto &parseTree{*parsing.parseTree()}; if (driver.dumpParseTree) { - Fortran::parser::DumpTree(std::cout, parseTree); + Fortran::parser::DumpTree(llvm::outs(), parseTree); return {}; } if (driver.dumpUnparse) { - Unparse(std::cout, parseTree, driver.encoding, true /*capitalize*/, + Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes)); return {}; @@ -233,8 +234,12 @@ std::string CompileFortran( std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90", static_cast(getpid())); { - std::ofstream tmpSource; - tmpSource.open(tmpSourcePath); + std::error_code EC; + llvm::raw_fd_ostream tmpSource(tmpSourcePath, EC, llvm::sys::fs::F_None); + if (EC) { + llvm::errs() << EC.message(); + std::exit(EXIT_FAILURE); + } Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes)); @@ -402,7 +407,7 @@ int main(int argc, char *const argv[]) { } else if (arg == "-i8" || arg == "-fdefault-integer-8") { defaultKinds.set_defaultIntegerKind(8); } else if (arg == "-help" || arg == "--help" || arg == "-?") { - std::cerr + llvm::errs() << "f18-parse-demo options:\n" << " -Mfixed | -Mfree force the source form\n" << " -Mextend 132-column fixed form\n" @@ -425,7 +430,7 @@ int main(int argc, char *const argv[]) { << "Other options are passed through to the $F18_FC compiler.\n"; return exitStatus; } else if (arg == "-V") { - std::cerr << "\nf18-parse-demo\n"; + llvm::errs() << "\nf18-parse-demo\n"; return exitStatus; } else { driver.fcArgs.push_back(arg); diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index edcd0ef66b2a..51d3aec3d118 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -23,12 +23,12 @@ #include "flang/Semantics/expression.h" #include "flang/Semantics/semantics.h" #include "flang/Semantics/unparse-with-symbols.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" -#include #include #include #include -#include #include #include #include @@ -58,8 +58,9 @@ struct MeasurementVisitor { void MeasureParseTree(const Fortran::parser::Program &program) { MeasurementVisitor visitor; Fortran::parser::Walk(program, visitor); - std::cout << "Parse tree comprises " << visitor.objects - << " objects and occupies " << visitor.bytes << " total bytes.\n"; + llvm::outs() << "Parse tree comprises " << visitor.objects + << " objects and occupies " << visitor.bytes + << " total bytes.\n"; } std::vector filesToDelete; @@ -123,14 +124,14 @@ bool ParentProcess() { void Exec(std::vector &argv, bool verbose = false) { if (verbose) { for (size_t j{0}; j < argv.size(); ++j) { - std::cerr << (j > 0 ? " " : "") << argv[j]; + llvm::errs() << (j > 0 ? " " : "") << argv[j]; } - std::cerr << '\n'; + llvm::errs() << '\n'; } argv.push_back(nullptr); execvp(argv[0], &argv[0]); - std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno) - << '\n'; + llvm::errs() << "execvp(" << argv[0] + << ") failed: " << llvm::sys::StrError(errno) << '\n'; exit(EXIT_FAILURE); } @@ -168,21 +169,22 @@ std::string RelocatableName(const DriverOptions &driver, std::string path) { int exitStatus{EXIT_SUCCESS}; static Fortran::parser::AnalyzedObjectsAsFortran asFortran{ - [](std::ostream &o, const Fortran::evaluate::GenericExprWrapper &x) { + [](llvm::raw_ostream &o, const Fortran::evaluate::GenericExprWrapper &x) { if (x.v) { x.v->AsFortran(o); } else { o << "(bad expression)"; } }, - [](std::ostream &o, const Fortran::evaluate::GenericAssignmentWrapper &x) { + [](llvm::raw_ostream &o, + const Fortran::evaluate::GenericAssignmentWrapper &x) { if (x.v) { x.v->AsFortran(o); } else { o << "(bad assignment)"; } }, - [](std::ostream &o, const Fortran::evaluate::ProcedureRef &x) { + [](llvm::raw_ostream &o, const Fortran::evaluate::ProcedureRef &x) { x.AsFortran(o << "CALL "); }, }; @@ -211,37 +213,37 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, parsing.Prescan(path, options); if (!parsing.messages().empty() && (driver.warningsAreErrors || parsing.messages().AnyFatalError())) { - std::cerr << driver.prefix << "could not scan " << path << '\n'; - parsing.messages().Emit(std::cerr, parsing.cooked()); + llvm::errs() << driver.prefix << "could not scan " << path << '\n'; + parsing.messages().Emit(llvm::errs(), parsing.cooked()); exitStatus = EXIT_FAILURE; return {}; } if (driver.dumpProvenance) { - parsing.DumpProvenance(std::cout); + parsing.DumpProvenance(llvm::outs()); return {}; } if (driver.dumpCookedChars) { - parsing.messages().Emit(std::cerr, parsing.cooked()); - parsing.DumpCookedChars(std::cout); + parsing.messages().Emit(llvm::errs(), parsing.cooked()); + parsing.DumpCookedChars(llvm::outs()); return {}; } - parsing.Parse(&std::cout); + parsing.Parse(llvm::outs()); if (options.instrumentedParse) { - parsing.DumpParsingLog(std::cout); + parsing.DumpParsingLog(llvm::outs()); return {}; } parsing.ClearLog(); - parsing.messages().Emit(std::cerr, parsing.cooked()); + parsing.messages().Emit(llvm::errs(), parsing.cooked()); if (!parsing.consumedWholeFile()) { - parsing.EmitMessage( - std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)"); + parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), + "parser FAIL (final position)"); exitStatus = EXIT_FAILURE; return {}; } if ((!parsing.messages().empty() && (driver.warningsAreErrors || parsing.messages().AnyFatalError())) || !parsing.parseTree()) { - std::cerr << driver.prefix << "could not parse " << path << '\n'; + llvm::errs() << driver.prefix << "could not parse " << path << '\n'; exitStatus = EXIT_FAILURE; return {}; } @@ -255,25 +257,25 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, Fortran::semantics::Semantics semantics{semanticsContext, parseTree, parsing.cooked(), driver.debugModuleWriter}; semantics.Perform(); - semantics.EmitMessages(std::cerr); + semantics.EmitMessages(llvm::errs()); if (driver.dumpSymbols) { - semantics.DumpSymbols(std::cout); + semantics.DumpSymbols(llvm::outs()); } if (semantics.AnyFatalError()) { - std::cerr << driver.prefix << "semantic errors in " << path << '\n'; + llvm::errs() << driver.prefix << "semantic errors in " << path << '\n'; exitStatus = EXIT_FAILURE; if (driver.dumpParseTree) { - Fortran::parser::DumpTree(std::cout, parseTree, &asFortran); + Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); } return {}; } if (driver.dumpUnparseWithSymbols) { Fortran::semantics::UnparseWithSymbols( - std::cout, parseTree, driver.encoding); + llvm::outs(), parseTree, driver.encoding); return {}; } if (driver.getSymbolsSources) { - semantics.DumpSymbolsSources(std::cout); + semantics.DumpSymbolsSources(llvm::outs()); return {}; } if (driver.getDefinition) { @@ -281,32 +283,32 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, driver.getDefinitionArgs.line, driver.getDefinitionArgs.startColumn, driver.getDefinitionArgs.endColumn)}) { - std::cerr << "String range: >" << cb->ToString() << "<\n"; + llvm::errs() << "String range: >" << cb->ToString() << "<\n"; if (auto symbol{semanticsContext.FindScope(*cb).FindSymbol(*cb)}) { - std::cerr << "Found symbol name: " << symbol->name().ToString() - << "\n"; + llvm::errs() << "Found symbol name: " << symbol->name().ToString() + << "\n"; if (auto sourceInfo{ parsing.cooked().GetSourcePositionRange(symbol->name())}) { - std::cout << symbol->name().ToString() << ": " - << sourceInfo->first.file.path() << ", " - << sourceInfo->first.line << ", " - << sourceInfo->first.column << "-" - << sourceInfo->second.column << "\n"; + llvm::outs() << symbol->name().ToString() << ": " + << sourceInfo->first.file.path() << ", " + << sourceInfo->first.line << ", " + << sourceInfo->first.column << "-" + << sourceInfo->second.column << "\n"; exitStatus = EXIT_SUCCESS; return {}; } } } - std::cerr << "Symbol not found.\n"; + llvm::errs() << "Symbol not found.\n"; exitStatus = EXIT_FAILURE; return {}; } } if (driver.dumpParseTree) { - Fortran::parser::DumpTree(std::cout, parseTree, &asFortran); + Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); } if (driver.dumpUnparse) { - Unparse(std::cout, parseTree, driver.encoding, true /*capitalize*/, + Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes), nullptr /* action before each statement */, &asFortran); @@ -317,7 +319,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, Fortran::lower::annotateControl(*ast); Fortran::lower::dumpPFT(llvm::outs(), *ast); } else { - std::cerr << "Pre FIR Tree is NULL.\n"; + llvm::errs() << "Pre FIR Tree is NULL.\n"; exitStatus = EXIT_FAILURE; } } @@ -331,8 +333,12 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90", static_cast(getpid())); { - std::ofstream tmpSource; - tmpSource.open(tmpSourcePath); + std::error_code EC; + llvm::raw_fd_ostream tmpSource(tmpSourcePath, EC, llvm::sys::fs::F_None); + if (EC) { + llvm::errs() << EC.message() << "\n"; + std::exit(EXIT_FAILURE); + } Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes), @@ -558,13 +564,13 @@ int main(int argc, char *const argv[]) { int arguments[3]; for (int i = 0; i < 3; i++) { if (args.empty()) { - std::cerr << "Must provide 3 arguments for -fget-definitions.\n"; + llvm::errs() << "Must provide 3 arguments for -fget-definitions.\n"; return EXIT_FAILURE; } arguments[i] = std::strtol(args.front().c_str(), &endptr, 10); if (*endptr != '\0') { - std::cerr << "Invalid argument to -fget-definitions: " << args.front() - << '\n'; + llvm::errs() << "Invalid argument to -fget-definitions: " + << args.front() << '\n'; return EXIT_FAILURE; } args.pop_front(); @@ -573,7 +579,7 @@ int main(int argc, char *const argv[]) { } else if (arg == "-fget-symbols-sources") { driver.getSymbolsSources = true; } else if (arg == "-help" || arg == "--help" || arg == "-?") { - std::cerr + llvm::errs() << "f18 options:\n" << " -Mfixed | -Mfree force the source form\n" << " -Mextend 132-column fixed form\n" @@ -608,7 +614,7 @@ int main(int argc, char *const argv[]) { << "Other options are passed through to the compiler.\n"; return exitStatus; } else if (arg == "-V") { - std::cerr << "\nf18 compiler (under development)\n"; + llvm::errs() << "\nf18 compiler (under development)\n"; return exitStatus; } else { driver.pgf90Args.push_back(arg); diff --git a/unittests/Decimal/CMakeLists.txt b/unittests/Decimal/CMakeLists.txt index 46a3d1fd44bf..780c92e74475 100644 --- a/unittests/Decimal/CMakeLists.txt +++ b/unittests/Decimal/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(quick-sanity-test target_link_libraries(quick-sanity-test FortranDecimal + LLVMSupport ) add_executable(thorough-test @@ -20,6 +21,7 @@ add_executable(thorough-test target_link_libraries(thorough-test FortranDecimal + LLVMSupport ) add_test(Sanity quick-sanity-test) diff --git a/unittests/Decimal/quick-sanity-test.cpp b/unittests/Decimal/quick-sanity-test.cpp index 47089499d4b5..476d2c64bafc 100644 --- a/unittests/Decimal/quick-sanity-test.cpp +++ b/unittests/Decimal/quick-sanity-test.cpp @@ -1,8 +1,8 @@ #include "flang/Decimal/decimal.h" +#include "llvm/Support/raw_ostream.h" #include #include #include -#include using namespace Fortran::decimal; @@ -14,11 +14,12 @@ union u { std::uint32_t u; }; -std::ostream &failed(float x) { +llvm::raw_ostream &failed(float x) { ++fails; union u u; u.x = x; - return std::cout << "FAIL: 0x" << std::hex << u.u << std::dec; + llvm::outs() << "FAIL: 0x"; + return llvm::outs().write_hex(u.u); } void testDirect(float x, const char *expect, int expectExpo, int flags = 0) { @@ -60,15 +61,13 @@ void testReadback(float x, int flags) { if (!(x == x)) { if (y == y || *p != '\0' || (rflags & Invalid)) { u.x = y; - failed(x) << " (NaN) " << flags << ": -> '" << result.str << "' -> 0x" - << std::hex << u.u << std::dec << " '" << p << "' " << rflags - << '\n'; + failed(x) << " (NaN) " << flags << ": -> '" << result.str << "' -> 0x"; + failed(x).write_hex(u.u) << " '" << p << "' " << rflags << '\n'; } } else if (x != y || *p != '\0' || (rflags & Invalid)) { u.x = y; - failed(x) << ' ' << flags << ": -> '" << result.str << "' -> 0x" - << std::hex << u.u << std::dec << " '" << p << "' " << rflags - << '\n'; + failed(x) << ' ' << flags << ": -> '" << result.str << "' -> 0x"; + failed(x).write_hex(u.u) << " '" << p << "' " << rflags << '\n'; } } } @@ -138,6 +137,6 @@ int main() { testReadback(u.x, Minimize); testReadback(-u.x, Minimize); } - std::cout << tests << " tests run, " << fails << " tests failed\n"; + llvm::outs() << tests << " tests run, " << fails << " tests failed\n"; return fails > 0; } diff --git a/unittests/Decimal/thorough-test.cpp b/unittests/Decimal/thorough-test.cpp index f10467d1300a..e7aeed01ec8e 100644 --- a/unittests/Decimal/thorough-test.cpp +++ b/unittests/Decimal/thorough-test.cpp @@ -1,8 +1,8 @@ #include "flang/Decimal/decimal.h" +#include "llvm/Support/raw_ostream.h" #include #include #include -#include static constexpr int incr{1}; // steps through all values static constexpr bool doNegative{true}; @@ -18,11 +18,12 @@ union u { std::uint32_t u; }; -std::ostream &failed(float x) { +llvm::raw_ostream &failed(float x) { ++fails; union u u; u.x = x; - return std::cout << "FAIL: 0x" << std::hex << u.u << std::dec; + llvm::outs() << "FAIL: 0x"; + return llvm::outs().write_hex(u.u); } void testReadback(float x, int flags) { @@ -30,9 +31,10 @@ void testReadback(float x, int flags) { union u u; u.x = x; if (!(tests & 0x3fffff)) { - std::cerr << "\n0x" << std::hex << u.u << std::dec << ' '; + llvm::errs() << "\n0x"; + llvm::errs().write_hex(u.u) << ' '; } else if (!(tests & 0xffff)) { - std::cerr << '.'; + llvm::errs() << '.'; } ++tests; auto result{ConvertFloatToDecimal(buffer, sizeof buffer, @@ -56,15 +58,13 @@ void testReadback(float x, int flags) { if (!(x == x)) { if (y == y || *p != '\0' || (rflags & Invalid)) { u.x = y; - failed(x) << " (NaN) " << flags << ": -> '" << result.str << "' -> 0x" - << std::hex << u.u << std::dec << " '" << p << "' " << rflags - << '\n'; + failed(x) << " (NaN) " << flags << ": -> '" << result.str << "' -> 0x"; + failed(x).write_hex(u.u) << " '" << p << "' " << rflags << '\n'; } } else if (x != y || *p != '\0' || (rflags & Invalid)) { u.x = y; - failed(x) << ' ' << flags << ": -> '" << result.str << "' -> 0x" - << std::hex << u.u << std::dec << " '" << p << "' " << rflags - << '\n'; + failed(x) << ' ' << flags << ": -> '" << result.str << "' -> 0x"; + failed(x).write_hex(u.u) << " '" << p << "' " << rflags << '\n'; } } } @@ -83,6 +83,6 @@ int main() { } } } - std::cout << '\n' << tests << " tests run, " << fails << " tests failed\n"; + llvm::outs() << '\n' << tests << " tests run, " << fails << " tests failed\n"; return fails > 0; } diff --git a/unittests/Evaluate/CMakeLists.txt b/unittests/Evaluate/CMakeLists.txt index d874fcb39dbc..fb195ae5730a 100644 --- a/unittests/Evaluate/CMakeLists.txt +++ b/unittests/Evaluate/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(leading-zero-bit-count-test target_link_libraries(leading-zero-bit-count-test FortranEvaluateTesting + LLVMSupport ) add_executable(bit-population-count-test @@ -25,6 +26,7 @@ add_executable(bit-population-count-test target_link_libraries(bit-population-count-test FortranEvaluateTesting + LLVMSupport ) add_executable(uint128-test @@ -33,6 +35,7 @@ add_executable(uint128-test target_link_libraries(uint128-test FortranEvaluateTesting + LLVMSupport ) # These routines live in lib/Common but we test them here. @@ -49,6 +52,7 @@ target_link_libraries(expression-test FortranEvaluate FortranSemantics FortranParser + LLVMSupport ) add_executable(integer-test @@ -59,6 +63,7 @@ target_link_libraries(integer-test FortranEvaluateTesting FortranEvaluate FortranSemantics + LLVMSupport ) add_executable(intrinsics-test @@ -72,6 +77,7 @@ target_link_libraries(intrinsics-test FortranSemantics FortranParser FortranRuntime + LLVMSupport ) add_executable(logical-test @@ -82,6 +88,7 @@ target_link_libraries(logical-test FortranEvaluateTesting FortranEvaluate FortranSemantics + LLVMSupport ) # GCC -fno-exceptions breaks the fenv.h interfaces needed to capture @@ -98,6 +105,7 @@ target_link_libraries(real-test FortranEvaluate FortranDecimal FortranSemantics + LLVMSupport ) add_executable(reshape-test @@ -109,6 +117,7 @@ target_link_libraries(reshape-test FortranSemantics FortranEvaluate FortranRuntime + LLVMSupport ) add_executable(ISO-Fortran-binding-test @@ -120,6 +129,7 @@ target_link_libraries(ISO-Fortran-binding-test FortranEvaluate FortranSemantics FortranRuntime + LLVMSupport ) add_executable(folding-test @@ -130,6 +140,7 @@ target_link_libraries(folding-test FortranEvaluateTesting FortranEvaluate FortranSemantics + LLVMSupport ) add_test(Expression expression-test) diff --git a/unittests/Evaluate/ISO-Fortran-binding.cpp b/unittests/Evaluate/ISO-Fortran-binding.cpp index 7b6bc458baf6..1fff9d4d1998 100644 --- a/unittests/Evaluate/ISO-Fortran-binding.cpp +++ b/unittests/Evaluate/ISO-Fortran-binding.cpp @@ -1,10 +1,8 @@ #include "testing.h" #include "../../include/flang/ISO_Fortran_binding.h" #include "../../runtime/descriptor.h" +#include "llvm/Support/raw_ostream.h" #include -#ifdef VERBOSE -#include -#endif using namespace Fortran::runtime; using namespace Fortran::ISO; @@ -71,13 +69,13 @@ static void AddNoiseToCdesc(CFI_cdesc_t *dv, CFI_rank_t rank) { static void DumpTestWorld(const void *bAddr, CFI_attribute_t attr, CFI_type_t ty, std::size_t eLen, CFI_rank_t rank, const CFI_index_t *eAddr) { - std::cout << " base_addr: " << std::hex - << reinterpret_cast(bAddr) - << " attribute: " << static_cast(attr) << std::dec - << " type: " << static_cast(ty) << " elem_len: " << eLen - << " rank: " << static_cast(rank) << " extent: " << std::hex - << reinterpret_cast(eAddr) << std::endl - << std::dec; + llvm::outs() << " base_addr: "; + llvm::outs().write_hex(reinterpret_cast(bAddr)) + << " attribute: " << static_cast(attr) + << " type: " << static_cast(ty) << " elem_len: " << eLen + << " rank: " << static_cast(rank) << " extent: "; + llvm::outs().write_hex(reinterpret_cast(eAddr)) << '\n'; + llvm::outs().flush(); } #endif diff --git a/unittests/Evaluate/fp-testing.cpp b/unittests/Evaluate/fp-testing.cpp index a1893a8df894..fe235eca7924 100644 --- a/unittests/Evaluate/fp-testing.cpp +++ b/unittests/Evaluate/fp-testing.cpp @@ -1,4 +1,5 @@ #include "fp-testing.h" +#include "llvm/Support/Errno.h" #include #include #include @@ -15,11 +16,13 @@ ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( ) { errno = 0; if (feholdexcept(&originalFenv_) != 0) { - std::fprintf(stderr, "feholdexcept() failed: %s\n", std::strerror(errno)); + std::fprintf(stderr, "feholdexcept() failed: %s\n", + llvm::sys::StrError(errno).c_str()); std::abort(); } if (fegetenv(¤tFenv_) != 0) { - std::fprintf(stderr, "fegetenv() failed: %s\n", std::strerror(errno)); + std::fprintf( + stderr, "fegetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } #if __x86_64__ @@ -38,7 +41,8 @@ ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( #endif errno = 0; if (fesetenv(¤tFenv_) != 0) { - std::fprintf(stderr, "fesetenv() failed: %s\n", std::strerror(errno)); + std::fprintf( + stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } } @@ -46,7 +50,8 @@ ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( ScopedHostFloatingPointEnvironment::~ScopedHostFloatingPointEnvironment() { errno = 0; if (fesetenv(&originalFenv_) != 0) { - std::fprintf(stderr, "fesetenv() failed: %s\n", std::strerror(errno)); + std::fprintf( + stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } } diff --git a/unittests/Evaluate/intrinsics.cpp b/unittests/Evaluate/intrinsics.cpp index 7a4ca4753b4f..e2bf1cf18cb0 100644 --- a/unittests/Evaluate/intrinsics.cpp +++ b/unittests/Evaluate/intrinsics.cpp @@ -4,8 +4,8 @@ #include "flang/Evaluate/expression.h" #include "flang/Evaluate/tools.h" #include "flang/Parser/provenance.h" +#include "llvm/Support/raw_ostream.h" #include -#include #include #include @@ -31,7 +31,7 @@ class CookedStrings { parser::ContextualMessages Messages(parser::Messages &buffer) { return parser::ContextualMessages{cooked_.data(), &buffer}; } - void Emit(std::ostream &o, const parser::Messages &messages) { + void Emit(llvm::raw_ostream &o, const parser::Messages &messages) { messages.Emit(o, cooked_); } @@ -88,17 +88,18 @@ struct TestCall { int rank = 0, bool isElemental = false) { Marshal(); parser::CharBlock fName{strings(name)}; - std::cout << "function: " << fName.ToString(); + llvm::outs() << "function: " << fName.ToString(); char sep{'('}; for (const auto &a : args) { - std::cout << sep; + llvm::outs() << sep; sep = ','; - a->AsFortran(std::cout); + a->AsFortran(llvm::outs()); } if (sep == '(') { - std::cout << '('; + llvm::outs() << '('; } - std::cout << ')' << std::endl; + llvm::outs() << ')' << '\n'; + llvm::outs().flush(); CallCharacteristics call{fName.ToString()}; auto messages{strings.Messages(buffer)}; FoldingContext context{messages, defaults, table}; @@ -126,7 +127,7 @@ struct TestCall { TEST((messages.messages() && messages.messages()->AnyFatalError()) || name == "bad"); } - strings.Emit(std::cout, buffer); + strings.Emit(llvm::outs(), buffer); } const common::IntrinsicTypeDefaultKinds &defaults; @@ -143,7 +144,7 @@ void TestIntrinsics() { MATCH(4, defaults.GetDefaultKind(TypeCategory::Integer)); MATCH(4, defaults.GetDefaultKind(TypeCategory::Real)); IntrinsicProcTable table{IntrinsicProcTable::Configure(defaults)}; - table.Dump(std::cout); + table.Dump(llvm::outs()); using Int1 = Type; using Int4 = Type; diff --git a/unittests/Evaluate/real.cpp b/unittests/Evaluate/real.cpp index 732a2de1bec2..90f9dcf408c2 100644 --- a/unittests/Evaluate/real.cpp +++ b/unittests/Evaluate/real.cpp @@ -1,10 +1,10 @@ #include "fp-testing.h" #include "testing.h" #include "flang/Evaluate/type.h" +#include "llvm/Support/raw_ostream.h" #include #include #include -#include #include using namespace Fortran::evaluate; @@ -149,7 +149,8 @@ template void basicTests(int rm, Rounding rounding) { TEST(ivf.flags.empty())(ldesc); MATCH(x, ivf.value.ToUInt64())(ldesc); if (rounding.mode == RoundingMode::TiesToEven) { // to match stold() - std::stringstream ss; + std::string buf; + llvm::raw_string_ostream ss{buf}; vr.value.AsFortran(ss, kind, false /*exact*/); std::string decimal{ss.str()}; const char *p{decimal.data()}; @@ -398,7 +399,9 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) { ("%d IsInfinite(0x%jx)", pass, static_cast(rj)); static constexpr int kind{REAL::bits / 8}; - std::stringstream ss, css; + std::string ssBuf, cssBuf; + llvm::raw_string_ostream ss{ssBuf}; + llvm::raw_string_ostream css{cssBuf}; x.AsFortran(ss, kind, false /*exact*/); std::string s{ss.str()}; if (IsNaN(rj)) { diff --git a/unittests/Evaluate/testing.cpp b/unittests/Evaluate/testing.cpp index 75380e51c592..9be31540ec7b 100644 --- a/unittests/Evaluate/testing.cpp +++ b/unittests/Evaluate/testing.cpp @@ -1,8 +1,8 @@ #include "testing.h" +#include "llvm/Support/raw_ostream.h" #include #include #include -#include namespace testing { @@ -103,22 +103,22 @@ FailureDetailPrinter Compare(const char *file, int line, const char *xs, int Complete() { if (failures == 0) { if (passes == 1) { - std::cout << "single test PASSES\n"; + llvm::outs() << "single test PASSES\n"; } else { - std::cout << "all " << std::dec << passes << " tests PASS\n"; + llvm::outs() << "all " << passes << " tests PASS\n"; } passes = 0; return EXIT_SUCCESS; } else { if (passes == 1) { - std::cerr << "1 test passes, "; + llvm::errs() << "1 test passes, "; } else { - std::cerr << std::dec << passes << " tests pass, "; + llvm::errs() << passes << " tests pass, "; } if (failures == 1) { - std::cerr << "1 test FAILS\n"; + llvm::errs() << "1 test FAILS\n"; } else { - std::cerr << std::dec << failures << " tests FAIL\n"; + llvm::errs() << failures << " tests FAIL\n"; } passes = failures = 0; return EXIT_FAILURE; diff --git a/unittests/Evaluate/uint128.cpp b/unittests/Evaluate/uint128.cpp index 07efc31c5a39..f2ddcbf557eb 100644 --- a/unittests/Evaluate/uint128.cpp +++ b/unittests/Evaluate/uint128.cpp @@ -1,8 +1,8 @@ #define AVOID_NATIVE_UINT128_T 1 #include "flang/Common/uint128.h" #include "testing.h" +#include "llvm/Support/raw_ostream.h" #include -#include #if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__ #define HAS_NATIVE_UINT128_T 1 @@ -123,10 +123,10 @@ int main() { } } #if HAS_NATIVE_UINT128_T - std::cout << "Environment has native __uint128_t\n"; + llvm::outs() << "Environment has native __uint128_t\n"; TestVsNative(); #else - std::cout << "Environment lacks native __uint128_t\n"; + llvm::outs() << "Environment lacks native __uint128_t\n"; #endif return testing::Complete(); } diff --git a/unittests/Runtime/CMakeLists.txt b/unittests/Runtime/CMakeLists.txt index 905e18e0cea2..3f73b79132f9 100644 --- a/unittests/Runtime/CMakeLists.txt +++ b/unittests/Runtime/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(format-test target_link_libraries(format-test FortranRuntime RuntimeTesting + LLVMSupport ) add_test(Format format-test) @@ -32,6 +33,7 @@ add_executable(hello-world target_link_libraries(hello-world FortranRuntime RuntimeTesting + LLVMSupport ) add_test(HelloWorld hello-world) @@ -42,6 +44,7 @@ add_executable(external-hello-world target_link_libraries(external-hello-world FortranRuntime + LLVMSupport ) add_executable(list-input-test @@ -51,6 +54,7 @@ add_executable(list-input-test target_link_libraries(list-input-test FortranRuntime RuntimeTesting + LLVMSupport ) add_test(ListInput list-input-test) diff --git a/unittests/Runtime/format.cpp b/unittests/Runtime/format.cpp index b00f89c72c1f..c855523b427e 100644 --- a/unittests/Runtime/format.cpp +++ b/unittests/Runtime/format.cpp @@ -3,9 +3,9 @@ #include "testing.h" #include "../runtime/format-implementation.h" #include "../runtime/io-error.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include #include #include @@ -93,13 +93,13 @@ void TestFormatContext::Check(Results &expect) { if (expect != results) { Fail() << "expected:"; for (const std::string &s : expect) { - std::cerr << ' ' << s; + llvm::errs() << ' ' << s; } - std::cerr << "\ngot:"; + llvm::errs() << "\ngot:"; for (const std::string &s : results) { - std::cerr << ' ' << s; + llvm::errs() << ' ' << s; } - std::cerr << '\n'; + llvm::errs() << '\n'; } expect.clear(); results.clear(); diff --git a/unittests/Runtime/hello.cpp b/unittests/Runtime/hello.cpp index 88628cecf1ed..cc2d1205a751 100644 --- a/unittests/Runtime/hello.cpp +++ b/unittests/Runtime/hello.cpp @@ -3,8 +3,8 @@ #include "testing.h" #include "../../runtime/descriptor.h" #include "../../runtime/io-api.h" +#include "llvm/Support/raw_ostream.h" #include -#include using namespace Fortran::runtime; using namespace Fortran::runtime::io; @@ -110,8 +110,8 @@ static void realInTest( Fail() << '\'' << format << "' failed reading '" << data << "', status " << static_cast(status) << " iomsg '" << iomsg << "'\n"; } else if (u.raw != want) { - Fail() << '\'' << format << "' failed reading '" << data << "', want 0x" - << std::hex << want << ", got 0x" << u.raw << std::dec << '\n'; + Fail() << '\'' << format << "' failed reading '" << data << "', want 0x"; + Fail().write_hex(want) << ", got 0x" << u.raw << '\n'; } } diff --git a/unittests/Runtime/list-input.cpp b/unittests/Runtime/list-input.cpp index cb9021e59509..9f6377656f91 100644 --- a/unittests/Runtime/list-input.cpp +++ b/unittests/Runtime/list-input.cpp @@ -4,9 +4,9 @@ #include "../../runtime/descriptor.h" #include "../../runtime/io-api.h" #include "../../runtime/io-error.h" +#include "llvm/Support/raw_ostream.h" #include #include -#include using namespace Fortran::runtime; using namespace Fortran::runtime::io; diff --git a/unittests/Runtime/testing.cpp b/unittests/Runtime/testing.cpp index 50ee686884cf..8a31f23e9ef5 100644 --- a/unittests/Runtime/testing.cpp +++ b/unittests/Runtime/testing.cpp @@ -1,10 +1,10 @@ #include "testing.h" #include "../../runtime/terminator.h" +#include "llvm/Support/raw_ostream.h" #include #include #include #include -#include #include static int failures{0}; @@ -21,16 +21,16 @@ void StartTests() { Fortran::runtime::Terminator::RegisterCrashHandler(CatchCrash); } -std::ostream &Fail() { +llvm::raw_ostream &Fail() { ++failures; - return std::cerr; + return llvm::errs(); } int EndTests() { if (failures == 0) { - std::cout << "PASS\n"; + llvm::outs() << "PASS\n"; } else { - std::cout << "FAIL " << failures << " tests\n"; + llvm::outs() << "FAIL " << failures << " tests\n"; } return failures != 0; } diff --git a/unittests/Runtime/testing.h b/unittests/Runtime/testing.h index 9571a34825dc..4bc2b9148a0a 100644 --- a/unittests/Runtime/testing.h +++ b/unittests/Runtime/testing.h @@ -4,8 +4,12 @@ #include #include +namespace llvm { + class raw_ostream; +} + void StartTests(); -std::ostream &Fail(); +llvm::raw_ostream &Fail(); int EndTests(); void SetCharacter(char *, std::size_t, const char *); From 02531d788e7550a2455633f87d22e3f30de7a3a7 Mon Sep 17 00:00:00 2001 From: David Truby Date: Wed, 18 Mar 2020 16:02:53 +0000 Subject: [PATCH 097/345] Remove non-alignment based divergences from LLVM formatting. This only changs the clang-format file and adds documentation referring to the new process for formatting code. A future commit will perform a reformatting according to these new formatting settings. --- .clang-format | 6 ------ documentation/C++style.md | 6 +++--- documentation/PullRequestChecklist.md | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.clang-format b/.clang-format index 21fb1ae51ac5..c1dfe06fdf57 100644 --- a/.clang-format +++ b/.clang-format @@ -3,16 +3,10 @@ BasedOnStyle: LLVM AlignAfterOpenBracket: DontAlign AlignEscapedNewlines: DontAlign -AllowShortCaseLabelsOnASingleLine: true -AllowShortIfStatementsOnASingleLine: true AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignOperands: false AlignTrailingComments: false -ConstructorInitializerIndentWidth: 2 -SpaceAfterTemplateKeyword: false -SpacesBeforeTrailingComments: 2 -FixNamespaceComments: false IncludeCategories: - Regex: '^<' Priority: 4 diff --git a/documentation/C++style.md b/documentation/C++style.md index f8e58968f330..ca532463ae83 100644 --- a/documentation/C++style.md +++ b/documentation/C++style.md @@ -88,9 +88,9 @@ well as you do and avoid distracting her by calling out usage of new features in comments. ### Layout -Always run `clang-format` before committing code. Other developers should -be able to run `git pull`, then `clang-format`, and see only their own -changes. Use `clang-format` from llvm 7. +Always run `clang-format` on your changes before committing code. LLVM +has a `git-clang-format` script to facilitate running clang-format only +on the lines that have changed. Here's what you can expect to see `clang-format` do: 1. Indent with two spaces. diff --git a/documentation/PullRequestChecklist.md b/documentation/PullRequestChecklist.md index 1097bd1f9b42..9a43fa9b46e0 100644 --- a/documentation/PullRequestChecklist.md +++ b/documentation/PullRequestChecklist.md @@ -31,7 +31,7 @@ can also be used when reviewing pull requests. ## Follow the style guide The following items are taken from the [C++ style guide](C++style.md). But even though I've read the style guide, they regularly trip me up. -* Run clang-format version 7 on all .cpp and .h files. +* Run clang-format using the git-clang-format script from LLVM HEAD. * Make sure that all source lines have 80 or fewer characters. Note that clang-format will do this for most code. But you may need to break up long strings. From d34df8435127d847867e2c0bb157def9f20f4202 Mon Sep 17 00:00:00 2001 From: David Truby Date: Thu, 27 Feb 2020 13:42:56 +0000 Subject: [PATCH 098/345] Replace manual mmap with llvm::MemoryBuffer The previous code had handling for cases when too many file descriptors may be opened; this is not necessary with MemoryBuffer as the file descriptors are closed after the mapping occurs. MemoryBuffer also internally handles the case where a file is small and therefore an mmap is bad for performance; such files are simply copied to memory after being opened. Many places elsewhere in the code assume that the buffer is not empty, and the old file opening code handles this by replacing an empty file with a buffer containing a single newline. That behavior is now kept in the new MemoryBuffer based code. --- include/flang/Parser/source.h | 19 ++- lib/Parser/prescan.cpp | 2 +- lib/Parser/provenance.cpp | 2 +- lib/Parser/source.cpp | 214 ++++++++-------------------------- lib/Semantics/mod-file.cpp | 6 +- test/Semantics/empty.f90 | 4 + 6 files changed, 66 insertions(+), 181 deletions(-) create mode 100644 test/Semantics/empty.f90 diff --git a/include/flang/Parser/source.h b/include/flang/Parser/source.h index cc7dc9219a88..4c5be0f62c0c 100644 --- a/include/flang/Parser/source.h +++ b/include/flang/Parser/source.h @@ -19,6 +19,7 @@ #include #include #include +#include "llvm/Support/MemoryBuffer.h" namespace llvm { class raw_ostream; @@ -42,8 +43,8 @@ class SourceFile { explicit SourceFile(Encoding e) : encoding_{e} {} ~SourceFile(); std::string path() const { return path_; } - const char *content() const { return content_; } - std::size_t bytes() const { return bytes_; } + llvm::ArrayRef content() const { return buf_->getBuffer().slice(bom_end_, buf_end_ - bom_end_); } + std::size_t bytes() const { return content().size(); } std::size_t lines() const { return lineStart_.size(); } Encoding encoding() const { return encoding_; } @@ -56,20 +57,16 @@ class SourceFile { } private: - bool ReadFile(std::string errorPath, llvm::raw_ostream &error); + void ReadFile(); void IdentifyPayload(); void RecordLineStarts(); std::string path_; - int fileDescriptor_{-1}; - bool isMemoryMapped_{false}; - const char *address_{nullptr}; // raw content - std::size_t size_{0}; - const char *content_{nullptr}; // usable content - std::size_t bytes_{0}; + std::unique_ptr buf_; std::vector lineStart_; - std::string normalized_; - Encoding encoding_{Encoding::UTF_8}; + std::size_t bom_end_ {0}; + std::size_t buf_end_; + Encoding encoding_; }; } #endif // FORTRAN_PARSER_SOURCE_H_ diff --git a/lib/Parser/prescan.cpp b/lib/Parser/prescan.cpp index 67d5cdfbfb1d..1648a0297603 100644 --- a/lib/Parser/prescan.cpp +++ b/lib/Parser/prescan.cpp @@ -63,7 +63,7 @@ void Prescanner::Prescan(ProvenanceRange range) { std::size_t offset{0}; const SourceFile *source{allSources.GetSourceFile(startProvenance_, &offset)}; CHECK(source); - start_ = source->content() + offset; + start_ = source->content().data() + offset; limit_ = start_ + range.size(); nextLine_ = start_; const bool beganInFixedForm{inFixedForm_}; diff --git a/lib/Parser/provenance.cpp b/lib/Parser/provenance.cpp index fb43496b53ef..1a2a77cd1daf 100644 --- a/lib/Parser/provenance.cpp +++ b/lib/Parser/provenance.cpp @@ -228,7 +228,7 @@ void AllSources::EmitMessage(llvm::raw_ostream &o, o << ':' << pos.line << ':' << pos.column; o << ": " << message << '\n'; if (echoSourceLine) { - const char *text{inc.source.content() + + const char *text{inc.source.content().data() + inc.source.GetLineStartOffset(pos.line)}; o << " "; for (const char *p{text}; *p != '\n'; ++p) { diff --git a/lib/Parser/source.cpp b/lib/Parser/source.cpp index 4f8a08aa5271..6b1a9df3b731 100644 --- a/lib/Parser/source.cpp +++ b/lib/Parser/source.cpp @@ -10,64 +10,42 @@ #include "flang/Common/idioms.h" #include "flang/Parser/char-buffer.h" #include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" #include -#include -#include -#include #include -#include -#include -#include -#include #include -// TODO: Port to Windows &c. - namespace Fortran::parser { -static constexpr bool useMMap{true}; -static constexpr int minMapFileBytes{1}; // i.e., no minimum requirement -static constexpr int maxMapOpenFileDescriptors{100}; -static int openFileDescriptors{0}; - SourceFile::~SourceFile() { Close(); } -static std::vector FindLineStarts( - const char *source, std::size_t bytes) { +static std::vector FindLineStarts(llvm::StringRef source) { std::vector result; - if (bytes > 0) { - CHECK(source[bytes - 1] == '\n' && "missing ultimate newline"); + if (source.size() > 0) { + CHECK(source.back() == '\n' && "missing ultimate newline"); std::size_t at{0}; do { result.push_back(at); - const void *vp{static_cast(&source[at])}; - const void *vnl{std::memchr(vp, '\n', bytes - at)}; - const char *nl{static_cast(vnl)}; - at = nl + 1 - source; - } while (at < bytes); + at = source.find('\n', at) + 1; + } while (at < source.size()); result.shrink_to_fit(); } return result; } void SourceFile::RecordLineStarts() { - lineStart_ = FindLineStarts(content_, bytes_); + lineStart_ = FindLineStarts({content().data(), bytes()}); } // Check for a Unicode byte order mark (BOM). // Module files all have one; so can source files. void SourceFile::IdentifyPayload() { - content_ = address_; - bytes_ = size_; - if (content_) { - static constexpr int BOMBytes{3}; - static const char UTF8_BOM[]{"\xef\xbb\xbf"}; - if (bytes_ >= BOMBytes && std::memcmp(content_, UTF8_BOM, BOMBytes) == 0) { - content_ += BOMBytes; - bytes_ -= BOMBytes; - encoding_ = Encoding::UTF_8; - } + llvm::StringRef content{buf_->getBufferStart(), buf_->getBufferSize()}; + constexpr llvm::StringLiteral UTF8_BOM{"\xef\xbb\xbf"}; + if (content.startswith(UTF8_BOM)) { + bom_end_ = UTF8_BOM.size(); + encoding_ = Encoding::UTF_8; } } @@ -83,17 +61,20 @@ std::string LocateSourceFile( } for (const std::string &dir : searchPath) { std::string path{dir + '/' + name}; - struct stat statbuf; - if (stat(path.c_str(), &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) { + bool isDir{false}; + auto er = llvm::sys::fs::is_directory(path, isDir); + if (!er && !isDir) { return path; } } return name; } -static std::size_t RemoveCarriageReturns(char *buffer, std::size_t bytes) { +std::size_t RemoveCarriageReturns(llvm::MutableArrayRef buf) { std::size_t wrote{0}; - char *p{buffer}; + char *buffer{buf.data()}; + char *p{buf.data()}; + std::size_t bytes = buf.size(); while (bytes > 0) { void *vp{static_cast(p)}; void *crvp{std::memchr(vp, '\r', bytes)}; @@ -115,154 +96,57 @@ static std::size_t RemoveCarriageReturns(char *buffer, std::size_t bytes) { bool SourceFile::Open(std::string path, llvm::raw_ostream &error) { Close(); path_ = path; - std::string errorPath{"'"s + path + "'"}; - errno = 0; - fileDescriptor_ = open(path.c_str(), O_RDONLY); - if (fileDescriptor_ < 0) { - error << "Could not open " << errorPath << ": " - << llvm::sys::StrError(errno); + std::string errorPath{"'"s + path_ + "'"}; + auto bufOr{llvm::WritableMemoryBuffer::getFile(path)}; + if (!bufOr) { + auto err = bufOr.getError(); + error << "Could not open " << errorPath << ": " << err.message(); return false; } - ++openFileDescriptors; - return ReadFile(errorPath, error); + buf_ = std::move(bufOr.get()); + ReadFile(); + return true; } bool SourceFile::ReadStandardInput(llvm::raw_ostream &error) { Close(); path_ = "standard input"; - fileDescriptor_ = 0; - return ReadFile(path_, error); -} -bool SourceFile::ReadFile(std::string errorPath, llvm::raw_ostream &error) { - struct stat statbuf; - if (fstat(fileDescriptor_, &statbuf) != 0) { - error << "fstat failed on " << errorPath << ": " - << llvm::sys::StrError(errno); - Close(); + auto buf_or = llvm::MemoryBuffer::getSTDIN(); + if (!buf_or) { + auto err = buf_or.getError(); + error << err.message(); return false; } - if (S_ISDIR(statbuf.st_mode)) { - error << errorPath << " is a directory"; - Close(); - return false; - } - - // Try to map a large source file into the process' address space. - // Don't bother with small ones. This also helps keep the number - // of open file descriptors from getting out of hand. - if (useMMap && S_ISREG(statbuf.st_mode)) { - size_ = static_cast(statbuf.st_size); - if (size_ >= minMapFileBytes && - openFileDescriptors <= maxMapOpenFileDescriptors) { - void *vp = mmap(0, size_, PROT_READ, MAP_SHARED, fileDescriptor_, 0); - if (vp != MAP_FAILED) { - address_ = static_cast(const_cast(vp)); - IdentifyPayload(); - if (bytes_ > 0 && content_[bytes_ - 1] == '\n' && - std::memchr(static_cast(content_), '\r', bytes_) == - nullptr) { - isMemoryMapped_ = true; - RecordLineStarts(); - return true; - } - // The file needs to have its line endings normalized to simple - // newlines. Remap it for a private rewrite in place. - vp = mmap( - vp, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileDescriptor_, 0); - if (vp != MAP_FAILED) { - address_ = static_cast(const_cast(vp)); - IdentifyPayload(); - auto mutableContent{const_cast(content_)}; - bytes_ = RemoveCarriageReturns(mutableContent, bytes_); - if (bytes_ > 0) { - if (mutableContent[bytes_ - 1] == '\n' || - (bytes_ & 0xfff) != 0 /* don't cross into next page */) { - if (mutableContent[bytes_ - 1] != '\n') { - // Append a final newline. - mutableContent[bytes_++] = '\n'; - } - bool isNowReadOnly{mprotect(vp, bytes_, PROT_READ) == 0}; - CHECK(isNowReadOnly); - content_ = mutableContent; - isMemoryMapped_ = true; - RecordLineStarts(); - return true; - } - } - } - munmap(vp, size_); - address_ = content_ = nullptr; - size_ = bytes_ = 0; - } - } - } + auto inbuf = std::move(buf_or.get()); + buf_ = + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(inbuf->getBufferSize()); + llvm::copy(inbuf->getBuffer(), buf_->getBufferStart()); + ReadFile(); + return true; +} - // Read it into an expandable buffer, then marshal its content into a single - // contiguous block. - CharBuffer buffer; - while (true) { - std::size_t count; - char *to{buffer.FreeSpace(count)}; - ssize_t got{read(fileDescriptor_, to, count)}; - if (got < 0) { - error << "could not read " << errorPath << ": " - << llvm::sys::StrError(errno); - Close(); - return false; - } - if (got == 0) { - break; - } - buffer.Claim(got); - } - if (fileDescriptor_ > 0) { - close(fileDescriptor_); - --openFileDescriptors; +void SourceFile::ReadFile() { + if (buf_->getBuffer().size() == 0) { + Close(); + buf_ = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(1); + buf_->getBuffer()[0] = '\n'; } - fileDescriptor_ = -1; - normalized_ = buffer.MarshalNormalized(); - address_ = normalized_.c_str(); - size_ = normalized_.size(); + buf_end_ = RemoveCarriageReturns(buf_->getBuffer()); IdentifyPayload(); RecordLineStarts(); - return true; } void SourceFile::Close() { - if (useMMap && isMemoryMapped_) { - munmap(reinterpret_cast(const_cast(address_)), size_); - isMemoryMapped_ = false; - } else if (!normalized_.empty()) { - normalized_.clear(); - } else if (address_) { - delete[] address_; - } - address_ = content_ = nullptr; - size_ = bytes_ = 0; - if (fileDescriptor_ > 0) { - close(fileDescriptor_); - --openFileDescriptors; - } - fileDescriptor_ = -1; path_.clear(); + buf_.reset(); } SourcePosition SourceFile::FindOffsetLineAndColumn(std::size_t at) const { - CHECK(at < bytes_); - if (lineStart_.empty()) { - return {*this, 1, static_cast(at + 1)}; - } - std::size_t low{0}, count{lineStart_.size()}; - while (count > 1) { - std::size_t mid{low + (count >> 1)}; - if (lineStart_[mid] > at) { - count = mid - low; - } else { - count -= mid - low; - low = mid; - } - } + CHECK(at < bytes()); + + auto it = llvm::upper_bound(lineStart_, at); + auto low = std::distance(lineStart_.begin(), it - 1); return {*this, static_cast(low + 1), static_cast(at - lineStart_[low] + 1)}; } diff --git a/lib/Semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp index bbf62f9c372f..5c457321cc68 100644 --- a/lib/Semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -728,8 +728,8 @@ static std::string CheckSum(const std::string_view &contents) { return result; } -static bool VerifyHeader(const char *content, std::size_t len) { - std::string_view sv{content, len}; +static bool VerifyHeader(llvm::ArrayRef content) { + std::string_view sv{content.data(), content.size()}; if (sv.substr(0, ModHeader::magicLen) != ModHeader::magic) { return false; } @@ -767,7 +767,7 @@ Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { return nullptr; } CHECK(sourceFile); - if (!VerifyHeader(sourceFile->content(), sourceFile->bytes())) { + if (!VerifyHeader(sourceFile->content())) { Say(name, ancestorName, "File has invalid checksum: %s"_en_US, sourceFile->path()); return nullptr; diff --git a/test/Semantics/empty.f90 b/test/Semantics/empty.f90 new file mode 100644 index 000000000000..e47c2e65342c --- /dev/null +++ b/test/Semantics/empty.f90 @@ -0,0 +1,4 @@ +! RUN: %f18 -fparse-only %s +! RUN: rm -rf %t && mkdir %t +! RUN: touch %t/empty.f90 +! RUN: %f18 -fparse-only %t/empty.f90 From 3f30e8a61e605b9ca6a67791469053286ae563b2 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld Date: Thu, 19 Mar 2020 20:07:01 -0700 Subject: [PATCH 099/345] Changes to enforce constraints C727 to C730 and most constraints related to attributes The full list of constraints is C727, C728, C729, C730, C743, C755, C759, C778, and C1543. I added a function to tools.cpp to check to see if a symbol name is the name of an intrinsic type. The biggest change was to resolve-names.cpp to check to see if attributes were either duplicated or in conflict with each other. I changed all locations where attributes were set to check for duplicates or conflicts. I also added tests for all checks and annotated the tests and code with the numbers of the constraints being tested/checked. --- include/flang/Semantics/attr.h | 8 +- include/flang/Semantics/tools.h | 1 + lib/Semantics/check-declarations.cpp | 4 + lib/Semantics/expression.cpp | 6 +- lib/Semantics/resolve-names.cpp | 115 ++++++++++++++++++++++----- lib/Semantics/tools.cpp | 16 ++++ test/Semantics/kinds02.f90 | 14 ++++ test/Semantics/resolve78.f90 | 32 ++++++++ test/Semantics/resolve79.f90 | 54 +++++++++++++ test/Semantics/resolve80.f90 | 61 ++++++++++++++ test/Semantics/resolve81.f90 | 64 +++++++++++++++ test/Semantics/resolve82.f90 | 47 +++++++++++ test/Semantics/resolve83.f90 | 57 +++++++++++++ test/Semantics/resolve84.f90 | 26 ++++++ test/Semantics/resolve85.f90 | 37 +++++++++ 15 files changed, 513 insertions(+), 29 deletions(-) create mode 100644 test/Semantics/resolve78.f90 create mode 100644 test/Semantics/resolve79.f90 create mode 100644 test/Semantics/resolve80.f90 create mode 100644 test/Semantics/resolve81.f90 create mode 100644 test/Semantics/resolve82.f90 create mode 100644 test/Semantics/resolve83.f90 create mode 100644 test/Semantics/resolve84.f90 create mode 100644 test/Semantics/resolve85.f90 diff --git a/include/flang/Semantics/attr.h b/include/flang/Semantics/attr.h index 9aa828da1f80..b8a8fec69aff 100644 --- a/include/flang/Semantics/attr.h +++ b/include/flang/Semantics/attr.h @@ -22,10 +22,10 @@ namespace Fortran::semantics { // All available attributes. ENUM_CLASS(Attr, ABSTRACT, ALLOCATABLE, ASYNCHRONOUS, BIND_C, CONTIGUOUS, - DEFERRED, ELEMENTAL, EXTERNAL, IMPURE, INTENT_IN, INTENT_INOUT, INTENT_OUT, - INTRINSIC, MODULE, NON_OVERRIDABLE, NON_RECURSIVE, NOPASS, OPTIONAL, - PARAMETER, PASS, POINTER, PRIVATE, PROTECTED, PUBLIC, PURE, RECURSIVE, SAVE, - TARGET, VALUE, VOLATILE) + DEFERRED, ELEMENTAL, EXTENDS, EXTERNAL, IMPURE, INTENT_IN, INTENT_INOUT, + INTENT_OUT, INTRINSIC, MODULE, NON_OVERRIDABLE, NON_RECURSIVE, NOPASS, + OPTIONAL, PARAMETER, PASS, POINTER, PRIVATE, PROTECTED, PUBLIC, PURE, + RECURSIVE, SAVE, TARGET, VALUE, VOLATILE) // Set of attributes class Attrs : public common::EnumSet { diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index e2179075e087..43ff6286579e 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -107,6 +107,7 @@ bool IsOrContainsEventOrLockComponent(const Symbol &); bool IsSaved(const Symbol &); bool CanBeTypeBoundProc(const Symbol *); bool IsInitialized(const Symbol &); +bool HasIntrinsicTypeName(const Symbol &); // Return an ultimate component of type that matches predicate, or nullptr. const Symbol *FindUltimateComponent(const DerivedTypeSpec &type, diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index c1cd33c75f33..ffd735e4b980 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -641,6 +641,10 @@ void CheckHelper::CheckDerivedType( } } } + if (HasIntrinsicTypeName(symbol)) { // C729 + messages_.Say("A derived type name cannot be the name of an intrinsic" + " type"_err_en_US); + } } void CheckHelper::CheckGeneric( diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index fa8f96e4c79a..182b90bdbd1f 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -626,7 +626,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) { TypeKindVisitor{ kind, std::move(value)})}; if (!result) { - Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); + Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728 } return result; } @@ -2494,7 +2494,7 @@ DynamicType ExpressionAnalyzer::GetDefaultKindOfType( bool ExpressionAnalyzer::CheckIntrinsicKind( TypeCategory category, std::int64_t kind) { - if (IsValidKindOfIntrinsicType(category, kind)) { // C712, C714, C715 + if (IsValidKindOfIntrinsicType(category, kind)) { // C712, C714, C715, C727 return true; } else { Say("%s(KIND=%jd) is not a supported type"_err_en_US, @@ -2543,7 +2543,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, const MaybeExpr &result, TypeCategory category, bool defaultKind) { if (result) { if (auto type{result->GetType()}) { - if (type->category() != category) { // C885 + if (type->category() != category) { // C885 Say(at, "Must have %s type, but is %s"_err_en_US, ToUpperCase(EnumToString(category)), ToUpperCase(type->AsFortran())); diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index c696d5e4fe82..f41a6fb72a33 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -242,10 +242,12 @@ class AttrsVisitor : public virtual BaseVisitor { bool Pre(const parser::IntentSpec &); bool Pre(const parser::Pass &); + bool CheckAndSet(Attr); + // Simple case: encountering CLASSNAME causes ATTRNAME to be set. #define HANDLE_ATTR_CLASS(CLASSNAME, ATTRNAME) \ bool Pre(const parser::CLASSNAME &) { \ - attrs_->set(Attr::ATTRNAME); \ + CheckAndSet(Attr::ATTRNAME); \ return false; \ } HANDLE_ATTR_CLASS(PrefixSpec::Elemental, ELEMENTAL) @@ -294,6 +296,10 @@ class AttrsVisitor : public virtual BaseVisitor { } private: + bool IsDuplicateAttr(Attr); + bool HaveAttrConflict(Attr, Attr, Attr); + bool IsConflictingAttr(Attr); + MaybeExpr bindName_; // from BIND(C, NAME="...") std::optional passName_; // from PASS(...) }; @@ -607,6 +613,7 @@ class ModuleVisitor : public virtual ScopeHandler { class InterfaceVisitor : public virtual ScopeHandler { public: bool Pre(const parser::InterfaceStmt &); + void Post(const parser::InterfaceStmt &); void Post(const parser::EndInterfaceStmt &); bool Pre(const parser::GenericSpec &); bool Pre(const parser::ProcedureStmt &); @@ -1548,26 +1555,69 @@ bool AttrsVisitor::SetBindNameOn(Symbol &symbol) { void AttrsVisitor::Post(const parser::LanguageBindingSpec &x) { CHECK(attrs_); - attrs_->set(Attr::BIND_C); - if (x.v) { - bindName_ = EvaluateExpr(*x.v); + if (CheckAndSet(Attr::BIND_C)) { + if (x.v) { + bindName_ = EvaluateExpr(*x.v); + } } } bool AttrsVisitor::Pre(const parser::IntentSpec &x) { CHECK(attrs_); - attrs_->set(IntentSpecToAttr(x)); + CheckAndSet(IntentSpecToAttr(x)); return false; } bool AttrsVisitor::Pre(const parser::Pass &x) { - if (x.v) { - passName_ = x.v->source; - MakePlaceholder(*x.v, MiscDetails::Kind::PassName); - } else { - attrs_->set(Attr::PASS); + if (CheckAndSet(Attr::PASS)) { + if (x.v) { + passName_ = x.v->source; + MakePlaceholder(*x.v, MiscDetails::Kind::PassName); + } } return false; } +// C730, C743, C755, C778, C1543 say no attribute or prefix repetitions +bool AttrsVisitor::IsDuplicateAttr(Attr attrName) { + if (attrs_->test(attrName)) { + Say(currStmtSource().value(), + "Attribute '%s' cannot be used more than once"_en_US, + AttrToString(attrName)); + return true; + } + return false; +} + +// See if attrName violates a constraint cause by a conflict. attr1 and attr2 +// name attributes that cannot be used on the same declaration +bool AttrsVisitor::HaveAttrConflict(Attr attrName, Attr attr1, Attr attr2) { + if ((attrName == attr1 && attrs_->test(attr2)) || + (attrName == attr2 && attrs_->test(attr1))) { + Say(currStmtSource().value(), + "Attributes '%s' and '%s' conflict with each other"_err_en_US, + AttrToString(attr1), AttrToString(attr2)); + return true; + } + return false; +} +// C759, C1543 +bool AttrsVisitor::IsConflictingAttr(Attr attrName) { + return HaveAttrConflict(attrName, Attr::INTENT_IN, Attr::INTENT_INOUT) || + HaveAttrConflict(attrName, Attr::INTENT_IN, Attr::INTENT_OUT) || + HaveAttrConflict(attrName, Attr::INTENT_INOUT, Attr::INTENT_OUT) || + HaveAttrConflict(attrName, Attr::PASS, Attr::NOPASS) || + HaveAttrConflict(attrName, Attr::PURE, Attr::IMPURE) || + HaveAttrConflict(attrName, Attr::PUBLIC, Attr::PRIVATE) || + HaveAttrConflict(attrName, Attr::RECURSIVE, Attr::NON_RECURSIVE); +} +bool AttrsVisitor::CheckAndSet(Attr attrName) { + CHECK(attrs_); + if (IsConflictingAttr(attrName) || IsDuplicateAttr(attrName)) { + return false; + } + attrs_->set(attrName); + return true; +} + // DeclTypeSpecVisitor implementation const DeclTypeSpec *DeclTypeSpecVisitor::GetDeclTypeSpec() { @@ -1824,14 +1874,22 @@ void ArraySpecVisitor::PostAttrSpec() { // Save dimension/codimension from attrs so we can process array/coarray-spec // on the entity-decl if (!arraySpec_.empty()) { - CHECK(attrArraySpec_.empty()); - attrArraySpec_ = arraySpec_; - arraySpec_.clear(); + if (attrArraySpec_.empty()) { + attrArraySpec_ = arraySpec_; + arraySpec_.clear(); + } else { + Say(currStmtSource().value(), + "Attribute 'DIMENSION' cannot be used more than once"_err_en_US); + } } if (!coarraySpec_.empty()) { - CHECK(attrCoarraySpec_.empty()); - attrCoarraySpec_ = coarraySpec_; - coarraySpec_.clear(); + if (attrCoarraySpec_.empty()) { + attrCoarraySpec_ = coarraySpec_; + coarraySpec_.clear(); + } else { + Say(currStmtSource().value(), + "Attribute 'CODIMENSION' cannot be used more than once"_err_en_US); + } } } @@ -2395,9 +2453,11 @@ void ModuleVisitor::ApplyDefaultAccess() { bool InterfaceVisitor::Pre(const parser::InterfaceStmt &x) { bool isAbstract{std::holds_alternative(x.u)}; genericInfo_.emplace(/*isInterface*/ true, isAbstract); - return true; + return BeginAttrs(); } +void InterfaceVisitor::Post(const parser::InterfaceStmt &) { EndAttrs(); } + void InterfaceVisitor::Post(const parser::EndInterfaceStmt &) { genericInfo_.pop(); } @@ -2624,9 +2684,15 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) { bool SubprogramVisitor::Pre(const parser::PrefixSpec &x) { // Save this to process after UseStmt and ImplicitPart if (const auto *parsedType{std::get_if(&x.u)}) { - funcInfo_.parsedType = parsedType; - funcInfo_.source = currStmtSource(); - return false; + if (funcInfo_.parsedType) { // C1543 + Say(currStmtSource().value(), + "FUNCTION prefix cannot specify the type more than once"_err_en_US); + return false; + } else { + funcInfo_.parsedType = parsedType; + funcInfo_.source = currStmtSource(); + return false; + } } else { return true; } @@ -3057,7 +3123,7 @@ bool DeclarationVisitor::Pre(const parser::AccessSpec &x) { "%s attribute may only appear in the specification part of a module"_err_en_US, EnumToString(attr)); } - attrs_->set(attr); + CheckAndSet(attr); return false; } @@ -3522,7 +3588,12 @@ void DeclarationVisitor::Post(const parser::TypeParamDefStmt &x) { EndDecl(); } bool DeclarationVisitor::Pre(const parser::TypeAttrSpec::Extends &x) { - derivedTypeInfo_.extends = &x.v; + if (derivedTypeInfo_.extends) { + Say(currStmtSource().value(), + "Attribute 'EXTENDS' cannot be used more than once"_err_en_US); + } else { + derivedTypeInfo_.extends = &x.v; + } return false; } diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index f6a4e39e511d..9b3a0326b10e 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -674,6 +674,22 @@ bool IsInitialized(const Symbol &symbol) { return false; } +bool HasIntrinsicTypeName(const Symbol &symbol) { + std::string name{symbol.name().ToString()}; + if (name == "doubleprecision") { + return true; + } else if (name == "derived") { + return false; + } else { + for (int i{0}; i != common::TypeCategory_enumSize; ++i) { + if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) { + return true; + } + } + return false; + } +} + bool IsFinalizable(const Symbol &symbol) { if (const DeclTypeSpec * type{symbol.GetType()}) { if (const DerivedTypeSpec * derived{type->AsDerived()}) { diff --git a/test/Semantics/kinds02.f90 b/test/Semantics/kinds02.f90 index 0983be564738..f1ff0b27caf5 100644 --- a/test/Semantics/kinds02.f90 +++ b/test/Semantics/kinds02.f90 @@ -10,6 +10,8 @@ ! double-colon separator appears in the typedeclaration- stmt. ! C727 The value of kind-param shall specify a representation method that ! exists on the processor. +! C728 The value of kind-param shall specify a representation method that +! exists on the processor. ! !ERROR: INTEGER(KIND=0) is not a supported type integer(kind=0) :: j0 @@ -53,6 +55,18 @@ logical(kind=3) :: l3 !ERROR: LOGICAL(KIND=16) is not a supported type logical(kind=16) :: l16 +integer, parameter :: negOne = -1 +!ERROR: unsupported LOGICAL(KIND=0) +logical :: lvar0 = .true._0 +logical :: lvar1 = .true._1 +logical :: lvar2 = .true._2 +!ERROR: unsupported LOGICAL(KIND=3) +logical :: lvar3 = .true._3 +logical :: lvar4 = .true._4 +!ERROR: unsupported LOGICAL(KIND=5) +logical :: lvar5 = .true._5 +!ERROR: unsupported LOGICAL(KIND=-1) +logical :: lvar6 = .true._negOne character (len=99, kind=1) :: cvar1 character (len=99, kind=2) :: cvar2 character *4, cvar3 diff --git a/test/Semantics/resolve78.f90 b/test/Semantics/resolve78.f90 new file mode 100644 index 000000000000..0e4efc081009 --- /dev/null +++ b/test/Semantics/resolve78.f90 @@ -0,0 +1,32 @@ +! RUN: %S/test_errors.sh %s %flang %t +module m +! C743 No component-attr-spec shall appear more than once in a +! given component-def-stmt. +! +! R737 data-component-def-stmt -> +! declaration-type-spec [[, component-attr-spec-list] ::] +! component-decl-list +! component-attr-spec values are: +! PUBLIC, PRIVATE, ALLOCATABLE, CODIMENSION [*], CONTIGUOUS, DIMENSION(5), +! POINTER + + type :: derived + !WARNING: Attribute 'PUBLIC' cannot be used more than once + real, public, allocatable, public :: field1 + !WARNING: Attribute 'PRIVATE' cannot be used more than once + real, private, allocatable, private :: field2 + !ERROR: Attributes 'PUBLIC' and 'PRIVATE' conflict with each other + real, public, allocatable, private :: field3 + !WARNING: Attribute 'ALLOCATABLE' cannot be used more than once + real, allocatable, public, allocatable :: field4 + !ERROR: Attribute 'CODIMENSION' cannot be used more than once + real, public, codimension[:], allocatable, codimension[:] :: field5 + !WARNING: Attribute 'CONTIGUOUS' cannot be used more than once + real, public, contiguous, pointer, contiguous, dimension(:) :: field6 + !ERROR: Attribute 'DIMENSION' cannot be used more than once + real, dimension(5), public, dimension(5) :: field7 + !WARNING: Attribute 'POINTER' cannot be used more than once + real, pointer, public, pointer :: field8 + end type derived + +end module m diff --git a/test/Semantics/resolve79.f90 b/test/Semantics/resolve79.f90 new file mode 100644 index 000000000000..5d0e2127ea10 --- /dev/null +++ b/test/Semantics/resolve79.f90 @@ -0,0 +1,54 @@ +! RUN: %S/test_errors.sh %s %flang %t +module m +! C755 The same proc-component-attr-spec shall not appear more than once in a +! given proc-component-def-stmt. +! C759 PASS and NOPASS shall not both appear in the same +! proc-component-attr-spec-list. +! +! R741 proc-component-def-stmt -> +! PROCEDURE ( [proc-interface] ) , proc-component-attr-spec-list +! :: proc-decl-list +! proc-component-attr-spec values are: +! PUBLIC, PRIVATE, NOPASS, PASS, POINTER + + type :: procComponentType + !WARNING: Attribute 'PUBLIC' cannot be used more than once + procedure(publicProc), public, pointer, public :: publicField + !WARNING: Attribute 'PRIVATE' cannot be used more than once + procedure(privateProc), private, pointer, private :: privateField + !WARNING: Attribute 'NOPASS' cannot be used more than once + procedure(nopassProc), nopass, pointer, nopass :: noPassField + !WARNING: Attribute 'PASS' cannot be used more than once + procedure(passProc), pass, pointer, pass :: passField + !ERROR: Attributes 'PASS' and 'NOPASS' conflict with each other + procedure(passNopassProc), pass, pointer, nopass :: passNopassField + !WARNING: Attribute 'POINTER' cannot be used more than once + procedure(pointerProc), pointer, public, pointer :: pointerField + contains + procedure :: noPassProc + procedure :: passProc + procedure :: passNopassProc + procedure :: publicProc + procedure :: privateProc + end type procComponentType + +contains + subroutine publicProc(arg) + class(procComponentType) :: arg + end + subroutine privateProc(arg) + class(procComponentType) :: arg + end + subroutine noPassProc(arg) + class(procComponentType) :: arg + end + subroutine passProc(arg) + class(procComponentType) :: arg + end + subroutine passNopassProc(arg) + class(procComponentType) :: arg + end + subroutine pointerProc(arg) + class(procComponentType) :: arg + end +end module m diff --git a/test/Semantics/resolve80.f90 b/test/Semantics/resolve80.f90 new file mode 100644 index 000000000000..98f5c79a343b --- /dev/null +++ b/test/Semantics/resolve80.f90 @@ -0,0 +1,61 @@ +! RUN: %S/test_errors.sh %s %flang %t +module m +!C778 The same binding-attr shall not appear more than once in a given +!binding-attr-list. +! +!R749 type-bound-procedure-stmt +! PROCEDURE [ [ ,binding-attr-list] :: ]type-bound-proc-decl-list +! or PROCEDURE (interface-name),binding-attr-list::binding-name-list +! +! +! binding-attr values are: +! PUBLIC, PRIVATE, DEFERRED, NON_OVERRIDABLE, NOPASS, PASS [ (arg-name) ] +! + type, abstract :: boundProcType + contains + !WARNING: Attribute 'PUBLIC' cannot be used more than once + procedure(subPublic), public, deferred, public :: publicBinding + !WARNING: Attribute 'PRIVATE' cannot be used more than once + procedure(subPrivate), private, deferred, private :: privateBinding + !WARNING: Attribute 'DEFERRED' cannot be used more than once + procedure(subDeferred), deferred, public, deferred :: deferredBinding + !WARNING: Attribute 'NON_OVERRIDABLE' cannot be used more than once + procedure, non_overridable, public, non_overridable :: subNon_overridable; + !WARNING: Attribute 'NOPASS' cannot be used more than once + procedure(subNopass), nopass, deferred, nopass :: nopassBinding + !WARNING: Attribute 'PASS' cannot be used more than once + procedure(subPass), pass, deferred, pass :: passBinding + !ERROR: Attributes 'PASS' and 'NOPASS' conflict with each other + procedure(subPassNopass), pass, deferred, nopass :: passNopassBinding + end type boundProcType + +contains + subroutine subPublic(x) + class(boundProcType), intent(in) :: x + end subroutine subPublic + + subroutine subPrivate(x) + class(boundProcType), intent(in) :: x + end subroutine subPrivate + + subroutine subDeferred(x) + class(boundProcType), intent(in) :: x + end subroutine subDeferred + + subroutine subNon_overridable(x) + class(boundProcType), intent(in) :: x + end subroutine subNon_overridable + + subroutine subNopass(x) + class(boundProcType), intent(in) :: x + end subroutine subNopass + + subroutine subPass(x) + class(boundProcType), intent(in) :: x + end subroutine subPass + + subroutine subPassNopass(x) + class(boundProcType), intent(in) :: x + end subroutine subPassNopass + +end module m diff --git a/test/Semantics/resolve81.f90 b/test/Semantics/resolve81.f90 new file mode 100644 index 000000000000..218d74ec6744 --- /dev/null +++ b/test/Semantics/resolve81.f90 @@ -0,0 +1,64 @@ +! RUN: %S/test_errors.sh %s %flang %t +! C801 The same attr-spec shall not appear more than once in a given +! type-declaration-stmt. +! +! R801 type-declaration-stmt -> +! declaration-type-spec [[, attr-spec]... ::] entity-decl-list +! attr-spec values are: +! PUBLIC, PRIVATE, ALLOCATABLE, ASYNCHRONOUS, CODIMENSION, CONTIGUOUS, +! DIMENSION (array-spec), EXTERNAL, INTENT (intent-spec), INTRINSIC, +! BIND(C), OPTIONAL, PARAMETER, POINTER, PROTECTED, SAVE, TARGET, VALUE, +! VOLATILE +module m + + !WARNING: Attribute 'PUBLIC' cannot be used more than once + real, public, allocatable, public :: publicVar + !WARNING: Attribute 'PRIVATE' cannot be used more than once + real, private, allocatable, private :: privateVar + !WARNING: Attribute 'ALLOCATABLE' cannot be used more than once + real, allocatable, allocatable :: allocVar + !WARNING: Attribute 'ASYNCHRONOUS' cannot be used more than once + real, asynchronous, public, asynchronous :: asynchVar + !ERROR: Attribute 'CODIMENSION' cannot be used more than once + real, codimension[*], codimension[*] :: codimensionVar + !WARNING: Attribute 'CONTIGUOUS' cannot be used more than once + real, contiguous, pointer, contiguous :: contigVar(:) + !ERROR: Attribute 'DIMENSION' cannot be used more than once + real, dimension(5), dimension(5) :: arrayVar + !WARNING: Attribute 'EXTERNAL' cannot be used more than once + real, external, external :: externFunc + !WARNING: Attribute 'INTRINSIC' cannot be used more than once + real, intrinsic, bind(c), intrinsic :: cos + !WARNING: Attribute 'BIND(C)' cannot be used more than once + integer, bind(c), volatile, bind(c) :: bindVar + !WARNING: Attribute 'PARAMETER' cannot be used more than once + real, parameter, parameter :: realConst = 4.3 + !WARNING: Attribute 'POINTER' cannot be used more than once + real, pointer, pointer :: realPtr + !WARNING: Attribute 'PROTECTED' cannot be used more than once + real, protected, protected :: realProt + !WARNING: Attribute 'SAVE' cannot be used more than once + real, save, save :: saveVar + !WARNING: Attribute 'TARGET' cannot be used more than once + real, target, target :: targetVar + !WARNING: Attribute 'VOLATILE' cannot be used more than once + real, volatile, volatile :: volatileVar + +contains + subroutine testTypeDecl(arg1, arg2, arg3, arg4, arg5, arg6) + !WARNING: Attribute 'INTENT(IN)' cannot be used more than once + real, intent(in), intent(in) :: arg1 + !WARNING: Attribute 'INTENT(OUT)' cannot be used more than once + real, intent(out), intent(out) :: arg2 + !WARNING: Attribute 'INTENT(INOUT)' cannot be used more than once + real, intent(inout), intent(inout) :: arg3 + !WARNING: Attribute 'OPTIONAL' cannot be used more than once + integer, optional, intent(in), optional :: arg4 + !WARNING: Attribute 'VALUE' cannot be used more than once + integer, value, intent(in), value :: arg5 + !ERROR: Attributes 'INTENT(IN)' and 'INTENT(INOUT)' conflict with each other + integer, intent(in), pointer, intent(inout) :: arg6 + + arg2 =3.5 + end subroutine testTypeDecl +end module m diff --git a/test/Semantics/resolve82.f90 b/test/Semantics/resolve82.f90 new file mode 100644 index 000000000000..378e8796db45 --- /dev/null +++ b/test/Semantics/resolve82.f90 @@ -0,0 +1,47 @@ +! RUN: %S/test_errors.sh %s %flang %t +! C815 An entity shall not be explicitly given any attribute more than once in +! a scoping unit. +! +! R1512 procedure-declaration-stmt -> +! PROCEDURE ( [proc-interface] ) [[, proc-attr-spec]... ::] +! proc-decl-list +! proc-attr-spec values are: +! PUBLIC, PRIVATE, BIND(C), INTENT (intent-spec), OPTIONAL, POINTER, +! PROTECTED, SAVE +module m + abstract interface + real function procFunc() + end function procFunc + end interface + + !WARNING: Attribute 'PUBLIC' cannot be used more than once + procedure(procFunc), public, pointer, public :: proc1 + !WARNING: Attribute 'PRIVATE' cannot be used more than once + procedure(procFunc), private, pointer, private :: proc2 + !WARNING: Attribute 'BIND(C)' cannot be used more than once + procedure(procFunc), bind(c), pointer, bind(c) :: proc3 + !WARNING: Attribute 'PROTECTED' cannot be used more than once + procedure(procFunc), protected, pointer, protected :: proc4 + +contains + + subroutine testProcDecl(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + !WARNING: Attribute 'INTENT(IN)' cannot be used more than once + procedure(procFunc), intent(in), pointer, intent(in) :: arg4 + !WARNING: Attribute 'INTENT(OUT)' cannot be used more than once + procedure(procFunc), intent(out), pointer, intent(out) :: arg5 + !WARNING: Attribute 'INTENT(INOUT)' cannot be used more than once + procedure(procFunc), intent(inout), pointer, intent(inout) :: arg6 + !ERROR: Attributes 'INTENT(INOUT)' and 'INTENT(OUT)' conflict with each other + procedure(procFunc), intent(inout), pointer, intent(out) :: arg7 + !ERROR: Attributes 'INTENT(INOUT)' and 'INTENT(OUT)' conflict with each other + procedure(procFunc), intent(out), pointer, intent(inout) :: arg8 + !WARNING: Attribute 'OPTIONAL' cannot be used more than once + procedure(procFunc), optional, pointer, optional :: arg9 + !WARNING: Attribute 'POINTER' cannot be used more than once + procedure(procFunc), pointer, optional, pointer :: arg10 + !WARNING: Attribute 'SAVE' cannot be used more than once + procedure(procFunc), save, pointer, save :: localProc + end subroutine testProcDecl + +end module m diff --git a/test/Semantics/resolve83.f90 b/test/Semantics/resolve83.f90 new file mode 100644 index 000000000000..cdd528a688e2 --- /dev/null +++ b/test/Semantics/resolve83.f90 @@ -0,0 +1,57 @@ +! RUN: %S/test_errors.sh %s %flang %t +module m + + ! For C1543 + interface intFace + !WARNING: Attribute 'MODULE' cannot be used more than once + module pure module real function moduleFunc() + end function moduleFunc + end interface + +contains + +! C1543 A prefix shall contain at most one of each prefix-spec. +! +! R1535 subroutine-stmt is +! [prefix] SUBROUTINE subroutine-name [ ( [dummy-arg-list] ) +! [proc-language-binding-spec] ] +! +! R1526 prefix is +! prefix-spec[prefix-spec]... +! +! prefix-spec values are: +! declaration-type-spec, ELEMENTAL, IMPURE, MODULE, NON_RECURSIVE, +! PURE, RECURSIVE + + !ERROR: FUNCTION prefix cannot specify the type more than once + real pure real function realFunc() + end function realFunc + + !WARNING: Attribute 'ELEMENTAL' cannot be used more than once + elemental real elemental function elementalFunc() + end function elementalFunc + + !WARNING: Attribute 'IMPURE' cannot be used more than once + impure real impure function impureFunc() + end function impureFunc + + !WARNING: Attribute 'PURE' cannot be used more than once + pure real pure function pureFunc() + end function pureFunc + + !ERROR: Attributes 'PURE' and 'IMPURE' conflict with each other + impure real pure function impurePureFunc() + end function impurePureFunc + + !WARNING: Attribute 'RECURSIVE' cannot be used more than once + recursive real recursive function recursiveFunc() + end function recursiveFunc + + !WARNING: Attribute 'NON_RECURSIVE' cannot be used more than once + non_recursive real non_recursive function non_recursiveFunc() + end function non_recursiveFunc + + !ERROR: Attributes 'RECURSIVE' and 'NON_RECURSIVE' conflict with each other + non_recursive real recursive function non_recursiveRecursiveFunc() + end function non_recursiveRecursiveFunc +end module m diff --git a/test/Semantics/resolve84.f90 b/test/Semantics/resolve84.f90 new file mode 100644 index 000000000000..79e393f4b689 --- /dev/null +++ b/test/Semantics/resolve84.f90 @@ -0,0 +1,26 @@ +! RUN: %S/test_errors.sh %s %flang %t +! C729 A derived type type-name shall not be DOUBLEPRECISION or the same as +! the name of any intrinsic type defined in this document. +subroutine s() + ! This one's OK + type derived + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type integer + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type real + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type doubleprecision + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type complex + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type character + end type + !ERROR: A derived type name cannot be the name of an intrinsic type + type logical + end type +end subroutine s diff --git a/test/Semantics/resolve85.f90 b/test/Semantics/resolve85.f90 new file mode 100644 index 000000000000..d228b7d03e47 --- /dev/null +++ b/test/Semantics/resolve85.f90 @@ -0,0 +1,37 @@ +! RUN: %S/test_errors.sh %s %flang %t +module m +! C730 The same type-attr-spec shall not appear more than once in a given +! derived-type-stmt. +! +! R727 derived-type-stmt -> +! TYPE [[, type-attr-spec-list] ::] type-name [( type-param-name-list )] +! type-attr-spec values are: +! ABSTRACT, PUBLIC, PRIVATE, BIND(C), EXTENDS(parent-type-name) + !WARNING: Attribute 'ABSTRACT' cannot be used more than once + type, abstract, public, abstract :: derived1 + end type derived1 + + !WARNING: Attribute 'PUBLIC' cannot be used more than once + type, public, abstract, public :: derived2 + end type derived2 + + !WARNING: Attribute 'PRIVATE' cannot be used more than once + type, private, abstract, private :: derived3 + end type derived3 + + !ERROR: Attributes 'PUBLIC' and 'PRIVATE' conflict with each other + type, public, abstract, private :: derived4 + end type derived4 + + !WARNING: Attribute 'BIND(C)' cannot be used more than once + type, bind(c), public, bind(c) :: derived5 + end type derived5 + + type, public :: derived6 + end type derived6 + + !ERROR: Attribute 'EXTENDS' cannot be used more than once + type, extends(derived6), public, extends(derived6) :: derived7 + end type derived7 + +end module m From e82cfee4325f90e3c6ed08e797fa940259948ffd Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 19 Mar 2020 16:31:10 -0700 Subject: [PATCH 100/345] Semantics for ENTRY initial test passes Move some checks to check-declarations Fix bugs found in testing Get tests all passing Allow declaration statements for function result to follow ENTRY Fix another bug --- include/flang/Semantics/symbol.h | 26 +- include/flang/Semantics/tools.h | 8 +- lib/Semantics/check-declarations.cpp | 81 +++- lib/Semantics/expression.cpp | 5 +- lib/Semantics/mod-file.cpp | 14 +- lib/Semantics/resolve-names.cpp | 586 ++++++++++++++++++--------- lib/Semantics/semantics.cpp | 15 +- lib/Semantics/symbol.cpp | 15 +- lib/Semantics/tools.cpp | 33 +- test/Semantics/assign04.f90 | 1 + test/Semantics/entry01.f90 | 184 +++++++++ 11 files changed, 727 insertions(+), 241 deletions(-) create mode 100644 test/Semantics/entry01.f90 diff --git a/include/flang/Semantics/symbol.h b/include/flang/Semantics/symbol.h index a9f4dd6bc185..3eaa95f08e2a 100644 --- a/include/flang/Semantics/symbol.h +++ b/include/flang/Semantics/symbol.h @@ -61,6 +61,9 @@ class SubprogramDetails { bool isFunction() const { return result_ != nullptr; } bool isInterface() const { return isInterface_; } void set_isInterface(bool value = true) { isInterface_ = value; } + Scope *entryScope() { return entryScope_; } + const Scope *entryScope() const { return entryScope_; } + void set_entryScope(Scope &scope) { entryScope_ = &scope; } MaybeExpr bindName() const { return bindName_; } void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); } const Symbol &result() const { @@ -82,8 +85,10 @@ class SubprogramDetails { MaybeExpr bindName_; std::vector dummyArgs_; // nullptr -> alternate return indicator Symbol *result_{nullptr}; + Scope *entryScope_{nullptr}; // if ENTRY, points to subprogram's scope MaybeExpr stmtFunction_; - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SubprogramDetails &); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const SubprogramDetails &); }; // For SubprogramNameDetails, the kind indicates whether it is the name @@ -115,17 +120,19 @@ class EntityDetails { void set_type(const DeclTypeSpec &); void ReplaceType(const DeclTypeSpec &); bool isDummy() const { return isDummy_; } + void set_isDummy(bool value = true) { isDummy_ = value; } bool isFuncResult() const { return isFuncResult_; } void set_funcResult(bool x) { isFuncResult_ = x; } MaybeExpr bindName() const { return bindName_; } void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); } private: - bool isDummy_; + bool isDummy_{false}; bool isFuncResult_{false}; const DeclTypeSpec *type_{nullptr}; MaybeExpr bindName_; - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const EntityDetails &); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const EntityDetails &); }; // Symbol is associated with a name or expression in a SELECT TYPE or ASSOCIATE. @@ -180,7 +187,8 @@ class ObjectEntityDetails : public EntityDetails { ArraySpec shape_; ArraySpec coshape_; const Symbol *commonBlock_{nullptr}; // common block this object is in - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ObjectEntityDetails &); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const ObjectEntityDetails &); }; // Mixin for details with passed-object dummy argument. @@ -217,7 +225,8 @@ class ProcEntityDetails : public EntityDetails, public WithPassArg { private: ProcInterface interface_; std::optional init_; - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ProcEntityDetails &); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const ProcEntityDetails &); }; // These derived type details represent the characteristics of a derived @@ -263,7 +272,8 @@ class DerivedTypeDetails { std::list componentNames_; bool sequence_{false}; bool isForwardReferenced_{false}; - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DerivedTypeDetails &); + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const DerivedTypeDetails &); }; class ProcBindingDetails : public WithPassArg { @@ -570,7 +580,6 @@ class Symbol { bool IsFuncResult() const; bool IsObjectArray() const; bool IsSubprogram() const; - bool IsSeparateModuleProc() const; bool IsFromModFile() const; bool HasExplicitInterface() const { return std::visit( @@ -662,7 +671,8 @@ class Symbol { Symbol() {} // only created in class Symbols const std::string GetDetailsName() const; friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &); - friend llvm::raw_ostream &DumpForUnparse(llvm::raw_ostream &, const Symbol &, bool); + friend llvm::raw_ostream &DumpForUnparse( + llvm::raw_ostream &, const Symbol &, bool); // If a derived type's symbol refers to an extended derived type, // return the parent component's symbol. The scope of the derived type diff --git a/include/flang/Semantics/tools.h b/include/flang/Semantics/tools.h index 43ff6286579e..da752d136024 100644 --- a/include/flang/Semantics/tools.h +++ b/include/flang/Semantics/tools.h @@ -108,6 +108,7 @@ bool IsSaved(const Symbol &); bool CanBeTypeBoundProc(const Symbol *); bool IsInitialized(const Symbol &); bool HasIntrinsicTypeName(const Symbol &); +bool IsSeparateModuleProcedureInterface(const Symbol *); // Return an ultimate component of type that matches predicate, or nullptr. const Symbol *FindUltimateComponent(const DerivedTypeSpec &type, @@ -164,7 +165,7 @@ inline bool IsAssumedRankArray(const Symbol &symbol) { return details && details->IsAssumedRank(); } bool IsAssumedLengthCharacter(const Symbol &); -bool IsAssumedLengthExternalCharacterFunction(const Symbol &); +bool IsExternal(const Symbol &); // Is the symbol modifiable in this scope std::optional WhyNotModifiable( const Symbol &, const Scope &); @@ -200,6 +201,11 @@ std::list OrderParameterNames(const Symbol &); const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &, DerivedTypeSpec &&, SemanticsContext &, DeclTypeSpec::Category = DeclTypeSpec::TypeDerived); +// When a subprogram defined in a submodule defines a separate module +// procedure whose interface is defined in an ancestor (sub)module, +// returns a pointer to that interface, else null. +const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *); + // Determines whether an object might be visible outside a // pure function (C1594); returns a non-null Symbol pointer for // diagnostic purposes if so. diff --git a/lib/Semantics/check-declarations.cpp b/lib/Semantics/check-declarations.cpp index ffd735e4b980..cb6888610b28 100644 --- a/lib/Semantics/check-declarations.cpp +++ b/lib/Semantics/check-declarations.cpp @@ -24,6 +24,7 @@ namespace Fortran::semantics { using evaluate::characteristics::DummyArgument; using evaluate::characteristics::DummyDataObject; using evaluate::characteristics::DummyProcedure; +using evaluate::characteristics::FunctionResult; using evaluate::characteristics::Procedure; class CheckHelper { @@ -109,6 +110,7 @@ class CheckHelper { } } } + bool IsResultOkToDiffer(const FunctionResult &); SemanticsContext &context_; evaluate::FoldingContext &foldingContext_{context_.foldingContext()}; @@ -208,7 +210,8 @@ void CheckHelper::Check(const Symbol &symbol) { } if (type) { // Section 7.2, paragraph 7 bool canHaveAssumedParameter{IsNamedConstant(symbol) || - IsAssumedLengthExternalCharacterFunction(symbol) || // C722 + (IsAssumedLengthCharacter(symbol) && // C722 + IsExternal(symbol)) || symbol.test(Symbol::Flag::ParentComp)}; if (!IsStmtFunctionDummy(symbol)) { // C726 if (const auto *object{symbol.detailsIf()}) { @@ -239,7 +242,7 @@ void CheckHelper::Check(const Symbol &symbol) { } } } - if (IsAssumedLengthExternalCharacterFunction(symbol)) { // C723 + if (IsAssumedLengthCharacter(symbol) && IsExternal(symbol)) { // C723 if (symbol.attrs().test(Attr::RECURSIVE)) { messages_.Say( "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US); @@ -270,6 +273,16 @@ void CheckHelper::Check(const Symbol &symbol) { symbol.Rank() == 0) { // C830 messages_.Say("CONTIGUOUS POINTER must be an array"_err_en_US); } + if (IsDummy(symbol)) { + if (IsNamedConstant(symbol)) { + messages_.Say( + "A dummy argument may not also be a named constant"_err_en_US); + } + if (IsSaved(symbol)) { + messages_.Say( + "A dummy argument may not have the SAVE attribute"_err_en_US); + } + } } void CheckHelper::CheckValue( @@ -600,12 +613,66 @@ class SubprogramMatchHelper { SemanticsContext &context; }; +// 15.6.2.6 para 3 - can the result of an ENTRY differ from its function? +bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) { + if (result.attrs.test(FunctionResult::Attr::Allocatable) || + result.attrs.test(FunctionResult::Attr::Pointer)) { + return false; + } + const auto *typeAndShape{result.GetTypeAndShape()}; + if (!typeAndShape || typeAndShape->Rank() != 0) { + return false; + } + auto category{typeAndShape->type().category()}; + if (category == TypeCategory::Character || + category == TypeCategory::Derived) { + return false; + } + int kind{typeAndShape->type().kind()}; + return kind == context_.GetDefaultKind(category) || + (category == TypeCategory::Real && + kind == context_.doublePrecisionKind()); +} + void CheckHelper::CheckSubprogram( - const Symbol &symbol, const SubprogramDetails &) { - const Scope &scope{symbol.owner()}; - if (symbol.attrs().test(Attr::MODULE) && scope.IsSubmodule()) { - if (const Symbol * iface{scope.parent().FindSymbol(symbol.name())}) { - SubprogramMatchHelper{context_}.Check(symbol, *iface); + const Symbol &symbol, const SubprogramDetails &details) { + if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) { + SubprogramMatchHelper{context_}.Check(symbol, *iface); + } + if (const Scope * entryScope{details.entryScope()}) { + // ENTRY 15.6.2.6, esp. C1571 + std::optional error; + const Symbol *subprogram{entryScope->symbol()}; + const SubprogramDetails *subprogramDetails{nullptr}; + if (subprogram) { + subprogramDetails = subprogram->detailsIf(); + } + if (entryScope->kind() != Scope::Kind::Subprogram) { + error = "ENTRY may appear only in a subroutine or function"_err_en_US; + } else if (!(entryScope->parent().IsGlobal() || + entryScope->parent().IsModule() || + entryScope->parent().IsSubmodule())) { + error = "ENTRY may not appear in an internal subprogram"_err_en_US; + } else if (FindSeparateModuleSubprogramInterface(subprogram)) { + error = "ENTRY may not appear in a separate module procedure"_err_en_US; + } else if (subprogramDetails && details.isFunction() && + subprogramDetails->isFunction()) { + auto result{FunctionResult::Characterize( + details.result(), context_.intrinsics())}; + auto subpResult{FunctionResult::Characterize( + subprogramDetails->result(), context_.intrinsics())}; + if (result && subpResult && *result != *subpResult && + (!IsResultOkToDiffer(*result) || !IsResultOkToDiffer(*subpResult))) { + error = + "Result of ENTRY is not compatible with result of containing function"_err_en_US; + } + } + if (error) { + if (auto *msg{messages_.Say(symbol.name(), *error)}) { + if (subprogram) { + msg->Attach(subprogram->name(), "Containing subprogram"_en_US); + } + } } } } diff --git a/lib/Semantics/expression.cpp b/lib/Semantics/expression.cpp index 182b90bdbd1f..b8e52d9a9802 100644 --- a/lib/Semantics/expression.cpp +++ b/lib/Semantics/expression.cpp @@ -1889,7 +1889,7 @@ void ExpressionAnalyzer::CheckForBadRecursion( if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3) msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US, callSite); - } else if (IsAssumedLengthExternalCharacterFunction(proc)) { + } else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) { msg = Say( // 15.6.2.1(3) "Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US, callSite); @@ -2046,7 +2046,8 @@ static bool IsExternalCalledImplicitly( if (const auto *symbol{proc.GetSymbol()}) { return symbol->has() && symbol->owner().IsGlobal() && - !symbol->scope()->sourceRange().Contains(callSite); + (!symbol->scope() /*ENTRY*/ || + !symbol->scope()->sourceRange().Contains(callSite)); } else { return false; } diff --git a/lib/Semantics/mod-file.cpp b/lib/Semantics/mod-file.cpp index 5c457321cc68..ba1df939fd74 100644 --- a/lib/Semantics/mod-file.cpp +++ b/lib/Semantics/mod-file.cpp @@ -69,8 +69,8 @@ static std::string CheckSum(const std::string_view &); // Collect symbols needed for a subprogram interface class SubprogramSymbolCollector { public: - SubprogramSymbolCollector(const Symbol &symbol) - : symbol_{symbol}, scope_{DEREF(symbol.scope())} {} + SubprogramSymbolCollector(const Symbol &symbol, const Scope &scope) + : symbol_{symbol}, scope_{scope} {} const SymbolVector &symbols() const { return need_; } const std::set &imports() const { return imports_; } void Collect(); @@ -335,12 +335,14 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) { } os << '\n'; - // walk symbols, collect ones needed - ModFileWriter writer{context_}; + // walk symbols, collect ones needed for interface + const Scope &scope{ + details.entryScope() ? *details.entryScope() : DEREF(symbol.scope())}; + SubprogramSymbolCollector collector{symbol, scope}; + collector.Collect(); std::string typeBindingsBuf; llvm::raw_string_ostream typeBindings{typeBindingsBuf}; - SubprogramSymbolCollector collector{symbol}; - collector.Collect(); + ModFileWriter writer{context_}; for (const Symbol &need : collector.symbols()) { writer.PutSymbol(typeBindings, need); } diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp index f41a6fb72a33..22f37a0347ec 100644 --- a/lib/Semantics/resolve-names.cpp +++ b/lib/Semantics/resolve-names.cpp @@ -43,7 +43,7 @@ namespace Fortran::semantics { using namespace parser::literals; -template using Indirection = common::Indirection; +template using Indirection = common::Indirection; using Message = parser::Message; using Messages = parser::Messages; using MessageFixedText = parser::MessageFixedText; @@ -58,7 +58,7 @@ class ResolveNamesVisitor; class ImplicitRules { public: ImplicitRules(SemanticsContext &context, ImplicitRules *parent) - : parent_{parent}, context_{context} { + : parent_{parent}, context_{context} { inheritFromParent_ = parent != nullptr; } bool isImplicitNoneType() const; @@ -78,7 +78,7 @@ class ImplicitRules { ImplicitRules *parent_; SemanticsContext &context_; - bool inheritFromParent_{false}; // look in parent if not specified here + bool inheritFromParent_{false}; // look in parent if not specified here bool isImplicitNoneType_{false}; bool isImplicitNoneExternal_{false}; // map_ contains the mapping between letters and types that were defined @@ -114,7 +114,7 @@ class MessageHandler { // Emit a message about a SourceName Message &Say(const SourceName &, MessageFixedText &&); // Emit a formatted message associated with a source location. - template + template Message &Say(const SourceName &source, MessageFixedText &&msg, A &&... args) { return context_->Say(source, std::move(msg), std::forward(args)...); } @@ -142,8 +142,9 @@ class BaseVisitor { BaseVisitor() { DIE("BaseVisitor: default-constructed"); } BaseVisitor( SemanticsContext &c, ResolveNamesVisitor &v, ImplicitRulesMap &rules) - : implicitRulesMap_{&rules}, this_{&v}, context_{&c}, messageHandler_{c} {} - template void Walk(const T &); + : implicitRulesMap_{&rules}, this_{&v}, context_{&c}, messageHandler_{c} { + } + template void Walk(const T &); MessageHandler &messageHandler() { return messageHandler_; } const std::optional &currStmtSource() { @@ -158,15 +159,15 @@ class BaseVisitor { // It is not in any scope and always has MiscDetails. void MakePlaceholder(const parser::Name &, MiscDetails::Kind); - template common::IfNoLvalue FoldExpr(T &&expr) { + template common::IfNoLvalue FoldExpr(T &&expr) { return evaluate::Fold(GetFoldingContext(), std::move(expr)); } - template MaybeExpr EvaluateExpr(const T &expr) { + template MaybeExpr EvaluateExpr(const T &expr) { return FoldExpr(AnalyzeExpr(*context_, expr)); } - template + template MaybeExpr EvaluateConvertedExpr( const Symbol &symbol, const T &expr, parser::CharBlock source) { if (context().HasError(symbol)) { @@ -193,7 +194,7 @@ class BaseVisitor { return FoldExpr(std::move(*converted)); } - template MaybeIntExpr EvaluateIntExpr(const T &expr) { + template MaybeIntExpr EvaluateIntExpr(const T &expr) { if (MaybeExpr maybeExpr{EvaluateExpr(expr)}) { if (auto *intExpr{evaluate::UnwrapExpr(*maybeExpr)}) { return std::move(*intExpr); @@ -202,7 +203,7 @@ class BaseVisitor { return std::nullopt; } - template + template MaybeSubscriptIntExpr EvaluateSubscriptIntExpr(const T &expr) { if (MaybeIntExpr maybeIntExpr{EvaluateIntExpr(expr)}) { return FoldExpr(evaluate::ConvertToType( @@ -212,10 +213,10 @@ class BaseVisitor { } } - template Message &Say(A &&... args) { + template Message &Say(A &&... args) { return messageHandler_.Say(std::forward(args)...); } - template + template Message &Say( const parser::Name &name, MessageFixedText &&text, const A &... args) { return messageHandler_.Say(name.source, std::move(text), args...); @@ -233,7 +234,7 @@ class BaseVisitor { // Provide Post methods to collect attributes into a member variable. class AttrsVisitor : public virtual BaseVisitor { public: - bool BeginAttrs(); // always returns true + bool BeginAttrs(); // always returns true Attrs GetAttrs(); Attrs EndAttrs(); bool SetPassNameOn(Symbol &); @@ -281,18 +282,23 @@ class AttrsVisitor : public virtual BaseVisitor { Attr AccessSpecToAttr(const parser::AccessSpec &x) { switch (x.v) { - case parser::AccessSpec::Kind::Public: return Attr::PUBLIC; - case parser::AccessSpec::Kind::Private: return Attr::PRIVATE; + case parser::AccessSpec::Kind::Public: + return Attr::PUBLIC; + case parser::AccessSpec::Kind::Private: + return Attr::PRIVATE; } - common::die("unreachable"); // suppress g++ warning + common::die("unreachable"); // suppress g++ warning } Attr IntentSpecToAttr(const parser::IntentSpec &x) { switch (x.v) { - case parser::IntentSpec::Intent::In: return Attr::INTENT_IN; - case parser::IntentSpec::Intent::Out: return Attr::INTENT_OUT; - case parser::IntentSpec::Intent::InOut: return Attr::INTENT_INOUT; + case parser::IntentSpec::Intent::In: + return Attr::INTENT_IN; + case parser::IntentSpec::Intent::Out: + return Attr::INTENT_OUT; + case parser::IntentSpec::Intent::InOut: + return Attr::INTENT_INOUT; } - common::die("unreachable"); // suppress g++ warning + common::die("unreachable"); // suppress g++ warning } private: @@ -300,8 +306,8 @@ class AttrsVisitor : public virtual BaseVisitor { bool HaveAttrConflict(Attr, Attr, Attr); bool IsConflictingAttr(Attr); - MaybeExpr bindName_; // from BIND(C, NAME="...") - std::optional passName_; // from PASS(...) + MaybeExpr bindName_; // from BIND(C, NAME="...") + std::optional passName_; // from PASS(...) }; // Find and create types from declaration-type-spec nodes. @@ -319,7 +325,7 @@ class DeclTypeSpecVisitor : public AttrsVisitor { protected: struct State { - bool expectDeclTypeSpec{false}; // should see decl-type-spec only when true + bool expectDeclTypeSpec{false}; // should see decl-type-spec only when true const DeclTypeSpec *declTypeSpec{nullptr}; struct { DerivedTypeSpec *type{nullptr}; @@ -336,7 +342,7 @@ class DeclTypeSpecVisitor : public AttrsVisitor { } // Walk the parse tree of a type spec and return the DeclTypeSpec for it. - template + template const DeclTypeSpec *ProcessTypeSpec(const T &x, bool allowForward = false) { auto restorer{common::ScopedSet(state_, State{})}; set_allowForwardReferenceToDerivedType(allowForward); @@ -446,6 +452,8 @@ class ScopeHandler : public ImplicitRulesVisitor { Scope &currScope() { return DEREF(currScope_); } // The enclosing scope, skipping blocks and derived types. + // TODO: Will return the scope of a FORALL or implied DO loop; is this ok? + // If not, should call FindProgramUnitContaining() instead. Scope &InclusiveScope(); // Create a new scope and push it on the scope stack. @@ -454,12 +462,12 @@ class ScopeHandler : public ImplicitRulesVisitor { void PopScope(); void SetScope(Scope &); - template bool Pre(const parser::Statement &x) { + template bool Pre(const parser::Statement &x) { messageHandler().set_currStmtSource(x.source); currScope_->AddSourceRange(x.source); return true; } - template void Post(const parser::Statement &) { + template void Post(const parser::Statement &) { messageHandler().set_currStmtSource(std::nullopt); } @@ -500,19 +508,19 @@ class ScopeHandler : public ImplicitRulesVisitor { Symbol &MakeSymbol(const SourceName &, Attrs = Attrs{}); Symbol &MakeSymbol(const parser::Name &, Attrs = Attrs{}); - template + template common::IfNoLvalue MakeSymbol( const parser::Name &name, D &&details) { return MakeSymbol(name, Attrs{}, std::move(details)); } - template + template common::IfNoLvalue MakeSymbol( const parser::Name &name, const Attrs &attrs, D &&details) { return Resolve(name, MakeSymbol(name.source, attrs, std::move(details))); } - template + template common::IfNoLvalue MakeSymbol( const SourceName &name, const Attrs &attrs, D &&details) { // Note: don't use FindSymbol here. If this is a derived type scope, @@ -633,10 +641,10 @@ class InterfaceVisitor : public virtual ScopeHandler { // A new GenericInfo is pushed for each interface block and generic stmt struct GenericInfo { GenericInfo(bool isInterface, bool isAbstract = false) - : isInterface{isInterface}, isAbstract{isAbstract} {} - bool isInterface; // in interface block - bool isAbstract; // in abstract interface block - Symbol *symbol{nullptr}; // the generic symbol being defined + : isInterface{isInterface}, isAbstract{isAbstract} {} + bool isInterface; // in interface block + bool isAbstract; // in abstract interface block + Symbol *symbol{nullptr}; // the generic symbol being defined }; std::stack genericInfo_; const GenericInfo &GetGenericInfo() const { return genericInfo_.top(); } @@ -658,6 +666,8 @@ class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor { void Post(const parser::SubroutineStmt &); bool Pre(const parser::FunctionStmt &); void Post(const parser::FunctionStmt &); + bool Pre(const parser::EntryStmt &); + void Post(const parser::EntryStmt &); bool Pre(const parser::InterfaceBody::Subroutine &); void Post(const parser::InterfaceBody::Subroutine &); bool Pre(const parser::InterfaceBody::Function &); @@ -675,6 +685,7 @@ class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor { protected: // Set when we see a stmt function that is really an array element assignment bool badStmtFuncFound_{false}; + bool inExecutionPart_{false}; private: // Info about the current function: parse tree of the type in the PrefixSpec; @@ -687,6 +698,7 @@ class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor { } funcInfo_; // Create a subprogram symbol in the current scope and push a new scope. + void CheckExtantExternal(const parser::Name &, Symbol::Flag); Symbol &PushSubprogramScope(const parser::Name &, Symbol::Flag); Symbol *GetSpecificFromGeneric(const parser::Name &); SubprogramDetails &PostSubprogramStmt(const parser::Name &); @@ -762,7 +774,7 @@ class DeclarationVisitor : public ArraySpecVisitor, void Post(const parser::ComponentDecl &); bool Pre(const parser::ProcedureDeclarationStmt &); void Post(const parser::ProcedureDeclarationStmt &); - bool Pre(const parser::DataComponentDefStmt &); // returns false + bool Pre(const parser::DataComponentDefStmt &); // returns false bool Pre(const parser::ProcComponentDefStmt &); void Post(const parser::ProcComponentDefStmt &); bool Pre(const parser::ProcPointerInit &); @@ -843,25 +855,25 @@ class DeclarationVisitor : public ArraySpecVisitor, } charInfo_; // Info about current derived type while walking DerivedTypeDef struct { - const parser::Name *extends{nullptr}; // EXTENDS(name) - bool privateComps{false}; // components are private by default - bool privateBindings{false}; // bindings are private by default - bool sawContains{false}; // currently processing bindings - bool sequence{false}; // is a sequence type - const Symbol *type{nullptr}; // derived type being defined + const parser::Name *extends{nullptr}; // EXTENDS(name) + bool privateComps{false}; // components are private by default + bool privateBindings{false}; // bindings are private by default + bool sawContains{false}; // currently processing bindings + bool sequence{false}; // is a sequence type + const Symbol *type{nullptr}; // derived type being defined } derivedTypeInfo_; // Collect equivalence sets and process at end of specification part std::vector *> equivalenceSets_; // Info about common blocks in the current scope struct { - Symbol *curr{nullptr}; // common block currently being processed - std::set names; // names in any common block of scope + Symbol *curr{nullptr}; // common block currently being processed + std::set names; // names in any common block of scope } commonBlockInfo_; // Info about about SAVE statements and attributes in current scope struct { - std::optional saveAll; // "SAVE" without entity list - std::set entities; // names of entities with save attr - std::set commons; // names of common blocks with save attr + std::optional saveAll; // "SAVE" without entity list + std::set entities; // names of entities with save attr + std::set commons; // names of common blocks with save attr } saveInfo_; // In a ProcedureDeclarationStmt or ProcComponentDefStmt, this is // the interface name, if any. @@ -903,7 +915,7 @@ class DeclarationVisitor : public ArraySpecVisitor, // Declare an object or procedure entity. // T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails - template + template Symbol &DeclareEntity(const parser::Name &name, Attrs attrs) { Symbol &symbol{MakeSymbol(name, attrs)}; if (symbol.has()) { @@ -987,7 +999,7 @@ class ConstructVisitor : public virtual DeclarationVisitor { bool Pre(const parser::ForallConstructStmt &x) { return CheckDef(x.t); } bool Pre(const parser::CriticalStmt &x) { return CheckDef(x.t); } bool Pre(const parser::LabelDoStmt &) { - return false; // error recovery + return false; // error recovery } bool Pre(const parser::NonLabelDoStmt &x) { return CheckDef(x.t); } bool Pre(const parser::IfThenStmt &x) { return CheckDef(x.t); } @@ -1024,7 +1036,7 @@ class ConstructVisitor : public virtual DeclarationVisitor { struct Selector { Selector() {} Selector(const SourceName &source, MaybeExpr &&expr) - : source{source}, expr{std::move(expr)} {} + : source{source}, expr{std::move(expr)} {} operator bool() const { return expr.has_value(); } parser::CharBlock source; MaybeExpr expr; @@ -1036,10 +1048,10 @@ class ConstructVisitor : public virtual DeclarationVisitor { }; std::vector associationStack_; - template bool CheckDef(const T &t) { + template bool CheckDef(const T &t) { return CheckDef(std::get>(t)); } - template void CheckRef(const T &t) { + template void CheckRef(const T &t) { CheckRef(std::get>(t)); } bool CheckDef(const std::optional &); @@ -1128,8 +1140,10 @@ bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) { switch (beginDir.v) { case parser::OmpBlockDirective::Directive::TargetData: case parser::OmpBlockDirective::Directive::Master: - case parser::OmpBlockDirective::Directive::Ordered: return false; - default: return true; + case parser::OmpBlockDirective::Directive::Ordered: + return false; + default: + return true; } } @@ -1156,12 +1170,12 @@ class OmpAttributeVisitor { public: explicit OmpAttributeVisitor( SemanticsContext &context, ResolveNamesVisitor &resolver) - : context_{context}, resolver_{resolver} {} + : context_{context}, resolver_{resolver} {} - template void Walk(const A &x) { parser::Walk(x, *this); } + template void Walk(const A &x) { parser::Walk(x, *this); } - template bool Pre(const A &) { return true; } - template void Post(const A &) {} + template bool Pre(const A &) { return true; } + template void Post(const A &) {} bool Pre(const parser::SpecificationPart &x) { Walk(std::get>(x.t)); @@ -1211,7 +1225,7 @@ class OmpAttributeVisitor { private: struct OmpContext { OmpContext(const parser::CharBlock &source, OmpDirective d, Scope &s) - : directiveSource{source}, directive{d}, scope{s} {} + : directiveSource{source}, directive{d}, scope{s} {} parser::CharBlock directiveSource; OmpDirective directive; Scope &scope; @@ -1312,11 +1326,11 @@ class OmpAttributeVisitor { Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); void CheckMultipleAppearances( const parser::Name &, const Symbol &, Symbol::Flag); - SymbolSet dataSharingAttributeObjects_; // on one directive + SymbolSet dataSharingAttributeObjects_; // on one directive SemanticsContext &context_; ResolveNamesVisitor &resolver_; - std::vector ompContext_; // used as a stack + std::vector ompContext_; // used as a stack }; // Walk the parse tree and resolve names to symbols. @@ -1345,13 +1359,13 @@ class ResolveNamesVisitor : public virtual ScopeHandler, using SubprogramVisitor::Pre; ResolveNamesVisitor(SemanticsContext &context, ImplicitRulesMap &rules) - : BaseVisitor{context, *this, rules} { + : BaseVisitor{context, *this, rules} { PushScope(context.globalScope()); } // Default action for a parse tree node is to visit children. - template bool Pre(const T &) { return true; } - template void Post(const T &) {} + template bool Pre(const T &) { return true; } + template void Post(const T &) {} bool Pre(const parser::SpecificationPart &); void Post(const parser::Program &); @@ -1360,7 +1374,7 @@ class ResolveNamesVisitor : public virtual ScopeHandler, void Post(const parser::AllocateObject &); bool Pre(const parser::PointerAssignmentStmt &); void Post(const parser::Designator &); - template + template void Post(const parser::LoopBounds &x) { ResolveName(*parser::Unwrap(x.name)); } @@ -1417,7 +1431,7 @@ bool ImplicitRules::isImplicitNoneType() const { } else if (map_.empty() && inheritFromParent_) { return parent_->isImplicitNoneType(); } else { - return false; // default if not specified + return false; // default if not specified } } @@ -1427,7 +1441,7 @@ bool ImplicitRules::isImplicitNoneExternal() const { } else if (inheritFromParent_) { return parent_->isImplicitNoneExternal(); } else { - return false; // default if not specified + return false; // default if not specified } } @@ -1465,10 +1479,14 @@ void ImplicitRules::SetTypeMapping(const DeclTypeSpec &type, // Return '\0' for the char after 'z'. char ImplicitRules::Incr(char ch) { switch (ch) { - case 'i': return 'j'; - case 'r': return 's'; - case 'z': return '\0'; - default: return ch + 1; + case 'i': + return 'j'; + case 'r': + return 's'; + case 'z': + return '\0'; + default: + return ch + 1; } } @@ -1491,7 +1509,7 @@ void ShowImplicitRule( } } -template void BaseVisitor::Walk(const T &x) { +template void BaseVisitor::Walk(const T &x) { parser::Walk(x, *this_); } @@ -1657,14 +1675,17 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) { switch (spec->category()) { case DeclTypeSpec::Numeric: case DeclTypeSpec::Logical: - case DeclTypeSpec::Character: typeSpec.declTypeSpec = spec; break; + case DeclTypeSpec::Character: + typeSpec.declTypeSpec = spec; + break; case DeclTypeSpec::TypeDerived: if (const DerivedTypeSpec * derived{spec->AsDerived()}) { - CheckForAbstractType(derived->typeSymbol()); // C703 + CheckForAbstractType(derived->typeSymbol()); // C703 typeSpec.declTypeSpec = spec; } break; - default: CRASH_NO_CASE; + default: + CRASH_NO_CASE; } } } @@ -2042,7 +2063,7 @@ Symbol &ScopeHandler::MakeSymbol( return *symbol; } else { const auto pair{scope.try_emplace(name, attrs, UnknownDetails{})}; - CHECK(pair.second); // name was not found, so must be able to add + CHECK(pair.second); // name was not found, so must be able to add return *pair.first->second; } } @@ -2184,7 +2205,7 @@ const DeclTypeSpec &ScopeHandler::MakeLogicalType( void ScopeHandler::MakeExternal(Symbol &symbol) { if (!symbol.attrs().test(Attr::EXTERNAL)) { symbol.attrs().set(Attr::EXTERNAL); - if (symbol.attrs().test(Attr::INTRINSIC)) { // C840 + if (symbol.attrs().test(Attr::INTRINSIC)) { // C840 Say(symbol.name(), "Symbol '%s' cannot have both EXTERNAL and INTRINSIC attributes"_err_en_US, symbol.name()); @@ -2282,7 +2303,7 @@ ModuleVisitor::SymbolRename ModuleVisitor::AddUse( ModuleVisitor::SymbolRename ModuleVisitor::AddUse( const SourceName &localName, const SourceName &useName, Symbol *useSymbol) { if (!useModuleScope_) { - return {}; // error occurred finding module + return {}; // error occurred finding module } if (!useSymbol) { Say(useName, @@ -2400,7 +2421,7 @@ bool ModuleVisitor::BeginSubmodule( if (!parentScope) { return false; } - PushScope(*parentScope); // submodule is hosted in parent + PushScope(*parentScope); // submodule is hosted in parent BeginModule(name, true); if (!ancestor->AddSubmodule(name.source, currScope())) { Say(name, "Module '%s' already has a submodule named '%s'"_err_en_US, @@ -2432,7 +2453,7 @@ Scope *ModuleVisitor::FindModule(const parser::Name &name, Scope *ancestor) { Say(name, "'%s' is not a module"_err_en_US); return nullptr; } - if (DoesScopeContain(scope, currScope())) { // 14.2.2(1) + if (DoesScopeContain(scope, currScope())) { // 14.2.2(1) Say(name, "Module '%s' cannot USE itself"_err_en_US); } Resolve(name, scope->symbol()); @@ -2519,7 +2540,7 @@ void InterfaceVisitor::AddSpecificProcs( // this generic interface. Resolve those names to symbols. void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) { auto &details{generic.get()}; - std::set namesSeen; // to check for duplicate names + std::set namesSeen; // to check for duplicate names for (const Symbol &symbol : details.specificProcs()) { namesSeen.insert(symbol.name()); } @@ -2597,7 +2618,7 @@ void InterfaceVisitor::CheckGenericProcedures(Symbol &generic) { const Symbol &firstSpecific{specifics.front()}; bool isFunction{firstSpecific.test(Symbol::Flag::Function)}; for (const Symbol &specific : specifics) { - if (isFunction != specific.test(Symbol::Flag::Function)) { // C1514 + if (isFunction != specific.test(Symbol::Flag::Function)) { // C1514 auto &msg{Say(generic.name(), "Generic interface '%s' has both a function and a subroutine"_err_en_US)}; if (isFunction) { @@ -2633,14 +2654,14 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) { } // TODO: check that attrs are compatible with stmt func resultType = details->type(); - symbol->details() = UnknownDetails{}; // will be replaced below + symbol->details() = UnknownDetails{}; // will be replaced below } if (badStmtFuncFound_) { Say(name, "'%s' has not been declared as an array"_err_en_US); return true; } auto &symbol{PushSubprogramScope(name, Symbol::Flag::Function)}; - EraseSymbol(symbol); // removes symbol added by PushSubprogramScope + EraseSymbol(symbol); // removes symbol added by PushSubprogramScope auto &details{symbol.get()}; for (const auto &dummyName : std::get>(x.t)) { ObjectEntityDetails dummyDetails{true}; @@ -2684,7 +2705,7 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) { bool SubprogramVisitor::Pre(const parser::PrefixSpec &x) { // Save this to process after UseStmt and ImplicitPart if (const auto *parsedType{std::get_if(&x.u)}) { - if (funcInfo_.parsedType) { // C1543 + if (funcInfo_.parsedType) { // C1543 Say(currStmtSource().value(), "FUNCTION prefix cannot specify the type more than once"_err_en_US); return false; @@ -2732,6 +2753,7 @@ bool SubprogramVisitor::Pre(const parser::SubroutineStmt &) { bool SubprogramVisitor::Pre(const parser::FunctionStmt &) { return BeginAttrs(); } +bool SubprogramVisitor::Pre(const parser::EntryStmt &) { return BeginAttrs(); } void SubprogramVisitor::Post(const parser::SubroutineStmt &stmt) { const auto &name{std::get(stmt.t)}; @@ -2758,7 +2780,7 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) { // Note that RESULT is ignored if it has the same name as the function. funcResultName = funcInfo_.resultName; } else { - EraseSymbol(name); // was added by PushSubprogramScope + EraseSymbol(name); // was added by PushSubprogramScope funcResultName = &name; } // add function result to function scope @@ -2768,7 +2790,7 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) { &MakeSymbol(*funcResultName, std::move(funcResultDetails)); details.set_result(*funcInfo_.resultSymbol); - // C1560. TODO also enforce on entry names when entry implemented + // C1560. if (funcInfo_.resultName && funcInfo_.resultName->source == name.source) { Say(funcInfo_.resultName->source, "The function name should not appear in RESULT, references to '%s' " @@ -2781,7 +2803,10 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) { // should be resolved to avoid internal errors. Resolve(*funcInfo_.resultName, funcInfo_.resultSymbol); } - name.symbol = currScope().symbol(); // must not be function result symbol + name.symbol = currScope().symbol(); // must not be function result symbol + // Clear the RESULT() name now in case an ENTRY statement in the implicit-part + // has a RESULT() suffix. + funcInfo_.resultName = nullptr; } SubprogramDetails &SubprogramVisitor::PostSubprogramStmt( @@ -2796,13 +2821,160 @@ SubprogramDetails &SubprogramVisitor::PostSubprogramStmt( return symbol.get(); } +void SubprogramVisitor::Post(const parser::EntryStmt &stmt) { + auto attrs{EndAttrs()}; // needs to be called even if early return + Scope &inclusiveScope{InclusiveScope()}; + const Symbol *subprogram{inclusiveScope.symbol()}; + if (!subprogram) { + CHECK(context().AnyFatalError()); + return; + } + const auto &name{std::get(stmt.t)}; + const auto *parentDetails{subprogram->detailsIf()}; + bool inFunction{parentDetails && parentDetails->isFunction()}; + const parser::Name *resultName{funcInfo_.resultName}; + if (resultName) { // RESULT(result) is present + funcInfo_.resultName = nullptr; + if (!inFunction) { + Say2(resultName->source, + "RESULT(%s) may appear only in a function"_err_en_US, + subprogram->name(), "Containing subprogram"_en_US); + } else if (resultName->source == subprogram->name()) { // C1574 + Say2(resultName->source, + "RESULT(%s) may not have the same name as the function"_err_en_US, + subprogram->name(), "Containing function"_en_US); + } else if (const Symbol * + symbol{FindSymbol(inclusiveScope.parent(), *resultName)}) { // C1574 + if (const auto *details{symbol->detailsIf()}) { + if (details->entryScope() == &inclusiveScope) { + Say2(resultName->source, + "RESULT(%s) may not have the same name as an ENTRY in the function"_err_en_US, + symbol->name(), "Conflicting ENTRY"_en_US); + } + } + } + if (Symbol * symbol{FindSymbol(name)}) { // C1570 + // When RESULT() appears, ENTRY name can't have been already declared + if (inclusiveScope.Contains(symbol->owner())) { + Say2(name, + "ENTRY name '%s' may not be declared when RESULT() is present"_err_en_US, + *symbol, "Previous declaration of '%s'"_en_US); + } + } + if (resultName->source == name.source) { + // ignore RESULT() hereafter when it's the same name as the ENTRY + resultName = nullptr; + } + } + SubprogramDetails entryDetails; + entryDetails.set_entryScope(inclusiveScope); + if (inFunction) { + // Create the entity to hold the function result, if necessary. + Symbol *resultSymbol{nullptr}; + auto &effectiveResultName{*(resultName ? resultName : &name)}; + resultSymbol = FindInScope(currScope(), effectiveResultName); + if (resultSymbol) { // C1574 + std::visit( + common::visitors{[](EntityDetails &x) { x.set_funcResult(true); }, + [](ObjectEntityDetails &x) { x.set_funcResult(true); }, + [](ProcEntityDetails &x) { x.set_funcResult(true); }, + [&](const auto &) { + Say2(effectiveResultName.source, + "'%s' was previously declared as an item that may not be used as a function result"_err_en_US, + resultSymbol->name(), "Previous declaration of '%s'"_en_US); + }}, + resultSymbol->details()); + } else if (inExecutionPart_) { + ObjectEntityDetails entity; + entity.set_funcResult(true); + resultSymbol = &MakeSymbol(effectiveResultName, std::move(entity)); + ApplyImplicitRules(*resultSymbol); + } else { + EntityDetails entity; + entity.set_funcResult(true); + resultSymbol = &MakeSymbol(effectiveResultName, std::move(entity)); + } + if (!resultName) { + name.symbol = nullptr; // symbol will be used for entry point below + } + entryDetails.set_result(*resultSymbol); + } + + for (const auto &dummyArg : std::get>(stmt.t)) { + if (const auto *dummyName{std::get_if(&dummyArg.u)}) { + Symbol *dummy{FindSymbol(*dummyName)}; + if (dummy) { + std::visit( + common::visitors{[](EntityDetails &x) { x.set_isDummy(); }, + [](ObjectEntityDetails &x) { x.set_isDummy(); }, + [](ProcEntityDetails &x) { x.set_isDummy(); }, + [&](const auto &) { + Say2(dummyName->source, + "ENTRY dummy argument '%s' is previously declared as an item that may not be used as a dummy argument"_err_en_US, + dummy->name(), "Previous declaration of '%s'"_en_US); + }}, + dummy->details()); + } else { + dummy = &MakeSymbol(*dummyName, EntityDetails(true)); + } + entryDetails.add_dummyArg(*dummy); + } else { + if (inFunction) { // C1573 + Say(name, + "ENTRY in a function may not have an alternate return dummy argument"_err_en_US); + break; + } + entryDetails.add_alternateReturn(); + } + } + + Symbol::Flag subpFlag{ + inFunction ? Symbol::Flag::Function : Symbol::Flag::Subroutine}; + CheckExtantExternal(name, subpFlag); + Scope &outer{inclusiveScope.parent()}; // global or module scope + if (Symbol * extant{FindSymbol(outer, name)}) { + if (extant->has()) { + if (!extant->test(subpFlag)) { + Say2(name, + subpFlag == Symbol::Flag::Function + ? "'%s' was previously called as a subroutine"_err_en_US + : "'%s' was previously called as a function"_err_en_US, + *extant, "Previous call of '%s'"_en_US); + } + if (extant->attrs().test(Attr::PRIVATE)) { + attrs.set(Attr::PRIVATE); + } + outer.erase(extant->name()); + } else { + if (outer.IsGlobal()) { + Say2(name, "'%s' is already defined as a global identifier"_err_en_US, + *extant, "Previous definition of '%s'"_en_US); + } else { + SayAlreadyDeclared(name, *extant); + } + return; + } + } + if (outer.IsModule() && !attrs.test(Attr::PRIVATE)) { + attrs.set(Attr::PUBLIC); + } + Symbol &entrySymbol{MakeSymbol(outer, name.source, attrs)}; + entrySymbol.set_details(std::move(entryDetails)); + if (outer.IsGlobal()) { + MakeExternal(entrySymbol); + } + SetBindNameOn(entrySymbol); + entrySymbol.set(subpFlag); + Resolve(name, entrySymbol); +} + // A subprogram declared with MODULE PROCEDURE bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) { auto *symbol{FindSymbol(name)}; if (symbol && symbol->has()) { symbol = FindSymbol(currScope().parent(), name); } - if (!symbol || !symbol->IsSeparateModuleProc()) { + if (!IsSeparateModuleProcedureInterface(symbol)) { Say(name, "'%s' was not declared a separate module procedure"_err_en_US); return false; } @@ -2831,12 +3003,11 @@ bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) { // A subprogram declared with SUBROUTINE or FUNCTION bool SubprogramVisitor::BeginSubprogram( const parser::Name &name, Symbol::Flag subpFlag, bool hasModulePrefix) { - if (hasModulePrefix && !inInterfaceBlock()) { - auto *symbol{FindSymbol(currScope().parent(), name)}; - if (!symbol || !symbol->IsSeparateModuleProc()) { - Say(name, "'%s' was not declared a separate module procedure"_err_en_US); - return false; - } + if (hasModulePrefix && !inInterfaceBlock() && + !IsSeparateModuleProcedureInterface( + FindSymbol(currScope().parent(), name))) { + Say(name, "'%s' was not declared a separate module procedure"_err_en_US); + return false; } PushSubprogramScope(name, subpFlag); return true; @@ -2844,24 +3015,28 @@ bool SubprogramVisitor::BeginSubprogram( void SubprogramVisitor::EndSubprogram() { PopScope(); } +void SubprogramVisitor::CheckExtantExternal( + const parser::Name &name, Symbol::Flag subpFlag) { + if (auto *prev{FindSymbol(name)}) { + if (prev->attrs().test(Attr::EXTERNAL) && prev->has()) { + // this subprogram was previously called, now being declared + if (!prev->test(subpFlag)) { + Say2(name, + subpFlag == Symbol::Flag::Function + ? "'%s' was previously called as a subroutine"_err_en_US + : "'%s' was previously called as a function"_err_en_US, + *prev, "Previous call of '%s'"_en_US); + } + EraseSymbol(name); + } + } +} + Symbol &SubprogramVisitor::PushSubprogramScope( const parser::Name &name, Symbol::Flag subpFlag) { auto *symbol{GetSpecificFromGeneric(name)}; if (!symbol) { - if (auto *prev{FindSymbol(name)}) { - if (prev->attrs().test(Attr::EXTERNAL) && - prev->has()) { - // this subprogram was previously called, now being declared - if (!prev->test(subpFlag)) { - Say2(name, - subpFlag == Symbol::Flag::Function - ? "'%s' was previously called as a subroutine"_err_en_US - : "'%s' was previously called as a function"_err_en_US, - *prev, "Previous call of '%s'"_en_US); - } - EraseSymbol(name); - } - } + CheckExtantExternal(name, subpFlag); symbol = &MakeSymbol(name, SubprogramDetails{}); } symbol->set(subpFlag); @@ -2877,7 +3052,7 @@ Symbol &SubprogramVisitor::PushSubprogramScope( } implicitRules().set_inheritFromParent(false); } - FindSymbol(name)->set(subpFlag); // PushScope() created symbol + FindSymbol(name)->set(subpFlag); // PushScope() created symbol return *symbol; } @@ -2958,7 +3133,7 @@ void DeclarationVisitor::CheckAccessibility( } void DeclarationVisitor::Post(const parser::TypeDeclarationStmt &) { - if (!GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { // C702 + if (!GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { // C702 if (const auto *typeSpec{GetDeclTypeSpec()}) { if (typeSpec->category() == DeclTypeSpec::Character) { if (typeSpec->characterTypeSpec().length().isDeferred()) { @@ -3006,7 +3181,7 @@ void DeclarationVisitor::Post(const parser::EntityDecl &x) { if (ConvertToObjectEntity(symbol)) { Initialization(name, *init, false); } - } else if (attrs.test(Attr::PARAMETER)) { // C882, C883 + } else if (attrs.test(Attr::PARAMETER)) { // C882, C883 Say(name, "Missing initialization for parameter '%s'"_err_en_US); } } @@ -3080,7 +3255,7 @@ bool DeclarationVisitor::Pre(const parser::Enumerator &enumerator) { if (auto &init{std::get>( enumerator.t)}) { - Walk(*init); // Resolve names in expression before evaluation. + Walk(*init); // Resolve names in expression before evaluation. MaybeIntExpr expr{EvaluateIntExpr(*init)}; if (auto value{evaluate::ToInt64(expr)}) { // Cast all init expressions to C_INT so that they can then be @@ -3118,7 +3293,7 @@ bool DeclarationVisitor::Pre(const parser::AccessSpec &x) { Attr attr{AccessSpecToAttr(x)}; const Scope &scope{ currScope().IsDerivedType() ? currScope().parent() : currScope()}; - if (!scope.IsModule()) { // C817 + if (!scope.IsModule()) { // C817 Say(currStmtSource().value(), "%s attribute may only appear in the specification part of a module"_err_en_US, EnumToString(attr)); @@ -3147,7 +3322,7 @@ bool DeclarationVisitor::Pre(const parser::ExternalStmt &x) { bool DeclarationVisitor::Pre(const parser::IntentStmt &x) { auto &intentSpec{std::get(x.t)}; auto &names{std::get>(x.t)}; - return CheckNotInBlock("INTENT") && // C1107 + return CheckNotInBlock("INTENT") && // C1107 HandleAttributeStmt(IntentSpecToAttr(intentSpec), names); } bool DeclarationVisitor::Pre(const parser::IntrinsicStmt &x) { @@ -3157,7 +3332,7 @@ bool DeclarationVisitor::Pre(const parser::IntrinsicStmt &x) { if (!ConvertToProcEntity(*symbol)) { SayWithDecl( name, *symbol, "INTRINSIC attribute not allowed on '%s'"_err_en_US); - } else if (symbol->attrs().test(Attr::EXTERNAL)) { // C840 + } else if (symbol->attrs().test(Attr::EXTERNAL)) { // C840 Say(symbol->name(), "Symbol '%s' cannot have both EXTERNAL and INTRINSIC attributes"_err_en_US, symbol->name()); @@ -3166,14 +3341,14 @@ bool DeclarationVisitor::Pre(const parser::IntrinsicStmt &x) { return false; } bool DeclarationVisitor::Pre(const parser::OptionalStmt &x) { - return CheckNotInBlock("OPTIONAL") && // C1107 + return CheckNotInBlock("OPTIONAL") && // C1107 HandleAttributeStmt(Attr::OPTIONAL, x.v); } bool DeclarationVisitor::Pre(const parser::ProtectedStmt &x) { return HandleAttributeStmt(Attr::PROTECTED, x.v); } bool DeclarationVisitor::Pre(const parser::ValueStmt &x) { - return CheckNotInBlock("VALUE") && // C1107 + return CheckNotInBlock("VALUE") && // C1107 HandleAttributeStmt(Attr::VALUE, x.v); } bool DeclarationVisitor::Pre(const parser::VolatileStmt &x) { @@ -3335,7 +3510,7 @@ void DeclarationVisitor::Post(const parser::CharSelector::LengthAndKind &x) { std::optional intKind{ToInt64(charInfo_.kind)}; if (intKind && !evaluate::IsValidKindOfIntrinsicType( - TypeCategory::Character, *intKind)) { // C715, C719 + TypeCategory::Character, *intKind)) { // C715, C719 Say(currStmtSource().value(), "KIND value (%jd) not valid for CHARACTER"_err_en_US, static_cast(*intKind)); @@ -3379,7 +3554,7 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Type &) { void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Type &type) { const parser::Name &derivedName{std::get(type.derived.t)}; if (const Symbol * derivedSymbol{derivedName.symbol}) { - CheckForAbstractType(*derivedSymbol); // C706 + CheckForAbstractType(*derivedSymbol); // C706 } } @@ -3392,7 +3567,7 @@ void DeclarationVisitor::Post( const parser::DeclarationTypeSpec::Class &parsedClass) { const auto &typeName{std::get(parsedClass.derived.t)}; if (auto spec{ResolveDerivedType(typeName)}; - spec && !IsExtensibleType(&*spec)) { // C705 + spec && !IsExtensibleType(&*spec)) { // C705 SayWithDecl(typeName, *typeName.symbol, "Non-extensible derived type '%s' may not be used with CLASS" " keyword"_err_en_US); @@ -3493,21 +3668,21 @@ bool DeclarationVisitor::Pre(const parser::DerivedTypeDef &x) { auto *symbol{FindInScope(scope, paramName)}; if (!symbol) { Say(paramName, - "No definition found for type parameter '%s'"_err_en_US); // C742 + "No definition found for type parameter '%s'"_err_en_US); // C742 } else if (!symbol->has()) { Say2(paramName, "'%s' is not defined as a type parameter"_err_en_US, - *symbol, "Definition of '%s'"_en_US); // C741 + *symbol, "Definition of '%s'"_en_US); // C741 } if (!paramNames.insert(paramName.source).second) { Say(paramName, - "Duplicate type parameter name: '%s'"_err_en_US); // C731 + "Duplicate type parameter name: '%s'"_err_en_US); // C731 } } for (const auto &[name, symbol] : currScope()) { if (symbol->has() && !paramNames.count(name)) { SayDerivedType(name, "'%s' is not a type parameter of this derived type"_err_en_US, - currScope()); // C742 + currScope()); // C742 } } Walk(std::get>>(x.t)); @@ -3515,11 +3690,11 @@ bool DeclarationVisitor::Pre(const parser::DerivedTypeDef &x) { details.set_sequence(true); if (derivedTypeInfo_.extends) { Say(stmt.source, - "A sequence type may not have the EXTENDS attribute"_err_en_US); // C735 + "A sequence type may not have the EXTENDS attribute"_err_en_US); // C735 } if (!details.paramNames().empty()) { Say(stmt.source, - "A sequence type may not have type parameters"_err_en_US); // C740 + "A sequence type may not have type parameters"_err_en_US); // C740 } } Walk(std::get>>(x.t)); @@ -3600,14 +3775,14 @@ bool DeclarationVisitor::Pre(const parser::TypeAttrSpec::Extends &x) { bool DeclarationVisitor::Pre(const parser::PrivateStmt &) { if (!currScope().parent().IsModule()) { Say("PRIVATE is only allowed in a derived type that is" - " in a module"_err_en_US); // C766 + " in a module"_err_en_US); // C766 } else if (derivedTypeInfo_.sawContains) { derivedTypeInfo_.privateBindings = true; } else if (!derivedTypeInfo_.privateComps) { derivedTypeInfo_.privateComps = true; } else { Say("PRIVATE may not appear more than once in" - " derived type components"_en_US); // C738 + " derived type components"_en_US); // C738 } return false; } @@ -3625,7 +3800,7 @@ void DeclarationVisitor::Post(const parser::ComponentDecl &x) { if (!attrs.HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { if (const auto *declType{GetDeclTypeSpec()}) { if (const auto *derived{declType->AsDerived()}) { - if (derivedTypeInfo_.type == &derived->typeSymbol()) { // C737 + if (derivedTypeInfo_.type == &derived->typeSymbol()) { // C737 Say("Recursive use of the derived type requires " "POINTER or ALLOCATABLE"_err_en_US); } @@ -3723,7 +3898,7 @@ void DeclarationVisitor::Post(const parser::TypeBoundProcedurePart &) { } auto [it, inserted]{specifics.insert(bindingName->source)}; if (!inserted) { - Say(*bindingName, // C773 + Say(*bindingName, // C773 "Binding name '%s' was already specified for generic '%s'"_err_en_US, bindingName->source, generic->name()) .Attach(*it, "Previous specification of '%s'"_en_US, *it); @@ -3731,10 +3906,10 @@ void DeclarationVisitor::Post(const parser::TypeBoundProcedurePart &) { } auto *symbol{FindInTypeOrParents(*bindingName)}; if (!symbol) { - Say(*bindingName, // C772 + Say(*bindingName, // C772 "Binding name '%s' not found in this derived type"_err_en_US); } else if (!symbol->has()) { - SayWithDecl(*bindingName, *symbol, // C772 + SayWithDecl(*bindingName, *symbol, // C772 "'%s' is not the name of a specific binding of this type"_err_en_US); } else { generic->get().AddSpecificProc( @@ -3746,13 +3921,13 @@ void DeclarationVisitor::Post(const parser::TypeBoundProcedurePart &) { void DeclarationVisitor::Post(const parser::ContainsStmt &) { if (derivedTypeInfo_.sequence) { - Say("A sequence type may not have a CONTAINS statement"_err_en_US); // C740 + Say("A sequence type may not have a CONTAINS statement"_err_en_US); // C740 } } void DeclarationVisitor::Post( const parser::TypeBoundProcedureStmt::WithoutInterface &x) { - if (GetAttrs().test(Attr::DEFERRED)) { // C783 + if (GetAttrs().test(Attr::DEFERRED)) { // C783 Say("DEFERRED is only allowed when an interface-name is provided"_err_en_US); } for (auto &declaration : x.declarations) { @@ -3802,7 +3977,7 @@ void DeclarationVisitor::CheckBindings( void DeclarationVisitor::Post( const parser::TypeBoundProcedureStmt::WithInterface &x) { - if (!GetAttrs().test(Attr::DEFERRED)) { // C783 + if (!GetAttrs().test(Attr::DEFERRED)) { // C783 Say("DEFERRED is required when an interface-name is provided"_err_en_US); } if (Symbol * interface{NoteInterfaceName(x.interfaceName)}) { @@ -3835,7 +4010,7 @@ bool DeclarationVisitor::Pre(const parser::TypeBoundGenericStmt &x) { auto *genericSymbol{info.FindInScope(context(), currScope())}; if (genericSymbol) { if (!genericSymbol->has()) { - genericSymbol = nullptr; // MakeTypeSymbol will report the error below + genericSymbol = nullptr; // MakeTypeSymbol will report the error below } } else { // look in parent types: @@ -3847,11 +4022,11 @@ bool DeclarationVisitor::Pre(const parser::TypeBoundGenericStmt &x) { } } if (inheritedSymbol && inheritedSymbol->has()) { - CheckAccessibility(symbolName, isPrivate, *inheritedSymbol); // C771 + CheckAccessibility(symbolName, isPrivate, *inheritedSymbol); // C771 } } if (genericSymbol) { - CheckAccessibility(symbolName, isPrivate, *genericSymbol); // C771 + CheckAccessibility(symbolName, isPrivate, *genericSymbol); // C771 } else { genericSymbol = MakeTypeSymbol(symbolName, GenericDetails{}); if (!genericSymbol) { @@ -3978,7 +4153,7 @@ bool DeclarationVisitor::Pre(const parser::BasedPointerStmt &x) { } bool DeclarationVisitor::Pre(const parser::NamelistStmt::Group &x) { - if (!CheckNotInBlock("NAMELIST")) { // C1107 + if (!CheckNotInBlock("NAMELIST")) { // C1107 return false; } @@ -4018,7 +4193,7 @@ bool DeclarationVisitor::Pre(const parser::IoControlSpec &x) { } bool DeclarationVisitor::Pre(const parser::CommonStmt::Block &x) { - CheckNotInBlock("COMMON"); // C1107 + CheckNotInBlock("COMMON"); // C1107 const auto &optName{std::get>(x.t)}; parser::Name blankCommon; blankCommon.source = @@ -4046,7 +4221,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) { ClearCoarraySpec(); auto *details{symbol.detailsIf()}; if (!details) { - return; // error was reported + return; // error was reported } commonBlockInfo_.curr->get().add_object(symbol); auto pair{commonBlockInfo_.names.insert(name.source)}; @@ -4061,18 +4236,18 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) { bool DeclarationVisitor::Pre(const parser::EquivalenceStmt &x) { // save equivalence sets to be processed after specification part - CheckNotInBlock("EQUIVALENCE"); // C1107 + CheckNotInBlock("EQUIVALENCE"); // C1107 for (const std::list &set : x.v) { equivalenceSets_.push_back(&set); } - return false; // don't implicitly declare names yet + return false; // don't implicitly declare names yet } void DeclarationVisitor::CheckEquivalenceSets() { EquivalenceSets equivSets{context()}; for (const auto *set : equivalenceSets_) { const auto &source{set->front().v.value().source}; - if (set->size() <= 1) { // R871 + if (set->size() <= 1) { // R871 Say(source, "Equivalence set must have more than one object"_err_en_US); } for (const parser::EquivalenceObject &object : *set) { @@ -4136,7 +4311,7 @@ void DeclarationVisitor::CheckSaveStmts() { Say(name, "'%s' appears as a COMMON block in a SAVE statement but not in" " a COMMON statement"_err_en_US); - } else { // C1108 + } else { // C1108 Say(name, "SAVE statement in BLOCK construct may not contain a" " common block name '%s'"_err_en_US); @@ -4314,10 +4489,10 @@ bool DeclarationVisitor::HandleUnrestrictedSpecificIntrinsicFunction( bool DeclarationVisitor::PassesSharedLocalityChecks( const parser::Name &name, Symbol &symbol) { if (!IsVariableName(symbol)) { - SayLocalMustBeVariable(name, symbol); // C1124 + SayLocalMustBeVariable(name, symbol); // C1124 return false; } - if (symbol.owner() == currScope()) { // C1125 and C1126 + if (symbol.owner() == currScope()) { // C1125 and C1126 SayAlreadyDeclared(name, symbol); return false; } @@ -4327,41 +4502,41 @@ bool DeclarationVisitor::PassesSharedLocalityChecks( // Checks for locality-specs LOCAL and LOCAL_INIT bool DeclarationVisitor::PassesLocalityChecks( const parser::Name &name, Symbol &symbol) { - if (IsAllocatable(symbol)) { // C1128 + if (IsAllocatable(symbol)) { // C1128 SayWithDecl(name, symbol, "ALLOCATABLE variable '%s' not allowed in a locality-spec"_err_en_US); return false; } - if (IsOptional(symbol)) { // C1128 + if (IsOptional(symbol)) { // C1128 SayWithDecl(name, symbol, "OPTIONAL argument '%s' not allowed in a locality-spec"_err_en_US); return false; } - if (IsIntentIn(symbol)) { // C1128 + if (IsIntentIn(symbol)) { // C1128 SayWithDecl(name, symbol, "INTENT IN argument '%s' not allowed in a locality-spec"_err_en_US); return false; } - if (IsFinalizable(symbol)) { // C1128 + if (IsFinalizable(symbol)) { // C1128 SayWithDecl(name, symbol, "Finalizable variable '%s' not allowed in a locality-spec"_err_en_US); return false; } - if (IsCoarray(symbol)) { // C1128 + if (IsCoarray(symbol)) { // C1128 SayWithDecl( name, symbol, "Coarray '%s' not allowed in a locality-spec"_err_en_US); return false; } if (const DeclTypeSpec * type{symbol.GetType()}) { if (type->IsPolymorphic() && symbol.IsDummy() && - !IsPointer(symbol)) { // C1128 + !IsPointer(symbol)) { // C1128 SayWithDecl(name, symbol, "Nonpointer polymorphic argument '%s' not allowed in a " "locality-spec"_err_en_US); return false; } } - if (IsAssumedSizeArray(symbol)) { // C1128 + if (IsAssumedSizeArray(symbol)) { // C1128 SayWithDecl(name, symbol, "Assumed size array '%s' not allowed in a locality-spec"_err_en_US); return false; @@ -4413,7 +4588,7 @@ Symbol *DeclarationVisitor::DeclareStatementEntity(const parser::Name &name, } Symbol &symbol{DeclareEntity(name, {})}; if (!symbol.has()) { - return nullptr; // error was reported in DeclareEntity + return nullptr; // error was reported in DeclareEntity } if (type) { declTypeSpec = ProcessTypeSpec(*type); @@ -4435,7 +4610,7 @@ void DeclarationVisitor::SetType( const parser::Name &name, const DeclTypeSpec &type) { CHECK(name.symbol); auto &symbol{*name.symbol}; - if (charInfo_.length) { // Declaration has "*length" (R723) + if (charInfo_.length) { // Declaration has "*length" (R723) auto length{std::move(*charInfo_.length)}; charInfo_.length.reset(); if (type.category() == DeclTypeSpec::Character) { @@ -4477,7 +4652,7 @@ std::optional DeclarationVisitor::ResolveDerivedType( DerivedTypeDetails details; details.set_isForwardReferenced(); symbol->set_details(std::move(details)); - } else { // C883 + } else { // C883 Say(name, "Derived type '%s' not found"_err_en_US); return std::nullopt; } @@ -4603,7 +4778,7 @@ ParamValue DeclarationVisitor::GetParamValue( const parser::TypeParamValue &x, common::TypeParamAttr attr) { return std::visit( common::visitors{ - [=](const parser::ScalarIntExpr &x) { // C704 + [=](const parser::ScalarIntExpr &x) { // C704 return ParamValue{EvaluateIntExpr(x), attr}; }, [=](const parser::Star &) { return ParamValue::Assumed(attr); }, @@ -4694,7 +4869,7 @@ bool ConstructVisitor::Pre(const parser::LocalitySpec::Shared &x) { if (PassesSharedLocalityChecks(name, prev)) { auto &symbol{MakeSymbol(name, HostAssocDetails{prev})}; symbol.set(Symbol::Flag::LocalityShared); - name.symbol = &symbol; // override resolution to parent + name.symbol = &symbol; // override resolution to parent } } return false; @@ -4833,7 +5008,7 @@ void ConstructVisitor::Post(const parser::Association &x) { SetTypeFromAssociation(*symbol); SetAttrsFromAssociation(*symbol); } - GetCurrentAssociation() = {}; // clean for further parser::Association. + GetCurrentAssociation() = {}; // clean for further parser::Association. } bool ConstructVisitor::Pre(const parser::ChangeTeamStmt &x) { @@ -4851,7 +5026,7 @@ void ConstructVisitor::Post(const parser::CoarrayAssociation &x) { if (auto sel{ResolveSelector(selector)}) { const Symbol *whole{UnwrapWholeSymbolDataRef(sel.expr)}; if (!whole || whole->Corank() == 0) { - Say(sel.source, // C1116 + Say(sel.source, // C1116 "Selector in coarray association must name a coarray"_err_en_US); } else if (auto dynType{sel.expr->GetType()}) { if (!symbol->GetType()) { @@ -4888,12 +5063,12 @@ void ConstructVisitor::Post(const parser::SelectTypeStmt &x) { whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) { ConvertToObjectEntity(const_cast(*whole)); if (!IsVariableName(*whole)) { - Say(association.selector.source, // C901 + Say(association.selector.source, // C901 "Selector is not a variable"_err_en_US); association = {}; } } else { - Say(association.selector.source, // C1157 + Say(association.selector.source, // C1157 "Selector is not a named variable: 'associate-name =>' is required"_err_en_US); association = {}; } @@ -4949,7 +5124,7 @@ Symbol *ConstructVisitor::MakeAssocEntity() { if (association.name) { symbol = &MakeSymbol(*association.name, UnknownDetails{}); if (symbol->has() && symbol->owner() == currScope()) { - Say(*association.name, // C1104 + Say(*association.name, // C1104 "The associate name '%s' is already used in this associate statement"_err_en_US); return nullptr; } @@ -5055,7 +5230,8 @@ const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec( ); } - case common::TypeCategory::Character: CRASH_NO_CASE; + case common::TypeCategory::Character: + CRASH_NO_CASE; } } @@ -5123,7 +5299,7 @@ bool ResolveNamesVisitor::Pre(const parser::ImportStmt &x) { return false; } break; - case Scope::Kind::BlockData: // C1415 (in part) + case Scope::Kind::BlockData: // C1415 (in part) Say("IMPORT is not allowed in a BLOCK DATA subprogram"_err_en_US); return false; default:; @@ -5207,7 +5383,7 @@ const parser::Name *DeclarationVisitor::ResolveVariable( const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) { if (Symbol * symbol{FindSymbol(name)}) { if (CheckUseError(name)) { - return nullptr; // reported an error + return nullptr; // reported an error } if (symbol->IsDummy() || (!symbol->GetType() && FindCommonBlockContaining(*symbol))) { @@ -5249,7 +5425,7 @@ const parser::Name *DeclarationVisitor::FindComponent( } auto *type{symbol.GetType()}; if (!type) { - return nullptr; // should have already reported error + return nullptr; // should have already reported error } if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) { auto name{component.ToString()}; @@ -5448,7 +5624,7 @@ void DeclarationVisitor::PointerInitialization( details.set_init(*targetName->symbol); } } else { - details.set_init(nullptr); // explicit NULL() + details.set_init(nullptr); // explicit NULL() } } else { Say(name, @@ -5505,7 +5681,7 @@ void ResolveNamesVisitor::HandleProcedureName( symbol = &Resolve(name, symbol)->GetUltimate(); ConvertToProcEntity(*symbol); if (!SetProcFlag(name, *symbol, flag)) { - return; // reported error + return; // reported error } if (IsProcedure(*symbol) || symbol->has() || symbol->has() || @@ -5567,7 +5743,7 @@ bool ResolveNamesVisitor::SetProcFlag( name, symbol, "Cannot call subroutine '%s' like a function"_err_en_US); return false; } else if (symbol.has()) { - symbol.set(flag); // in case it hasn't been set yet + symbol.set(flag); // in case it hasn't been set yet if (flag == Symbol::Flag::Function) { ApplyImplicitRules(symbol); } @@ -5580,7 +5756,7 @@ bool ResolveNamesVisitor::SetProcFlag( bool ModuleVisitor::Pre(const parser::AccessStmt &x) { Attr accessAttr{AccessSpecToAttr(std::get(x.t))}; - if (!currScope().IsModule()) { // C869 + if (!currScope().IsModule()) { // C869 Say(currStmtSource().value(), "%s statement may only appear in the specification part of a module"_err_en_US, EnumToString(accessAttr)); @@ -5588,7 +5764,7 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) { } const auto &accessIds{std::get>(x.t)}; if (accessIds.empty()) { - if (prevAccessStmt_) { // C869 + if (prevAccessStmt_) { // C869 Say("The default accessibility of this module has already been declared"_err_en_US) .Attach(*prevAccessStmt_, "Previous declaration"_en_US); } @@ -5705,7 +5881,7 @@ void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) { if (Symbol * existing{info.FindInScope(context(), currScope())}) { if (existing->has()) { info.Resolve(existing); - return; // already have generic, add to it + return; // already have generic, add to it } Symbol &ultimate{existing->GetUltimate()}; if (auto *ultimateDetails{ultimate.detailsIf()}) { @@ -5751,7 +5927,8 @@ void ResolveNamesVisitor::FinishSpecificationPart() { void ResolveNamesVisitor::CheckImports() { auto &scope{currScope()}; switch (scope.GetImportKind()) { - case common::ImportKind::None: break; + case common::ImportKind::None: + break; case common::ImportKind::All: // C8102: all entities in host must not be hidden for (const auto &pair : scope.parent()) { @@ -5782,7 +5959,7 @@ void ResolveNamesVisitor::CheckImport( } bool ResolveNamesVisitor::Pre(const parser::ImplicitStmt &x) { - return CheckNotInBlock("IMPLICIT") && // C1107 + return CheckNotInBlock("IMPLICIT") && // C1107 ImplicitRulesVisitor::Pre(x); } @@ -5834,7 +6011,7 @@ void ResolveNamesVisitor::Post(const parser::TypeGuardStmt &x) { ConstructVisitor::Post(x); } bool ResolveNamesVisitor::Pre(const parser::StmtFunctionStmt &x) { - CheckNotInBlock("STATEMENT FUNCTION"); // C1107 + CheckNotInBlock("STATEMENT FUNCTION"); // C1107 if (HandleStmtFunction(x)) { return false; } else { @@ -5866,7 +6043,9 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { SetScope(context().globalScope()); ResolveSpecificationParts(root); FinishSpecificationParts(root); + inExecutionPart_ = true; ResolveExecutionParts(root); + inExecutionPart_ = false; ResolveOmpParts(x); return false; } @@ -5876,7 +6055,7 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { class ExecutionPartSkimmer { public: explicit ExecutionPartSkimmer(ResolveNamesVisitor &resolver) - : resolver_{resolver} {} + : resolver_{resolver} {} void Walk(const parser::ExecutionPart *exec) { if (exec) { @@ -5884,8 +6063,8 @@ class ExecutionPartSkimmer { } } - template bool Pre(const A &) { return true; } - template void Post(const A &) {} + template bool Pre(const A &) { return true; } + template void Post(const A &) {} void Post(const parser::FunctionReference &fr) { resolver_.NoteExecutablePartCall(Symbol::Flag::Function, fr.v); } @@ -5901,11 +6080,11 @@ class ExecutionPartSkimmer { // node and its children void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) { if (node.isSpecificationPartResolved()) { - return; // been here already + return; // been here already } node.set_isSpecificationPartResolved(); if (!BeginScopeForNode(node)) { - return; // an error prevented scope from being created + return; // an error prevented scope from being created } Scope &scope{currScope()}; node.set_scope(scope); @@ -5969,8 +6148,11 @@ bool ResolveNamesVisitor::BeginScopeForNode(const ProgramTree &node) { case ProgramTree::Kind::Subroutine: return BeginSubprogram( node.name(), node.GetSubpFlag(), node.HasModulePrefix()); - case ProgramTree::Kind::MpSubprogram: return BeginMpSubprogram(node.name()); - case ProgramTree::Kind::Module: BeginModule(node.name(), false); return true; + case ProgramTree::Kind::MpSubprogram: + return BeginMpSubprogram(node.name()); + case ProgramTree::Kind::Module: + BeginModule(node.name(), false); + return true; case ProgramTree::Kind::Submodule: return BeginSubmodule(node.name(), node.GetParentId()); case ProgramTree::Kind::BlockData: @@ -5986,12 +6168,12 @@ bool ResolveNamesVisitor::BeginScopeForNode(const ProgramTree &node) { class DeferredCheckVisitor { public: explicit DeferredCheckVisitor(ResolveNamesVisitor &resolver) - : resolver_{resolver} {} + : resolver_{resolver} {} - template void Walk(const A &x) { parser::Walk(x, *this); } + template void Walk(const A &x) { parser::Walk(x, *this); } - template bool Pre(const A &) { return true; } - template void Post(const A &) {} + template bool Pre(const A &) { return true; } + template void Post(const A &) {} void Post(const parser::DerivedTypeStmt &x) { const auto &name{std::get(x.t)}; @@ -6200,7 +6382,7 @@ void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { targetIt++; symbol->set(Symbol::Flag::OmpPreDetermined); - iv.symbol = symbol; // adjust the symbol within region + iv.symbol = symbol; // adjust the symbol within region for (auto it{ompContext_.rbegin()}; it != targetIt; ++it) { AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); } @@ -6259,8 +6441,8 @@ std::size_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( return orderedLevel; } else if (!orderedLevel && collapseLevel) { return collapseLevel; - } // orderedLevel < collapseLevel is an error handled in structural checks - return 1; // default is outermost loop + } // orderedLevel < collapseLevel is an error handled in structural checks + return 1; // default is outermost loop } // 2.15.1.1 Data-sharing Attribute Rules - Predetermined @@ -6292,7 +6474,7 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex( const parser::Name &iv{GetLoopIndex(*loop)}; if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { symbol->set(Symbol::Flag::OmpPreDetermined); - iv.symbol = symbol; // adjust the symbol within region + iv.symbol = symbol; // adjust the symbol within region AddToContextObjectWithDSA(*symbol, ivDSA); } @@ -6358,7 +6540,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { // determined data-sharing attributes (2.15.1.1). if (Symbol * found{currScope().FindSymbol(name.source)}) { if (symbol != found) { - name.symbol = found; // adjust the symbol within region + name.symbol = found; // adjust the symbol within region } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { context_.Say(name.source, "The DEFAULT(NONE) clause requires that '%s' must be listed in " @@ -6367,7 +6549,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { } } } - } // within OpenMP construct + } // within OpenMP construct } bool OmpAttributeVisitor::HasDataSharingAttributeObject(const Symbol &object) { @@ -6428,7 +6610,7 @@ void OmpAttributeVisitor::ResolveOmpObject( } } }, - [&](const parser::Name &name) { // common block + [&](const parser::Name &name) { // common block if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { CheckMultipleAppearances( name, *symbol, Symbol::Flag::OmpCommonBlock); @@ -6444,7 +6626,7 @@ void OmpAttributeVisitor::ResolveOmpObject( } } } else { - context_.Say(name.source, // 2.15.3 + context_.Say(name.source, // 2.15.3 "COMMON block must be declared in the same scoping unit " "in which the OpenMP directive or clause appears"_err_en_US); } @@ -6474,7 +6656,7 @@ Symbol *OmpAttributeVisitor::ResolveOmp( Symbol *OmpAttributeVisitor::DeclarePrivateAccessEntity( const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { if (!name.symbol) { - return nullptr; // not resolved by Name Resolution step, do nothing + return nullptr; // not resolved by Name Resolution step, do nothing } name.symbol = DeclarePrivateAccessEntity(*name.symbol, ompFlag, scope); return name.symbol; @@ -6542,7 +6724,7 @@ void OmpAttributeVisitor::CheckMultipleAppearances( // the specification parts but before any of the execution parts. void ResolveNamesVisitor::FinishSpecificationParts(const ProgramTree &node) { if (!node.scope()) { - return; // error occurred creating scope + return; // error occurred creating scope } SetScope(*node.scope()); // The initializers of pointers, pointer components, and non-deferred @@ -6550,7 +6732,7 @@ void ResolveNamesVisitor::FinishSpecificationParts(const ProgramTree &node) { // We do that now, when any (formerly) forward references that appear // in those initializers will resolve to the right symbols. DeferredCheckVisitor{*this}.Walk(node.spec()); - DeferredCheckVisitor{*this}.Walk(node.exec()); // for BLOCK + DeferredCheckVisitor{*this}.Walk(node.exec()); // for BLOCK for (Scope &childScope : currScope().children()) { if (childScope.IsDerivedType() && !childScope.symbol()) { FinishDerivedTypeInstantiation(childScope); @@ -6595,13 +6777,13 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) { // Resolve names in the execution part of this node and its children void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) { if (!node.scope()) { - return; // error occurred creating scope + return; // error occurred creating scope } SetScope(*node.scope()); if (const auto *exec{node.exec()}) { Walk(*exec); } - PopScope(); // converts unclassified entities into objects + PopScope(); // converts unclassified entities into objects for (const auto &child : node.children()) { ResolveExecutionParts(child); } @@ -6650,4 +6832,4 @@ void ResolveSpecificationParts( visitor.ResolveSpecificationParts(node); context.set_location(std::move(originalLocation)); } -} +} // namespace Fortran::semantics diff --git a/lib/Semantics/semantics.cpp b/lib/Semantics/semantics.cpp index 53be760b7741..340c0a98f7cc 100644 --- a/lib/Semantics/semantics.cpp +++ b/lib/Semantics/semantics.cpp @@ -112,10 +112,23 @@ template class SemanticsVisitor : public virtual C... { SemanticsContext &context_; }; +class EntryChecker : public virtual BaseChecker { +public: + explicit EntryChecker(SemanticsContext &context) : context_{context} {} + void Leave(const parser::EntryStmt &) { + if (!context_.constructStack().empty()) { // C1571 + context_.Say("ENTRY may not appear in an executable construct"_err_en_US); + } + } + +private: + SemanticsContext &context_; +}; + using StatementSemanticsPass1 = ExprChecker; using StatementSemanticsPass2 = SemanticsVisitor; diff --git a/lib/Semantics/symbol.cpp b/lib/Semantics/symbol.cpp index a13f2c0d3779..ecda5d1f6303 100644 --- a/lib/Semantics/symbol.cpp +++ b/lib/Semantics/symbol.cpp @@ -92,6 +92,12 @@ llvm::raw_ostream &operator<<( os << ", " << x.result_->attrs(); } } + if (x.entryScope_) { + os << " entry"; + if (x.entryScope_->symbol()) { + os << " in " << x.entryScope_->symbol()->name(); + } + } char sep{'('}; os << ' '; for (const Symbol *arg : x.dummyArgs_) { @@ -318,15 +324,6 @@ bool Symbol::IsSubprogram() const { details_); } -bool Symbol::IsSeparateModuleProc() const { - if (attrs().test(Attr::MODULE)) { - if (auto *details{detailsIf()}) { - return details->isInterface(); - } - } - return false; -} - bool Symbol::IsFromModFile() const { return test(Flag::ModFile) || (!owner_->IsGlobal() && owner_->symbol()->IsFromModFile()); diff --git a/lib/Semantics/tools.cpp b/lib/Semantics/tools.cpp index 9b3a0326b10e..ce59825167ee 100644 --- a/lib/Semantics/tools.cpp +++ b/lib/Semantics/tools.cpp @@ -690,6 +690,15 @@ bool HasIntrinsicTypeName(const Symbol &symbol) { } } +bool IsSeparateModuleProcedureInterface(const Symbol *symbol) { + if (symbol && symbol->attrs().test(Attr::MODULE)) { + if (auto *details{symbol->detailsIf()}) { + return details->isInterface(); + } + } + return false; +} + bool IsFinalizable(const Symbol &symbol) { if (const DeclTypeSpec * type{symbol.GetType()}) { if (const DerivedTypeSpec * derived{type->AsDerived()}) { @@ -729,11 +738,9 @@ bool IsAssumedLengthCharacter(const Symbol &symbol) { // C722 and C723: For a function to be assumed length, it must be external and // of CHARACTER type -bool IsAssumedLengthExternalCharacterFunction(const Symbol &symbol) { - return IsAssumedLengthCharacter(symbol) && - ((symbol.has() && symbol.owner().IsGlobal()) || - (symbol.test(Symbol::Flag::Function) && - symbol.attrs().test(Attr::EXTERNAL))); +bool IsExternal(const Symbol &symbol) { + return (symbol.has() && symbol.owner().IsGlobal()) || + symbol.attrs().test(Attr::EXTERNAL); } const Symbol *IsExternalInPureContext( @@ -1022,6 +1029,22 @@ const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &scope, return type; } +const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) { + if (proc) { + if (const Symbol * submodule{proc->owner().symbol()}) { + if (const auto *details{submodule->detailsIf()}) { + if (const Scope * ancestor{details->ancestor()}) { + const Symbol *iface{ancestor->FindSymbol(proc->name())}; + if (IsSeparateModuleProcedureInterface(iface)) { + return iface; + } + } + } + } + } + return nullptr; +} + // ComponentIterator implementation template diff --git a/test/Semantics/assign04.f90 b/test/Semantics/assign04.f90 index f8798138c15c..dd0159bdd0bd 100644 --- a/test/Semantics/assign04.f90 +++ b/test/Semantics/assign04.f90 @@ -14,6 +14,7 @@ subroutine s1 ! C901 subroutine s2(x) + !ERROR: A dummy argument may not also be a named constant real, parameter :: x = 0.0 real, parameter :: a(*) = [1, 2, 3] character, parameter :: c(2) = "ab" diff --git a/test/Semantics/entry01.f90 b/test/Semantics/entry01.f90 new file mode 100644 index 000000000000..ccb03a7f6083 --- /dev/null +++ b/test/Semantics/entry01.f90 @@ -0,0 +1,184 @@ +! RUN: %S/test_errors.sh %s %flang %t +! Tests valid and invalid ENTRY statements + +module m1 + !ERROR: ENTRY may appear only in a subroutine or function + entry badentryinmodule + interface + module subroutine separate + end subroutine + end interface + contains + subroutine modproc + entry entryinmodproc ! ok + block + !ERROR: ENTRY may not appear in an executable construct + entry badentryinblock ! C1571 + end block + if (.true.) then + !ERROR: ENTRY may not appear in an executable construct + entry ibadconstr() ! C1571 + end if + contains + subroutine internal + !ERROR: ENTRY may not appear in an internal subprogram + entry badentryininternal ! C1571 + end subroutine + end subroutine +end module + +submodule(m1) m1s1 + contains + module procedure separate + !ERROR: ENTRY may not appear in a separate module procedure + entry badentryinsmp ! 1571 + end procedure +end submodule + +program main + !ERROR: ENTRY may appear only in a subroutine or function + entry badentryinprogram ! C1571 +end program + +block data bd1 + !ERROR: ENTRY may appear only in a subroutine or function + entry badentryinbd ! C1571 +end block data + +subroutine subr(goodarg1) + real, intent(in) :: goodarg1 + real :: goodarg2 + !ERROR: A dummy argument may not also be a named constant + integer, parameter :: badarg1 = 1 + type :: badarg2 + end type + common /badarg3/ x + namelist /badarg4/ x + !ERROR: A dummy argument may not have the SAVE attribute + integer :: badarg5 = 2 + entry okargs(goodarg1, goodarg2) + !ERROR: RESULT(br1) may appear only in a function + entry badresult() result(br1) ! C1572 + !ERROR: ENTRY dummy argument 'badarg2' is previously declared as an item that may not be used as a dummy argument + !ERROR: ENTRY dummy argument 'badarg4' is previously declared as an item that may not be used as a dummy argument + entry badargs(badarg1,badarg2,badarg3,badarg4,badarg5) +end subroutine + +function ifunc() + integer :: ifunc + integer :: ibad1 + type :: ibad2 + end type + save :: ibad3 + real :: weird1 + double precision :: weird2 + complex :: weird3 + logical :: weird4 + character :: weird5 + type(ibad2) :: weird6 + integer :: iarr(1) + integer, allocatable :: alloc + integer, pointer :: ptr + entry iok1() + !ERROR: ENTRY name 'ibad1' may not be declared when RESULT() is present + entry ibad1() result(ibad1res) ! C1570 + !ERROR: 'ibad2' was previously declared as an item that may not be used as a function result + entry ibad2() + !ERROR: ENTRY in a function may not have an alternate return dummy argument + entry ibadalt(*) ! C1573 + !ERROR: RESULT(ifunc) may not have the same name as the function + entry isameres() result(ifunc) ! C1574 + entry iok() + !ERROR: RESULT(iok) may not have the same name as an ENTRY in the function + entry isameres2() result(iok) ! C1574 + entry isameres3() result(iok2) ! C1574 + entry iok2() + !These cases are all acceptably incompatible + entry iok3() result(weird1) + entry iok4() result(weird2) + entry iok5() result(weird3) + entry iok6() result(weird4) + !ERROR: Result of ENTRY is not compatible with result of containing function + entry ibadt1() result(weird5) + !ERROR: Result of ENTRY is not compatible with result of containing function + entry ibadt2() result(weird6) + !ERROR: Result of ENTRY is not compatible with result of containing function + entry ibadt3() result(iarr) + !ERROR: Result of ENTRY is not compatible with result of containing function + entry ibadt4() result(alloc) + !ERROR: Result of ENTRY is not compatible with result of containing function + entry ibadt5() result(ptr) + call isubr + !ERROR: 'isubr' was previously called as a subroutine + entry isubr() + continue ! force transition to execution part + entry implicit() + implicit = 666 ! ok, just ensure that it works +end function + +function chfunc() result(chr) + character(len=1) :: chr + character(len=2) :: chr1 + !ERROR: Result of ENTRY is not compatible with result of containing function + entry chfunc1() result(chr1) +end function + +subroutine externals + !ERROR: 'subr' is already defined as a global identifier + entry subr + !ERROR: 'ifunc' is already defined as a global identifier + entry ifunc + !ERROR: 'm1' is already defined as a global identifier + entry m1 + !ERROR: 'iok1' is already defined as a global identifier + entry iok1 + integer :: ix + ix = iproc() + !ERROR: 'iproc' was previously called as a function + entry iproc +end subroutine + +module m2 + external m2entry2 + contains + subroutine m2subr1 + entry m2entry1 ! ok + entry m2entry2 ! ok + entry m2entry3 ! ok + end subroutine +end module + +subroutine usem2 + use m2 + interface + subroutine simplesubr + end subroutine + end interface + procedure(simplesubr), pointer :: p + p => m2subr1 ! ok + p => m2entry1 ! ok + p => m2entry2 ! ok + p => m2entry3 ! ok +end subroutine + +module m3 + interface + module subroutine m3entry1 + end subroutine + end interface + contains + subroutine m3subr1 + !ERROR: 'm3entry1' is already declared in this scoping unit + entry m3entry1 + end subroutine +end module + +function inone + implicit none + integer :: inone + !ERROR: No explicit type declared for 'implicitbad1' + entry implicitbad1 + inone = 0 ! force transition to execution part + !ERROR: No explicit type declared for 'implicitbad2' + entry implicitbad2 +end From ff8dad10bffb70666ba3b4ff229dc4b608f9e116 Mon Sep 17 00:00:00 2001 From: David Truby Date: Thu, 19 Mar 2020 11:04:28 +0000 Subject: [PATCH 101/345] Add explicit nullptr check in initialisation of inDoConstruct. This explicit check is needed as we are using braced initialisation here so implicit narrowing conversions (such as pointer to bool) are not allowed. --- lib/Semantics/check-do-forall.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Semantics/check-do-forall.cpp b/lib/Semantics/check-do-forall.cpp index 6dfd51c8a823..58b434b18a60 100644 --- a/lib/Semantics/check-do-forall.cpp +++ b/lib/Semantics/check-do-forall.cpp @@ -912,7 +912,7 @@ void DoForallChecker::CheckForBadLeave( static bool StmtMatchesConstruct(const parser::Name *stmtName, StmtType stmtType, const parser::Name *constructName, const ConstructNode &construct) { - bool inDoConstruct{MaybeGetDoConstruct(construct)}; + bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr}; if (!stmtName) { return inDoConstruct; // Unlabeled statements match all DO constructs } else if (constructName && constructName->source == stmtName->source) { From d1c7184159b2d3c542a8f36c58a0c817e7506845 Mon Sep 17 00:00:00 2001 From: Patrick McCormick Date: Tue, 25 Feb 2020 16:22:14 -0700 Subject: [PATCH 102/345] A rework of the cmake build components for in and out of tree builds. In general all the basic functionality seems to work and removes some redundancy and more complicated features in favor of borrowing infrastructure from LLVM build configurations. Here's a quick summary of details and remaining issues: * Testing has spanned Ubuntu 18.04 & 19.10, CentOS 7, RHEL 8, and MacOS/darwin. Architectures include x86_64 and Arm. Without access to Window nothing has been tested there yet. * As we change file and directory naming schemes (i.e., capitalization) some odd things can occur on MacOS systems with case preserving but not case senstive file system configurations. Can be painful and certainly something to watch out for as any any such changes continue. * Testing infrastructure still needs to be tuned up and worked on. Note that there do appear to be cases of some tests hanging (on MacOS in particular). They appear unrelated to the build process. * Shared library configurations need testing (and probably fixing). * Tested both standalone and 'in-mono repo' builds. Changes for supporting the mono repo builds will require LLVM-level changes that are straightforward when the time comes. * The configuration contains a work-around for LLVM's C++ standard mode passing down into Flang/F18 builds (i.e., LLVM CMake configuration would force a -std=c++11 flag to show up in command line arguments. The current configuration removes that automatically and is more strict in following new CMake guidelines for enforcing C++17 mode across all the CMake files. * Cleaned up a lot of repetition in the command line arguments. It is likely that more work is still needed to both allow for customization and working around CMake defailts (or those inherited from LLVM's configuration files). On some platforms agressive optimization flags (e.g. -O3) can actually break builds due to the inlining of templates in .cpp source files that then no longer are available for use cases outside those source files (shows up as link errors). Sticking at -O2 appears to fix this. Currently this CMake configuration forces this in release mode but at the cost of stomping on any CMake, or user customized, settings for the release flags. * Made the lit tests non-source directory dependent where appropriate. This is done by configuring certain test shell files to refer to the correct paths whether an in or out of tree build is being performed. These configured files are output in the build directory. A %B substitution is introduced in lit to refer to the build directory, mirroring the %S substitution for the source directory, so that the tests can refer to the configured shell scripts. Co-authored-by: David Truby --- .gitignore | 2 + CMakeLists.txt | 448 +++++++++++++----- README.md | 25 - cmake/modules/AddFlang.cmake | 141 ++++++ cmake/modules/CMakeLists.txt | 74 +++ cmake/modules/FlangConfig.cmake.in | 13 + include/CMakeLists.txt | 1 + include/flang/Version.inc.in | 5 + lib/CMakeLists.txt | 8 - lib/Common/CMakeLists.txt | 9 +- lib/Decimal/CMakeLists.txt | 9 +- lib/Evaluate/CMakeLists.txt | 9 +- lib/Parser/CMakeLists.txt | 9 +- lib/Semantics/CMakeLists.txt | 9 +- test/CMakeLists.txt | 16 +- test/Semantics/CMakeLists.txt | 1 + test/Semantics/allocate01.f90 | 2 +- test/Semantics/allocate02.f90 | 2 +- test/Semantics/allocate03.f90 | 2 +- test/Semantics/allocate04.f90 | 2 +- test/Semantics/allocate05.f90 | 2 +- test/Semantics/allocate06.f90 | 2 +- test/Semantics/allocate07.f90 | 2 +- test/Semantics/allocate08.f90 | 2 +- test/Semantics/allocate09.f90 | 2 +- test/Semantics/allocate10.f90 | 2 +- test/Semantics/allocate11.f90 | 2 +- test/Semantics/allocate12.f90 | 2 +- test/Semantics/allocate13.f90 | 2 +- test/Semantics/altreturn01.f90 | 2 +- test/Semantics/altreturn02.f90 | 2 +- test/Semantics/altreturn03.f90 | 2 +- test/Semantics/altreturn04.f90 | 2 +- test/Semantics/altreturn05.f90 | 2 +- test/Semantics/assign01.f90 | 2 +- test/Semantics/assign02.f90 | 2 +- test/Semantics/assign03.f90 | 2 +- test/Semantics/assign04.f90 | 2 +- test/Semantics/bad-forward-type.f90 | 2 +- test/Semantics/bindings01.f90 | 2 +- test/Semantics/block-data01.f90 | 2 +- test/Semantics/blockconstruct01.f90 | 2 +- test/Semantics/blockconstruct02.f90 | 2 +- test/Semantics/blockconstruct03.f90 | 2 +- test/Semantics/c_f_pointer.f90 | 2 +- test/Semantics/call01.f90 | 2 +- test/Semantics/call02.f90 | 2 +- test/Semantics/call03.f90 | 2 +- test/Semantics/call04.f90 | 2 +- test/Semantics/call05.f90 | 2 +- test/Semantics/call06.f90 | 2 +- test/Semantics/call07.f90 | 2 +- test/Semantics/call08.f90 | 2 +- test/Semantics/call09.f90 | 2 +- test/Semantics/call10.f90 | 2 +- test/Semantics/call11.f90 | 2 +- test/Semantics/call12.f90 | 2 +- test/Semantics/call13.f90 | 2 +- test/Semantics/call14.f90 | 2 +- test/Semantics/call15.f90 | 2 +- test/Semantics/canondo16.f90 | 4 +- test/Semantics/coarrays01.f90 | 2 +- test/Semantics/complex01.f90 | 2 +- test/Semantics/computed-goto01.f90 | 2 +- test/Semantics/computed-goto02.f90 | 2 +- test/Semantics/critical01.f90 | 2 +- test/Semantics/critical02.f90 | 2 +- test/Semantics/critical03.f90 | 2 +- test/Semantics/data01.f90 | 2 +- test/Semantics/data02.f90 | 2 +- test/Semantics/deallocate01.f90 | 2 +- test/Semantics/deallocate04.f90 | 2 +- test/Semantics/deallocate05.f90 | 2 +- test/Semantics/doconcurrent01.f90 | 2 +- test/Semantics/doconcurrent05.f90 | 2 +- test/Semantics/doconcurrent06.f90 | 2 +- test/Semantics/doconcurrent08.f90 | 2 +- test/Semantics/dosemantics01.f90 | 2 +- test/Semantics/dosemantics02.f90 | 2 +- test/Semantics/dosemantics03.f90 | 2 +- test/Semantics/dosemantics04.f90 | 2 +- test/Semantics/dosemantics05.f90 | 2 +- test/Semantics/dosemantics06.f90 | 2 +- test/Semantics/dosemantics07.f90 | 2 +- test/Semantics/dosemantics08.f90 | 2 +- test/Semantics/dosemantics09.f90 | 2 +- test/Semantics/dosemantics10.f90 | 2 +- test/Semantics/dosemantics11.f90 | 2 +- test/Semantics/dosemantics12.f90 | 2 +- test/Semantics/entry01.f90 | 2 +- test/Semantics/equivalence01.f90 | 2 +- test/Semantics/expr-errors01.f90 | 2 +- test/Semantics/expr-errors02.f90 | 2 +- test/Semantics/forall01.f90 | 2 +- test/Semantics/if_arith01.f90 | 2 +- test/Semantics/if_arith02.f90 | 2 +- test/Semantics/if_arith03.f90 | 2 +- test/Semantics/if_arith04.f90 | 2 +- test/Semantics/if_construct01.f90 | 2 +- test/Semantics/if_construct02.f90 | 2 +- test/Semantics/if_stmt01.f90 | 2 +- test/Semantics/if_stmt02.f90 | 2 +- test/Semantics/if_stmt03.f90 | 2 +- test/Semantics/implicit01.f90 | 2 +- test/Semantics/implicit02.f90 | 2 +- test/Semantics/implicit03.f90 | 2 +- test/Semantics/implicit04.f90 | 2 +- test/Semantics/implicit05.f90 | 2 +- test/Semantics/implicit06.f90 | 2 +- test/Semantics/implicit07.f90 | 2 +- test/Semantics/implicit08.f90 | 2 +- test/Semantics/init01.f90 | 2 +- test/Semantics/int-literals.f90 | 2 +- test/Semantics/io01.f90 | 2 +- test/Semantics/io02.f90 | 2 +- test/Semantics/io03.f90 | 2 +- test/Semantics/io04.f90 | 2 +- test/Semantics/io05.f90 | 2 +- test/Semantics/io06.f90 | 2 +- test/Semantics/io07.f90 | 2 +- test/Semantics/io08.f90 | 2 +- test/Semantics/io09.f90 | 2 +- test/Semantics/io10.f90 | 2 +- test/Semantics/kinds02.f90 | 2 +- test/Semantics/kinds04.f90 | 2 +- test/Semantics/misc-declarations.f90 | 2 +- test/Semantics/namelist01.f90 | 2 +- test/Semantics/null01.f90 | 2 +- test/Semantics/nullify01.f90 | 2 +- test/Semantics/nullify02.f90 | 2 +- test/Semantics/omp-atomic.f90 | 2 +- test/Semantics/omp-clause-validity01.f90 | 2 +- test/Semantics/omp-declarative-directive.f90 | 2 +- test/Semantics/omp-device-constructs.f90 | 2 +- test/Semantics/omp-loop-association.f90 | 2 +- test/Semantics/omp-nested01.f90 | 2 +- test/Semantics/omp-resolve01.f90 | 2 +- test/Semantics/omp-resolve02.f90 | 2 +- test/Semantics/omp-resolve03.f90 | 2 +- test/Semantics/omp-resolve04.f90 | 2 +- test/Semantics/omp-resolve05.f90 | 2 +- test/Semantics/resolve01.f90 | 2 +- test/Semantics/resolve02.f90 | 2 +- test/Semantics/resolve03.f90 | 2 +- test/Semantics/resolve04.f90 | 2 +- test/Semantics/resolve05.f90 | 2 +- test/Semantics/resolve06.f90 | 2 +- test/Semantics/resolve07.f90 | 2 +- test/Semantics/resolve08.f90 | 2 +- test/Semantics/resolve09.f90 | 2 +- test/Semantics/resolve10.f90 | 2 +- test/Semantics/resolve11.f90 | 2 +- test/Semantics/resolve12.f90 | 2 +- test/Semantics/resolve13.f90 | 2 +- test/Semantics/resolve14.f90 | 2 +- test/Semantics/resolve15.f90 | 2 +- test/Semantics/resolve16.f90 | 2 +- test/Semantics/resolve17.f90 | 2 +- test/Semantics/resolve18.f90 | 2 +- test/Semantics/resolve19.f90 | 2 +- test/Semantics/resolve20.f90 | 2 +- test/Semantics/resolve21.f90 | 2 +- test/Semantics/resolve22.f90 | 2 +- test/Semantics/resolve23.f90 | 2 +- test/Semantics/resolve24.f90 | 2 +- test/Semantics/resolve25.f90 | 2 +- test/Semantics/resolve26.f90 | 2 +- test/Semantics/resolve27.f90 | 2 +- test/Semantics/resolve28.f90 | 2 +- test/Semantics/resolve29.f90 | 2 +- test/Semantics/resolve30.f90 | 2 +- test/Semantics/resolve31.f90 | 2 +- test/Semantics/resolve32.f90 | 2 +- test/Semantics/resolve33.f90 | 2 +- test/Semantics/resolve34.f90 | 2 +- test/Semantics/resolve35.f90 | 2 +- test/Semantics/resolve36.f90 | 3 +- test/Semantics/resolve37.f90 | 2 +- test/Semantics/resolve38.f90 | 2 +- test/Semantics/resolve39.f90 | 2 +- test/Semantics/resolve40.f90 | 2 +- test/Semantics/resolve41.f90 | 2 +- test/Semantics/resolve42.f90 | 2 +- test/Semantics/resolve43.f90 | 2 +- test/Semantics/resolve44.f90 | 2 +- test/Semantics/resolve45.f90 | 2 +- test/Semantics/resolve46.f90 | 2 +- test/Semantics/resolve47.f90 | 2 +- test/Semantics/resolve48.f90 | 2 +- test/Semantics/resolve49.f90 | 2 +- test/Semantics/resolve50.f90 | 2 +- test/Semantics/resolve51.f90 | 2 +- test/Semantics/resolve52.f90 | 2 +- test/Semantics/resolve53.f90 | 2 +- test/Semantics/resolve54.f90 | 2 +- test/Semantics/resolve55.f90 | 2 +- test/Semantics/resolve56.f90 | 2 +- test/Semantics/resolve57.f90 | 2 +- test/Semantics/resolve58.f90 | 2 +- test/Semantics/resolve59.f90 | 2 +- test/Semantics/resolve60.f90 | 2 +- test/Semantics/resolve61.f90 | 2 +- test/Semantics/resolve62.f90 | 2 +- test/Semantics/resolve63.f90 | 2 +- test/Semantics/resolve64.f90 | 2 +- test/Semantics/resolve65.f90 | 2 +- test/Semantics/resolve66.f90 | 2 +- test/Semantics/resolve67.f90 | 2 +- test/Semantics/resolve68.f90 | 2 +- test/Semantics/resolve69.f90 | 2 +- test/Semantics/resolve70.f90 | 2 +- test/Semantics/resolve71.f90 | 2 +- test/Semantics/resolve72.f90 | 2 +- test/Semantics/resolve73.f90 | 2 +- test/Semantics/resolve74.f90 | 2 +- test/Semantics/resolve75.f90 | 2 +- test/Semantics/resolve76.f90 | 2 +- test/Semantics/resolve77.f90 | 2 +- test/Semantics/resolve78.f90 | 2 +- test/Semantics/resolve79.f90 | 2 +- test/Semantics/resolve80.f90 | 2 +- test/Semantics/resolve81.f90 | 2 +- test/Semantics/resolve82.f90 | 2 +- test/Semantics/resolve83.f90 | 2 +- test/Semantics/resolve84.f90 | 2 +- test/Semantics/resolve85.f90 | 2 +- test/Semantics/separate-mp01.f90 | 2 +- test/Semantics/separate-mp02.f90 | 2 +- test/Semantics/stop01.f90 | 2 +- test/Semantics/structconst01.f90 | 2 +- test/Semantics/structconst02.f90 | 2 +- test/Semantics/structconst03.f90 | 2 +- test/Semantics/structconst04.f90 | 2 +- test/Semantics/test_any.sh | 4 +- .../{test_errors.sh => test_errors.sh.in} | 4 +- test/lit.cfg.py | 4 +- tools/CMakeLists.txt | 1 + tools/f18-parse-demo/CMakeLists.txt | 13 + .../f18-parse-demo.cpp | 0 .../{f18 => f18-parse-demo}/stub-evaluate.cpp | 0 tools/f18/CMakeLists.txt | 62 +-- tools/f18/{flang.sh => flang.sh.in} | 2 +- unittests/CMakeLists.txt | 8 - unittests/Decimal/CMakeLists.txt | 11 +- unittests/Evaluate/CMakeLists.txt | 9 +- unittests/Runtime/CMakeLists.txt | 19 +- 246 files changed, 857 insertions(+), 496 deletions(-) create mode 100644 cmake/modules/AddFlang.cmake create mode 100644 cmake/modules/CMakeLists.txt create mode 100644 cmake/modules/FlangConfig.cmake.in create mode 100644 include/CMakeLists.txt create mode 100644 include/flang/Version.inc.in create mode 100644 test/Semantics/CMakeLists.txt rename test/Semantics/{test_errors.sh => test_errors.sh.in} (93%) create mode 100644 tools/f18-parse-demo/CMakeLists.txt rename tools/{f18 => f18-parse-demo}/f18-parse-demo.cpp (100%) rename tools/{f18 => f18-parse-demo}/stub-evaluate.cpp (100%) rename tools/f18/{flang.sh => flang.sh.in} (95%) diff --git a/.gitignore b/.gitignore index c45f199bbfe0..4da4ee1178ba 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ CMakeCache.txt */*/Makefile cmake_install.cmake formatted +.DS_Store +.vs_code diff --git a/CMakeLists.txt b/CMakeLists.txt index 8883fb7e7597..54c5d52c45f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,97 +1,140 @@ -#===-- CMakeLists.txt ------------------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - cmake_minimum_required(VERSION 3.9.0) -option(LINK_WITH_FIR "Link driver with FIR and LLVM" ON) +# RPATH settings on macOS do not affect INSTALL_NAME. +if (POLICY CMP0068) + cmake_policy(SET CMP0068 NEW) + set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON) +endif() -# Pass -DGCC=... to cmake to use a specific gcc installation. -if( GCC ) - set(CMAKE_CXX_COMPILER "${GCC}/bin/g++") - set(CMAKE_CC_COMPILER "${GCC}/bin/gcc") - set(CMAKE_BUILD_RPATH "${GCC}/lib64") - set(CMAKE_INSTALL_RPATH "${GCC}/lib64") -endif() -if(BUILD_WITH_CLANG) - file(TO_CMAKE_PATH "${BUILD_WITH_CLANG}" CLANG_PATH) - set(CMAKE_CXX_COMPILER "${CLANG_PATH}/bin/clang++") - set(CMAKE_CC_COMPILER "${CLANG_PATH}/bin/clang") - if(GCC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wstring-conversion") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcovered-switch-default") +# Include file check macros honor CMAKE_REQUIRED_LIBRARIES. +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) endif() -# Set RPATH in every executable, overriding the default setting. -# If you set this first variable back to true (the default), -# also set the second one. -set(CMAKE_SKIP_BUILD_RPATH false) -set(CMAKE_BUILD_WITH_INSTALL_RPATH false) +# option() honors normal variables. +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" "${CMAKE_INSTALL_RPATH}") +option(LINK_WITH_FIR "Link driver with FIR and LLVM" ON) -# Reminder: Setting CMAKE_CXX_COMPILER must be done before calling project() +# Flang requires C++17. +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) -project(f18 CXX) +set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -if( NOT CMAKE_BUILD_TYPE ) - set( CMAKE_BUILD_TYPE Debug ) +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE) + message(FATAL_ERROR "In-source builds are not allowed. \ + Please create a directory and run cmake from there,\ + passing the path to this source directory as the last argument.\ + This process created the file `CMakeCache.txt' and the directory\ + `CMakeFiles'. Please delete them.") endif() -message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}" ) +# Add Flang-centric modules to cmake path. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") +include(AddFlang) -find_package(LLVM REQUIRED CONFIG) -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION} in ${LLVM_DIR}") -# If LLVM links to zlib we need the imported targets so we can too. -if(LLVM_ENABLE_ZLIB) - find_package(ZLIB REQUIRED) -endif() +# Check for a standalone build and configure as appropriate from +# there. +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) +message("Building Flang as a standalone project.") +project(Flang) + + set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + if (NOT MSVC_IDE) + set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS} + CACHE BOOL "Enable assertions") + # Assertions follow llvm's configuration. + mark_as_advanced(LLVM_ENABLE_ASSERTIONS) + endif() + + # We need a pre-built/installed version of LLVM. + find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_PATH}") + list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR}) -list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR}) + # If LLVM links to zlib we need the imported targets so we can too. + if(LLVM_ENABLE_ZLIB) + find_package(ZLIB REQUIRED) + endif() + + include(CMakeParseArguments) + include(AddLLVM) + include(HandleLLVMOptions) + include(VersionFromVCS) + + if(LINK_WITH_FIR) + include(TableGen) + include(AddMLIR) + find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH) + endif() + + option(LLVM_ENABLE_WARNINGS "Enable compiler warnings." ON) + option(LLVM_INSTALL_TOOLCHAIN_ONLY + "Only include toolchain files in the 'install' target." OFF) + option(LLVM_FORCE_USE_OLD_HOST_TOOLCHAIN + "Set to ON to force using an old, unsupported host toolchain." OFF) -include(AddLLVM) -# Get names for the LLVM libraries -# -# The full list of LLVM components can be obtained with -# -# llvm-config --components -# -# Similarly, the (static) libraries corresponding to some -# components (default is 'all') can be obtained with -# -# llvm-config --libs --link-static [component ...] -# -# See also -# http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project -# https://stackoverflow.com/questions/41924375/llvm-how-to-specify-all-link-libraries-as-input-to-llvm-map-components-to-libna -# https://stackoverflow.com/questions/33948633/how-do-i-link-when-building-with-llvm-libraries + # Add LLVM include files as if they were SYSTEM because there are complex unused + # parameter issues that may or may not appear depending on the environments and + # compilers (ifdefs are involved). This allows warnings from LLVM headers to be + # ignored while keeping -Wunused-parameter a fatal error inside f18 code base. + # This may have to be fine-tuned if flang headers are consider part of this + # LLVM_INCLUDE_DIRS when merging in the monorepo (Warning from flang headers + # should not be suppressed). + include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) -# Add LLVM include files as if they were SYSTEM because there are complex unused -# parameter issues that may or may not appear depending on the environments and -# compilers (ifdefs are involved). This allows warnings from LLVM headers to be -# ignored while keeping -Wunused-parameter a fatal error inside f18 code base. -# This may have to be fine-tuned if flang headers are consider part of this -# LLVM_INCLUDE_DIRS when merging in the monorepo (Warning from flang headers -# should not be suppressed). -include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) -add_definitions(${LLVM_DEFINITIONS}) + # LLVM's cmake configuration files currently sneak in a c++11 flag. + # We look for it here and remove it from Flang's compile flags to + # avoid some mixed compilation flangs (e.g. -std=c++11 ... -std=c++17). + if (DEFINED LLVM_CXX_STD) + message("LLVM configuration set a C++ standard: ${LLVM_CXX_STD}") + if (NOT LLVM_CXX_STD EQUAL "c++17") + message("Flang: Overriding LLVM's 'cxx_std' setting...") + message(" removing '-std=${LLVM_CXX_STD}'") + message(" CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}'") + string(REPLACE " -std=${LLVM_CXX_STD}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + message(" [NEW] CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}'") + endif() + endif() -# LLVM_LIT_EXTERNAL store in cache so it could be used by AddLLVM.cmake -set(LLVM_EXTERNAL_LIT ${LLVM_TOOLS_BINARY_DIR}/llvm-lit CACHE STRING "Command used to spawn lit") + link_directories("${LLVM_LIBRARY_DIR}") + + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + + set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}") + set(LLVM_EXTERNAL_LIT "${LLVM_TOOLS_BINARY_DIR}/llvm-lit" CACHE STRING "Command used to spawn lit") + + option(FLANG_INCLUDE_TESTS + "Generate build targets for the Flang unit tests." + ON) + add_custom_target(check-all DEPENDS check-flang) +else() + option(FLANG_INCLUDE_TESTS + "Generate build targets for the Flang unit tests." + ${LLVM_INCLUDE_TESTS}) + set(FLANG_BINARY_DIR ${CMAKE_BINARY_DIR}/tools/flang) + set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}") + if (LINK_WITH_FIR) + set(MLIR_MAIN_SRC_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include ) # --src-root + set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include ) # --includedir + set(MLIR_TABLEGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/tools/mlir/include) + set(MLIR_TABLEGEN_EXE $) + include_directories(SYSTEM ${MLIR_INCLUDE_DIR}) + include_directories(SYSTEM ${MLIR_TABLEGEN_OUTPUT_DIR}) + endif() +endif() if(LINK_WITH_FIR) - include(TableGen) - include(AddMLIR) - find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} - NO_DEFAULT_PATH) # tco tool and FIR lib output directories set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin) set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/lib) @@ -102,62 +145,227 @@ if(LINK_WITH_FIR) message(STATUS "LLVM libraries: ${LLVM_COMMON_LIBS}") endif() -if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(BUILD_WITH_CLANG_LIBRARIES) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++ -I${BUILD_WITH_CLANG_LIBRARIES}/include/c++/v1 -DCLANG_LIBRARIES") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-rpath,${BUILD_WITH_CLANG_LIBRARIES}/lib -L${BUILD_WITH_CLANG_LIBRARIES}/lib") - else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") - endif() - if(GCC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") - endif() - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Werror") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wimplicit-fallthrough") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdelete-non-virtual-dtor") - set(CMAKE_CXX_FLAGS_RELEASE "-O2") - set(CMAKE_CXX_FLAGS_MINSIZEREL "-O2 '-DCHECK=(void)'") - set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUGF18") - - # Building shared libraries is death on performance with GCC by default - # due to the need to preserve the right to override external entry points - # at dynamic link time. -fno-semantic-interposition waives that right and - # recovers a little bit of that performance. - if (BUILD_SHARED_LIBS AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-semantic-interposition") - endif() +# Add Flang-centric modules to cmake path. +include_directories(BEFORE + ${FLANG_BINARY_DIR}/include + ${FLANG_SOURCE_DIR}/include) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") + +if (NOT DEFAULT_SYSROOT) + set(DEFAULT_SYSROOT "" CACHE PATH + "The to use for the system root for all compiler invocations (--sysroot=).") +endif() + +if (NOT ENABLE_LINKER_BUILD_ID) + set(ENABLE_LINKER_BUILD_ID OFF CACHE BOOL "pass --build-id to ld") endif() +set(FLANG_DEFAULT_LINKER "" CACHE STRING + "Default linker to use (linker name or absolute path, empty for platform default)") + +set(FLANG_DEFAULT_RTLIB "" CACHE STRING + "Default Fortran runtime library to use (\"libFortranRuntime\"), leave empty for platform default.") + +if (NOT(FLANG_DEFAULT_RTLIB STREQUAL "")) + message(WARNING "Resetting Flang's default runtime library to use platform default.") + set(FLANG_DEFAULT_RTLIB "" CACHE STRING + "Default runtime library to use (empty for platform default)" FORCE) +endif() + + + +set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") +# Override LLVM versioning for now... set(FLANG_VERSION_MAJOR "0") set(FLANG_VERSION_MINOR "1") set(FLANG_VERSION_PATCHLEVEL "0") + + +if (NOT DEFINED FLANG_VERSION_MAJOR) + set(FLANG_VERSION_MAJOR ${LLVM_VERSION_MAJOR}) +endif() + +if (NOT DEFINED FLANG_VERSION_MINOR) + set(FLANG_VERSION_MINOR ${LLVM_VERSION_MINOR}) +endif() + +if (NOT DEFINED FLANG_VERSION_PATCHLEVEL) + set(FLANG_VERSION_PATCHLEVEL ${LLVM_VERSION_PATCH}) +endif() + +# Unlike PACKAGE_VERSION, FLANG_VERSION does not include LLVM_VERSION_SUFFIX. set(FLANG_VERSION "${FLANG_VERSION_MAJOR}.${FLANG_VERSION_MINOR}.${FLANG_VERSION_PATCHLEVEL}") -message(STATUS "FLANG version: ${FLANG_VERSION}") +message(STATUS "Flang version: ${FLANG_VERSION}") +# Flang executable version information +set(FLANG_EXECUTABLE_VERSION + "${FLANG_VERSION_MAJOR}" CACHE STRING + "Major version number to appended to the flang executable name.") +set(LIBFLANG_LIBRARY_VERSION + "${FLANG_VERSION_MAJOR}" CACHE STRING + "Major version number to appended to the libflang library.") -set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -set(FLANG_TOOLS_DIR ${FLANG_BINARY_DIR}/tools/f18/bin) +mark_as_advanced(FLANG_EXECUTABLE_VERSION LIBFLANG_LIBRARY_VERSION) -include_directories(BEFORE - ${FLANG_BINARY_DIR}/include - ${FLANG_SOURCE_DIR}/include - ) +set(FLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING + "Vendor-specific Flang version information.") +set(FLANG_VENDOR_UTI "org.llvm.flang" CACHE STRING + "Vendor-specific uti.") -enable_testing() +if (FLANG_VENDOR) + add_definitions(-DFLANG_VENDOR="${FLANG_VENDOR} ") +endif() -add_subdirectory(include/flang) -add_subdirectory(lib) -add_subdirectory(runtime) -add_subdirectory(unittests) -add_subdirectory(tools) -add_subdirectory(test) +set(FLANG_REPOSITORY_STRING "" CACHE STRING + "Vendor-specific text for showing the repository the source is taken from.") +if (FLANG_REPOSITORY_STRING) + add_definitions(-DFLANG_REPOSITORY_STRING="${FLANG_REPOSITORY_STRING}") +endif() +# Configure Flang's Version.inc file. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/flang/Version.inc.in + ${CMAKE_CURRENT_BINARY_DIR}/include/flang/Version.inc) +# Configure Flang's version info header file. configure_file( ${FLANG_SOURCE_DIR}/include/flang/Config/config.h.cmake ${FLANG_BINARY_DIR}/include/flang/Config/config.h) + +# Add global F18 flags. +set(CMAKE_CXX_FLAGS "-fno-rtti -fno-exceptions -pedantic -Wall -Wextra -Werror -Wcast-qual -Wimplicit-fallthrough -Wdelete-non-virtual-dtor ${CMAKE_CXX_FLAGS}") + +# Builtin check_cxx_compiler_flag doesn't seem to work correctly +macro(check_compiler_flag flag resultVar) + unset(${resultVar} CACHE) + check_cxx_compiler_flag("${flag}" ${resultVar}) +endmacro() + +check_compiler_flag("-Werror -Wno-deprecated-copy" CXX_SUPPORTS_NO_DEPRECATED_COPY_FLAG) +if (CXX_SUPPORTS_NO_DEPRECATED_COPY_FLAG) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy") +endif() +check_compiler_flag("-Wstring-conversion" CXX_SUPPORTS_NO_STRING_CONVERSION_FLAG) +if (CXX_SUPPORTS_NO_STRING_CONVERSION_FLAG) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion") +endif() + +# Add appropriate flags for GCC +if (LLVM_COMPILER_IS_GCC_COMPATIBLE) + + if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -fno-semantic-interposition") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument -Wstring-conversion \ + -Wcovered-switch-default") + endif() # Clang. + + check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) + if (CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types") + endif() + + # Add to or adjust build type flags. + # + # TODO: This needs some extra thought. CMake's default for release builds + # is -O3, which can cause build failures on certain platforms (and compilers) + # with the current code base -- some templated functions are inlined and don't + # become available at link time when using -O3 (with Clang under MacOS/darwin). + # If we reset CMake's default flags we also clobber any user provided settings; + # make it difficult to customize a build in this regard... The setup below + # has this side effect but enables successful builds across multiple platforms + # in release mode... + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGF18") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -DCHECK=\"(void)\"") # do we need -O2 here? + set(CMAKE_CXX_FLAGS_RELEASE "-O2") + + # Building shared libraries is bad for performance with GCC by default + # due to the need to preserve the right to override external entry points + if (BUILD_SHARED_LIBS AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-semantic-interposition") + endif() + +endif() + +list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS) + +# Determine HOST_LINK_VERSION on Darwin. +set(HOST_LINK_VERSION) +if (APPLE) + set(LD_V_OUTPUT) + execute_process( + COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1" + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LD_V_OUTPUT) + if (NOT HAD_ERROR) + if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*") + string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT}) + elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*") + string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT}) + endif() + else() + message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}") + endif() +endif() + +include(CMakeParseArguments) +include(AddFlang) + + +add_subdirectory(include) +add_subdirectory(lib) +add_subdirectory(cmake/modules) + +option(FLANG_BUILD_TOOLS + "Build the Flang tools. If OFF, just generate build targets." ON) +if (FLANG_BUILD_TOOLS) + add_subdirectory(tools) +endif() +add_subdirectory(runtime) + +if (FLANG_INCLUDE_TESTS) + enable_testing() + add_subdirectory(test) + add_subdirectory(unittests) +endif() + +# TODO: Add doxygen support. +#option(FLANG_INCLUDE_DOCS "Generate build targets for the Flang docs." +# ${LLVM_INCLUDE_DOCS}) +#if (FLANG_INCLUDE_DOCS) +# add_subdirectory(documentation) +#endif() + +# Custom target to install Flang libraries. +add_custom_target(flang-libraries) +set_target_properties(flang-libraries PROPERTIES FOLDER "Misc") + +if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-flang-libraries + DEPENDS flang-libraries + COMPONENT flang-libraries) +endif() + +get_property(FLANG_LIBS GLOBAL PROPERTY FLANG_LIBS) +if (FLANG_LIBS) + list(REMOVE_DUPLICATES FLANG_LIBS) + foreach(lib ${FLANG_LIBS}) + add_dependencies(flang-libraries ${lib}) + if (NOT LLVM_ENABLE_IDE) + add_dependencies(install-flang-libraries install-${lib}) + endif() + endforeach() +endif() + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(DIRECTORY include/flang + DESTINATION include + COMPONENT flang-headers + FILES_MATCHING + PATTERN "*.def" + PATTERN "*.h" + PATTERN "*.inc" + PATTERN "*.td" + PATTERN "config.h" EXCLUDE + PATTERN ".git" EXCLUDE + PATTERN "CMakeFiles" EXCLUDE) +endif() diff --git a/README.md b/README.md index d8e2cbdef63b..926abc0ecf5d 100644 --- a/README.md +++ b/README.md @@ -150,15 +150,6 @@ or ``` CXX=/opt/gcc-7.2/bin/g++-7.2 cmake ... ``` -There's a third option! -The CMakeList.txt file uses the variable GCC -as the path to the bin directory containing the C++ compiler. - -GCC can be defined on the cmake command line -where `` is the path to a GCC installation with bin, lib, etc: -``` -cmake -DGCC= ... -``` ### Building f18 with clang @@ -166,27 +157,11 @@ To build f18 with clang, cmake needs to know how to find clang++ and the GCC library and tools that were used to build clang++. -The CMakeList.txt file expects either CXX or BUILD_WITH_CLANG to be set. - CXX should include the full path to clang++ or clang++ should be found on your PATH. ``` export CXX=clang++ ``` -BUILD_WITH_CLANG can be defined on the cmake command line -where `` -is the path to a clang installation with bin, lib, etc: -``` -cmake -DBUILD_WITH_CLANG= -``` -Or GCC can be defined on the f18 cmake command line -where `` is the path to a GCC installation with bin, lib, etc: -``` -cmake -DGCC= ... -``` -To use f18 after it is built, -the environment variables PATH and LD_LIBRARY_PATH -must be set to use GCC and its associated libraries. ### Installation Directory diff --git a/cmake/modules/AddFlang.cmake b/cmake/modules/AddFlang.cmake new file mode 100644 index 000000000000..84610a633a04 --- /dev/null +++ b/cmake/modules/AddFlang.cmake @@ -0,0 +1,141 @@ +macro(set_flang_windows_version_resource_properties name) + if (DEFINED windows_resource_file) + set_windows_version_resource_properties(${name} ${windows_resource_file} + VERSION_MAJOR ${FLANG_VERSION_MAJOR} + VERSION_MINOR ${FLANG_VERSION_MINOR} + VERSION_PATCHLEVEL ${FLANG_VERSION_PATCHLEVEL} + VERSION_STRING "${FLANG_VERSION} (${BACKEND_PACKAGE_STRING})" + PRODUCT_NAME "flang") + endif() +endmacro() + +macro(add_flang_subdirectory name) + add_llvm_subdirectory(FLANG TOOL ${name}) +endmacro() + +macro(add_flang_library name) + cmake_parse_arguments(ARG + "SHARED" + "" + "ADDITIONAL_HEADERS" + ${ARGN}) + set(srcs) + if (MSVC_IDE OR XCODE) + # Add public headers + file(RELATIVE_PATH lib_path + ${FLANG_SOURCE_DIR}/lib/ + ${CMAKE_CURRENT_SOURCE_DIR}) + if(NOT lib_path MATCHES "^[.][.]") + file( GLOB_RECURSE headers + ${FLANG_SOURCE_DIR}/include/flang/${lib_path}/*.h + ${FLANG_SOURCE_DIR}/include/flang/${lib_path}/*.def) + set_source_files_properties(${headers} PROPERTIES HEADER_FILE_ONLY ON) + + if (headers) + set(srcs ${headers}) + endif() + endif() + endif(MSVC_IDE OR XCODE) + + if (srcs OR ARG_ADDITIONAL_HEADERS) + set(srcs + ADDITIONAL_HEADERS + ${srcs} + ${ARG_ADDITIONAL_HEADERS}) # It may contain unparsed unknown args. + + endif() + + if (ARG_SHARED) + set(LIBTYPE SHARED) + else() + # llvm_add_library ignores BUILD_SHARED_LIBS if STATIC is explicitly set, + # so we need to handle it here. + if (BUILD_SHARED_LIBS) + set(LIBTYPE SHARED OBJECT) + else() + set(LIBTYPE STATIC OBJECT) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_STATIC_LIBS ${name}) + endif() + + llvm_add_library(${name} ${LIBTYPE} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) + + if (TARGET ${name}) + target_link_libraries(${name} INTERFACE ${LLVM_COMMON_LIBS}) + + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libflang") + set(export_to_flangtargets) + if (${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + "flang-libraries" IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) + set(export_to_flangtargets EXPORT FlangTargets) + set_property(GLOBAL PROPERTY FLANG_HAS_EXPORTS True) + endif() + + install(TARGETS ${name} + COMPONENT ${name} + ${export_to_flangtargets} + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + RUNTIME DESTINATION bin) + + if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-${name} + DEPENDS ${name} + COMPONENT ${name}) + endif() + + set_property(GLOBAL APPEND PROPERTY FLANG_LIBS ${name}) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_EXPORTS ${name}) + else() + # Add empty "phony" target + add_custom_target(${name}) + endif() + + set_target_properties(${name} PROPERTIES FOLDER "Flang libraries") + set_flang_windows_version_resource_properties(${name}) +endmacro(add_flang_library) + +macro(add_flang_executable name) + add_llvm_executable(${name} ${ARGN}) + set_target_properties(${name} PROPERTIES FOLDER "Flang executables") + set_flang_windows_version_resource_properties(${name}) +endmacro(add_flang_executable) + +macro(add_flang_tool name) + if (NOT FLANG_BUILD_TOOLS) + set(EXCLUDE_FROM_ALL ON) + endif() + + add_flang_executable(${name} ${ARGN}) + add_dependencies(${name} flang-resource-headers) + + if (FLANG_BUILD_TOOLS) + set(export_to_flangtargets) + if (${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) + set(export_to_flangtargets EXPORT FlangTargets) + set_property(GLOBAL PROPERTY FLANG_HAS_EXPORTS True) + endif() + + install(TARGETS ${name} + ${export_to_flangtargets} + RUNTIME DESTINATION bin + COMPONENT ${name}) + + if(NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-${name} + DEPENDS ${name} + COMPONENT ${name}) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_EXPORTS ${name}) + endif() +endmacro() + +macro(add_flang_symlink name dest) + add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) + # Always generate install targets + llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE) +endmacro() + diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt new file mode 100644 index 000000000000..4822124ca412 --- /dev/null +++ b/cmake/modules/CMakeLists.txt @@ -0,0 +1,74 @@ +# Generate a list of CMake library targets so that other CMake projects can +# link against them. LLVM calls its version of this file LLVMExports.cmake, but +# the usual CMake convention seems to be ${Project}Targets.cmake. +set(FLANG_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/flang) +set(flang_cmake_builddir "${CMAKE_BINARY_DIR}/${FLANG_INSTALL_PACKAGE_DIR}") + +# Keep this in sync with llvm/cmake/CMakeLists.txt! +set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm) +set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}") + +get_property(FLANG_EXPORTS GLOBAL PROPERTY FLANG_EXPORTS) +export(TARGETS ${FLANG_EXPORTS} FILE ${flang_cmake_builddir}/FlangTargets.cmake) + +# Generate FlangConfig.cmake for the build tree. +set(FLANG_CONFIG_CMAKE_DIR "${flang_cmake_builddir}") +set(FLANG_CONFIG_LLVM_CMAKE_DIR "${llvm_cmake_builddir}") +set(FLANG_CONFIG_EXPORTS_FILE "${flang_cmake_builddir}/FlangTargets.cmake") +set(FLANG_CONFIG_INCLUDE_DIRS + "${FLANG_SOURCE_DIR}/include" + "${FLANG_BINARY_DIR}/include" + ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/FlangConfig.cmake.in + ${flang_cmake_builddir}/FlangConfig.cmake + @ONLY) +set(FLANG_CONFIG_CMAKE_DIR) +set(FLANG_CONFIG_LLVM_CMAKE_DIR) +set(FLANG_CONFIG_EXPORTS_FILE) + +# Generate FlangConfig.cmake for the install tree. +set(FLANG_CONFIG_CODE " + # Compute the installation prefix from this LLVMConfig.cmake file location. + get_filename_component(FLANG_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)") +# Construct the proper number of get_filename_component(... PATH) +# calls to compute the installation prefix. +string(REGEX REPLACE "/" ";" _count "${FLANG_INSTALL_PACKAGE_DIR}") +foreach(p ${_count}) + set(FLANG_CONFIG_CODE "${FLANG_CONFIG_CODE} + get_filename_component(FLANG_INSTALL_PREFIX \"\${FLANG_INSTALL_PREFIX}\" PATH)") +endforeach(p) + +set(FLANG_CONFIG_CMAKE_DIR "\${FLANG_INSTALL_PREFIX}/${FLANG_INSTALL_PACKAGE_DIR}") +set(FLANG_CONFIG_LLVM_CMAKE_DIR "\${FLANG_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}") +set(FLANG_CONFIG_EXPORTS_FILE "\${FLANG_CMAKE_DIR}/FlangTargets.cmake") +set(FLANG_CONFIG_INCLUDE_DIRS "\${FLANG_INSTALL_PREFIX}/include") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/FlangConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/FlangConfig.cmake + @ONLY) + +set(FLANG_CONFIG_CODE) +set(FLANG_CONFIG_CMAKE_DIR) +set(FLANG_CONFIG_EXPORTS_FILE) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + get_property(flang_has_exports GLOBAL PROPERTY FLANG_HAS_EXPORTS) + if(flang_has_exports) + install(EXPORT FlangTargets DESTINATION ${FLANG_INSTALL_PACKAGE_DIR} + COMPONENT flang-cmake-exports) + endif() + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/FlangConfig.cmake + DESTINATION ${FLANG_INSTALL_PACKAGE_DIR} + COMPONENT flang-cmake-exports) + + if(NOT LLVM_ENABLE_IDE) + # Add a dummy target so this can be used with LLVM_DISTRIBUTION_COMPONENTS + add_custom_target(flang-cmake-exports) + add_llvm_install_targets(install-flang-cmake-exports + COMPONENT flang-cmake-exports) + endif() +endif() diff --git a/cmake/modules/FlangConfig.cmake.in b/cmake/modules/FlangConfig.cmake.in new file mode 100644 index 000000000000..3540e2df3406 --- /dev/null +++ b/cmake/modules/FlangConfig.cmake.in @@ -0,0 +1,13 @@ +# This file allows users to call find_package(Flang) and pick up our targets. + +@FLANG_CONFIG_CODE@ + +find_package(LLVM REQUIRED CONFIG + HINTS "@FLANG_CONFIG_LLVM_CMAKE_DIR@") + +set(FLANG_EXPORTED_TARGETS "@FLANG_EXPORTS@") +set(FLANG_CMAKE_DIR "FLANG_CONFIG_CMAKE_DIR@") +set(FLANG_INCLUDE_DIRS "@FLANG_CONFIG_INCLUDE_DIRS@") + +# Provide all our library targets to users. +include("@FLANG_CONFIG_EXPORTS_FILE@") diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 000000000000..e6bb9db72ff6 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(flang) diff --git a/include/flang/Version.inc.in b/include/flang/Version.inc.in new file mode 100644 index 000000000000..a0eeab2686a9 --- /dev/null +++ b/include/flang/Version.inc.in @@ -0,0 +1,5 @@ +#define FLANG_VERSION @FLANG_VERSION@ +#define FLANG_VERSION_STRING "@FLANG_VERSION@" +#define FLANG_VERSION_MAJOR @FLANG_VERSION_MAJOR@ +#define FLANG_VERSION_MINOR @FLANG_VERSION_MINOR@ +#define FLANG_VERSION_PATCHLEVEL @FLANG_VERSION_PATCHLEVEL@ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f2fe30dca5a1..ae321b872a76 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,11 +1,3 @@ -#===-- lib/CMakeLists.txt --------------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - add_subdirectory(Common) add_subdirectory(Evaluate) add_subdirectory(Decimal) diff --git a/lib/Common/CMakeLists.txt b/lib/Common/CMakeLists.txt index acbe9d125b99..f1be58f0e6d0 100644 --- a/lib/Common/CMakeLists.txt +++ b/lib/Common/CMakeLists.txt @@ -1,10 +1,3 @@ -#===-- lib/Common/CMakeLists.txt -------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# add_library(FortranCommon Fortran.cpp @@ -13,6 +6,8 @@ add_library(FortranCommon idioms.cpp ) +target_compile_features(FortranCommon PUBLIC cxx_std_17) + install (TARGETS FortranCommon ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/lib/Decimal/CMakeLists.txt b/lib/Decimal/CMakeLists.txt index 54542187aea4..92f87621fc05 100644 --- a/lib/Decimal/CMakeLists.txt +++ b/lib/Decimal/CMakeLists.txt @@ -1,16 +1,11 @@ -#===-- lib/Decimal/CMakeLists.txt ------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# add_library(FortranDecimal binary-to-decimal.cpp decimal-to-binary.cpp ) +target_compile_features(FortranDecimal PUBLIC cxx_std_17) + install (TARGETS FortranDecimal ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/lib/Evaluate/CMakeLists.txt b/lib/Evaluate/CMakeLists.txt index a3391dc40394..2b23455165aa 100644 --- a/lib/Evaluate/CMakeLists.txt +++ b/lib/Evaluate/CMakeLists.txt @@ -1,10 +1,3 @@ -#===-- lib/Evaluate/CMakeLists.txt -----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# add_library(FortranEvaluate call.cpp @@ -34,6 +27,8 @@ add_library(FortranEvaluate variable.cpp ) +target_compile_features(FortranEvaluate PUBLIC cxx_std_17) + target_link_libraries(FortranEvaluate FortranCommon FortranDecimal diff --git a/lib/Parser/CMakeLists.txt b/lib/Parser/CMakeLists.txt index a04f37c71aec..9dc6480a2e9d 100644 --- a/lib/Parser/CMakeLists.txt +++ b/lib/Parser/CMakeLists.txt @@ -1,10 +1,3 @@ -#===-- lib/Parser/CMakeLists.txt -------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# add_library(FortranParser Fortran-parsers.cpp @@ -32,6 +25,8 @@ add_library(FortranParser user-state.cpp ) +target_compile_features(FortranParser PRIVATE cxx_std_17) + target_link_libraries(FortranParser FortranCommon LLVMSupport diff --git a/lib/Semantics/CMakeLists.txt b/lib/Semantics/CMakeLists.txt index cbe9fc9b6b30..1ca03d05341f 100644 --- a/lib/Semantics/CMakeLists.txt +++ b/lib/Semantics/CMakeLists.txt @@ -1,10 +1,3 @@ -#===-- lib/Semantics/CMakeLists.txt ----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# add_library(FortranSemantics assignment.cpp @@ -43,6 +36,8 @@ add_library(FortranSemantics unparse-with-symbols.cpp ) +target_compile_features(FortranSemantics PUBLIC cxx_std_17) + target_link_libraries(FortranSemantics FortranCommon FortranEvaluate diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cdc5dd58d3ca..3e22d9f18c82 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,9 @@ # Test runner infrastructure for Flang. This configures the Flang test trees # for use by Lit, and delegates to LLVM's lit test handlers. -set(FLANG_INTRINSIC_MODULES_DIR ${FLANG_BINARY_DIR}/tools/f18/include) +set(FLANG_INTRINSIC_MODULES_DIR ${FLANG_BINARY_DIR}/include/flang) + +set(FLANG_TOOLS_DIR ${FLANG_BINARY_DIR}/bin) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in @@ -10,6 +12,8 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py ) +add_subdirectory(Semantics) + set(FLANG_TEST_PARAMS flang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py) @@ -21,10 +25,16 @@ if (LINK_WITH_FIR) list(APPEND FLANG_TEST_DEPENDS tco) endif() -add_lit_testsuite(check-all "Running the Flang regression tests" +add_custom_target(flang-test-depends DEPENDS ${FLANG_TEST_DEPENDS}) + +add_lit_testsuite(check-flang "Running the Flang regression tests" ${CMAKE_CURRENT_BINARY_DIR} PARAMS ${FLANG_TEST_PARAMS} DEPENDS ${FLANG_TEST_DEPENDS} ) -set_target_properties(check-all PROPERTIES FOLDER "Tests") +set_target_properties(check-flang PROPERTIES FOLDER "Tests") + +add_lit_testsuites(FLANG ${CMAKE_CURRENT_SOURCE_DIR} + PARAMS ${FLANG_TEST_PARAMS} + DEPENDS ${FLANG_TEST_DEPENDS}) diff --git a/test/Semantics/CMakeLists.txt b/test/Semantics/CMakeLists.txt new file mode 100644 index 000000000000..bdc81bbbe18c --- /dev/null +++ b/test/Semantics/CMakeLists.txt @@ -0,0 +1 @@ +configure_file(test_errors.sh.in ${FLANG_BINARY_DIR}/test/Semantics/test_errors.sh @ONLY) diff --git a/test/Semantics/allocate01.f90 b/test/Semantics/allocate01.f90 index 0948230a3ea2..4907a9d9e21f 100644 --- a/test/Semantics/allocate01.f90 +++ b/test/Semantics/allocate01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements ! Creating a symbol that allocate should accept diff --git a/test/Semantics/allocate02.f90 b/test/Semantics/allocate02.f90 index 13a68e811a55..16895ef35001 100644 --- a/test/Semantics/allocate02.f90 +++ b/test/Semantics/allocate02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate03.f90 b/test/Semantics/allocate03.f90 index 63598f0786df..21b093ddefe9 100644 --- a/test/Semantics/allocate03.f90 +++ b/test/Semantics/allocate03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C933_a(b1, ca3, ca4, cp3, cp3mold, cp4, cp7, cp8, bsrc) diff --git a/test/Semantics/allocate04.f90 b/test/Semantics/allocate04.f90 index 40e7562938df..9371fcb2b1af 100644 --- a/test/Semantics/allocate04.f90 +++ b/test/Semantics/allocate04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate05.f90 b/test/Semantics/allocate05.f90 index 84814b674735..e69ed9e5399f 100644 --- a/test/Semantics/allocate05.f90 +++ b/test/Semantics/allocate05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate06.f90 b/test/Semantics/allocate06.f90 index 1de258ccfb46..ae9f4f60d318 100644 --- a/test/Semantics/allocate06.f90 +++ b/test/Semantics/allocate06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements diff --git a/test/Semantics/allocate07.f90 b/test/Semantics/allocate07.f90 index 14077a24013e..5f261f332381 100644 --- a/test/Semantics/allocate07.f90 +++ b/test/Semantics/allocate07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C936(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) diff --git a/test/Semantics/allocate08.f90 b/test/Semantics/allocate08.f90 index 3e235fcc9cdc..7733b3a0767a 100644 --- a/test/Semantics/allocate08.f90 +++ b/test/Semantics/allocate08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C945_a(srca, srcb, srcc, src_complex, src_logical, & diff --git a/test/Semantics/allocate09.f90 b/test/Semantics/allocate09.f90 index 61046fb13ce2..6e20521fedd7 100644 --- a/test/Semantics/allocate09.f90 +++ b/test/Semantics/allocate09.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C946(param_ca_4_assumed, param_ta_4_assumed, param_ca_4_deferred) diff --git a/test/Semantics/allocate10.f90 b/test/Semantics/allocate10.f90 index c15dc57b4472..2746f8e1e6dc 100644 --- a/test/Semantics/allocate10.f90 +++ b/test/Semantics/allocate10.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements !TODO: mixing expr and source-expr? diff --git a/test/Semantics/allocate11.f90 b/test/Semantics/allocate11.f90 index b883edc4980a..594bd1ded385 100644 --- a/test/Semantics/allocate11.f90 +++ b/test/Semantics/allocate11.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements ! TODO: Function Pointer in allocate and derived types! diff --git a/test/Semantics/allocate12.f90 b/test/Semantics/allocate12.f90 index 41de8edc83ed..52fabf888f78 100644 --- a/test/Semantics/allocate12.f90 +++ b/test/Semantics/allocate12.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements subroutine C941_C942b_C950(xsrc, x1, a2, b2, cx1, ca2, cb1, cb2, c1) diff --git a/test/Semantics/allocate13.f90 b/test/Semantics/allocate13.f90 index b7010f5b0c89..99812f9d3df6 100644 --- a/test/Semantics/allocate13.f90 +++ b/test/Semantics/allocate13.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in ALLOCATE statements module not_iso_fortran_env diff --git a/test/Semantics/altreturn01.f90 b/test/Semantics/altreturn01.f90 index 0449ff774c36..b35d0799d154 100644 --- a/test/Semantics/altreturn01.f90 +++ b/test/Semantics/altreturn01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check calls with alt returns CALL TEST (N, *100, *200 ) diff --git a/test/Semantics/altreturn02.f90 b/test/Semantics/altreturn02.f90 index 74ff96933a83..a09df81f6ada 100644 --- a/test/Semantics/altreturn02.f90 +++ b/test/Semantics/altreturn02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check subroutine with alt return SUBROUTINE TEST (N, *, *) diff --git a/test/Semantics/altreturn03.f90 b/test/Semantics/altreturn03.f90 index 73a63860efc7..15c5ce650b96 100644 --- a/test/Semantics/altreturn03.f90 +++ b/test/Semantics/altreturn03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for various alt return error conditions SUBROUTINE TEST (N, *, *) diff --git a/test/Semantics/altreturn04.f90 b/test/Semantics/altreturn04.f90 index e3714fb92223..4a9cf5b13ee3 100644 --- a/test/Semantics/altreturn04.f90 +++ b/test/Semantics/altreturn04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Functions cannot use alt return REAL FUNCTION altreturn01(X) diff --git a/test/Semantics/altreturn05.f90 b/test/Semantics/altreturn05.f90 index cbd222cba9e7..baa8bcfa11ea 100644 --- a/test/Semantics/altreturn05.f90 +++ b/test/Semantics/altreturn05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test extension: RETURN from main program return !ok diff --git a/test/Semantics/assign01.f90 b/test/Semantics/assign01.f90 index bd41a5b5cc9f..e8ec06785843 100644 --- a/test/Semantics/assign01.f90 +++ b/test/Semantics/assign01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! 10.2.3.1(2) All masks and LHS of assignments in a WHERE must conform subroutine s1 diff --git a/test/Semantics/assign02.f90 b/test/Semantics/assign02.f90 index e97be64d6aab..c504f7a8ab1f 100644 --- a/test/Semantics/assign02.f90 +++ b/test/Semantics/assign02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Pointer assignment constraints 10.2.2.2 module m1 diff --git a/test/Semantics/assign03.f90 b/test/Semantics/assign03.f90 index 5b9fe269addc..62749641ea29 100644 --- a/test/Semantics/assign03.f90 +++ b/test/Semantics/assign03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Pointer assignment constraints 10.2.2.2 (see also assign02.f90) module m diff --git a/test/Semantics/assign04.f90 b/test/Semantics/assign04.f90 index dd0159bdd0bd..c12857c66fd2 100644 --- a/test/Semantics/assign04.f90 +++ b/test/Semantics/assign04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! 9.4.5 subroutine s1 type :: t(k, l) diff --git a/test/Semantics/bad-forward-type.f90 b/test/Semantics/bad-forward-type.f90 index 62ad9d4b2b4c..0c6de01ad06e 100644 --- a/test/Semantics/bad-forward-type.f90 +++ b/test/Semantics/bad-forward-type.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Forward references to derived types (error cases) !ERROR: The derived type 'undef' was forward-referenced but not defined diff --git a/test/Semantics/bindings01.f90 b/test/Semantics/bindings01.f90 index 54aaacd2e9f8..4c517ad6c439 100644 --- a/test/Semantics/bindings01.f90 +++ b/test/Semantics/bindings01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Confirm enforcement of constraints and restrictions in 7.5.7.3 ! and C779-C785. diff --git a/test/Semantics/block-data01.f90 b/test/Semantics/block-data01.f90 index 164709118f6f..d9c6dcd0843a 100644 --- a/test/Semantics/block-data01.f90 +++ b/test/Semantics/block-data01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test BLOCK DATA subprogram (14.3) block data foo !ERROR: IMPORT is not allowed in a BLOCK DATA subprogram diff --git a/test/Semantics/blockconstruct01.f90 b/test/Semantics/blockconstruct01.f90 index 7f7eec5b56c3..86c4ff1a77bd 100644 --- a/test/Semantics/blockconstruct01.f90 +++ b/test/Semantics/blockconstruct01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1107 -- COMMON, EQUIVALENCE, INTENT, NAMELIST, OPTIONAL, VALUE or ! STATEMENT FUNCTIONS not allow in specification part diff --git a/test/Semantics/blockconstruct02.f90 b/test/Semantics/blockconstruct02.f90 index 2a1a95f312bf..77ce3c1a8f57 100644 --- a/test/Semantics/blockconstruct02.f90 +++ b/test/Semantics/blockconstruct02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1108 -- Save statement in a BLOCK construct shall not conatin a ! saved-entity-list that does not specify a common-block-name diff --git a/test/Semantics/blockconstruct03.f90 b/test/Semantics/blockconstruct03.f90 index df5aff7699ea..3f1974d19408 100644 --- a/test/Semantics/blockconstruct03.f90 +++ b/test/Semantics/blockconstruct03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests implemented for this standard: ! Block Construct ! C1109 diff --git a/test/Semantics/c_f_pointer.f90 b/test/Semantics/c_f_pointer.f90 index 1064461c509d..ab1b479cfa7d 100644 --- a/test/Semantics/c_f_pointer.f90 +++ b/test/Semantics/c_f_pointer.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Enforce 18.2.3.3 program test diff --git a/test/Semantics/call01.f90 b/test/Semantics/call01.f90 index 88274dd42844..ed77fb81026f 100644 --- a/test/Semantics/call01.f90 +++ b/test/Semantics/call01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Confirm enforcement of constraints and restrictions in 15.6.2.1 non_recursive function f01(n) result(res) diff --git a/test/Semantics/call02.f90 b/test/Semantics/call02.f90 index 5d9bdf1cd5a2..e100a8fcc483 100644 --- a/test/Semantics/call02.f90 +++ b/test/Semantics/call02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! 15.5.1 procedure reference constraints and restrictions subroutine s01(elem, subr) diff --git a/test/Semantics/call03.f90 b/test/Semantics/call03.f90 index 098106aed45e..13aba93a2f00 100644 --- a/test/Semantics/call03.f90 +++ b/test/Semantics/call03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.4 constraints and restrictions for non-POINTER non-ALLOCATABLE ! dummy arguments. diff --git a/test/Semantics/call04.f90 b/test/Semantics/call04.f90 index 3064fee5decc..120cd5435a73 100644 --- a/test/Semantics/call04.f90 +++ b/test/Semantics/call04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 8.5.10 & 8.5.18 constraints on dummy argument declarations module m diff --git a/test/Semantics/call05.f90 b/test/Semantics/call05.f90 index 80f1874ff2d5..a7cd6d9f9b78 100644 --- a/test/Semantics/call05.f90 +++ b/test/Semantics/call05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.5 constraints and restrictions for POINTER & ALLOCATABLE ! arguments when both sides of the call have the same attributes. diff --git a/test/Semantics/call06.f90 b/test/Semantics/call06.f90 index eb4bd3755f87..77eb0c406e5e 100644 --- a/test/Semantics/call06.f90 +++ b/test/Semantics/call06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.6 constraints and restrictions for ALLOCATABLE ! dummy arguments. diff --git a/test/Semantics/call07.f90 b/test/Semantics/call07.f90 index f596e3600288..af9be0235435 100644 --- a/test/Semantics/call07.f90 +++ b/test/Semantics/call07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.7 constraints and restrictions for POINTER dummy arguments. module m diff --git a/test/Semantics/call08.f90 b/test/Semantics/call08.f90 index 88ec7e3b4cca..ae4497f316f7 100644 --- a/test/Semantics/call08.f90 +++ b/test/Semantics/call08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.8 coarray dummy arguments module m diff --git a/test/Semantics/call09.f90 b/test/Semantics/call09.f90 index 02224477a28f..337932d3fe0d 100644 --- a/test/Semantics/call09.f90 +++ b/test/Semantics/call09.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.5.2.9(2,3,5) dummy procedure requirements module m diff --git a/test/Semantics/call10.f90 b/test/Semantics/call10.f90 index 567d85d5d0e0..74a0474175f6 100644 --- a/test/Semantics/call10.f90 +++ b/test/Semantics/call10.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.7 (C1583-C1590, C1592-C1599) constraints and restrictions ! for pure procedures. ! (C1591 is tested in call11.f90; C1594 in call12.f90.) diff --git a/test/Semantics/call11.f90 b/test/Semantics/call11.f90 index b53b40334e93..d7b590427794 100644 --- a/test/Semantics/call11.f90 +++ b/test/Semantics/call11.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.7 C1591 & others: contexts requiring pure subprograms module m diff --git a/test/Semantics/call12.f90 b/test/Semantics/call12.f90 index 3ce0812560ac..e25a2608c441 100644 --- a/test/Semantics/call12.f90 +++ b/test/Semantics/call12.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.7 C1594 - prohibited assignments in pure subprograms module used diff --git a/test/Semantics/call13.f90 b/test/Semantics/call13.f90 index 952a7d0c8b1d..23ef745f8e1c 100644 --- a/test/Semantics/call13.f90 +++ b/test/Semantics/call13.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 15.4.2.2 constraints and restrictions for calls to implicit ! interfaces diff --git a/test/Semantics/call14.f90 b/test/Semantics/call14.f90 index e25620b2694b..b874e6b00912 100644 --- a/test/Semantics/call14.f90 +++ b/test/Semantics/call14.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test 8.5.18 constraints on the VALUE attribute module m diff --git a/test/Semantics/call15.f90 b/test/Semantics/call15.f90 index 08886e4e7c6d..1f6646755205 100644 --- a/test/Semantics/call15.f90 +++ b/test/Semantics/call15.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C711 An assumed-type actual argument that corresponds to an assumed-rank ! dummy argument shall be assumed-shape or assumed-rank. subroutine s(arg1, arg2, arg3) diff --git a/test/Semantics/canondo16.f90 b/test/Semantics/canondo16.f90 index d5c5db464930..8eebde23b22f 100644 --- a/test/Semantics/canondo16.f90 +++ b/test/Semantics/canondo16.f90 @@ -1,11 +1,11 @@ -! RUN: %S/test_any.sh %s %flang %t +! RUN: %S/test_any.sh %s %f18 %t ! Error test -- DO loop uses obsolete loop termination statement ! See R1131 and C1133 ! By default, this is not an error and label do are rewritten to non-label do. ! A warning is generated with -Mstandard -! EXEC: ${F18} -funparse-with-symbols -Mstandard -I../../tools/f18/include %s 2>&1 | ${FileCheck} %s +! EXEC: ${F18} -funparse-with-symbols -Mstandard -I../../include/flang %s 2>&1 | ${FileCheck} %s ! CHECK: end do diff --git a/test/Semantics/coarrays01.f90 b/test/Semantics/coarrays01.f90 index 3e8e1672a47b..c96e76ceebbd 100644 --- a/test/Semantics/coarrays01.f90 +++ b/test/Semantics/coarrays01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test selector and team-value in CHANGE TEAM statement ! OK diff --git a/test/Semantics/complex01.f90 b/test/Semantics/complex01.f90 index c70f0defad6a..060760ff6e5a 100644 --- a/test/Semantics/complex01.f90 +++ b/test/Semantics/complex01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C718 Each named constant in a complex literal constant shall be of type ! integer or real. subroutine s() diff --git a/test/Semantics/computed-goto01.f90 b/test/Semantics/computed-goto01.f90 index 9f24996f41a0..ff38b729608c 100644 --- a/test/Semantics/computed-goto01.f90 +++ b/test/Semantics/computed-goto01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that a basic computed goto compiles INTEGER, DIMENSION (2) :: B diff --git a/test/Semantics/computed-goto02.f90 b/test/Semantics/computed-goto02.f90 index eea61a827052..aaca63ab3bad 100644 --- a/test/Semantics/computed-goto02.f90 +++ b/test/Semantics/computed-goto02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that computed goto express must be a scalar integer expression ! TODO: PGI, for example, accepts a float & converts the value to int. diff --git a/test/Semantics/critical01.f90 b/test/Semantics/critical01.f90 index 5ca97ade6998..1fa2553a5d9a 100644 --- a/test/Semantics/critical01.f90 +++ b/test/Semantics/critical01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !C1117 subroutine test1(a, i) diff --git a/test/Semantics/critical02.f90 b/test/Semantics/critical02.f90 index ba5e0f4c55a7..a339c46c3192 100644 --- a/test/Semantics/critical02.f90 +++ b/test/Semantics/critical02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !C1118 subroutine test1 diff --git a/test/Semantics/critical03.f90 b/test/Semantics/critical03.f90 index 2ab60e5d59a9..2964a3b5321f 100644 --- a/test/Semantics/critical03.f90 +++ b/test/Semantics/critical03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !C1119 subroutine test1(a, i) diff --git a/test/Semantics/data01.f90 b/test/Semantics/data01.f90 index 4bdf7ea9dd4a..1c8608993868 100644 --- a/test/Semantics/data01.f90 +++ b/test/Semantics/data01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !Test for checking data constraints, C882-C887 module m1 type person diff --git a/test/Semantics/data02.f90 b/test/Semantics/data02.f90 index ac6902622d83..361f3a2793a3 100644 --- a/test/Semantics/data02.f90 +++ b/test/Semantics/data02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that expressions are analyzed in data statements subroutine s1 diff --git a/test/Semantics/deallocate01.f90 b/test/Semantics/deallocate01.f90 index 8aaf14496d71..9aa69e77876d 100644 --- a/test/Semantics/deallocate01.f90 +++ b/test/Semantics/deallocate01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test that DEALLOCATE works INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/deallocate04.f90 b/test/Semantics/deallocate04.f90 index 2a1ad62b9920..ce9acf994684 100644 --- a/test/Semantics/deallocate04.f90 +++ b/test/Semantics/deallocate04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for type errors in DEALLOCATE statements INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/deallocate05.f90 b/test/Semantics/deallocate05.f90 index fdc66004e2ce..862d88578b5f 100644 --- a/test/Semantics/deallocate05.f90 +++ b/test/Semantics/deallocate05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in DEALLOCATE statements Module share diff --git a/test/Semantics/doconcurrent01.f90 b/test/Semantics/doconcurrent01.f90 index a4161a5c3073..7a3f9c078e00 100644 --- a/test/Semantics/doconcurrent01.f90 +++ b/test/Semantics/doconcurrent01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1141 ! A reference to the procedure IEEE_SET_HALTING_MODE ! from the intrinsic ! module IEEE_EXCEPTIONS, shall not ! appear within a DO CONCURRENT construct. diff --git a/test/Semantics/doconcurrent05.f90 b/test/Semantics/doconcurrent05.f90 index d92ef6d18322..df548f23e8b5 100644 --- a/test/Semantics/doconcurrent05.f90 +++ b/test/Semantics/doconcurrent05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if ! it belongs to that construct or an outer construct. diff --git a/test/Semantics/doconcurrent06.f90 b/test/Semantics/doconcurrent06.f90 index f178b7a11640..e20a830f5d80 100644 --- a/test/Semantics/doconcurrent06.f90 +++ b/test/Semantics/doconcurrent06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if ! it belongs to that construct or an outer construct. diff --git a/test/Semantics/doconcurrent08.f90 b/test/Semantics/doconcurrent08.f90 index 91a077fade49..826bc84b20ae 100644 --- a/test/Semantics/doconcurrent08.f90 +++ b/test/Semantics/doconcurrent08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1140 -- A statement that might result in the deallocation of a polymorphic ! entity shall not appear within a DO CONCURRENT construct. module m1 diff --git a/test/Semantics/dosemantics01.f90 b/test/Semantics/dosemantics01.f90 index 2261f184e3cc..55eae4582396 100644 --- a/test/Semantics/dosemantics01.f90 +++ b/test/Semantics/dosemantics01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1131 -- check valid and invalid DO loop naming PROGRAM C1131 diff --git a/test/Semantics/dosemantics02.f90 b/test/Semantics/dosemantics02.f90 index 96047f0a3678..c40d3b842dbd 100644 --- a/test/Semantics/dosemantics02.f90 +++ b/test/Semantics/dosemantics02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1121 -- any procedure referenced in a concurrent header must be pure ! Also, check that the step expressions are not zero. This is prohibited by diff --git a/test/Semantics/dosemantics03.f90 b/test/Semantics/dosemantics03.f90 index c063a7b8c854..f82a7e4879f7 100644 --- a/test/Semantics/dosemantics03.f90 +++ b/test/Semantics/dosemantics03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Issue 458 -- semantic checks for a normal DO loop. The DO variable ! and the initial, final, and step expressions must be INTEGER if the ! options for standard conformance and turning warnings into errors diff --git a/test/Semantics/dosemantics04.f90 b/test/Semantics/dosemantics04.f90 index 35a3c9493ca2..80bccf59d55e 100644 --- a/test/Semantics/dosemantics04.f90 +++ b/test/Semantics/dosemantics04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1123 -- Expressions in DO CONCURRENT header cannot reference variables ! declared in the same header PROGRAM dosemantics04 diff --git a/test/Semantics/dosemantics05.f90 b/test/Semantics/dosemantics05.f90 index f565f9b71679..4e660498e3a3 100644 --- a/test/Semantics/dosemantics05.f90 +++ b/test/Semantics/dosemantics05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test DO loop semantics for constraint C1130 -- ! The constraint states that "If the locality-spec DEFAULT ( NONE ) appears in a ! DO CONCURRENT statement; a variable that is a local or construct entity of a diff --git a/test/Semantics/dosemantics06.f90 b/test/Semantics/dosemantics06.f90 index 41b9598970b5..445eadcec6ca 100644 --- a/test/Semantics/dosemantics06.f90 +++ b/test/Semantics/dosemantics06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1131, C1133 -- check valid and invalid DO loop naming ! C1131 (R1119) If the do-stmt of a do-construct specifies a do-construct-name, ! the corresponding end-do shall be an end-do-stmt specifying the same diff --git a/test/Semantics/dosemantics07.f90 b/test/Semantics/dosemantics07.f90 index f1450dda31eb..95584075e2cc 100644 --- a/test/Semantics/dosemantics07.f90 +++ b/test/Semantics/dosemantics07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !C1132 ! If the do-stmt is a nonlabel-do-stmt, the corresponding end-do shall be an ! end-do-stmt. diff --git a/test/Semantics/dosemantics08.f90 b/test/Semantics/dosemantics08.f90 index 388fb75254f8..431443a11a80 100644 --- a/test/Semantics/dosemantics08.f90 +++ b/test/Semantics/dosemantics08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1138 -- ! A branch (11.2) within a DO CONCURRENT construct shall not have a branch ! target that is outside the construct. diff --git a/test/Semantics/dosemantics09.f90 b/test/Semantics/dosemantics09.f90 index 46136f29c74e..3d53e39ff3ee 100644 --- a/test/Semantics/dosemantics09.f90 +++ b/test/Semantics/dosemantics09.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !C1129 !A variable that is referenced by the scalar-mask-expr of a !concurrent-header or by any concurrent-limit or concurrent-step in that diff --git a/test/Semantics/dosemantics10.f90 b/test/Semantics/dosemantics10.f90 index 561f9b7fb7ea..3d813184a3b4 100644 --- a/test/Semantics/dosemantics10.f90 +++ b/test/Semantics/dosemantics10.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1134 A CYCLE statement must be within a DO construct ! ! C1166 An EXIT statement must be within a DO construct diff --git a/test/Semantics/dosemantics11.f90 b/test/Semantics/dosemantics11.f90 index 760f9f5f9b60..226f0073f9a4 100644 --- a/test/Semantics/dosemantics11.f90 +++ b/test/Semantics/dosemantics11.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1135 A cycle-stmt shall not appear within a CHANGE TEAM, CRITICAL, or DO ! CONCURRENT construct if it belongs to an outer construct. ! diff --git a/test/Semantics/dosemantics12.f90 b/test/Semantics/dosemantics12.f90 index 48ecd14feda5..4cd406e0892b 100644 --- a/test/Semantics/dosemantics12.f90 +++ b/test/Semantics/dosemantics12.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. ! ! Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/Semantics/entry01.f90 b/test/Semantics/entry01.f90 index ccb03a7f6083..f458ef515451 100644 --- a/test/Semantics/entry01.f90 +++ b/test/Semantics/entry01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests valid and invalid ENTRY statements module m1 diff --git a/test/Semantics/equivalence01.f90 b/test/Semantics/equivalence01.f90 index 31b561e33b0d..68b2cd4d38ef 100644 --- a/test/Semantics/equivalence01.f90 +++ b/test/Semantics/equivalence01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 integer i, j real r(2) diff --git a/test/Semantics/expr-errors01.f90 b/test/Semantics/expr-errors01.f90 index a479e863dcaf..36064553684c 100644 --- a/test/Semantics/expr-errors01.f90 +++ b/test/Semantics/expr-errors01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1003 - can't parenthesize function call returning procedure pointer module m1 type :: dt diff --git a/test/Semantics/expr-errors02.f90 b/test/Semantics/expr-errors02.f90 index 4b0d6d4118f3..af51e1c3ee48 100644 --- a/test/Semantics/expr-errors02.f90 +++ b/test/Semantics/expr-errors02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test specification expressions module m diff --git a/test/Semantics/forall01.f90 b/test/Semantics/forall01.f90 index ecb243bc2a09..f4652370bd18 100644 --- a/test/Semantics/forall01.f90 +++ b/test/Semantics/forall01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine forall1 real :: a(9) !ERROR: 'i' is already declared in this scoping unit diff --git a/test/Semantics/if_arith01.f90 b/test/Semantics/if_arith01.f90 index 5ec06b47485d..16e616fc5a0d 100644 --- a/test/Semantics/if_arith01.f90 +++ b/test/Semantics/if_arith01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that a basic arithmetic if compiles. if ( A ) 100, 200, 300 diff --git a/test/Semantics/if_arith02.f90 b/test/Semantics/if_arith02.f90 index f8e24b42dffa..4dfe72d36a5d 100644 --- a/test/Semantics/if_arith02.f90 +++ b/test/Semantics/if_arith02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that only labels are allowed in arithmetic if statements. ! TODO: Revisit error message "expected 'ASSIGN'" etc. ! TODO: Revisit error message "expected one of '0123456789'" diff --git a/test/Semantics/if_arith03.f90 b/test/Semantics/if_arith03.f90 index 1e5eb67d184c..45ceec4e4e54 100644 --- a/test/Semantics/if_arith03.f90 +++ b/test/Semantics/if_arith03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !ERROR: label '600' was not found diff --git a/test/Semantics/if_arith04.f90 b/test/Semantics/if_arith04.f90 index 9a436cd5eb67..d947b0b1a7b0 100644 --- a/test/Semantics/if_arith04.f90 +++ b/test/Semantics/if_arith04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Make sure arithmetic if expressions are non-complex numeric exprs. INTEGER I diff --git a/test/Semantics/if_construct01.f90 b/test/Semantics/if_construct01.f90 index c133b7d8cc9f..adac3c252cc2 100644 --- a/test/Semantics/if_construct01.f90 +++ b/test/Semantics/if_construct01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Simple check that if constructs are ok. if (a < b) then diff --git a/test/Semantics/if_construct02.f90 b/test/Semantics/if_construct02.f90 index 9ba6caa45355..de9428649937 100644 --- a/test/Semantics/if_construct02.f90 +++ b/test/Semantics/if_construct02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that if constructs only accept scalar logical expressions. ! TODO: expand the test to check this restriction for more types. diff --git a/test/Semantics/if_stmt01.f90 b/test/Semantics/if_stmt01.f90 index 51454a9d2116..337d5190e329 100644 --- a/test/Semantics/if_stmt01.f90 +++ b/test/Semantics/if_stmt01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Simple check that if statements are ok. IF (A > 0.0) A = LOG (A) diff --git a/test/Semantics/if_stmt02.f90 b/test/Semantics/if_stmt02.f90 index 71c458381ac2..5672811c4670 100644 --- a/test/Semantics/if_stmt02.f90 +++ b/test/Semantics/if_stmt02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !ERROR: IF statement is not allowed in IF statement IF (A > 0.0) IF (B < 0.0) A = LOG (A) END diff --git a/test/Semantics/if_stmt03.f90 b/test/Semantics/if_stmt03.f90 index 2a2595404960..970b70e00889 100644 --- a/test/Semantics/if_stmt03.f90 +++ b/test/Semantics/if_stmt03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check that non-logical expressions are not allowed. ! Check that non-scalar expressions are not allowed. ! TODO: Insure all non-logicals are prohibited. diff --git a/test/Semantics/implicit01.f90 b/test/Semantics/implicit01.f90 index f0893f7ed33f..5cc8709a4dfd 100644 --- a/test/Semantics/implicit01.f90 +++ b/test/Semantics/implicit01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 implicit none !ERROR: More than one IMPLICIT NONE statement diff --git a/test/Semantics/implicit02.f90 b/test/Semantics/implicit02.f90 index 5d2b6e09474f..f30170587cf0 100644 --- a/test/Semantics/implicit02.f90 +++ b/test/Semantics/implicit02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 implicit none !ERROR: IMPLICIT statement after IMPLICIT NONE or IMPLICIT NONE(TYPE) statement diff --git a/test/Semantics/implicit03.f90 b/test/Semantics/implicit03.f90 index 9636743233a3..bb6c4958da2d 100644 --- a/test/Semantics/implicit03.f90 +++ b/test/Semantics/implicit03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 implicit integer(a-z) !ERROR: IMPLICIT NONE statement after IMPLICIT statement diff --git a/test/Semantics/implicit04.f90 b/test/Semantics/implicit04.f90 index 86adb95f9852..20de8c403037 100644 --- a/test/Semantics/implicit04.f90 +++ b/test/Semantics/implicit04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s parameter(a=1.0) !ERROR: IMPLICIT NONE statement after PARAMETER statement diff --git a/test/Semantics/implicit05.f90 b/test/Semantics/implicit05.f90 index 7649c228fa44..e6dec7d61533 100644 --- a/test/Semantics/implicit05.f90 +++ b/test/Semantics/implicit05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s !ERROR: 'a' does not follow 'b' alphabetically implicit integer(b-a) diff --git a/test/Semantics/implicit06.f90 b/test/Semantics/implicit06.f90 index 3f6672008d53..9f54282c2fd5 100644 --- a/test/Semantics/implicit06.f90 +++ b/test/Semantics/implicit06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 implicit integer(a-c) !ERROR: More than one implicit type specified for 'c' diff --git a/test/Semantics/implicit07.f90 b/test/Semantics/implicit07.f90 index 68fa37de8ce7..5ec659233f85 100644 --- a/test/Semantics/implicit07.f90 +++ b/test/Semantics/implicit07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t implicit none(external) external x call x diff --git a/test/Semantics/implicit08.f90 b/test/Semantics/implicit08.f90 index 44e96d89855e..a4a1c33fb233 100644 --- a/test/Semantics/implicit08.f90 +++ b/test/Semantics/implicit08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 block !ERROR: IMPLICIT statement is not allowed in a BLOCK construct diff --git a/test/Semantics/init01.f90 b/test/Semantics/init01.f90 index 1fc1ed877fa3..f8481506a809 100644 --- a/test/Semantics/init01.f90 +++ b/test/Semantics/init01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Object pointer initializer error tests subroutine test(j) diff --git a/test/Semantics/int-literals.f90 b/test/Semantics/int-literals.f90 index 3c48b7e1b7da..01d31c5c0ca6 100644 --- a/test/Semantics/int-literals.f90 +++ b/test/Semantics/int-literals.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Fortran syntax considers signed int literals in complex literals ! to be a distinct production, not an application of unary +/- to ! an unsigned int literal, so they're used here to test overflow diff --git a/test/Semantics/io01.f90 b/test/Semantics/io01.f90 index 81b537d7e4c5..56936b6e68fe 100644 --- a/test/Semantics/io01.f90 +++ b/test/Semantics/io01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t character(len=20) :: access = "direcT" character(len=20) :: access_(2) = (/"direcT", "streaM"/) character(len=20) :: action_(2) = (/"reaD ", "writE"/) diff --git a/test/Semantics/io02.f90 b/test/Semantics/io02.f90 index 7cb901d34027..a405f3e91502 100644 --- a/test/Semantics/io02.f90 +++ b/test/Semantics/io02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t integer :: unit10 = 10 integer :: unit11 = 11 diff --git a/test/Semantics/io03.f90 b/test/Semantics/io03.f90 index a6696176b126..6c91afc00b01 100644 --- a/test/Semantics/io03.f90 +++ b/test/Semantics/io03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t character(kind=1,len=50) internal_file character(kind=2,len=50) internal_file2 character(kind=4,len=50) internal_file4 diff --git a/test/Semantics/io04.f90 b/test/Semantics/io04.f90 index 09776ef94ab1..5cda7fff8bc8 100644 --- a/test/Semantics/io04.f90 +++ b/test/Semantics/io04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t character(kind=1,len=50) internal_file character(kind=1,len=100) msg character(20) sign diff --git a/test/Semantics/io05.f90 b/test/Semantics/io05.f90 index 1df878197237..8d10ab12416d 100644 --- a/test/Semantics/io05.f90 +++ b/test/Semantics/io05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t character*20 c(25), cv character(kind=1,len=59) msg logical*2 v(5), lv diff --git a/test/Semantics/io06.f90 b/test/Semantics/io06.f90 index eba437c86c86..1b19fc6bc217 100644 --- a/test/Semantics/io06.f90 +++ b/test/Semantics/io06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t character(kind=1,len=100) msg1 character(kind=2,len=200) msg2 integer(1) stat1 diff --git a/test/Semantics/io07.f90 b/test/Semantics/io07.f90 index 9462a099d67e..e3154689ab80 100644 --- a/test/Semantics/io07.f90 +++ b/test/Semantics/io07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t 1001 format(A) !ERROR: Format statement must be labeled diff --git a/test/Semantics/io08.f90 b/test/Semantics/io08.f90 index 1b75e8094a9a..ca9638fb3a3f 100644 --- a/test/Semantics/io08.f90 +++ b/test/Semantics/io08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t write(*,*) write(*,'()') write(*,'(A)') diff --git a/test/Semantics/io09.f90 b/test/Semantics/io09.f90 index 5f50e4e0151e..7ce5e6435568 100644 --- a/test/Semantics/io09.f90 +++ b/test/Semantics/io09.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !ERROR: String edit descriptor in READ format expression read(*,'("abc")') diff --git a/test/Semantics/io10.f90 b/test/Semantics/io10.f90 index 90ae8b194330..a3023861c1cf 100644 --- a/test/Semantics/io10.f90 +++ b/test/Semantics/io10.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -Mstandard write(*, '(B0)') diff --git a/test/Semantics/kinds02.f90 b/test/Semantics/kinds02.f90 index f1ff0b27caf5..bdc998bbdfe7 100644 --- a/test/Semantics/kinds02.f90 +++ b/test/Semantics/kinds02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C712 The value of scalar-int-constant-expr shall be nonnegative and ! shall specify a representation method that exists on the processor. ! C714 The value of kind-param shall be nonnegative. diff --git a/test/Semantics/kinds04.f90 b/test/Semantics/kinds04.f90 index af6a8965ca65..54f953fec5ff 100644 --- a/test/Semantics/kinds04.f90 +++ b/test/Semantics/kinds04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C716 If both kind-param and exponent-letter appear, exponent-letter ! shall be E. ! C717 The value of kind-param shall specify an approximation method that diff --git a/test/Semantics/misc-declarations.f90 b/test/Semantics/misc-declarations.f90 index 9103ad7bcf7d..7680eed793bc 100644 --- a/test/Semantics/misc-declarations.f90 +++ b/test/Semantics/misc-declarations.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Miscellaneous constraint and requirement checking on declarations: ! - 8.5.6.2 & 8.5.6.3 constraints on coarrays ! - 8.5.19 constraints on the VOLATILE attribute diff --git a/test/Semantics/namelist01.f90 b/test/Semantics/namelist01.f90 index f659c998c7ef..b85357faf9ae 100644 --- a/test/Semantics/namelist01.f90 +++ b/test/Semantics/namelist01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test for checking namelist constraints, C8103-C8105 module dup diff --git a/test/Semantics/null01.f90 b/test/Semantics/null01.f90 index 09c6dce22c48..478bedbc44ed 100644 --- a/test/Semantics/null01.f90 +++ b/test/Semantics/null01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! NULL() intrinsic function error tests subroutine test diff --git a/test/Semantics/nullify01.f90 b/test/Semantics/nullify01.f90 index 9af635f8f08c..62cde3055f77 100644 --- a/test/Semantics/nullify01.f90 +++ b/test/Semantics/nullify01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test that NULLIFY works Module share diff --git a/test/Semantics/nullify02.f90 b/test/Semantics/nullify02.f90 index 49bcc9ef5d11..7a2408348cd4 100644 --- a/test/Semantics/nullify02.f90 +++ b/test/Semantics/nullify02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Check for semantic errors in NULLIFY statements INTEGER, PARAMETER :: maxvalue=1024 diff --git a/test/Semantics/omp-atomic.f90 b/test/Semantics/omp-atomic.f90 index 760d1ee4f619..2a27bfaf6011 100644 --- a/test/Semantics/omp-atomic.f90 +++ b/test/Semantics/omp-atomic.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP 2.13.6 atomic Construct diff --git a/test/Semantics/omp-clause-validity01.f90 b/test/Semantics/omp-clause-validity01.f90 index 523b2eeb6c10..bcfea4c5b250 100644 --- a/test/Semantics/omp-clause-validity01.f90 +++ b/test/Semantics/omp-clause-validity01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP clause validity for the following directives: diff --git a/test/Semantics/omp-declarative-directive.f90 b/test/Semantics/omp-declarative-directive.f90 index 639ed7d4d895..98787eea3031 100644 --- a/test/Semantics/omp-declarative-directive.f90 +++ b/test/Semantics/omp-declarative-directive.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP declarative directives diff --git a/test/Semantics/omp-device-constructs.f90 b/test/Semantics/omp-device-constructs.f90 index 7973dc2ef77f..15daec33580a 100644 --- a/test/Semantics/omp-device-constructs.f90 +++ b/test/Semantics/omp-device-constructs.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP clause validity for the following directives: ! 2.10 Device constructs diff --git a/test/Semantics/omp-loop-association.f90 b/test/Semantics/omp-loop-association.f90 index 22e9365b2f3f..036d7c3d124d 100644 --- a/test/Semantics/omp-loop-association.f90 +++ b/test/Semantics/omp-loop-association.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check the association between OpenMPLoopConstruct and DoConstruct diff --git a/test/Semantics/omp-nested01.f90 b/test/Semantics/omp-nested01.f90 index 1c0e84ab8fd9..b13f536da27f 100644 --- a/test/Semantics/omp-nested01.f90 +++ b/test/Semantics/omp-nested01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! OPTIONS: -fopenmp ! Check OpenMP 2.17 Nesting of Regions diff --git a/test/Semantics/omp-resolve01.f90 b/test/Semantics/omp-resolve01.f90 index 528915e88f8d..47479b4954f7 100644 --- a/test/Semantics/omp-resolve01.f90 +++ b/test/Semantics/omp-resolve01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.4 An array section designates a subset of the elements in an array. Although diff --git a/test/Semantics/omp-resolve02.f90 b/test/Semantics/omp-resolve02.f90 index 3d341662b2da..3f28973a907b 100644 --- a/test/Semantics/omp-resolve02.f90 +++ b/test/Semantics/omp-resolve02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! Test the effect to name resolution from illegal clause diff --git a/test/Semantics/omp-resolve03.f90 b/test/Semantics/omp-resolve03.f90 index a896ef30c9f4..8e20d23fafa6 100644 --- a/test/Semantics/omp-resolve03.f90 +++ b/test/Semantics/omp-resolve03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Although variables in common blocks can be accessed by use association diff --git a/test/Semantics/omp-resolve04.f90 b/test/Semantics/omp-resolve04.f90 index 234013898b87..a216616eb2fd 100644 --- a/test/Semantics/omp-resolve04.f90 +++ b/test/Semantics/omp-resolve04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/omp-resolve05.f90 b/test/Semantics/omp-resolve05.f90 index ebc50476b499..dc15b18a18db 100644 --- a/test/Semantics/omp-resolve05.f90 +++ b/test/Semantics/omp-resolve05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -fopenmp ! 2.15.3 Data-Sharing Attribute Clauses diff --git a/test/Semantics/resolve01.f90 b/test/Semantics/resolve01.f90 index eee8d662517f..f64599ec06a0 100644 --- a/test/Semantics/resolve01.f90 +++ b/test/Semantics/resolve01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t integer :: x !ERROR: The type of 'x' has already been declared real :: x diff --git a/test/Semantics/resolve02.f90 b/test/Semantics/resolve02.f90 index 0d8e83b0ed29..9978a95409e3 100644 --- a/test/Semantics/resolve02.f90 +++ b/test/Semantics/resolve02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s !ERROR: Declaration of 'x' conflicts with its use as internal procedure real :: x diff --git a/test/Semantics/resolve03.f90 b/test/Semantics/resolve03.f90 index 773aaab3d453..825509da84d7 100644 --- a/test/Semantics/resolve03.f90 +++ b/test/Semantics/resolve03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t implicit none integer :: x !ERROR: No explicit type declared for 'y' diff --git a/test/Semantics/resolve04.f90 b/test/Semantics/resolve04.f90 index 5132b9f780f6..eeb6cb686896 100644 --- a/test/Semantics/resolve04.f90 +++ b/test/Semantics/resolve04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !ERROR: No explicit type declared for 'f' function f() implicit none diff --git a/test/Semantics/resolve05.f90 b/test/Semantics/resolve05.f90 index d1960e1808b1..89d501c664fd 100644 --- a/test/Semantics/resolve05.f90 +++ b/test/Semantics/resolve05.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t program p integer :: p ! this is ok end diff --git a/test/Semantics/resolve06.f90 b/test/Semantics/resolve06.f90 index 276feb3b4ee4..c0fd7a1ae5d4 100644 --- a/test/Semantics/resolve06.f90 +++ b/test/Semantics/resolve06.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t implicit none allocatable :: x integer :: x diff --git a/test/Semantics/resolve07.f90 b/test/Semantics/resolve07.f90 index f2e46f42a9d1..08156c4343f8 100644 --- a/test/Semantics/resolve07.f90 +++ b/test/Semantics/resolve07.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 integer :: x(2) !ERROR: The dimensions of 'x' have already been declared diff --git a/test/Semantics/resolve08.f90 b/test/Semantics/resolve08.f90 index 7252c79ef033..db238a496133 100644 --- a/test/Semantics/resolve08.f90 +++ b/test/Semantics/resolve08.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t integer :: g(10) f(i) = i + 1 ! statement function g(i) = i + 2 ! mis-parsed array assignment diff --git a/test/Semantics/resolve09.f90 b/test/Semantics/resolve09.f90 index 5104a371a639..cf9195992455 100644 --- a/test/Semantics/resolve09.f90 +++ b/test/Semantics/resolve09.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t integer :: y procedure() :: a procedure(real) :: b diff --git a/test/Semantics/resolve10.f90 b/test/Semantics/resolve10.f90 index 9990935899fa..5506d3916c76 100644 --- a/test/Semantics/resolve10.f90 +++ b/test/Semantics/resolve10.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m public type t diff --git a/test/Semantics/resolve11.f90 b/test/Semantics/resolve11.f90 index d94c0f8c87d1..1ff6a63ebf07 100644 --- a/test/Semantics/resolve11.f90 +++ b/test/Semantics/resolve11.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m public i integer, private :: j diff --git a/test/Semantics/resolve12.f90 b/test/Semantics/resolve12.f90 index 03bad9f5616f..b68e3b76544f 100644 --- a/test/Semantics/resolve12.f90 +++ b/test/Semantics/resolve12.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 end diff --git a/test/Semantics/resolve13.f90 b/test/Semantics/resolve13.f90 index 6fc03b1e8be0..5ee05db5e782 100644 --- a/test/Semantics/resolve13.f90 +++ b/test/Semantics/resolve13.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 integer :: x integer, private :: y diff --git a/test/Semantics/resolve14.f90 b/test/Semantics/resolve14.f90 index 326fe8e94894..d24a5c621655 100644 --- a/test/Semantics/resolve14.f90 +++ b/test/Semantics/resolve14.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 integer :: x integer :: y diff --git a/test/Semantics/resolve15.f90 b/test/Semantics/resolve15.f90 index 1cca8ce3dd7b..d91713a2b91a 100644 --- a/test/Semantics/resolve15.f90 +++ b/test/Semantics/resolve15.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m real :: var interface i diff --git a/test/Semantics/resolve16.f90 b/test/Semantics/resolve16.f90 index 8ce084a26fe9..a9d0842db7be 100644 --- a/test/Semantics/resolve16.f90 +++ b/test/Semantics/resolve16.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m interface subroutine sub0 diff --git a/test/Semantics/resolve17.f90 b/test/Semantics/resolve17.f90 index f9c9451dcfe2..4d1afee86b1a 100644 --- a/test/Semantics/resolve17.f90 +++ b/test/Semantics/resolve17.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m integer :: foo !Note: PGI, Intel, and GNU allow this; NAG and Sun do not diff --git a/test/Semantics/resolve18.f90 b/test/Semantics/resolve18.f90 index dff395f4bc9b..50246ea01dc7 100644 --- a/test/Semantics/resolve18.f90 +++ b/test/Semantics/resolve18.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 implicit none contains diff --git a/test/Semantics/resolve19.f90 b/test/Semantics/resolve19.f90 index f28f2b45abdf..3234f4ccc1f2 100644 --- a/test/Semantics/resolve19.f90 +++ b/test/Semantics/resolve19.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m interface a subroutine s(x) diff --git a/test/Semantics/resolve20.f90 b/test/Semantics/resolve20.f90 index 38dbd2367fe4..b38b8e35a494 100644 --- a/test/Semantics/resolve20.f90 +++ b/test/Semantics/resolve20.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m abstract interface subroutine foo diff --git a/test/Semantics/resolve21.f90 b/test/Semantics/resolve21.f90 index 764537a565f5..dfd87b348591 100644 --- a/test/Semantics/resolve21.f90 +++ b/test/Semantics/resolve21.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 type :: t integer :: i diff --git a/test/Semantics/resolve22.f90 b/test/Semantics/resolve22.f90 index 3549ec76e777..b9290cb9de23 100644 --- a/test/Semantics/resolve22.f90 +++ b/test/Semantics/resolve22.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 !OK: interface followed by type with same name interface t diff --git a/test/Semantics/resolve23.f90 b/test/Semantics/resolve23.f90 index 41644843bf1f..ffd408f660dc 100644 --- a/test/Semantics/resolve23.f90 +++ b/test/Semantics/resolve23.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m type :: t real :: y diff --git a/test/Semantics/resolve24.f90 b/test/Semantics/resolve24.f90 index c2ce595d9054..5b4a1adb11ab 100644 --- a/test/Semantics/resolve24.f90 +++ b/test/Semantics/resolve24.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine test1 !ERROR: Generic interface 'foo' has both a function and a subroutine interface foo diff --git a/test/Semantics/resolve25.f90 b/test/Semantics/resolve25.f90 index 4d3ec8c81495..780c07535bac 100644 --- a/test/Semantics/resolve25.f90 +++ b/test/Semantics/resolve25.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m interface foo subroutine s1(x) diff --git a/test/Semantics/resolve26.f90 b/test/Semantics/resolve26.f90 index f39366faaef0..65cfccf0f68b 100644 --- a/test/Semantics/resolve26.f90 +++ b/test/Semantics/resolve26.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 interface module subroutine s() diff --git a/test/Semantics/resolve27.f90 b/test/Semantics/resolve27.f90 index b10105ed9e7d..c8e3d82b094f 100644 --- a/test/Semantics/resolve27.f90 +++ b/test/Semantics/resolve27.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m interface module subroutine s() diff --git a/test/Semantics/resolve28.f90 b/test/Semantics/resolve28.f90 index 0fd81807c97f..17e603251518 100644 --- a/test/Semantics/resolve28.f90 +++ b/test/Semantics/resolve28.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s type t end type diff --git a/test/Semantics/resolve29.f90 b/test/Semantics/resolve29.f90 index d328eba594e7..7dcd61671e5c 100644 --- a/test/Semantics/resolve29.f90 +++ b/test/Semantics/resolve29.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m type t1 end type diff --git a/test/Semantics/resolve30.f90 b/test/Semantics/resolve30.f90 index 98777124b134..c3abaf5fd1a6 100644 --- a/test/Semantics/resolve30.f90 +++ b/test/Semantics/resolve30.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 integer x block diff --git a/test/Semantics/resolve31.f90 b/test/Semantics/resolve31.f90 index 3c61cd0bb9dc..a1fb7cea54b1 100644 --- a/test/Semantics/resolve31.f90 +++ b/test/Semantics/resolve31.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 integer :: t0 !ERROR: 't0' is not a derived type diff --git a/test/Semantics/resolve32.f90 b/test/Semantics/resolve32.f90 index 317a0ad9ed12..1b0140e285ed 100644 --- a/test/Semantics/resolve32.f90 +++ b/test/Semantics/resolve32.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m2 public s2, s4 private s3 diff --git a/test/Semantics/resolve33.f90 b/test/Semantics/resolve33.f90 index 4a37c5fb57aa..d4265cd3e2a0 100644 --- a/test/Semantics/resolve33.f90 +++ b/test/Semantics/resolve33.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Derived type parameters module m diff --git a/test/Semantics/resolve34.f90 b/test/Semantics/resolve34.f90 index 9d148ff43046..39730cee62de 100644 --- a/test/Semantics/resolve34.f90 +++ b/test/Semantics/resolve34.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Extended derived types module m1 diff --git a/test/Semantics/resolve35.f90 b/test/Semantics/resolve35.f90 index 7f6a8ea9492b..d78c1cbd4b74 100644 --- a/test/Semantics/resolve35.f90 +++ b/test/Semantics/resolve35.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Construct names subroutine s1 diff --git a/test/Semantics/resolve36.f90 b/test/Semantics/resolve36.f90 index 7ed9391c2f9e..13f6a144db5c 100644 --- a/test/Semantics/resolve36.f90 +++ b/test/Semantics/resolve36.f90 @@ -1,8 +1,7 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1568 The procedure-name shall have been declared to be a separate module ! procedure in the containing program unit or an ancestor of that program unit. - module m1 interface module subroutine sub1(arg1) diff --git a/test/Semantics/resolve37.f90 b/test/Semantics/resolve37.f90 index a07ebbc6625b..c56ac3719dd7 100644 --- a/test/Semantics/resolve37.f90 +++ b/test/Semantics/resolve37.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C701 The type-param-value for a kind type parameter shall be a constant ! expression. This constraint looks like a mistake in the standard. integer, parameter :: k = 8 diff --git a/test/Semantics/resolve38.f90 b/test/Semantics/resolve38.f90 index 53e8db813380..98ac17f2d366 100644 --- a/test/Semantics/resolve38.f90 +++ b/test/Semantics/resolve38.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C772 module m1 type t1 diff --git a/test/Semantics/resolve39.f90 b/test/Semantics/resolve39.f90 index d0052f16f863..b34bbeca84f2 100644 --- a/test/Semantics/resolve39.f90 +++ b/test/Semantics/resolve39.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 implicit none real(8) :: x = 2.0 diff --git a/test/Semantics/resolve40.f90 b/test/Semantics/resolve40.f90 index 95c2c9e8034c..b4d8aa0d915a 100644 --- a/test/Semantics/resolve40.f90 +++ b/test/Semantics/resolve40.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 namelist /nl/x block diff --git a/test/Semantics/resolve41.f90 b/test/Semantics/resolve41.f90 index e2bf877016ed..40522d8f4b7b 100644 --- a/test/Semantics/resolve41.f90 +++ b/test/Semantics/resolve41.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m implicit none real, parameter :: a = 8.0 diff --git a/test/Semantics/resolve42.f90 b/test/Semantics/resolve42.f90 index 5b6ac9f88b2b..af5d6e5ee377 100644 --- a/test/Semantics/resolve42.f90 +++ b/test/Semantics/resolve42.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1 !ERROR: Array 'z' without ALLOCATABLE or POINTER attribute must have explicit shape common x, y(4), z(:) diff --git a/test/Semantics/resolve43.f90 b/test/Semantics/resolve43.f90 index 385dfedc34bd..2ef585a60021 100644 --- a/test/Semantics/resolve43.f90 +++ b/test/Semantics/resolve43.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for structure constructors. ! Errors caught by expression resolution are tested elsewhere; these are the ! errors meant to be caught by name resolution, as well as acceptable use diff --git a/test/Semantics/resolve44.f90 b/test/Semantics/resolve44.f90 index dd082adc89df..2d8b70178753 100644 --- a/test/Semantics/resolve44.f90 +++ b/test/Semantics/resolve44.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for recursive use of derived types. program main diff --git a/test/Semantics/resolve45.f90 b/test/Semantics/resolve45.f90 index e28dc33c4e72..bb5eaf4a5317 100644 --- a/test/Semantics/resolve45.f90 +++ b/test/Semantics/resolve45.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t function f1(x, y) integer x !ERROR: SAVE attribute may not be applied to dummy argument 'x' diff --git a/test/Semantics/resolve46.f90 b/test/Semantics/resolve46.f90 index 181ccfb5c280..da31741163ab 100644 --- a/test/Semantics/resolve46.f90 +++ b/test/Semantics/resolve46.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C1030 - pointers to intrinsic procedures program main intrinsic :: cos ! a specific & generic intrinsic name diff --git a/test/Semantics/resolve47.f90 b/test/Semantics/resolve47.f90 index 04dab5616855..0f27ee4b5fa2 100644 --- a/test/Semantics/resolve47.f90 +++ b/test/Semantics/resolve47.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m1 !ERROR: Logical constant '.true.' may not be used as a defined operator interface operator(.TRUE.) diff --git a/test/Semantics/resolve48.f90 b/test/Semantics/resolve48.f90 index 887505d16442..6651a72cfe84 100644 --- a/test/Semantics/resolve48.f90 +++ b/test/Semantics/resolve48.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test correct use-association of a derived type. module m1 implicit none diff --git a/test/Semantics/resolve49.f90 b/test/Semantics/resolve49.f90 index 97d2cbdb1267..583399044977 100644 --- a/test/Semantics/resolve49.f90 +++ b/test/Semantics/resolve49.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test section subscript program p1 real :: a(10,10) diff --git a/test/Semantics/resolve50.f90 b/test/Semantics/resolve50.f90 index 34d6f1c1d5d5..8158ab6bd72a 100644 --- a/test/Semantics/resolve50.f90 +++ b/test/Semantics/resolve50.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test coarray association in CHANGE TEAM statement subroutine s1 diff --git a/test/Semantics/resolve51.f90 b/test/Semantics/resolve51.f90 index de763ef49911..d2942a8f345b 100644 --- a/test/Semantics/resolve51.f90 +++ b/test/Semantics/resolve51.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test SELECT TYPE errors: C1157 subroutine s1() diff --git a/test/Semantics/resolve52.f90 b/test/Semantics/resolve52.f90 index 846b412f05ca..33eef54755af 100644 --- a/test/Semantics/resolve52.f90 +++ b/test/Semantics/resolve52.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests for C760: ! The passed-object dummy argument shall be a scalar, nonpointer, nonallocatable ! dummy data object with the same declared type as the type being defined; diff --git a/test/Semantics/resolve53.f90 b/test/Semantics/resolve53.f90 index 1aee5e79bcc9..e501941f5a6f 100644 --- a/test/Semantics/resolve53.f90 +++ b/test/Semantics/resolve53.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! 15.4.3.4.5 Restrictions on generic declarations ! Specific procedures of generic interfaces must be distinguishable. diff --git a/test/Semantics/resolve54.f90 b/test/Semantics/resolve54.f90 index f9f895fa7f05..f8b80fc126e4 100644 --- a/test/Semantics/resolve54.f90 +++ b/test/Semantics/resolve54.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests based on examples in C.10.6 ! C.10.6(10) diff --git a/test/Semantics/resolve55.f90 b/test/Semantics/resolve55.f90 index 98006bc0a07b..422168be90a8 100644 --- a/test/Semantics/resolve55.f90 +++ b/test/Semantics/resolve55.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests for C1128: ! A variable-name that appears in a LOCAL or LOCAL_INIT locality-spec shall not ! have the ALLOCATABLE; INTENT (IN); or OPTIONAL attribute; shall not be of diff --git a/test/Semantics/resolve56.f90 b/test/Semantics/resolve56.f90 index 1efa535bd434..ef99f99ec620 100644 --- a/test/Semantics/resolve56.f90 +++ b/test/Semantics/resolve56.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test that associations constructs can be correctly combined. The intrinsic ! functions are not what is tested here, they are only use to reveal the types ! of local variables. diff --git a/test/Semantics/resolve57.f90 b/test/Semantics/resolve57.f90 index 265decd3bcde..50843a1bbfbc 100644 --- a/test/Semantics/resolve57.f90 +++ b/test/Semantics/resolve57.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests for the last sentence of C1128: !A variable-name that is not permitted to appear in a variable definition !context shall not appear in a LOCAL or LOCAL_INIT locality-spec. diff --git a/test/Semantics/resolve58.f90 b/test/Semantics/resolve58.f90 index db11e6779335..15fe4675c17d 100644 --- a/test/Semantics/resolve58.f90 +++ b/test/Semantics/resolve58.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1(x, y) !ERROR: Array pointer 'x' must have deferred shape or assumed rank real, pointer :: x(1:) ! C832 diff --git a/test/Semantics/resolve59.f90 b/test/Semantics/resolve59.f90 index fdc437030971..49e46a9c8c7d 100644 --- a/test/Semantics/resolve59.f90 +++ b/test/Semantics/resolve59.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Testing 15.6.2.2 point 4 (What function-name refers to depending on the ! presence of RESULT). diff --git a/test/Semantics/resolve60.f90 b/test/Semantics/resolve60.f90 index 3232bc0fb87a..811460e35975 100644 --- a/test/Semantics/resolve60.f90 +++ b/test/Semantics/resolve60.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Testing 7.6 enum ! OK diff --git a/test/Semantics/resolve61.f90 b/test/Semantics/resolve61.f90 index eb5ba13a07a3..fe2840c74921 100644 --- a/test/Semantics/resolve61.f90 +++ b/test/Semantics/resolve61.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t program p1 integer(8) :: a, b, c, d pointer(a, b) diff --git a/test/Semantics/resolve62.f90 b/test/Semantics/resolve62.f90 index 5de3a45e900f..1ce28f3426cc 100644 --- a/test/Semantics/resolve62.f90 +++ b/test/Semantics/resolve62.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Resolve generic based on number of arguments subroutine s1 interface f diff --git a/test/Semantics/resolve63.f90 b/test/Semantics/resolve63.f90 index 07ae767d676b..59091574d5d8 100644 --- a/test/Semantics/resolve63.f90 +++ b/test/Semantics/resolve63.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Invalid operand types when user-defined operator is available module m1 type :: t diff --git a/test/Semantics/resolve64.f90 b/test/Semantics/resolve64.f90 index 3be2ae14fd5d..b0c2a608a4eb 100644 --- a/test/Semantics/resolve64.f90 +++ b/test/Semantics/resolve64.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t !OPTIONS: -flogical-abbreviations -fxor-operator ! Like m4 in resolve63 but compiled with different options. diff --git a/test/Semantics/resolve65.f90 b/test/Semantics/resolve65.f90 index 9e1278b66dd5..f43d70bb22c7 100644 --- a/test/Semantics/resolve65.f90 +++ b/test/Semantics/resolve65.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test restrictions on what subprograms can be used for defined assignment. module m1 diff --git a/test/Semantics/resolve66.f90 b/test/Semantics/resolve66.f90 index d54fd2bfe66c..2f2e3595786c 100644 --- a/test/Semantics/resolve66.f90 +++ b/test/Semantics/resolve66.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test that user-defined assignment is used in the right places module m1 diff --git a/test/Semantics/resolve67.f90 b/test/Semantics/resolve67.f90 index 7a8537a0a65e..883909e13936 100644 --- a/test/Semantics/resolve67.f90 +++ b/test/Semantics/resolve67.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test restrictions on what subprograms can be used for defined operators. ! See: 15.4.3.4.2 diff --git a/test/Semantics/resolve68.f90 b/test/Semantics/resolve68.f90 index 6accdafd5263..caa6f2533f98 100644 --- a/test/Semantics/resolve68.f90 +++ b/test/Semantics/resolve68.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Test resolution of type-bound generics. module m1 diff --git a/test/Semantics/resolve69.f90 b/test/Semantics/resolve69.f90 index 3bbc37e3f7aa..d5a35aa00306 100644 --- a/test/Semantics/resolve69.f90 +++ b/test/Semantics/resolve69.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t subroutine s1() ! C701 (R701) The type-param-value for a kind type parameter shall be a ! constant expression. diff --git a/test/Semantics/resolve70.f90 b/test/Semantics/resolve70.f90 index 31f33c345b63..8f805b6be72d 100644 --- a/test/Semantics/resolve70.f90 +++ b/test/Semantics/resolve70.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C703 (R702) The derived-type-spec shall not specify an abstract type (7.5.7). ! This constraint refers to the derived-type-spec in a type-spec. A type-spec ! can appear in an ALLOCATE statement, an ac-spec for an array constructor, and diff --git a/test/Semantics/resolve71.f90 b/test/Semantics/resolve71.f90 index 8c1c56fd9b0e..b4a232ebc2fa 100644 --- a/test/Semantics/resolve71.f90 +++ b/test/Semantics/resolve71.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C708 An entity declared with the CLASS keyword shall be a dummy argument ! or have the ALLOCATABLE or POINTER attribute. subroutine s() diff --git a/test/Semantics/resolve72.f90 b/test/Semantics/resolve72.f90 index 284fb2fc2055..0e7dfcbfd762 100644 --- a/test/Semantics/resolve72.f90 +++ b/test/Semantics/resolve72.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C709 An assumed-type entity shall be a dummy data object that does not have ! the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE attribute and ! is not an explicit-shape array. diff --git a/test/Semantics/resolve73.f90 b/test/Semantics/resolve73.f90 index 35f8429aeacf..195f7027a2a4 100644 --- a/test/Semantics/resolve73.f90 +++ b/test/Semantics/resolve73.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C721 A type-param-value of * shall be used only ! * to declare a dummy argument, ! * to declare a named constant, diff --git a/test/Semantics/resolve74.f90 b/test/Semantics/resolve74.f90 index 60927b198769..79a1b2cec49d 100644 --- a/test/Semantics/resolve74.f90 +++ b/test/Semantics/resolve74.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C722 A function name shall not be declared with an asterisk type-param-value ! unless it is of type CHARACTER and is the name of a dummy function or the ! name of the result of an external function. diff --git a/test/Semantics/resolve75.f90 b/test/Semantics/resolve75.f90 index 708ce8ffaeec..025159d78dd4 100644 --- a/test/Semantics/resolve75.f90 +++ b/test/Semantics/resolve75.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C726 The length specified for a character statement function or for a ! statement function dummy argument of type character shall be a constant ! expression. diff --git a/test/Semantics/resolve76.f90 b/test/Semantics/resolve76.f90 index e68c81f36fb2..e5e22a99dd19 100644 --- a/test/Semantics/resolve76.f90 +++ b/test/Semantics/resolve76.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! 15.6.2.5(3) diff --git a/test/Semantics/resolve77.f90 b/test/Semantics/resolve77.f90 index 4d34ce3b8b48..efd04d975c79 100644 --- a/test/Semantics/resolve77.f90 +++ b/test/Semantics/resolve77.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Tests valid and invalid usage of forward references to procedures ! in specification expressions. module m diff --git a/test/Semantics/resolve78.f90 b/test/Semantics/resolve78.f90 index 0e4efc081009..280e1256dc55 100644 --- a/test/Semantics/resolve78.f90 +++ b/test/Semantics/resolve78.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m ! C743 No component-attr-spec shall appear more than once in a ! given component-def-stmt. diff --git a/test/Semantics/resolve79.f90 b/test/Semantics/resolve79.f90 index 5d0e2127ea10..3bac3bf30583 100644 --- a/test/Semantics/resolve79.f90 +++ b/test/Semantics/resolve79.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m ! C755 The same proc-component-attr-spec shall not appear more than once in a ! given proc-component-def-stmt. diff --git a/test/Semantics/resolve80.f90 b/test/Semantics/resolve80.f90 index 98f5c79a343b..4a196e26fc76 100644 --- a/test/Semantics/resolve80.f90 +++ b/test/Semantics/resolve80.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m !C778 The same binding-attr shall not appear more than once in a given !binding-attr-list. diff --git a/test/Semantics/resolve81.f90 b/test/Semantics/resolve81.f90 index 218d74ec6744..14f80ac9aaba 100644 --- a/test/Semantics/resolve81.f90 +++ b/test/Semantics/resolve81.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C801 The same attr-spec shall not appear more than once in a given ! type-declaration-stmt. ! diff --git a/test/Semantics/resolve82.f90 b/test/Semantics/resolve82.f90 index 378e8796db45..673abaa765ed 100644 --- a/test/Semantics/resolve82.f90 +++ b/test/Semantics/resolve82.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C815 An entity shall not be explicitly given any attribute more than once in ! a scoping unit. ! diff --git a/test/Semantics/resolve83.f90 b/test/Semantics/resolve83.f90 index cdd528a688e2..c7a4502fd6f5 100644 --- a/test/Semantics/resolve83.f90 +++ b/test/Semantics/resolve83.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m ! For C1543 diff --git a/test/Semantics/resolve84.f90 b/test/Semantics/resolve84.f90 index 79e393f4b689..06afdfc492e1 100644 --- a/test/Semantics/resolve84.f90 +++ b/test/Semantics/resolve84.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! C729 A derived type type-name shall not be DOUBLEPRECISION or the same as ! the name of any intrinsic type defined in this document. subroutine s() diff --git a/test/Semantics/resolve85.f90 b/test/Semantics/resolve85.f90 index d228b7d03e47..99391a364598 100644 --- a/test/Semantics/resolve85.f90 +++ b/test/Semantics/resolve85.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t module m ! C730 The same type-attr-spec shall not appear more than once in a given ! derived-type-stmt. diff --git a/test/Semantics/separate-mp01.f90 b/test/Semantics/separate-mp01.f90 index 305c147e66c9..b34b3500e28f 100644 --- a/test/Semantics/separate-mp01.f90 +++ b/test/Semantics/separate-mp01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! case 1: ma_create_new_fun' was not declared a separate module procedure module m1 diff --git a/test/Semantics/separate-mp02.f90 b/test/Semantics/separate-mp02.f90 index 1f514c2ccd37..823bfaca1413 100644 --- a/test/Semantics/separate-mp02.f90 +++ b/test/Semantics/separate-mp02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! When a module subprogram has the MODULE prefix the following must match ! with the corresponding separate module procedure interface body: diff --git a/test/Semantics/stop01.f90 b/test/Semantics/stop01.f90 index 2ae8d65a84bb..69c5ad83e481 100644 --- a/test/Semantics/stop01.f90 +++ b/test/Semantics/stop01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t program main implicit none integer :: i = -1 diff --git a/test/Semantics/structconst01.f90 b/test/Semantics/structconst01.f90 index 68f0261cd85d..cdd2e77a506a 100644 --- a/test/Semantics/structconst01.f90 +++ b/test/Semantics/structconst01.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for structure constructors. ! Errors caught by name resolution are tested elsewhere; these are the ! errors meant to be caught by expression semantic analysis, as well as diff --git a/test/Semantics/structconst02.f90 b/test/Semantics/structconst02.f90 index 22428651fa1c..a309b02e8fe0 100644 --- a/test/Semantics/structconst02.f90 +++ b/test/Semantics/structconst02.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for structure constructors: per-component type ! (in)compatibility. diff --git a/test/Semantics/structconst03.f90 b/test/Semantics/structconst03.f90 index 776b4d082309..01d40720e771 100644 --- a/test/Semantics/structconst03.f90 +++ b/test/Semantics/structconst03.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for structure constructors: C1594 violations ! from assigning globally-visible data to POINTER components. ! test/Semantics/structconst04.f90 is this same test without type diff --git a/test/Semantics/structconst04.f90 b/test/Semantics/structconst04.f90 index 07a9d69df868..3f0b21c6d5ec 100644 --- a/test/Semantics/structconst04.f90 +++ b/test/Semantics/structconst04.f90 @@ -1,4 +1,4 @@ -! RUN: %S/test_errors.sh %s %flang %t +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t ! Error tests for structure constructors: C1594 violations ! from assigning globally-visible data to POINTER components. ! This test is structconst03.f90 with the type parameters removed. diff --git a/test/Semantics/test_any.sh b/test/Semantics/test_any.sh index b0735935928f..19fa22f1574d 100755 --- a/test/Semantics/test_any.sh +++ b/test/Semantics/test_any.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#/usr/bin/env bash # Compile a source file with '-funparse-with-symbols' and verify # we get the right symbols in the output, i.e. the output should be # the same as the input, except for the copyright comment. @@ -7,7 +7,7 @@ srcdir=$(dirname $0) source $srcdir/common.sh -FileCheck=${FileCheck:=internal_check} +FileCheck=internal_check function internal_check() { r=true diff --git a/test/Semantics/test_errors.sh b/test/Semantics/test_errors.sh.in similarity index 93% rename from test/Semantics/test_errors.sh rename to test/Semantics/test_errors.sh.in index 9fcb06b03320..535ded3882bf 100755 --- a/test/Semantics/test_errors.sh +++ b/test/Semantics/test_errors.sh.in @@ -3,7 +3,7 @@ # Change the compiler by setting the F18 environment variable. F18_OPTIONS="-fdebug-resolve-names -fparse-only" -srcdir=$(dirname $0) +srcdir="@CMAKE_CURRENT_SOURCE_DIR@" source $srcdir/common.sh [[ ! -f $src ]] && die "File not found: $src" @@ -12,7 +12,7 @@ actual=$temp/actual expect=$temp/expect diffs=$temp/diffs -include=$(dirname $(dirname $F18))/include +include="@FLANG_INTRINSIC_MODULES_DIR@" cmd="$F18 $F18_OPTIONS $USER_OPTIONS -I$include $src" ( cd $temp; $cmd ) > $log 2>&1 if [[ $? -ge 128 ]]; then diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 57dc7383d88b..439f9710ef66 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -54,6 +54,8 @@ if config.llvm_tools_dir != config.flang_llvm_tools_dir : llvm_config.with_environment('PATH', config.flang_llvm_tools_dir, append_path=True) +config.substitutions.append(('%B', config.flang_obj_root)) + # For each occurrence of a flang tool name, replace it with the full path to # the build directory holding that tool. We explicitly specify the directories # to search to ensure that we get the tools just built and not some random @@ -71,4 +73,4 @@ # Enable libpgmath testing result = lit_config.params.get("LIBPGMATH") if result: - config.environment["LIBPGMATH"] = True \ No newline at end of file + config.environment["LIBPGMATH"] = True diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a02679436d14..b973127d3443 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory(f18) if(LINK_WITH_FIR) add_subdirectory(tco) endif() +add_subdirectory(f18-parse-demo) diff --git a/tools/f18-parse-demo/CMakeLists.txt b/tools/f18-parse-demo/CMakeLists.txt new file mode 100644 index 000000000000..fc64a3f0d904 --- /dev/null +++ b/tools/f18-parse-demo/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_tool(f18-parse-demo + f18-parse-demo.cpp + stub-evaluate.cpp + ) +set_property(TARGET f18-parse-demo PROPERTY CXX_STANDARD 17) +target_compile_features(f18-parse-demo PRIVATE cxx_std_17) + +target_link_libraries(f18-parse-demo + PRIVATE + FortranParser + ) + +#install(TARGETS f18-parse-demo DESTINATION bin) diff --git a/tools/f18/f18-parse-demo.cpp b/tools/f18-parse-demo/f18-parse-demo.cpp similarity index 100% rename from tools/f18/f18-parse-demo.cpp rename to tools/f18-parse-demo/f18-parse-demo.cpp diff --git a/tools/f18/stub-evaluate.cpp b/tools/f18-parse-demo/stub-evaluate.cpp similarity index 100% rename from tools/f18/stub-evaluate.cpp rename to tools/f18-parse-demo/stub-evaluate.cpp diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index b2f8e129e83a..8745f7c1caef 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -1,20 +1,12 @@ -#===-- tools/f18/CMakeLists.txt --------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") - -add_executable(f18 - f18.cpp +add_llvm_tool(f18 dump.cpp + f18.cpp ) +set_property(TARGET f18 PROPERTY CXX_STANDARD 17) +target_compile_features(f18 PRIVATE cxx_std_17) target_link_libraries(f18 + PRIVATE FortranParser FortranEvaluate FortranSemantics @@ -22,20 +14,6 @@ target_link_libraries(f18 LLVMSupport ) -add_executable(f18-parse-demo - f18-parse-demo.cpp - stub-evaluate.cpp -) - -target_link_libraries(f18-parse-demo - FortranParser -) - -set_target_properties(f18 f18-parse-demo - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" -) - set(MODULES "ieee_arithmetic" "ieee_exceptions" @@ -46,7 +24,9 @@ set(MODULES "__fortran_builtins" ) -set(include ${CMAKE_CURRENT_BINARY_DIR}/include) +set(include ${FLANG_BINARY_DIR}/include/flang) + +set(include ${FLANG_BINARY_DIR}/include/flang) # Create module files directly from the top-level module source directory foreach(filename ${MODULES}) @@ -57,9 +37,9 @@ foreach(filename ${MODULES}) endif() add_custom_command(OUTPUT ${include}/${filename}.mod COMMAND f18 -fparse-only -I${include} - ${PROJECT_SOURCE_DIR}/module/${filename}.f90 + ${FLANG_SOURCE_DIR}/module/${filename}.f90 WORKING_DIRECTORY ${include} - DEPENDS f18 ${PROJECT_SOURCE_DIR}/module/${filename}.f90 ${depends} + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ${depends} ) add_custom_command(OUTPUT ${include}/${filename}.f18.mod DEPENDS ${include}/${filename}.mod @@ -67,21 +47,19 @@ foreach(filename ${MODULES}) copy ${include}/${filename}.mod ${include}/${filename}.f18.mod) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) - install(FILES ${include}/${filename}.mod DESTINATION include) - install(FILES ${include}/${filename}.f18.mod DESTINATION include) + install(FILES ${include}/${filename}.mod DESTINATION include/flang) + install(FILES ${include}/${filename}.f18.mod DESTINATION include/flang) endforeach() add_custom_target(module_files ALL DEPENDS ${MODULE_FILES}) -install(TARGETS f18 f18-parse-demo DESTINATION bin) +install(TARGETS f18 DESTINATION bin) -file(COPY flang.sh - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/bin" - FILE_PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE -) -file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/bin/flang.sh" "${CMAKE_CURRENT_BINARY_DIR}/bin/flang") +set(FLANG_INTRINSIC_MODULES_DIR ${FLANG_BINARY_DIR}/include/flang) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/flang.sh.in ${CMAKE_BINARY_DIR}/tools/flang/bin/flang @ONLY) +file(COPY ${CMAKE_BINARY_DIR}/tools/flang/bin/flang DESTINATION ${CMAKE_BINARY_DIR}/bin FILE_PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE) +# The flang script to be installed needs a different path to the headers. +set(FLANG_INTRINSIC_MODULES_DIR ${CMAKE_INSTALL_PREFIX}/include/flang) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/flang.sh.in ${FLANG_BINARY_DIR}/bin/flang-install.sh @ONLY) -install(PROGRAMS flang.sh DESTINATION bin RENAME flang) +install(PROGRAMS ${FLANG_BINARY_DIR}/bin/flang-install.sh DESTINATION bin RENAME flang PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE) diff --git a/tools/f18/flang.sh b/tools/f18/flang.sh.in similarity index 95% rename from tools/f18/flang.sh rename to tools/f18/flang.sh.in index bf37e99f4d22..7f0d1335aec4 100644 --- a/tools/f18/flang.sh +++ b/tools/f18/flang.sh.in @@ -26,4 +26,4 @@ function abspath() { wd=`abspath $(dirname "$0")/..` -${wd}/bin/f18 -module-suffix .f18.mod -intrinsic-module-directory ${wd}/include $* +${wd}/bin/f18 -module-suffix .f18.mod -intrinsic-module-directory @FLANG_INTRINSIC_MODULES_DIR@ $* diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 6d49e6c72b57..2171927aa104 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -1,11 +1,3 @@ -#===-- test/CMakeLists.txt -------------------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - add_subdirectory(Decimal) add_subdirectory(Evaluate) add_subdirectory(Runtime) diff --git a/unittests/Decimal/CMakeLists.txt b/unittests/Decimal/CMakeLists.txt index 780c92e74475..f26aca5d0e9b 100644 --- a/unittests/Decimal/CMakeLists.txt +++ b/unittests/Decimal/CMakeLists.txt @@ -1,11 +1,4 @@ -#===-- test/Decimal/CMakeLists.txt -----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_executable(quick-sanity-test quick-sanity-test.cpp ) @@ -24,4 +17,4 @@ target_link_libraries(thorough-test LLVMSupport ) -add_test(Sanity quick-sanity-test) +add_test(NAME Sanity COMMAND quick-sanity-test) diff --git a/unittests/Evaluate/CMakeLists.txt b/unittests/Evaluate/CMakeLists.txt index fb195ae5730a..54c816ef6c55 100644 --- a/unittests/Evaluate/CMakeLists.txt +++ b/unittests/Evaluate/CMakeLists.txt @@ -1,11 +1,4 @@ -#===-- test/Evaluate/CMakeLists.txt ----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_library(FortranEvaluateTesting testing.cpp fp-testing.cpp diff --git a/unittests/Runtime/CMakeLists.txt b/unittests/Runtime/CMakeLists.txt index 3f73b79132f9..a5297ac67821 100644 --- a/unittests/Runtime/CMakeLists.txt +++ b/unittests/Runtime/CMakeLists.txt @@ -1,18 +1,11 @@ -#===-- test/Runtime/CMakeLists.txt -----------------------------------------===# -# -# 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 -# -#------------------------------------------------------------------------------# - if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions") endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_library(RuntimeTesting testing.cpp -) + ) add_executable(format-test format.cpp @@ -24,7 +17,7 @@ target_link_libraries(format-test LLVMSupport ) -add_test(Format format-test) +add_test(NAME Format COMMAND format-test) add_executable(hello-world hello.cpp @@ -36,7 +29,7 @@ target_link_libraries(hello-world LLVMSupport ) -add_test(HelloWorld hello-world) +add_test(NAME HelloWorld COMMAND hello-world) add_executable(external-hello-world external-hello.cpp @@ -49,7 +42,7 @@ target_link_libraries(external-hello-world add_executable(list-input-test list-input.cpp -) + ) target_link_libraries(list-input-test FortranRuntime @@ -57,4 +50,4 @@ target_link_libraries(list-input-test LLVMSupport ) -add_test(ListInput list-input-test) +add_test(NAME ListInput COMMAND list-input-test) From 116f64315f6218a7f74a8ccdc25f94d322ac6344 Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Fri, 27 Mar 2020 09:23:32 -0700 Subject: [PATCH 103/345] [mlir rebase] Add MLIR config and react to MLIR name changes (#1090) [mlir rebase] Add MLIR config and react to MLIR name changes Similar to #1085. Now use the MLIR package to set up paths for include files and libraries. Three MLIR names changed: * VectorOpsDialect to VectorDialect * AffineOpsDialect to AffineDialect * createVectorizePass to createSuperVectorizePass Update README.md to explain how to link with MLIR. Update the example gcc to version 8.3. Update drone.io config to define -DMLIR_DIR Co-authored-by: Jean Perier --- .drone.star | 4 +-- CMakeLists.txt | 4 +++ README.md | 34 ++++++++++++-------- include/flang/Optimizer/Dialect/FIRDialect.h | 6 ++-- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.drone.star b/.drone.star index 27ff72e911cf..b03169c5014a 100644 --- a/.drone.star +++ b/.drone.star @@ -14,7 +14,7 @@ def clang(arch): "ninja install", "cd ../..", "mkdir build && cd build", - 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', + 'env CC=clang-8 CXX=clang++-8 CXXFLAGS="-UNDEBUG -stdlib=libc++" LDFLAGS="-fuse-ld=lld" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DMLIR_DIR=/drone/src/llvm-project/install/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", @@ -40,7 +40,7 @@ def gcc(arch): "ninja install", "cd ../..", "mkdir build && cd build", - 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', + 'env CC=gcc CXX=g++ CXXFLAGS="-UNDEBUG" LDFLAGS="-fuse-ld=gold" cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. -DLLVM_DIR=/drone/src/llvm-project/install/lib/cmake/llvm -DMLIR_DIR=/drone/src/llvm-project/install/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=/drone/src/llvm-project/build/bin/llvm-lit', "ninja -j8", "ctest --output-on-failure -j24", "ninja check-all", diff --git a/CMakeLists.txt b/CMakeLists.txt index 54c5d52c45f6..dceeeb16f25a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,10 @@ project(Flang) if(LINK_WITH_FIR) include(TableGen) + find_package(MLIR REQUIRED CONFIG) + # Use SYSTEM for the same reasons as for LLVM includes + include_directories(SYSTEM ${MLIR_INCLUDE_DIRS}) + list(APPEND CMAKE_MODULE_PATH ${MLIR_DIR}) include(AddMLIR) find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) diff --git a/README.md b/README.md index 926abc0ecf5d..a64bab0cb912 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ - # F18 @@ -72,12 +72,16 @@ https://llvm.org/docs/GettingStarted.html. We highly recommend using the same compiler to compile both llvm and f18. The f18 CMakeList.txt file uses -the variable `LLVM_DIR` to find the installed components. +the variable `LLVM_DIR` to find the installed LLVM components +and +the variable `MLIR_DIR` to find the installed MLIR components. -To get the correct LLVM libraries included in your f18 build, -define LLVM_DIR on the cmake command line. +To get the correct LLVM and MLIR libraries included in your f18 build, +define LLVM_DIR and MLIR_DIR on the cmake command line. ``` -LLVM=/lib/cmake/llvm cmake -DLLVM_DIR=$LLVM ... +LLVM=/lib/cmake/llvm \ +MLIR=/lib/cmake/mlir \ +cmake -DLLVM_DIR=$LLVM -DMLIR_DIR=$MLIR ... ``` where `LLVM_BUILD_DIR` is the top-level directory where LLVM was built. @@ -121,7 +125,11 @@ make install ``` Then, `-DLLVM_DIR` would have to be set to - `/install/lib/cmake/llvm` in f18 cmake command. + `/install/lib/cmake/llvm` +and, `-DMLIR_DIR` would have to be set to + `/install/lib/cmake/mlir` + +in f18 cmake command. To run lit tests, `-DLLVM_EXTERNAL_LIT=/build/bin/llvm-lit` must be @@ -142,13 +150,13 @@ Or, cmake will use the variable CXX to find the C++ compiler. CXX should include the full path to the compiler or a name that will be found on your PATH, -e.g. g++-7.2, assuming g++-7.2 is on your PATH. +e.g. g++-8.3, assuming g++-8.3 is on your PATH. ``` -export CXX=g++-7.2 +export CXX=g++-8.3 ``` or ``` -CXX=/opt/gcc-7.2/bin/g++-7.2 cmake ... +CXX=/opt/gcc-8.3/bin/g++-8.3 cmake ... ``` ### Building f18 with clang @@ -189,7 +197,7 @@ Release builds execute quickly. ### Build F18 ``` cd ~/f18/build -cmake -DLLVM_DIR=$LLVM ~/f18/src +cmake -DLLVM_DIR=$LLVM -DMLIR_DIR=$MLIR ~/f18/src make ``` @@ -198,7 +206,7 @@ make To run all tests: ``` cd ~/f18/build -cmake -DLLVM_DIR=$LLVM ~/f18/src +cmake -DLLVM_DIR=$LLVM -DMLIR_DIR=$MLIR ~/f18/src make test check-all ``` diff --git a/include/flang/Optimizer/Dialect/FIRDialect.h b/include/flang/Optimizer/Dialect/FIRDialect.h index 4818a7100b9c..7a8fc18937fc 100644 --- a/include/flang/Optimizer/Dialect/FIRDialect.h +++ b/include/flang/Optimizer/Dialect/FIRDialect.h @@ -50,11 +50,11 @@ class FIROpsDialect final : public mlir::Dialect { inline void registerFIR() { // we want to register exactly once [[maybe_unused]] static bool init_once = [] { - mlir::registerDialect(); + mlir::registerDialect(); mlir::registerDialect(); mlir::registerDialect(); mlir::registerDialect(); - mlir::registerDialect(); + mlir::registerDialect(); mlir::registerDialect(); return true; }(); @@ -65,7 +65,7 @@ inline void registerFIR() { inline void registerGeneralPasses() { mlir::createCanonicalizerPass(); mlir::createCSEPass(); - mlir::createVectorizePass({}); + mlir::createSuperVectorizePass({}); mlir::createLoopUnrollPass(); mlir::createLoopUnrollAndJamPass(); mlir::createSimplifyAffineStructuresPass(); From 12f6f30600db4cb3902677cd42764450ddeda5e0 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 26 Mar 2020 12:25:29 -0700 Subject: [PATCH 104/345] Semantics for SELECT CASE Prep for review Respond to review comments Fix first line in new test --- include/flang/Evaluate/integer.h | 42 +++-- include/flang/Evaluate/logical.h | 38 ++++- lib/Semantics/CMakeLists.txt | 1 + lib/Semantics/check-case.cpp | 253 +++++++++++++++++++++++++++++++ lib/Semantics/check-case.h | 30 ++++ lib/Semantics/semantics.cpp | 41 ++--- test/Semantics/case01.f90 | 165 ++++++++++++++++++++ 7 files changed, 528 insertions(+), 42 deletions(-) create mode 100644 lib/Semantics/check-case.cpp create mode 100644 lib/Semantics/check-case.h create mode 100644 test/Semantics/case01.f90 diff --git a/include/flang/Evaluate/integer.h b/include/flang/Evaluate/integer.h index 6f997c967e69..f519c2e64148 100644 --- a/include/flang/Evaluate/integer.h +++ b/include/flang/Evaluate/integer.h @@ -49,7 +49,7 @@ namespace Fortran::evaluate::value { // Member functions that correspond to Fortran intrinsic functions are // named accordingly in ALL CAPS so that they can be referenced easily in // the language standard. -template, typename BIGPART = HostUnsignedInt> @@ -110,13 +110,13 @@ class Integer { }; // Constructors and value-generating static functions - constexpr Integer() { Clear(); } // default constructor: zero + constexpr Integer() { Clear(); } // default constructor: zero constexpr Integer(const Integer &) = default; constexpr Integer(Integer &&) = default; // C++'s integral types can all be converted to Integer // with silent truncation. - template>> + template >> constexpr Integer(INT n) { constexpr int nBits = CHAR_BIT * sizeof n; if constexpr (nBits < partBits) { @@ -175,12 +175,24 @@ class Integer { constexpr Integer &operator=(const Integer &) = default; + constexpr bool operator<(const Integer &that) const { + return CompareUnsigned(that) == Ordering::Less; + } + constexpr bool operator<=(const Integer &that) const { + return CompareUnsigned(that) != Ordering::Greater; + } constexpr bool operator==(const Integer &that) const { return CompareUnsigned(that) == Ordering::Equal; } constexpr bool operator!=(const Integer &that) const { return !(*this == that); } + constexpr bool operator>=(const Integer &that) const { + return CompareUnsigned(that) != Ordering::Less; + } + constexpr bool operator>(const Integer &that) const { + return CompareUnsigned(that) == Ordering::Greater; + } // Left-justified mask (e.g., MASKL(1) has only its sign bit set) static constexpr Integer MASKL(int places) { @@ -265,7 +277,7 @@ class Integer { return {result, overflow}; } - template + template static constexpr ValueWithOverflow ConvertUnsigned(const FROM &that) { std::uint64_t field{that.ToUInt64()}; ValueWithOverflow result{field, false}; @@ -286,7 +298,7 @@ class Integer { return result; } - template + template static constexpr ValueWithOverflow ConvertSigned(const FROM &that) { ValueWithOverflow result{ConvertUnsigned(that)}; if constexpr (bits > FROM::bits) { @@ -344,7 +356,7 @@ class Integer { return result; } - static constexpr int DIGITS{bits - 1}; // don't count the sign bit + static constexpr int DIGITS{bits - 1}; // don't count the sign bit static constexpr Integer HUGE() { return MASKR(bits - 1); } static constexpr int RANGE{// in the sense of SELECTED_INT_KIND // This magic value is LOG10(2.)*1E12. @@ -404,9 +416,9 @@ class Integer { constexpr bool POPPAR() const { return POPCNT() & 1; } constexpr int TRAILZ() const { - auto minus1{AddUnsigned(MASKR(bits))}; // { x-1, carry = x > 0 } + auto minus1{AddUnsigned(MASKR(bits))}; // { x-1, carry = x > 0 } if (!minus1.carry) { - return bits; // was zero + return bits; // was zero } else { // x ^ (x-1) has all bits set at and below original least-order set bit. return IEOR(minus1.value).POPCNT() - 1; @@ -786,7 +798,7 @@ class Integer { } constexpr Product MultiplyUnsigned(const Integer &y) const { - Part product[2 * parts]{}; // little-endian full product + Part product[2 * parts]{}; // little-endian full product for (int j{0}; j < parts; ++j) { if (Part xpart{LEPart(j)}) { for (int k{0}; k < parts; ++k) { @@ -842,7 +854,7 @@ class Integer { constexpr QuotientWithRemainder DivideUnsigned(const Integer &divisor) const { if (divisor.IsZero()) { - return {MASKR(bits), Integer{}, true, false}; // overflow to max value + return {MASKR(bits), Integer{}, true, false}; // overflow to max value } int bitsDone{LEADZ()}; Integer top{SHIFTL(bitsDone)}; @@ -942,13 +954,13 @@ class Integer { result.divisionByZero = true; result.power = MASKR(bits - 1); } else if (CompareSigned(Integer{1}) == Ordering::Equal) { - result.power = *this; // 1**x -> 1 + result.power = *this; // 1**x -> 1 } else if (CompareSigned(Integer{-1}) == Ordering::Equal) { if (exponent.BTEST(0)) { - result.power = *this; // (-1)**x -> -1 if x is odd + result.power = *this; // (-1)**x -> -1 if x is odd } } else { - result.power.Clear(); // j**k -> 0 if |j| > 1 and k < 0 + result.power.Clear(); // j**k -> 0 if |j| > 1 and k < 0 } } else { Integer shifted{*this}; @@ -1016,5 +1028,5 @@ extern template class Integer<32>; extern template class Integer<64>; extern template class Integer<80>; extern template class Integer<128>; -} -#endif // FORTRAN_EVALUATE_INTEGER_H_ +} // namespace Fortran::evaluate::value +#endif // FORTRAN_EVALUATE_INTEGER_H_ diff --git a/include/flang/Evaluate/logical.h b/include/flang/Evaluate/logical.h index a7813ecfdd70..44ba30ae6e17 100644 --- a/include/flang/Evaluate/logical.h +++ b/include/flang/Evaluate/logical.h @@ -14,7 +14,7 @@ namespace Fortran::evaluate::value { -template class Logical { +template class Logical { public: static constexpr int bits{BITS}; @@ -22,19 +22,43 @@ template class Logical { // C's bit representation (.TRUE. -> 1, .FALSE. -> 0). static constexpr bool IsLikeC{BITS <= 8 || IS_LIKE_C}; - constexpr Logical() {} // .FALSE. - template + constexpr Logical() {} // .FALSE. + template constexpr Logical(Logical x) : word_{Represent(x.IsTrue())} {} constexpr Logical(bool truth) : word_{Represent(truth)} {} - template constexpr Logical &operator=(Logical x) { + template constexpr Logical &operator=(Logical x) { word_ = Represent(x.IsTrue()); } - template + // Fortran actually has only .EQV. & .NEQV. relational operations + // for LOGICAL, but this template class supports more so that + // it can be used with the STL for sorting and as a key type for + // std::set<> & std::map<>. + template + constexpr bool operator<(const Logical &that) const { + return !IsTrue() && that.IsTrue(); + } + template + constexpr bool operator<=(const Logical &) const { + return !IsTrue(); + } + template constexpr bool operator==(const Logical &that) const { return IsTrue() == that.IsTrue(); } + template + constexpr bool operator!=(const Logical &that) const { + return IsTrue() != that.IsTrue(); + } + template + constexpr bool operator>=(const Logical &) const { + return IsTrue(); + } + template + constexpr bool operator>(const Logical &that) const { + return IsTrue() && !that.IsTrue(); + } constexpr bool IsTrue() const { if constexpr (IsLikeC) { @@ -75,5 +99,5 @@ extern template class Logical<8>; extern template class Logical<16>; extern template class Logical<32>; extern template class Logical<64>; -} -#endif // FORTRAN_EVALUATE_LOGICAL_H_ +} // namespace Fortran::evaluate::value +#endif // FORTRAN_EVALUATE_LOGICAL_H_ diff --git a/lib/Semantics/CMakeLists.txt b/lib/Semantics/CMakeLists.txt index 1ca03d05341f..feedbab17860 100644 --- a/lib/Semantics/CMakeLists.txt +++ b/lib/Semantics/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(FortranSemantics check-allocate.cpp check-arithmeticif.cpp check-call.cpp + check-case.cpp check-coarray.cpp check-data.cpp check-deallocate.cpp diff --git a/lib/Semantics/check-case.cpp b/lib/Semantics/check-case.cpp new file mode 100644 index 000000000000..c0f957a83728 --- /dev/null +++ b/lib/Semantics/check-case.cpp @@ -0,0 +1,253 @@ +//===-- lib/Semantics/check-case.cpp --------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "check-case.h" +#include "flang/Common/idioms.h" +#include "flang/Common/reference.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/type.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" +#include + +namespace Fortran::semantics { + +template class CaseValues { +public: + CaseValues(SemanticsContext &c, const evaluate::DynamicType &t) + : context_{c}, caseExprType_{t} {} + + void Check(const std::list &cases) { + for (const parser::CaseConstruct::Case &c : cases) { + AddCase(c); + } + if (!hasErrors_) { + cases_.sort(Comparator{}); + if (!AreCasesDisjoint()) { // C1149 + ReportConflictingCases(); + } + } + } + +private: + using Value = evaluate::Scalar; + + void AddCase(const parser::CaseConstruct::Case &c) { + const auto &stmt{std::get>(c.t)}; + const parser::CaseStmt &caseStmt{stmt.statement}; + const auto &selector{std::get(caseStmt.t)}; + std::visit( + common::visitors{ + [&](const std::list &ranges) { + for (const auto &range : ranges) { + auto pair{ComputeBounds(range)}; + if (pair.first && pair.second && *pair.first > *pair.second) { + context_.Say(stmt.source, + "CASE has lower bound greater than upper bound"_en_US); + } else { + if constexpr (T::category == TypeCategory::Logical) { // C1148 + if ((pair.first || pair.second) && + (!pair.first || !pair.second || + *pair.first != *pair.second)) { + context_.Say(stmt.source, + "CASE range is not allowed for LOGICAL"_err_en_US); + } + } + cases_.emplace_back(stmt); + cases_.back().lower = std::move(pair.first); + cases_.back().upper = std::move(pair.second); + } + } + }, + [&](const parser::Default &) { cases_.emplace_front(stmt); }, + }, + selector.u); + } + + std::optional GetValue(const parser::CaseValue &caseValue) { + const parser::Expr &expr{caseValue.thing.thing.value()}; + auto *x{expr.typedExpr.get()}; + if (x && x->v) { // C1147 + auto type{x->v->GetType()}; + if (type && type->category() == caseExprType_.category() && + (type->category() != TypeCategory::Character || + type->kind() == caseExprType_.kind())) { + x->v = evaluate::Fold(context_.foldingContext(), + evaluate::ConvertToType(T::GetType(), std::move(*x->v))); + if (x->v) { + if (auto value{evaluate::GetScalarConstantValue(*x->v)}) { + return *value; + } + } + context_.Say( + expr.source, "CASE value must be a constant scalar"_err_en_US); + } else { + std::string typeStr{type ? type->AsFortran() : "typeless"s}; + context_.Say(expr.source, + "CASE value has type '%s' which is not compatible with the SELECT CASE expression's type '%s'"_err_en_US, + typeStr, caseExprType_.AsFortran()); + } + hasErrors_ = true; + } + return std::nullopt; + } + + using PairOfValues = std::pair, std::optional>; + PairOfValues ComputeBounds(const parser::CaseValueRange &range) { + return std::visit( + common::visitors{ + [&](const parser::CaseValue &x) { + auto value{GetValue(x)}; + return PairOfValues{value, value}; + }, + [&](const parser::CaseValueRange::Range &x) { + std::optional lo, hi; + if (x.lower) { + lo = GetValue(*x.lower); + } + if (x.upper) { + hi = GetValue(*x.upper); + } + if ((x.lower && !lo) || (x.upper && !hi)) { + return PairOfValues{}; // error case + } + return PairOfValues{std::move(lo), std::move(hi)}; + }, + }, + range.u); + } + + struct Case { + explicit Case(const parser::Statement &s) : stmt{s} {} + bool IsDefault() const { return !lower && !upper; } + std::string AsFortran() const { + std::string result; + { + llvm::raw_string_ostream bs{result}; + if (lower) { + evaluate::Constant{*lower}.AsFortran(bs << '('); + if (!upper) { + bs << ':'; + } else if (*lower != *upper) { + evaluate::Constant{*upper}.AsFortran(bs << ':'); + } + bs << ')'; + } else if (upper) { + evaluate::Constant{*upper}.AsFortran(bs << "(:") << ')'; + } else { + bs << "DEFAULT"; + } + } + return result; + } + + const parser::Statement &stmt; + std::optional lower, upper; + }; + + // Defines a comparator for use with std::list<>::sort(). + // Returns true if and only if the highest value in range x is less + // than the least value in range y. The DEFAULT case is arbitrarily + // defined to be less than all others. When two ranges overlap, + // neither is less than the other. + struct Comparator { + bool operator()(const Case &x, const Case &y) const { + if (x.IsDefault()) { + return !y.IsDefault(); + } else { + return x.upper && y.lower && *x.upper < *y.lower; + } + } + }; + + bool AreCasesDisjoint() const { + auto endIter{cases_.end()}; + for (auto iter{cases_.begin()}; iter != endIter; ++iter) { + auto next{iter}; + if (++next != endIter && !Comparator{}(*iter, *next)) { + return false; + } + } + return true; + } + + // This has quadratic time, but only runs in error cases + void ReportConflictingCases() { + for (auto iter{cases_.begin()}; iter != cases_.end(); ++iter) { + parser::Message *msg{nullptr}; + for (auto p{cases_.begin()}; p != cases_.end(); ++p) { + if (p->stmt.source.begin() < iter->stmt.source.begin() && + !Comparator{}(*p, *iter) && !Comparator{}(*iter, *p)) { + if (!msg) { + msg = &context_.Say(iter->stmt.source, + "CASE %s conflicts with previous cases"_err_en_US, + iter->AsFortran()); + } + msg->Attach( + p->stmt.source, "Conflicting CASE %s"_en_US, p->AsFortran()); + } + } + } + } + + SemanticsContext &context_; + const evaluate::DynamicType &caseExprType_; + std::list cases_; + bool hasErrors_{false}; +}; + +void CaseChecker::Enter(const parser::CaseConstruct &construct) { + const auto &selectCaseStmt{ + std::get>(construct.t)}; + const auto &selectCase{selectCaseStmt.statement}; + const auto &selectExpr{ + std::get>(selectCase.t).thing}; + const auto *x{GetExpr(selectExpr)}; + if (!x) { + return; // expression semantics failed + } + if (auto exprType{x->GetType()}) { + const auto &caseList{ + std::get>(construct.t)}; + switch (exprType->category()) { + case TypeCategory::Integer: + CaseValues>{context_, *exprType} + .Check(caseList); + return; + case TypeCategory::Logical: + CaseValues>{context_, *exprType} + .Check(caseList); + return; + case TypeCategory::Character: + switch (exprType->kind()) { + SWITCH_COVERS_ALL_CASES + case 1: + CaseValues>{ + context_, *exprType} + .Check(caseList); + return; + case 2: + CaseValues>{ + context_, *exprType} + .Check(caseList); + return; + case 4: + CaseValues>{ + context_, *exprType} + .Check(caseList); + return; + } + default: + break; + } + } + context_.Say(selectExpr.source, + "SELECT CASE expression must be integer, logical, or character"_err_en_US); +} +} // namespace Fortran::semantics diff --git a/lib/Semantics/check-case.h b/lib/Semantics/check-case.h new file mode 100644 index 000000000000..6abd6c69e2b9 --- /dev/null +++ b/lib/Semantics/check-case.h @@ -0,0 +1,30 @@ +//===-- lib/Semantics/check-case.h ------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_CHECK_CASE_H_ +#define FORTRAN_SEMANTICS_CHECK_CASE_H_ + +#include "flang/Semantics/semantics.h" + +namespace Fortran::parser { +struct CaseConstruct; +} + +namespace Fortran::semantics { + +class CaseChecker : public virtual BaseChecker { +public: + explicit CaseChecker(SemanticsContext &context) : context_{context} {}; + + void Enter(const parser::CaseConstruct &); + +private: + SemanticsContext &context_; +}; +} // namespace Fortran::semantics +#endif // FORTRAN_SEMANTICS_CHECK_CASE_H_ diff --git a/lib/Semantics/semantics.cpp b/lib/Semantics/semantics.cpp index 340c0a98f7cc..406396b2f776 100644 --- a/lib/Semantics/semantics.cpp +++ b/lib/Semantics/semantics.cpp @@ -12,6 +12,7 @@ #include "canonicalize-omp.h" #include "check-allocate.h" #include "check-arithmeticif.h" +#include "check-case.h" #include "check-coarray.h" #include "check-data.h" #include "check-deallocate.h" @@ -61,44 +62,44 @@ static void GetSymbolNames(const Scope &scope, NameToSymbolMap &symbols) { // children are visited, Leave is called after. No two checkers may have the // same Enter or Leave function. Each checker must be constructible from // SemanticsContext and have BaseChecker as a virtual base class. -template class SemanticsVisitor : public virtual C... { +template class SemanticsVisitor : public virtual C... { public: using C::Enter...; using C::Leave...; using BaseChecker::Enter; using BaseChecker::Leave; SemanticsVisitor(SemanticsContext &context) - : C{context}..., context_{context} {} + : C{context}..., context_{context} {} - template bool Pre(const N &node) { + template bool Pre(const N &node) { if constexpr (common::HasMember) { context_.PushConstruct(node); } Enter(node); return true; } - template void Post(const N &node) { + template void Post(const N &node) { Leave(node); if constexpr (common::HasMember) { context_.PopConstruct(); } } - template bool Pre(const parser::Statement &node) { + template bool Pre(const parser::Statement &node) { context_.set_location(node.source); Enter(node); return true; } - template bool Pre(const parser::UnlabeledStatement &node) { + template bool Pre(const parser::UnlabeledStatement &node) { context_.set_location(node.source); Enter(node); return true; } - template void Post(const parser::Statement &node) { + template void Post(const parser::Statement &node) { Leave(node); context_.set_location(std::nullopt); } - template void Post(const parser::UnlabeledStatement &node) { + template void Post(const parser::UnlabeledStatement &node) { Leave(node); context_.set_location(std::nullopt); } @@ -116,7 +117,7 @@ class EntryChecker : public virtual BaseChecker { public: explicit EntryChecker(SemanticsContext &context) : context_{context} {} void Leave(const parser::EntryStmt &) { - if (!context_.constructStack().empty()) { // C1571 + if (!context_.constructStack().empty()) { // C1571 context_.Say("ENTRY may not appear in an executable construct"_err_en_US); } } @@ -127,10 +128,10 @@ class EntryChecker : public virtual BaseChecker { using StatementSemanticsPass1 = ExprChecker; using StatementSemanticsPass2 = SemanticsVisitor; + ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker, + DataChecker, DeallocateChecker, DoForallChecker, EntryChecker, + IfStmtChecker, IoChecker, NamelistChecker, NullifyChecker, + OmpStructureChecker, PurityChecker, ReturnStmtChecker, StopChecker>; static bool PerformStatementSemantics( SemanticsContext &context, parser::Program &program) { @@ -146,11 +147,11 @@ SemanticsContext::SemanticsContext( const common::IntrinsicTypeDefaultKinds &defaultKinds, const common::LanguageFeatureControl &languageFeatures, parser::AllSources &allSources) - : defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures}, - allSources_{allSources}, - intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds_)}, - foldingContext_{ - parser::ContextualMessages{&messages_}, defaultKinds_, intrinsics_} {} + : defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures}, + allSources_{allSources}, + intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds_)}, + foldingContext_{ + parser::ContextualMessages{&messages_}, defaultKinds_, intrinsics_} {} SemanticsContext::~SemanticsContext() {} @@ -290,7 +291,7 @@ SymbolVector SemanticsContext::GetIndexVars(IndexVarKind kind) { bool Semantics::Perform() { return ValidateLabels(context_, program_) && - parser::CanonicalizeDo(program_) && // force line break + parser::CanonicalizeDo(program_) && // force line break CanonicalizeOmp(context_.messages(), program_) && PerformStatementSemantics(context_, program_) && ModFileWriter{context_}.WriteAll(); @@ -376,4 +377,4 @@ static void PutIndent(llvm::raw_ostream &os, int indent) { os << " "; } } -} +} // namespace Fortran::semantics diff --git a/test/Semantics/case01.f90 b/test/Semantics/case01.f90 new file mode 100644 index 000000000000..7e5efc6a45a8 --- /dev/null +++ b/test/Semantics/case01.f90 @@ -0,0 +1,165 @@ +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t +! Test SELECT CASE Constraints: C1145, C1146, C1147, C1148, C1149 +program selectCaseProg + implicit none + ! local variable declaration + character :: grade1 = 'B' + integer :: grade2 = 3 + logical :: grade3 = .false. + real :: grade4 = 2.0 + character (len = 10) :: name = 'test' + logical, parameter :: grade5 = .false. + CHARACTER(KIND=1), parameter :: ASCII_parm1 = 'a', ASCII_parm2='b' + CHARACTER(KIND=2), parameter :: UCS16_parm = 'c' + CHARACTER(KIND=4), parameter :: UCS32_parm ='d' + type scores + integer :: val + end type + type (scores) :: score = scores(25) + type (scores), parameter :: score_val = scores(50) + + ! Valid Cases + select case (grade1) + case ('A') + case ('B') + case ('C') + case default + end select + + select case (grade2) + case (1) + case (2) + case (3) + case default + end select + + select case (grade3) + case (.true.) + case (.false.) + end select + + select case (name) + case default + case ('now') + case ('test') + end select + + ! C1145 + !ERROR: SELECT CASE expression must be integer, logical, or character + select case (grade4) + case (1.0) + case (2.0) + case (3.0) + case default + end select + + !ERROR: SELECT CASE expression must be integer, logical, or character + select case (score) + case (score_val) + case (scores(100)) + end select + + ! C1146 + select case (grade3) + case default + case (.true.) + !ERROR: CASE DEFAULT conflicts with previous cases + case default + end select + + ! C1147 + select case (grade2) + !ERROR: CASE value has type 'CHARACTER(1)' which is not compatible with the SELECT CASE expression's type 'INTEGER(4)' + case (:'Z') + case default + end select + + select case (grade1) + !ERROR: CASE value has type 'INTEGER(4)' which is not compatible with the SELECT CASE expression's type 'CHARACTER(KIND=1,LEN=1_8)' + case (:1) + case default + end select + + select case (grade3) + case default + case (.true.) + !ERROR: CASE value has type 'INTEGER(4)' which is not compatible with the SELECT CASE expression's type 'LOGICAL(4)' + case (3) + end select + + select case (grade2) + case default + case (2 :) + !ERROR: CASE value has type 'LOGICAL(4)' which is not compatible with the SELECT CASE expression's type 'INTEGER(4)' + case (.true. :) + !ERROR: CASE value has type 'REAL(4)' which is not compatible with the SELECT CASE expression's type 'INTEGER(4)' + case (1.0) + !ERROR: CASE value has type 'CHARACTER(1)' which is not compatible with the SELECT CASE expression's type 'INTEGER(4)' + case ('wow') + end select + + select case (ASCII_parm1) + case (ASCII_parm2) + !ERROR: CASE value has type 'CHARACTER(4)' which is not compatible with the SELECT CASE expression's type 'CHARACTER(1)' + case (UCS32_parm) + !ERROR: CASE value has type 'CHARACTER(2)' which is not compatible with the SELECT CASE expression's type 'CHARACTER(1)' + case (UCS16_parm) + !ERROR: CASE value has type 'CHARACTER(4)' which is not compatible with the SELECT CASE expression's type 'CHARACTER(1)' + case (4_"ucs-32") + !ERROR: CASE value has type 'CHARACTER(2)' which is not compatible with the SELECT CASE expression's type 'CHARACTER(1)' + case (2_"ucs-16") + case default + end select + + ! C1148 + select case (grade3) + case default + !ERROR: CASE range is not allowed for LOGICAL + case (.true. :) + end select + + ! C1149 + select case (grade3) + case (.true.) + case (.false.) + !ERROR: CASE (.true._1) conflicts with previous cases + case (.true.) + !ERROR: CASE (.false._1) conflicts with previous cases + case (grade5) + end select + + select case (grade2) + case (51:50) ! warning + case (100:) + case (:30) + case (40) + case (90) + case (91:99) + !ERROR: CASE (81_16:90_16) conflicts with previous cases + case (81:90) + !ERROR: CASE (:80_16) conflicts with previous cases + case (:80) + !ERROR: CASE (200_16) conflicts with previous cases + case (200) + case default + end select + + select case (name) + case ('hello') + case ('hey') + !ERROR: CASE (:"hh") conflicts with previous cases + case (:'hh') + !ERROR: CASE (:"hd") conflicts with previous cases + case (:'hd') + case ( 'hu':) + case ('hi':'ho') + !ERROR: CASE ("hj") conflicts with previous cases + case ('hj') + !ERROR: CASE ("ha") conflicts with previous cases + case ('ha') + !ERROR: CASE ("hz") conflicts with previous cases + case ('hz') + case default + end select + +end program From 3b0c150b2e4d194cb5a46b40f41b3be59ef5fa51 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 27 Mar 2020 14:17:25 -0700 Subject: [PATCH 105/345] Fix missing substring bounds (bug #1091) --- include/flang/Evaluate/tools.h | 203 ++++++++++++++++++--------------- lib/Evaluate/tools.cpp | 40 +++---- lib/Semantics/assignment.cpp | 18 +-- lib/Semantics/expression.cpp | 138 ++++++++++++---------- lib/Semantics/tools.cpp | 49 ++++---- 5 files changed, 241 insertions(+), 207 deletions(-) diff --git a/include/flang/Evaluate/tools.h b/include/flang/Evaluate/tools.h index 2d2a46fc3eb1..d14827377b22 100644 --- a/include/flang/Evaluate/tools.h +++ b/include/flang/Evaluate/tools.h @@ -31,7 +31,7 @@ namespace Fortran::evaluate { // When an Expr holds something that is a Variable (i.e., a Designator // or pointer-valued FunctionRef), return a copy of its contents in // a Variable. -template +template std::optional> AsVariable(const Expr &expr) { using Variant = decltype(Variable::u); return std::visit( @@ -44,7 +44,7 @@ std::optional> AsVariable(const Expr &expr) { expr.u); } -template +template std::optional> AsVariable(const std::optional> &expr) { if (expr) { return AsVariable(*expr); @@ -58,8 +58,8 @@ std::optional> AsVariable(const std::optional> &expr) { // pointer is a "variable" in Fortran (it can be the left-hand side of // an assignment). struct IsVariableHelper - : public AnyTraverse> { - using Result = std::optional; // effectively tri-state + : public AnyTraverse> { + using Result = std::optional; // effectively tri-state using Base = AnyTraverse; IsVariableHelper() : Base{*this} {} using Base::operator(); @@ -71,7 +71,7 @@ struct IsVariableHelper Result operator()(const CoarrayRef &) const { return true; } Result operator()(const ComplexPart &) const { return true; } Result operator()(const ProcedureDesignator &) const; - template Result operator()(const Expr &x) const { + template Result operator()(const Expr &x) const { if constexpr (common::HasMember || std::is_same_v) { // Expression with a specific type @@ -88,7 +88,7 @@ struct IsVariableHelper } }; -template bool IsVariable(const A &x) { +template bool IsVariable(const A &x) { if (auto known{IsVariableHelper{}(x)}) { return *known; } else { @@ -99,39 +99,39 @@ template bool IsVariable(const A &x) { // Predicate: true when an expression is assumed-rank bool IsAssumedRank(const Symbol &); bool IsAssumedRank(const ActualArgument &); -template bool IsAssumedRank(const A &) { return false; } -template bool IsAssumedRank(const Designator &designator) { +template bool IsAssumedRank(const A &) { return false; } +template bool IsAssumedRank(const Designator &designator) { if (const auto *symbol{std::get_if(&designator.u)}) { return IsAssumedRank(symbol->get()); } else { return false; } } -template bool IsAssumedRank(const Expr &expr) { +template bool IsAssumedRank(const Expr &expr) { return std::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u); } -template bool IsAssumedRank(const std::optional &x) { +template bool IsAssumedRank(const std::optional &x) { return x && IsAssumedRank(*x); } // Generalizing packagers: these take operations and expressions of more // specific types and wrap them in Expr<> containers of more abstract types. -template common::IfNoLvalue>, A> AsExpr(A &&x) { +template common::IfNoLvalue>, A> AsExpr(A &&x) { return Expr>{std::move(x)}; } -template Expr AsExpr(Expr &&x) { +template Expr AsExpr(Expr &&x) { static_assert(IsSpecificIntrinsicType); return std::move(x); } -template +template Expr> AsCategoryExpr(Expr> &&x) { return std::move(x); } -template +template common::IfNoLvalue, A> AsGenericExpr(A &&x) { if constexpr (common::HasMember) { return Expr{std::move(x)}; @@ -140,7 +140,7 @@ common::IfNoLvalue, A> AsGenericExpr(A &&x) { } } -template +template common::IfNoLvalue::category>>, A> AsCategoryExpr( A &&x) { return Expr::category>>{AsExpr(std::move(x))}; @@ -153,13 +153,13 @@ Expr Parenthesize(Expr &&); Expr GetComplexPart( const Expr &, bool isImaginary = false); -template +template Expr MakeComplex(Expr> &&re, Expr> &&im) { return AsCategoryExpr(ComplexConstructor{std::move(re), std::move(im)}); } -template constexpr bool IsNumericCategoryExpr() { +template constexpr bool IsNumericCategoryExpr() { if constexpr (common::HasMember) { return false; } else { @@ -170,7 +170,7 @@ template constexpr bool IsNumericCategoryExpr() { // Specializing extractor. If an Expr wraps some type of object, perhaps // in several layers, return a pointer to it; otherwise null. Also works // with expressions contained in ActualArgument. -template +template auto UnwrapExpr(B &x) -> common::Constify * { using Ty = std::decay_t; if constexpr (std::is_same_v) { @@ -190,7 +190,7 @@ auto UnwrapExpr(B &x) -> common::Constify * { return nullptr; } -template +template const A *UnwrapExpr(const std::optional &x) { if (x) { return UnwrapExpr(*x); @@ -199,7 +199,7 @@ const A *UnwrapExpr(const std::optional &x) { } } -template A *UnwrapExpr(std::optional &x) { +template A *UnwrapExpr(std::optional &x) { if (x) { return UnwrapExpr(*x); } else { @@ -208,41 +208,52 @@ template A *UnwrapExpr(std::optional &x) { } // If an expression simply wraps a DataRef, extract and return it. -template -common::IfNoLvalue, A> ExtractDataRef(const A &) { - return std::nullopt; // default base case -} -template -std::optional ExtractDataRef(const Designator &d) { +// The Boolean argument controls the handling of Substring +// references: when true (not default), it extracts the base DataRef +// of a substring, if it has one. +template +common::IfNoLvalue, A> ExtractDataRef( + const A &, bool intoSubstring) { + return std::nullopt; // default base case +} +template +std::optional ExtractDataRef( + const Designator &d, bool intoSubstring = false) { return std::visit( - [](const auto &x) -> std::optional { + [=](const auto &x) -> std::optional { if constexpr (common::HasMember) { return DataRef{x}; } if constexpr (std::is_same_v, Substring>) { - return ExtractDataRef(x); + if (intoSubstring) { + return ExtractSubstringBase(x); + } } - return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning + return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning }, d.u); } -template -std::optional ExtractDataRef(const Expr &expr) { - return std::visit([](const auto &x) { return ExtractDataRef(x); }, expr.u); +template +std::optional ExtractDataRef( + const Expr &expr, bool intoSubstring = false) { + return std::visit( + [=](const auto &x) { return ExtractDataRef(x, intoSubstring); }, expr.u); } -template -std::optional ExtractDataRef(const std::optional &x) { +template +std::optional ExtractDataRef( + const std::optional &x, bool intoSubstring = false) { if (x) { - return ExtractDataRef(*x); + return ExtractDataRef(*x, intoSubstring); } else { return std::nullopt; } } -std::optional ExtractDataRef(const Substring &); +std::optional ExtractSubstringBase(const Substring &); // Predicate: is an expression is an array element reference? -template bool IsArrayElement(const Expr &expr) { - if (auto dataRef{ExtractDataRef(expr)}) { +template +bool IsArrayElement(const Expr &expr, bool intoSubstring = false) { + if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) { const DataRef *ref{&*dataRef}; while (const Component * component{std::get_if(&ref->u)}) { ref = &component->base(); @@ -253,8 +264,9 @@ template bool IsArrayElement(const Expr &expr) { } } -template std::optional ExtractNamedEntity(const A &x) { - if (auto dataRef{ExtractDataRef(x)}) { +template +std::optional ExtractNamedEntity(const A &x) { + if (auto dataRef{ExtractDataRef(x, true)}) { return std::visit( common::visitors{ [](SymbolRef &&symbol) -> std::optional { @@ -275,11 +287,11 @@ template std::optional ExtractNamedEntity(const A &x) { } struct ExtractCoindexedObjectHelper { - template std::optional operator()(const A &) const { + template std::optional operator()(const A &) const { return std::nullopt; } std::optional operator()(const CoarrayRef &x) const { return x; } - template + template std::optional operator()(const Expr &expr) const { return std::visit(*this, expr.u); } @@ -309,8 +321,8 @@ struct ExtractCoindexedObjectHelper { } }; -template std::optional ExtractCoarrayRef(const A &x) { - if (auto dataRef{ExtractDataRef(x)}) { +template std::optional ExtractCoarrayRef(const A &x) { + if (auto dataRef{ExtractDataRef(x, true)}) { return ExtractCoindexedObjectHelper{}(*dataRef); } else { return ExtractCoindexedObjectHelper{}(x); @@ -319,7 +331,7 @@ template std::optional ExtractCoarrayRef(const A &x) { // If an expression is simply a whole symbol data designator, // extract and return that symbol, else null. -template const Symbol *UnwrapWholeSymbolDataRef(const A &x) { +template const Symbol *UnwrapWholeSymbolDataRef(const A &x) { if (auto dataRef{ExtractDataRef(x)}) { if (const SymbolRef * p{std::get_if(&dataRef->u)}) { return &p->get(); @@ -329,8 +341,8 @@ template const Symbol *UnwrapWholeSymbolDataRef(const A &x) { } // GetFirstSymbol(A%B%C[I]%D) -> A -template const Symbol *GetFirstSymbol(const A &x) { - if (auto dataRef{ExtractDataRef(x)}) { +template const Symbol *GetFirstSymbol(const A &x) { + if (auto dataRef{ExtractDataRef(x, true)}) { return &dataRef->GetFirstSymbol(); } else { return nullptr; @@ -341,7 +353,7 @@ template const Symbol *GetFirstSymbol(const A &x) { // specific intrinsic type with ConvertToType(x) or by converting // one arbitrary expression to the type of another with ConvertTo(to, from). -template +template Expr ConvertToType(Expr> &&x) { static_assert(IsSpecificIntrinsicType); if constexpr (FROMCAT != TO::category) { @@ -390,12 +402,12 @@ Expr ConvertToType(Expr> &&x) { } } -template +template Expr ConvertToType(Expr> &&x) { return ConvertToType(Expr>{std::move(x)}); } -template Expr ConvertToType(BOZLiteralConstant &&x) { +template Expr ConvertToType(BOZLiteralConstant &&x) { static_assert(IsSpecificIntrinsicType); if constexpr (TO::category == TypeCategory::Integer) { return Expr{ @@ -418,13 +430,13 @@ std::optional> ConvertToType( const Symbol &, std::optional> &&); // Conversions to the type of another expression -template +template common::IfNoLvalue>, FROM> ConvertTo( const Expr> &, FROM &&x) { return ConvertToType>(std::move(x)); } -template +template common::IfNoLvalue>, FROM> ConvertTo( const Expr> &to, FROM &&from) { return std::visit( @@ -436,7 +448,7 @@ common::IfNoLvalue>, FROM> ConvertTo( to.u); } -template +template common::IfNoLvalue, FROM> ConvertTo( const Expr &to, FROM &&from) { return std::visit( @@ -448,11 +460,11 @@ common::IfNoLvalue, FROM> ConvertTo( // Convert an expression of some known category to a dynamically chosen // kind of some category (usually but not necessarily distinct). -template struct ConvertToKindHelper { +template struct ConvertToKindHelper { using Result = std::optional>>; using Types = CategoryTypes; ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {} - template Result Test() { + template Result Test() { if (kind == T::kind) { return std::make_optional( AsCategoryExpr(ConvertToType(std::move(value)))); @@ -463,7 +475,7 @@ template struct ConvertToKindHelper { VALUE value; }; -template +template common::IfNoLvalue>, VALUE> ConvertToKind( int kind, VALUE &&x) { return common::SearchTypes( @@ -474,11 +486,11 @@ common::IfNoLvalue>, VALUE> ConvertToKind( // Given a type category CAT, SameKindExprs is a variant that // holds an arrays of expressions of the same supported kind in that // category. -template using SameExprs = std::array, N>; -template struct SameKindExprsHelper { - template using SameExprs = std::array, N>; +template using SameExprs = std::array, N>; +template struct SameKindExprsHelper { + template using SameExprs = std::array, N>; }; -template +template using SameKindExprs = common::MapTemplate::template SameExprs, CategoryTypes>; @@ -486,7 +498,7 @@ using SameKindExprs = // Given references to two expressions of arbitrary kind in the same type // category, convert one to the kind of the other when it has the smaller kind, // then return them in a type-safe package. -template +template SameKindExprs AsSameKindExprs( Expr> &&x, Expr> &&y) { return std::visit( @@ -528,7 +540,7 @@ std::optional> ConstructComplex(parser::ContextualMessages &, std::optional> &&, std::optional> &&, int defaultRealKind); -template Expr> ScalarConstantToExpr(const A &x) { +template Expr> ScalarConstantToExpr(const A &x) { using Ty = TypeOf; static_assert( std::is_same_v, std::decay_t>, "TypeOf<> is broken"); @@ -538,7 +550,7 @@ template Expr> ScalarConstantToExpr(const A &x) { // Combine two expressions of the same specific numeric type with an operation // to produce a new expression. Implements piecewise addition and subtraction // for COMPLEX. -template class OPR, typename SPECIFIC> +template