diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h index 2a5929b873d74..30505a89d16cd 100644 --- a/flang/include/flang/Evaluate/call.h +++ b/flang/include/flang/Evaluate/call.h @@ -99,7 +99,7 @@ class ActualArgument { } const Symbol *GetAssumedTypeDummy() const { - if (const AssumedType * aType{std::get_if(&u_)}) { + if (const AssumedType *aType{std::get_if(&u_)}) { return &aType->symbol(); } else { return nullptr; @@ -219,6 +219,7 @@ struct ProcedureDesignator { int Rank() const; bool IsElemental() const; bool IsPure() const; + bool IsSimple() const; std::optional> LEN() const; llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h index b6a9ebefec9df..7d094fa2236fb 100644 --- a/flang/include/flang/Evaluate/characteristics.h +++ b/flang/include/flang/Evaluate/characteristics.h @@ -363,10 +363,10 @@ struct FunctionResult { // 15.3.1 struct Procedure { - ENUM_CLASS(Attr, Pure, Elemental, BindC, ImplicitInterface, NullPointer, - NullAllocatable, Subroutine) + ENUM_CLASS(Attr, Pure, Simple, Elemental, BindC, ImplicitInterface, + NullPointer, NullAllocatable, Subroutine) using Attrs = common::EnumSet; - Procedure(){}; + Procedure() {}; Procedure(FunctionResult &&, DummyArguments &&, Attrs); Procedure(DummyArguments &&, Attrs); // for subroutines and NULL() DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) @@ -396,6 +396,7 @@ struct Procedure { bool IsSubroutine() const { return attrs.test(Attr::Subroutine); } bool IsPure() const { return attrs.test(Attr::Pure); } + bool IsSimple() const { return attrs.test(Attr::Simple); } bool IsElemental() const { return attrs.test(Attr::Elemental); } bool IsBindC() const { return attrs.test(Attr::BindC); } bool HasExplicitInterface() const { diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h index 5f2f199e778c7..4300dfb27c37f 100644 --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -380,7 +380,7 @@ const Symbol *IsArrayElement(const Expr &expr, bool intoSubstring = true, bool skipComponents = false) { if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) { for (const DataRef *ref{&*dataRef}; ref;) { - if (const Component * component{std::get_if(&ref->u)}) { + if (const Component *component{std::get_if(&ref->u)}) { ref = skipComponents ? &component->base() : nullptr; } else if (const auto *coarrayRef{std::get_if(&ref->u)}) { ref = &coarrayRef->base(); @@ -436,7 +436,7 @@ struct ExtractCoindexedObjectHelper { return common::visit(*this, dataRef.u); } std::optional operator()(const NamedEntity &named) const { - if (const Component * component{named.UnwrapComponent()}) { + if (const Component *component{named.UnwrapComponent()}) { return (*this)(*component); } else { return std::nullopt; @@ -969,7 +969,7 @@ template const Symbol *GetLastSymbol(const A &x) { // its set of attributes, otherwise the empty set. Also works on variables that // are pointer results of functions. template semantics::Attrs GetAttrs(const A &x) { - if (const Symbol * symbol{GetLastSymbol(x)}) { + if (const Symbol *symbol{GetLastSymbol(x)}) { return symbol->attrs(); } else { return {}; @@ -980,7 +980,7 @@ template <> inline semantics::Attrs GetAttrs>(const Expr &x) { if (IsVariable(x)) { if (const auto *procRef{UnwrapProcedureRef(x)}) { - if (const Symbol * interface{procRef->proc().GetInterfaceSymbol()}) { + if (const Symbol *interface{procRef->proc().GetInterfaceSymbol()}) { if (const auto *details{ interface->detailsIf()}) { if (details->isFunction() && @@ -992,7 +992,7 @@ inline semantics::Attrs GetAttrs>(const Expr &x) { } } } - if (const Symbol * symbol{GetLastSymbol(x)}) { + if (const Symbol *symbol{GetLastSymbol(x)}) { return symbol->attrs(); } else { return {}; @@ -1543,6 +1543,8 @@ inline bool IsAlternateEntry(const Symbol *symbol) { bool IsVariableName(const Symbol &); bool IsPureProcedure(const Symbol &); bool IsPureProcedure(const Scope &); +bool IsSimpleProcedure(const Symbol &); +bool IsSimpleProcedure(const Scope &); bool IsExplicitlyImpureProcedure(const Symbol &); bool IsElementalProcedure(const Symbol &); bool IsFunction(const Symbol &); diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 1c9fd7673e06d..73c9803df97a7 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -769,6 +769,7 @@ class ParseTreeDumper { NODE(PrefixSpec, Non_Recursive) NODE(PrefixSpec, Pure) NODE(PrefixSpec, Recursive) + NODE(PrefixSpec, Simple) NODE(PrefixSpec, Attributes) NODE(PrefixSpec, Launch_Bounds) NODE(PrefixSpec, Cluster_Dims) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 951c96b974141..57222f2c3d4f0 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3110,7 +3110,7 @@ struct ProcedureDeclarationStmt { // R1527 prefix-spec -> // declaration-type-spec | ELEMENTAL | IMPURE | MODULE | -// NON_RECURSIVE | PURE | RECURSIVE | +// NON_RECURSIVE | PURE | SIMPLE | RECURSIVE | // (CUDA) ATTRIBUTES ( (DEVICE | GLOBAL | GRID_GLOBAL | HOST)... ) // LAUNCH_BOUNDS(expr-list) | CLUSTER_DIMS(expr-list) struct PrefixSpec { @@ -3121,11 +3121,12 @@ struct PrefixSpec { EMPTY_CLASS(Non_Recursive); EMPTY_CLASS(Pure); EMPTY_CLASS(Recursive); + EMPTY_CLASS(Simple); WRAPPER_CLASS(Attributes, std::list); WRAPPER_CLASS(Launch_Bounds, std::list); WRAPPER_CLASS(Cluster_Dims, std::list); std::variant + Pure, Recursive, Simple, Attributes, Launch_Bounds, Cluster_Dims> u; }; diff --git a/flang/include/flang/Semantics/attr.h b/flang/include/flang/Semantics/attr.h index 76fab5e0c904d..488f325de5887 100644 --- a/flang/include/flang/Semantics/attr.h +++ b/flang/include/flang/Semantics/attr.h @@ -25,7 +25,7 @@ ENUM_CLASS(Attr, ABSTRACT, ALLOCATABLE, ASYNCHRONOUS, BIND_C, CONTIGUOUS, 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) + RECURSIVE, SAVE, SIMPLE, TARGET, VALUE, VOLATILE) // Set of attributes class Attrs : public common::EnumSet { diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp index f77df92a7597a..56db7730d8608 100644 --- a/flang/lib/Evaluate/call.cpp +++ b/flang/lib/Evaluate/call.cpp @@ -66,8 +66,8 @@ void ActualArgument::Parenthesize() { SpecificIntrinsic::SpecificIntrinsic( IntrinsicProcedure n, characteristics::Procedure &&chars) - : name{n}, characteristics{ - new characteristics::Procedure{std::move(chars)}} {} + : name{n}, + characteristics{new characteristics::Procedure{std::move(chars)}} {} DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(SpecificIntrinsic) @@ -98,7 +98,7 @@ std::optional ProcedureDesignator::GetType() const { } int ProcedureDesignator::Rank() const { - if (const Symbol * symbol{GetSymbol()}) { + if (const Symbol *symbol{GetSymbol()}) { // Subtle: will be zero for functions returning procedure pointers return symbol->Rank(); } @@ -116,7 +116,7 @@ int ProcedureDesignator::Rank() const { } const Symbol *ProcedureDesignator::GetInterfaceSymbol() const { - if (const Symbol * symbol{GetSymbol()}) { + if (const Symbol *symbol{GetSymbol()}) { const Symbol &ultimate{symbol->GetUltimate()}; if (const auto *proc{ultimate.detailsIf()}) { return proc->procInterface(); @@ -131,9 +131,9 @@ const Symbol *ProcedureDesignator::GetInterfaceSymbol() const { } bool ProcedureDesignator::IsElemental() const { - if (const Symbol * interface{GetInterfaceSymbol()}) { + if (const Symbol *interface{GetInterfaceSymbol()}) { return IsElementalProcedure(*interface); - } else if (const Symbol * symbol{GetSymbol()}) { + } else if (const Symbol *symbol{GetSymbol()}) { return IsElementalProcedure(*symbol); } else if (const auto *intrinsic{std::get_if(&u)}) { return intrinsic->characteristics.value().attrs.test( @@ -145,9 +145,9 @@ bool ProcedureDesignator::IsElemental() const { } bool ProcedureDesignator::IsPure() const { - if (const Symbol * interface{GetInterfaceSymbol()}) { + if (const Symbol *interface{GetInterfaceSymbol()}) { return IsPureProcedure(*interface); - } else if (const Symbol * symbol{GetSymbol()}) { + } else if (const Symbol *symbol{GetSymbol()}) { return IsPureProcedure(*symbol); } else if (const auto *intrinsic{std::get_if(&u)}) { return intrinsic->characteristics.value().attrs.test( @@ -158,6 +158,20 @@ bool ProcedureDesignator::IsPure() const { return false; } +bool ProcedureDesignator::IsSimple() const { + if (const Symbol *interface{GetInterfaceSymbol()}) { + return IsSimpleProcedure(*interface); + } else if (const Symbol *symbol{GetSymbol()}) { + return IsSimpleProcedure(*symbol); + } else if (const auto *intrinsic{std::get_if(&u)}) { + return intrinsic->characteristics.value().attrs.test( + characteristics::Procedure::Attr::Simple); + } else { + DIE("ProcedureDesignator::IsSimple(): no case"); + } + return false; +} + const SpecificIntrinsic *ProcedureDesignator::GetSpecificIntrinsic() const { return std::get_if(&u); } diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp index 8931cbe485ac2..c222bd2c583a0 100644 --- a/flang/lib/Evaluate/check-expression.cpp +++ b/flang/lib/Evaluate/check-expression.cpp @@ -320,7 +320,7 @@ class IsInitialDataTargetHelper return (*this)(x.left()); } bool operator()(const ProcedureRef &x) const { - if (const SpecificIntrinsic * intrinsic{x.proc().GetSpecificIntrinsic()}) { + if (const SpecificIntrinsic *intrinsic{x.proc().GetSpecificIntrinsic()}) { return intrinsic->characteristics.value().attrs.test( characteristics::Procedure::Attr::NullPointer) || intrinsic->characteristics.value().attrs.test( @@ -1091,7 +1091,7 @@ class IsContiguousHelper upperIsLen = len && *upper >= *len; } else if (const auto *inquiry{ UnwrapConvertedExpr(*upperExpr)}; - inquiry && inquiry->field() == DescriptorInquiry::Field::Len) { + inquiry && inquiry->field() == DescriptorInquiry::Field::Len) { upperIsLen = &parentDataRef.GetLastSymbol() == &inquiry->base().GetLastSymbol(); } @@ -1400,7 +1400,7 @@ class StmtFunctionChecker } } Result operator()(const ProcedureDesignator &proc) const { - if (const Symbol * symbol{proc.GetSymbol()}) { + if (const Symbol *symbol{proc.GetSymbol()}) { const Symbol &ultimate{symbol->GetUltimate()}; if (const auto *subp{ ultimate.detailsIf()}) { diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp index 1f3cbbf6a0c36..20f2961de9f54 100644 --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -100,7 +100,7 @@ auto IsVariableHelper::operator()(const Substring &x) const -> Result { } auto IsVariableHelper::operator()(const ProcedureDesignator &x) const -> Result { - if (const Symbol * symbol{x.GetSymbol()}) { + if (const Symbol *symbol{x.GetSymbol()}) { const Symbol *result{FindFunctionResult(*symbol)}; return result && IsPointer(*result) && !IsProcedurePointer(*result); } @@ -903,7 +903,7 @@ bool IsProcedurePointer(const Expr &expr) { if (IsNullProcedurePointer(&expr)) { return true; } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) { - if (const Symbol * proc{funcRef->proc().GetSymbol()}) { + if (const Symbol *proc{funcRef->proc().GetSymbol()}) { const Symbol *result{FindFunctionResult(*proc)}; return result && IsProcedurePointer(*result); } else { @@ -940,7 +940,7 @@ bool IsObjectPointer(const Expr &expr) { return false; } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) { return IsVariable(*funcRef); - } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) { + } else if (const Symbol *symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) { return IsPointer(symbol->GetUltimate()); } else { return false; @@ -1294,6 +1294,12 @@ std::optional CheckProcCompatibility(bool isCall, } else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) { msg = "PURE procedure %s may not be associated with non-PURE" " procedure designator '%s'"_err_en_US; + } else if (lhsProcedure->IsSimple() && !rhsProcedure->IsSimple()) { + msg = "SIMPLE procedure %s may not be associated with non-SIMPLE" + " procedure designator '%s'"_err_en_US; + } else if (!lhsProcedure->IsSimple() && rhsProcedure->IsSimple()) { + msg = "non-SIMPLE procedure %s may not be associated with SIMPLE" + " procedure designator '%s'"_err_en_US; } else if (lhsProcedure->IsFunction() && rhsProcedure->IsSubroutine()) { msg = "Function %s may not be associated with subroutine" " designator '%s'"_err_en_US; @@ -1338,7 +1344,7 @@ const Symbol *UnwrapWholeSymbolDataRef(const std::optional &dataRef) { } const Symbol *UnwrapWholeSymbolOrComponentDataRef(const DataRef &dataRef) { - if (const Component * c{std::get_if(&dataRef.u)}) { + if (const Component *c{std::get_if(&dataRef.u)}) { return c->base().Rank() == 0 ? &c->GetLastSymbol() : nullptr; } else { return UnwrapWholeSymbolDataRef(dataRef); @@ -1351,7 +1357,7 @@ const Symbol *UnwrapWholeSymbolOrComponentDataRef( } const Symbol *UnwrapWholeSymbolOrComponentOrCoarrayRef(const DataRef &dataRef) { - if (const CoarrayRef * c{std::get_if(&dataRef.u)}) { + if (const CoarrayRef *c{std::get_if(&dataRef.u)}) { return UnwrapWholeSymbolOrComponentOrCoarrayRef(c->base()); } else { return UnwrapWholeSymbolOrComponentDataRef(dataRef); @@ -1415,7 +1421,7 @@ static std::optional> DataConstantConversionHelper( auto at{fromConst->lbounds()}; auto shape{fromConst->shape()}; for (auto n{GetSize(shape)}; n-- > 0; - fromConst->IncrementSubscripts(at)) { + fromConst->IncrementSubscripts(at)) { auto elt{fromConst->At(at)}; if constexpr (TO == TypeCategory::Logical) { values.emplace_back(std::move(elt)); @@ -1466,8 +1472,8 @@ bool IsAllocatableOrPointerObject(const Expr &expr) { bool IsAllocatableDesignator(const Expr &expr) { // Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2). - if (const semantics::Symbol * - sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) { + if (const semantics::Symbol *sym{ + UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) { return semantics::IsAllocatable(sym->GetUltimate()); } return false; @@ -1960,7 +1966,7 @@ const Symbol &ResolveAssociations( if (const auto *details{symbol.detailsIf()}) { if (!details->rank() /* not RANK(n) or RANK(*) */ && !(stopAtTypeGuard && details->isTypeGuard())) { - if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) { + if (const Symbol *nested{UnwrapWholeSymbolDataRef(details->expr())}) { return ResolveAssociations(*nested); } } @@ -1975,7 +1981,7 @@ const Symbol &ResolveAssociations( static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) { if (const auto &expr{details.expr()}) { if (IsVariable(*expr) && !HasVectorSubscript(*expr)) { - if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) { + if (const Symbol *varSymbol{GetFirstSymbol(*expr)}) { return &GetAssociationRoot(*varSymbol); } } @@ -1986,7 +1992,7 @@ static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) { const Symbol &GetAssociationRoot(const Symbol &original, bool stopAtTypeGuard) { const Symbol &symbol{ResolveAssociations(original, stopAtTypeGuard)}; if (const auto *details{symbol.detailsIf()}) { - if (const Symbol * root{GetAssociatedVariable(*details)}) { + if (const Symbol *root{GetAssociatedVariable(*details)}) { return *root; } } @@ -1996,8 +2002,8 @@ const Symbol &GetAssociationRoot(const Symbol &original, bool stopAtTypeGuard) { const Symbol *GetMainEntry(const Symbol *symbol) { if (symbol) { if (const auto *subpDetails{symbol->detailsIf()}) { - if (const Scope * scope{subpDetails->entryScope()}) { - if (const Symbol * main{scope->symbol()}) { + if (const Scope *scope{subpDetails->entryScope()}) { + if (const Symbol *main{scope->symbol()}) { return main; } } @@ -2064,6 +2070,15 @@ bool IsPureProcedure(const Scope &scope) { return symbol && IsPureProcedure(*symbol); } +bool IsSimpleProcedure(const Symbol &original) { + return original.attrs().test(Attr::SIMPLE); +} + +bool IsSimpleProcedure(const Scope &scope) { + const Symbol *symbol{scope.GetSymbol()}; + return symbol && IsSimpleProcedure(*symbol); +} + bool IsExplicitlyImpureProcedure(const Symbol &original) { // An ENTRY is IMPURE if its containing subprogram is so return DEREF(GetMainEntry(&original.GetUltimate())) @@ -2178,7 +2193,7 @@ bool IsAutomatic(const Symbol &original) { const Symbol &symbol{original.GetUltimate()}; if (const auto *object{symbol.detailsIf()}) { if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) { - if (const DeclTypeSpec * type{symbol.GetType()}) { + if (const DeclTypeSpec *type{symbol.GetType()}) { // If a type parameter value is not a constant expression, the // object is automatic. if (type->category() == DeclTypeSpec::Character) { @@ -2188,7 +2203,7 @@ bool IsAutomatic(const Symbol &original) { return true; } } - } else if (const DerivedTypeSpec * derived{type->AsDerived()}) { + } else if (const DerivedTypeSpec *derived{type->AsDerived()}) { for (const auto &pair : derived->parameters()) { if (const auto &value{pair.second.GetExplicit()}) { if (!evaluate::IsConstantExpr(*value)) { @@ -2513,7 +2528,7 @@ common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) { common::IgnoreTKRSet result; if (const auto *object{symbol.detailsIf()}) { result = object->ignoreTKR(); - if (const Symbol * ownerSymbol{symbol.owner().symbol()}) { + if (const Symbol *ownerSymbol{symbol.owner().symbol()}) { if (const auto *ownerSubp{ownerSymbol->detailsIf()}) { if (ownerSubp->defaultIgnoreTKR()) { result |= common::ignoreTKRAll; @@ -2527,7 +2542,7 @@ common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) { std::optional GetDummyArgumentNumber(const Symbol *symbol) { if (symbol) { if (IsDummy(*symbol)) { - if (const Symbol * subpSym{symbol->owner().symbol()}) { + if (const Symbol *subpSym{symbol->owner().symbol()}) { if (const auto *subp{subpSym->detailsIf()}) { int j{0}; for (const Symbol *dummy : subp->dummyArgs()) { @@ -2552,12 +2567,12 @@ const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) { nameDetails && nameDetails->kind() == semantics::SubprogramKind::Module) { const Symbol *next{symInSubmodule->owner().symbol()}; - while (const Symbol * submodSym{next}) { + while (const Symbol *submodSym{next}) { next = nullptr; if (const auto *modDetails{ submodSym->detailsIf()}; modDetails && modDetails->isSubmodule() && modDetails->scope()) { - if (const semantics::Scope & parent{modDetails->scope()->parent()}; + if (const semantics::Scope &parent{modDetails->scope()->parent()}; parent.IsSubmodule() || parent.IsModule()) { if (auto iter{parent.find(symInSubmodule->name())}; iter != parent.end()) { diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp index 5f4e62ffdbbf2..7debce6da51b7 100644 --- a/flang/lib/Parser/program-parsers.cpp +++ b/flang/lib/Parser/program-parsers.cpp @@ -524,7 +524,7 @@ TYPE_PARSER(construct(star >> label)) // R1527 prefix-spec -> // declaration-type-spec | ELEMENTAL | IMPURE | MODULE | -// NON_RECURSIVE | PURE | RECURSIVE | +// NON_RECURSIVE | PURE | SIMPLE | RECURSIVE | // (CUDA) ATTRIBUTES ( (DEVICE | GLOBAL | GRID_GLOBAL | HOST)... ) | // LAUNCH_BOUNDS(expr-list) | CLUSTER_DIMS(expr-list) TYPE_PARSER(first("DEVICE" >> pure(common::CUDASubprogramAttrs::Device), @@ -539,6 +539,7 @@ TYPE_PARSER(first(construct(declarationTypeSpec), construct("NON_RECURSIVE"_tok)), construct(construct("PURE"_tok)), construct(construct("RECURSIVE"_tok)), + construct(construct("SIMPLE"_tok)), extension( construct(construct("ATTRIBUTES" >> parenthesized( diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index dc6d33607146b..d59cb01fe4bcb 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -1761,6 +1761,7 @@ class UnparseVisitor { void Post(const PrefixSpec::Non_Recursive) { Word("NON_RECURSIVE"); } void Post(const PrefixSpec::Pure) { Word("PURE"); } void Post(const PrefixSpec::Recursive) { Word("RECURSIVE"); } + void Post(const PrefixSpec::Simple) { Word("SIMPLE"); } void Unparse(const PrefixSpec::Attributes &x) { Word("ATTRIBUTES("), Walk(x.v), Word(")"); } diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 077bee930675e..dbb0c172cb473 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -266,6 +266,7 @@ class AttrsVisitor : public virtual BaseVisitor { HANDLE_ATTR_CLASS(PrefixSpec::Non_Recursive, NON_RECURSIVE) HANDLE_ATTR_CLASS(PrefixSpec::Pure, PURE) HANDLE_ATTR_CLASS(PrefixSpec::Recursive, RECURSIVE) + HANDLE_ATTR_CLASS(PrefixSpec::Simple, SIMPLE) HANDLE_ATTR_CLASS(TypeAttrSpec::BindC, BIND_C) HANDLE_ATTR_CLASS(BindAttr::Deferred, DEFERRED) HANDLE_ATTR_CLASS(BindAttr::Non_Overridable, NON_OVERRIDABLE) @@ -2325,7 +2326,7 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) { } symbol.SetBindName(std::move(*label)); if (!oldBindName.empty()) { - if (const std::string * newBindName{symbol.GetBindName()}) { + if (const std::string *newBindName{symbol.GetBindName()}) { if (oldBindName != *newBindName) { Say(symbol.name(), "The entity '%s' has multiple BIND names ('%s' and '%s')"_err_en_US, @@ -2448,7 +2449,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) { // expression semantics if the DeclTypeSpec is a valid TypeSpec. // The grammar ensures that it's an intrinsic or derived type spec, // not TYPE(*) or CLASS(*) or CLASS(T). - if (const DeclTypeSpec * spec{state_.declTypeSpec}) { + if (const DeclTypeSpec *spec{state_.declTypeSpec}) { switch (spec->category()) { case DeclTypeSpec::Numeric: case DeclTypeSpec::Logical: @@ -2456,7 +2457,7 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) { typeSpec.declTypeSpec = spec; break; case DeclTypeSpec::TypeDerived: - if (const DerivedTypeSpec * derived{spec->AsDerived()}) { + if (const DerivedTypeSpec *derived{spec->AsDerived()}) { CheckForAbstractType(derived->typeSymbol()); // C703 typeSpec.declTypeSpec = spec; } @@ -3024,8 +3025,8 @@ Symbol &ScopeHandler::MakeSymbol(const parser::Name &name, Attrs attrs) { Symbol &ScopeHandler::MakeHostAssocSymbol( const parser::Name &name, const Symbol &hostSymbol) { Symbol &symbol{*NonDerivedTypeScope() - .try_emplace(name.source, HostAssocDetails{hostSymbol}) - .first->second}; + .try_emplace(name.source, HostAssocDetails{hostSymbol}) + .first->second}; name.symbol = &symbol; symbol.attrs() = hostSymbol.attrs(); // TODO: except PRIVATE, PUBLIC? // These attributes can be redundantly reapplied without error @@ -3113,7 +3114,7 @@ void ScopeHandler::ApplyImplicitRules( if (context().HasError(symbol) || !NeedsType(symbol)) { return; } - if (const DeclTypeSpec * type{GetImplicitType(symbol)}) { + if (const DeclTypeSpec *type{GetImplicitType(symbol)}) { if (!skipImplicitTyping_) { symbol.set(Symbol::Flag::Implicit); symbol.SetType(*type); @@ -3213,7 +3214,7 @@ const DeclTypeSpec *ScopeHandler::GetImplicitType( const auto *type{implicitRulesMap_->at(scope).GetType( symbol.name(), respectImplicitNoneType)}; if (type) { - if (const DerivedTypeSpec * derived{type->AsDerived()}) { + if (const DerivedTypeSpec *derived{type->AsDerived()}) { // Resolve any forward-referenced derived type; a quick no-op else. auto &instantiatable{*const_cast(derived)}; instantiatable.Instantiate(currScope()); @@ -3928,10 +3929,10 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName, } else if (IsPointer(p1) || IsPointer(p2)) { return false; } else if (const auto *subp{p1.detailsIf()}; - subp && !subp->isInterface()) { + subp && !subp->isInterface()) { return false; // defined in module, not an external } else if (const auto *subp{p2.detailsIf()}; - subp && !subp->isInterface()) { + subp && !subp->isInterface()) { return false; // defined in module, not an external } else { // Both are external interfaces, perhaps to the same procedure @@ -4191,7 +4192,7 @@ Scope *ModuleVisitor::FindModule(const parser::Name &name, if (scope) { if (DoesScopeContain(scope, currScope())) { // 14.2.2(1) std::optional submoduleName; - if (const Scope * container{FindModuleOrSubmoduleContaining(currScope())}; + if (const Scope *container{FindModuleOrSubmoduleContaining(currScope())}; container && container->IsSubmodule()) { submoduleName = container->GetName(); } @@ -4296,7 +4297,7 @@ bool InterfaceVisitor::isAbstract() const { void InterfaceVisitor::AddSpecificProcs( const std::list &names, ProcedureKind kind) { - if (Symbol * symbol{GetGenericInfo().symbol}; + if (Symbol *symbol{GetGenericInfo().symbol}; symbol && symbol->has()) { for (const auto &name : names) { specificsForGenericProcs_.emplace(symbol, std::make_pair(&name, kind)); @@ -4396,7 +4397,7 @@ void GenericHandler::DeclaredPossibleSpecificProc(Symbol &proc) { } void InterfaceVisitor::ResolveNewSpecifics() { - if (Symbol * generic{genericInfo_.top().symbol}; + if (Symbol *generic{genericInfo_.top().symbol}; generic && generic->has()) { ResolveSpecificsInGeneric(*generic, false); } @@ -4481,7 +4482,7 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) { name.source); MakeSymbol(name, Attrs{}, UnknownDetails{}); } else if (auto *entity{ultimate.detailsIf()}; - entity && !ultimate.has()) { + entity && !ultimate.has()) { resultType = entity->type(); ultimate.details() = UnknownDetails{}; // will be replaced below } else { @@ -4537,7 +4538,7 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) { } else { Message &msg{Say(*suffix.resultName, "RESULT(%s) may appear only in a function"_err_en_US)}; - if (const Symbol * subprogram{InclusiveScope().symbol()}) { + if (const Symbol *subprogram{InclusiveScope().symbol()}) { msg.Attach(subprogram->name(), "Containing subprogram"_en_US); } } @@ -5053,7 +5054,7 @@ Symbol *ScopeHandler::FindSeparateModuleProcedureInterface( symbol = generic->specific(); } } - if (const Symbol * defnIface{FindSeparateModuleSubprogramInterface(symbol)}) { + if (const Symbol *defnIface{FindSeparateModuleSubprogramInterface(symbol)}) { // Error recovery in case of multiple definitions symbol = const_cast(defnIface); } @@ -5189,8 +5190,8 @@ bool SubprogramVisitor::HandlePreviousCalls( return generic->specific() && HandlePreviousCalls(name, *generic->specific(), subpFlag); } else if (const auto *proc{symbol.detailsIf()}; proc && - !proc->isDummy() && - !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) { + !proc->isDummy() && + !symbol.attrs().HasAny(Attrs{Attr::INTRINSIC, Attr::POINTER})) { // There's a symbol created for previous calls to this subprogram or // ENTRY's name. We have to replace that symbol in situ to avoid the // obligation to rewrite symbol pointers in the parse tree. @@ -5232,7 +5233,7 @@ const Symbol *SubprogramVisitor::CheckExtantProc( if (prev) { if (IsDummy(*prev)) { } else if (auto *entity{prev->detailsIf()}; - IsPointer(*prev) && entity && !entity->type()) { + IsPointer(*prev) && entity && !entity->type()) { // POINTER attribute set before interface } else if (inInterfaceBlock() && currScope() != prev->owner()) { // Procedures in an INTERFACE block do not resolve to symbols @@ -5302,7 +5303,7 @@ Symbol &SubprogramVisitor::PushSubprogramScope(const parser::Name &name, } set_inheritFromParent(false); // interfaces don't inherit, even if MODULE } - if (Symbol * found{FindSymbol(name)}; + if (Symbol *found{FindSymbol(name)}; found && found->has()) { found->set(subpFlag); // PushScope() created symbol } @@ -6149,9 +6150,9 @@ void DeclarationVisitor::Post(const parser::VectorTypeSpec &x) { vectorDerivedType.CookParameters(GetFoldingContext()); } - if (const DeclTypeSpec * - extant{ppcBuiltinTypesScope->FindInstantiatedDerivedType( - vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) { + if (const DeclTypeSpec *extant{ + ppcBuiltinTypesScope->FindInstantiatedDerivedType( + vectorDerivedType, DeclTypeSpec::Category::TypeDerived)}) { // This derived type and parameter expressions (if any) are already present // in the __ppc_intrinsics scope. SetDeclTypeSpec(*extant); @@ -6173,7 +6174,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}) { + if (const Symbol *derivedSymbol{derivedName.symbol}) { CheckForAbstractType(*derivedSymbol); // C706 } } @@ -6242,8 +6243,8 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) { if (!spec->MightBeParameterized()) { spec->EvaluateParameters(context()); } - if (const DeclTypeSpec * - extant{currScope().FindInstantiatedDerivedType(*spec, category)}) { + if (const DeclTypeSpec *extant{ + currScope().FindInstantiatedDerivedType(*spec, category)}) { // This derived type and parameter expressions (if any) are already present // in this scope. SetDeclTypeSpec(*extant); @@ -6274,8 +6275,7 @@ void DeclarationVisitor::Post(const parser::DeclarationTypeSpec::Record &rec) { if (auto spec{ResolveDerivedType(typeName)}) { spec->CookParameters(GetFoldingContext()); spec->EvaluateParameters(context()); - if (const DeclTypeSpec * - extant{currScope().FindInstantiatedDerivedType( + if (const DeclTypeSpec *extant{currScope().FindInstantiatedDerivedType( *spec, DeclTypeSpec::TypeDerived)}) { SetDeclTypeSpec(*extant); } else { @@ -7195,7 +7195,7 @@ void DeclarationVisitor::CheckCommonBlocks() { } else if (symbol->IsFuncResult()) { Say(name, "Function result '%s' may not appear in a COMMON block"_err_en_US); - } else if (const DeclTypeSpec * type{symbol->GetType()}) { + } else if (const DeclTypeSpec *type{symbol->GetType()}) { if (type->category() == DeclTypeSpec::ClassStar) { Say(name, "Unlimited polymorphic pointer '%s' may not appear in a COMMON block"_err_en_US); @@ -7348,7 +7348,7 @@ bool DeclarationVisitor::PassesLocalityChecks( "Coarray '%s' not allowed in a %s locality-spec"_err_en_US, specName); return false; } - if (const DeclTypeSpec * type{symbol.GetType()}) { + if (const DeclTypeSpec *type{symbol.GetType()}) { if (type->IsPolymorphic() && IsDummy(symbol) && !IsPointer(symbol) && !isReduce) { // F'2023 C1130 SayWithDecl(name, symbol, @@ -7575,7 +7575,7 @@ Symbol *DeclarationVisitor::NoteInterfaceName(const parser::Name &name) { } void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) { - if (const Symbol * symbol{name.symbol}) { + if (const Symbol *symbol{name.symbol}) { const Symbol &ultimate{symbol->GetUltimate()}; if (!context().HasError(*symbol) && !context().HasError(ultimate) && !BypassGeneric(ultimate).HasExplicitInterface()) { @@ -7893,7 +7893,7 @@ bool ConstructVisitor::Pre(const parser::DataStmtValue &x) { 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 *symbol{FindSymbol(*name)}; symbol && symbol->GetUltimate().has()) { mutableData.u = elem->ConvertToStructureConstructor( DerivedTypeSpec{name->source, *symbol}); @@ -8039,15 +8039,15 @@ void ConstructVisitor::Post(const parser::SelectTypeStmt &x) { } } } else { - if (const Symbol * - whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) { + if (const Symbol *whole{ + UnwrapWholeSymbolDataRef(association.selector.expr)}) { ConvertToObjectEntity(const_cast(*whole)); if (!IsVariableName(*whole)) { Say(association.selector.source, // C901 "Selector is not a variable"_err_en_US); association = {}; } - if (const DeclTypeSpec * type{whole->GetType()}) { + if (const DeclTypeSpec *type{whole->GetType()}) { if (!type->IsPolymorphic()) { // C1159 Say(association.selector.source, "Selector '%s' in SELECT TYPE statement must be " @@ -8187,8 +8187,8 @@ Symbol *ConstructVisitor::MakeAssocEntity() { "The associate name '%s' is already used in this associate statement"_err_en_US); return nullptr; } - } else if (const Symbol * - whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) { + } else if (const Symbol *whole{ + UnwrapWholeSymbolDataRef(association.selector.expr)}) { symbol = &MakeSymbol(whole->name()); } else { return nullptr; @@ -8810,7 +8810,7 @@ bool DeclarationVisitor::CheckForHostAssociatedImplicit( if (name.symbol) { ApplyImplicitRules(*name.symbol, true); } - if (Scope * host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) { + if (Scope *host{GetHostProcedure()}; host && !isImplicitNoneType(*host)) { Symbol *hostSymbol{nullptr}; if (!name.symbol) { if (currScope().CanImport(name.source)) { @@ -8881,7 +8881,7 @@ const parser::Name *DeclarationVisitor::FindComponent( if (!type) { return nullptr; // should have already reported error } - if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) { + if (const IntrinsicTypeSpec *intrinsic{type->AsIntrinsic()}) { auto category{intrinsic->category()}; MiscDetails::Kind miscKind{MiscDetails::Kind::None}; if (component.source == "kind") { @@ -8903,7 +8903,7 @@ const parser::Name *DeclarationVisitor::FindComponent( } } else if (DerivedTypeSpec * derived{type->AsDerived()}) { derived->Instantiate(currScope()); // in case of forward referenced type - if (const Scope * scope{derived->scope()}) { + if (const Scope *scope{derived->scope()}) { if (Resolve(component, scope->FindComponent(component.source))) { if (auto msg{CheckAccessibleSymbol(currScope(), *component.symbol)}) { context().Say(component.source, *msg); @@ -9051,8 +9051,8 @@ void DeclarationVisitor::PointerInitialization( if (evaluate::IsNullProcedurePointer(&*expr)) { CHECK(!details->init()); details->set_init(nullptr); - } else if (const Symbol * - targetSymbol{evaluate::UnwrapWholeSymbolDataRef(*expr)}) { + } else if (const Symbol *targetSymbol{ + evaluate::UnwrapWholeSymbolDataRef(*expr)}) { CHECK(!details->init()); details->set_init(*targetSymbol); } else { @@ -9571,7 +9571,7 @@ void ResolveNamesVisitor::EarlyDummyTypeDeclaration( for (const auto &ent : entities) { const auto &objName{std::get(ent.t)}; Resolve(objName, FindInScope(currScope(), objName)); - if (Symbol * symbol{objName.symbol}; + if (Symbol *symbol{objName.symbol}; symbol && IsDummy(*symbol) && NeedsType(*symbol)) { if (!type) { type = ProcessTypeSpec(declTypeSpec); @@ -9710,7 +9710,7 @@ void ResolveNamesVisitor::FinishSpecificationPart( if (auto *proc{symbol.detailsIf()}; proc && !proc->isDummy() && !IsPointer(symbol) && !symbol.attrs().test(Attr::BIND_C)) { - if (const Symbol * iface{proc->procInterface()}; + if (const Symbol *iface{proc->procInterface()}; iface && IsBindCProcedure(*iface)) { SetImplicitAttr(symbol, Attr::BIND_C); SetBindNameOn(symbol); @@ -9843,7 +9843,7 @@ bool ResolveNamesVisitor::Pre(const parser::PointerAssignmentStmt &x) { Symbol *ptrSymbol{parser::GetLastName(dataRef).symbol}; Walk(bounds); // Resolve unrestricted specific intrinsic procedures as in "p => cos". - if (const parser::Name * name{parser::Unwrap(expr)}) { + if (const parser::Name *name{parser::Unwrap(expr)}) { if (NameIsKnownOrIntrinsic(*name)) { if (Symbol * symbol{name->symbol}) { if (IsProcedurePointer(ptrSymbol) && @@ -10284,8 +10284,8 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) { // implied SAVE so that evaluate::IsSaved() will return true. if (node.scope()->kind() == Scope::Kind::MainProgram) { if (const auto *object{symbol.detailsIf()}) { - if (const DeclTypeSpec * type{object->type()}) { - if (const DerivedTypeSpec * derived{type->AsDerived()}) { + if (const DeclTypeSpec *type{object->type()}) { + if (const DerivedTypeSpec *derived{type->AsDerived()}) { if (!IsSaved(symbol) && FindCoarrayPotentialComponent(*derived)) { SetImplicitAttr(symbol, Attr::SAVE); } @@ -10538,7 +10538,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) { if (DerivedTypeSpec * spec{scope.derivedTypeSpec()}) { spec->Instantiate(currScope()); const Symbol &origTypeSymbol{spec->typeSymbol()}; - if (const Scope * origTypeScope{origTypeSymbol.scope()}) { + if (const Scope *origTypeScope{origTypeSymbol.scope()}) { CHECK(origTypeScope->IsDerivedType() && origTypeScope->symbol() == &origTypeSymbol); auto &foldingContext{GetFoldingContext()}; @@ -10549,7 +10549,7 @@ void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) { if (IsPointer(comp)) { if (auto *details{comp.detailsIf()}) { auto origDetails{origComp.get()}; - if (const MaybeExpr & init{origDetails.init()}) { + if (const MaybeExpr &init{origDetails.init()}) { SomeExpr newInit{*init}; MaybeExpr folded{FoldExpr(std::move(newInit))}; details->set_init(std::move(folded)); diff --git a/flang/test/Parser/simple-unparse.f90 b/flang/test/Parser/simple-unparse.f90 new file mode 100644 index 0000000000000..c2b187e329761 --- /dev/null +++ b/flang/test/Parser/simple-unparse.f90 @@ -0,0 +1,13 @@ +! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s + +! Test that SIMPLE function specifier is recognized +! by the parser and the unparser. This test does not +! exercise semantic checks. + +simple function foo() + return +end function + +! CHECK: SIMPLE FUNCTION foo() +! CHECK-NEXT: RETURN +! CHECK-NEXT: END FUNCTION diff --git a/flang/test/Parser/simple.f90 b/flang/test/Parser/simple.f90 new file mode 100644 index 0000000000000..2959938824395 --- /dev/null +++ b/flang/test/Parser/simple.f90 @@ -0,0 +1,10 @@ +! RUN: %flang_fc1 -fdebug-dump-parse-tree %s | FileCheck %s + +! Check that SIMPLE is recognized in the parse tree + +simple function foo() + return +end function + +! CHECK: Simple +