-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[Flang] Add parsing and attribute registration for SIMPLE procedures #159763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-flang-semantics Author: Šárka Holendová (mlir-maiden) ChangesThis patch introduces the Fortran 2023 Changes include:
Note: This patch only covers attribute registration and parsing. Tests:
@klausler Patch is 42.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/159763.diff 14 Files Affected:
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<AssumedType>(&u_)}) {
+ if (const AssumedType *aType{std::get_if<AssumedType>(&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<Expr<SubscriptInteger>> 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<Attr, Attr_enumSize>;
- 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<T> &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<Component>(&ref->u)}) {
+ if (const Component *component{std::get_if<Component>(&ref->u)}) {
ref = skipComponents ? &component->base() : nullptr;
} else if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) {
ref = &coarrayRef->base();
@@ -436,7 +436,7 @@ struct ExtractCoindexedObjectHelper {
return common::visit(*this, dataRef.u);
}
std::optional<CoarrayRef> 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 <typename A> 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 <typename A> 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<Expr<SomeType>>(const Expr<SomeType> &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<semantics::SubprogramDetails>()}) {
if (details->isFunction() &&
@@ -992,7 +992,7 @@ inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &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<common::CUDASubprogramAttrs>);
WRAPPER_CLASS(Launch_Bounds, std::list<ScalarIntConstantExpr>);
WRAPPER_CLASS(Cluster_Dims, std::list<ScalarIntConstantExpr>);
std::variant<DeclarationTypeSpec, Elemental, Impure, Module, Non_Recursive,
- Pure, Recursive, Attributes, Launch_Bounds, Cluster_Dims>
+ 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<Attr, Attr_enumSize> {
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<DynamicType> 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<semantics::ProcEntityDetails>()}) {
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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&u);
}
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 8931cbe485ac2..931e9862bcd89 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -132,7 +132,7 @@ bool IsConstantExprHelper<INVARIANT>::operator()(
} else if (intrinsic->name == "shape" || intrinsic->name == "size") {
auto shape{GetShape(call.arguments()[0]->UnwrapExpr())};
return shape && IsConstantExprShape(*shape);
- } else if (proc.IsPure()) {
+ } else if (proc.IsPure() || proc.IsSimple()) {
std::size_t j{0};
for (const auto &arg : call.arguments()) {
if (const auto *dataDummy{j < proc.dummyArguments.size()
@@ -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<DescriptorInquiry>(*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<semantics::SubprogramDetails>()}) {
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 1f3cbbf6a0c36..9257e4e5aeb24 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<SomeType> &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<SomeType> &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<parser::MessageFixedText> 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> &dataRef) {
}
const Symbol *UnwrapWholeSymbolOrComponentDataRef(const DataRef &dataRef) {
- if (const Component * c{std::get_if<Component>(&dataRef.u)}) {
+ if (const Component *c{std::get_if<Component>(&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<CoarrayRef>(&dataRef.u)}) {
+ if (const CoarrayRef *c{std::get_if<CoarrayRef>(&dataRef.u)}) {
return UnwrapWholeSymbolOrComponentOrCoarrayRef(c->base());
} else {
return UnwrapWholeSymbolOrComponentDataRef(dataRef);
@@ -1415,7 +1421,7 @@ static std::optional<Expr<SomeType>> 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<SomeType> &expr) {
bool IsAllocatableDesignator(const Expr<SomeType> &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<AssocEntityDetails>()}) {
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<AssocEntityDetails>()}) {
- 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<SubprogramDetails>()}) {
- 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;
}
}
@@ -2050,6 +2056,8 @@ static bool IsPureProcedureImpl(
return true; // statement function was not found to be impure
}
return symbol.attrs().test(Attr::PURE) ||
+ symbol.attrs().test(
+ Attr::SIMPLE) || // included to pass isPureProcedure check
(symbol.attrs().test(Attr::ELEMENTAL) &&
!symbol.attrs().test(Attr::IMPURE));
}
@@ -2064,6 +2072,15 @@ bool IsPureProcedure(const Scope &scope) {
return symbol && IsPureProcedure(*symbol);
}
+bool IsSimpleProcedure(const Symbol &original) {
+ return original.attrs().test(Attr::SIMPLE);
+}
+
+bool IsimpleProcedure(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 +2195,7 @@ bool IsAutomatic(const Symbol &original) {
const Symbol &symbol{original.GetUltimate()};
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
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 +2205,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 +253...
[truncated]
|
@llvm/pr-subscribers-flang-parser Author: Šárka Holendová (mlir-maiden) ChangesThis patch introduces the Fortran 2023 Changes include:
Note: This patch only covers attribute registration and parsing. Tests:
@klausler Patch is 42.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/159763.diff 14 Files Affected:
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<AssumedType>(&u_)}) {
+ if (const AssumedType *aType{std::get_if<AssumedType>(&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<Expr<SubscriptInteger>> 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<Attr, Attr_enumSize>;
- 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<T> &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<Component>(&ref->u)}) {
+ if (const Component *component{std::get_if<Component>(&ref->u)}) {
ref = skipComponents ? &component->base() : nullptr;
} else if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) {
ref = &coarrayRef->base();
@@ -436,7 +436,7 @@ struct ExtractCoindexedObjectHelper {
return common::visit(*this, dataRef.u);
}
std::optional<CoarrayRef> 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 <typename A> 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 <typename A> 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<Expr<SomeType>>(const Expr<SomeType> &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<semantics::SubprogramDetails>()}) {
if (details->isFunction() &&
@@ -992,7 +992,7 @@ inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &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<common::CUDASubprogramAttrs>);
WRAPPER_CLASS(Launch_Bounds, std::list<ScalarIntConstantExpr>);
WRAPPER_CLASS(Cluster_Dims, std::list<ScalarIntConstantExpr>);
std::variant<DeclarationTypeSpec, Elemental, Impure, Module, Non_Recursive,
- Pure, Recursive, Attributes, Launch_Bounds, Cluster_Dims>
+ 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<Attr, Attr_enumSize> {
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<DynamicType> 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<semantics::ProcEntityDetails>()}) {
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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&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<SpecificIntrinsic>(&u);
}
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 8931cbe485ac2..931e9862bcd89 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -132,7 +132,7 @@ bool IsConstantExprHelper<INVARIANT>::operator()(
} else if (intrinsic->name == "shape" || intrinsic->name == "size") {
auto shape{GetShape(call.arguments()[0]->UnwrapExpr())};
return shape && IsConstantExprShape(*shape);
- } else if (proc.IsPure()) {
+ } else if (proc.IsPure() || proc.IsSimple()) {
std::size_t j{0};
for (const auto &arg : call.arguments()) {
if (const auto *dataDummy{j < proc.dummyArguments.size()
@@ -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<DescriptorInquiry>(*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<semantics::SubprogramDetails>()}) {
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 1f3cbbf6a0c36..9257e4e5aeb24 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<SomeType> &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<SomeType> &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<parser::MessageFixedText> 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> &dataRef) {
}
const Symbol *UnwrapWholeSymbolOrComponentDataRef(const DataRef &dataRef) {
- if (const Component * c{std::get_if<Component>(&dataRef.u)}) {
+ if (const Component *c{std::get_if<Component>(&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<CoarrayRef>(&dataRef.u)}) {
+ if (const CoarrayRef *c{std::get_if<CoarrayRef>(&dataRef.u)}) {
return UnwrapWholeSymbolOrComponentOrCoarrayRef(c->base());
} else {
return UnwrapWholeSymbolOrComponentDataRef(dataRef);
@@ -1415,7 +1421,7 @@ static std::optional<Expr<SomeType>> 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<SomeType> &expr) {
bool IsAllocatableDesignator(const Expr<SomeType> &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<AssocEntityDetails>()}) {
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<AssocEntityDetails>()}) {
- 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<SubprogramDetails>()}) {
- 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;
}
}
@@ -2050,6 +2056,8 @@ static bool IsPureProcedureImpl(
return true; // statement function was not found to be impure
}
return symbol.attrs().test(Attr::PURE) ||
+ symbol.attrs().test(
+ Attr::SIMPLE) || // included to pass isPureProcedure check
(symbol.attrs().test(Attr::ELEMENTAL) &&
!symbol.attrs().test(Attr::IMPURE));
}
@@ -2064,6 +2072,15 @@ bool IsPureProcedure(const Scope &scope) {
return symbol && IsPureProcedure(*symbol);
}
+bool IsSimpleProcedure(const Symbol &original) {
+ return original.attrs().test(Attr::SIMPLE);
+}
+
+bool IsimpleProcedure(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 +2195,7 @@ bool IsAutomatic(const Symbol &original) {
const Symbol &symbol{original.GetUltimate()};
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
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 +2205,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 +253...
[truncated]
|
Suggestion for the future: this PR seems to have a lot of simple formatting changes that don't necessarily relate to SIMPLE. If something needs to be just reformatted, I would recommend submitting separate NFC PR for that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't reformat entire source files. Use git clang-format
to limit automatic code formatting to your new & changed source lines.
@@ -132,7 +132,7 @@ bool IsConstantExprHelper<INVARIANT>::operator()( | |||
} else if (intrinsic->name == "shape" || intrinsic->name == "size") { | |||
auto shape{GetShape(call.arguments()[0]->UnwrapExpr())}; | |||
return shape && IsConstantExprShape(*shape); | |||
} else if (proc.IsPure()) { | |||
} else if (proc.IsPure() || proc.IsSimple()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All simple procedures are also pure, so you don't have to check for simple
when you know it's not pure
.
flang/lib/Evaluate/tools.cpp
Outdated
@@ -2050,6 +2056,8 @@ static bool IsPureProcedureImpl( | |||
return true; // statement function was not found to be impure | |||
} | |||
return symbol.attrs().test(Attr::PURE) || | |||
symbol.attrs().test( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SIMPLE
implies PURE
.
flang/lib/Evaluate/tools.cpp
Outdated
return original.attrs().test(Attr::SIMPLE); | ||
} | ||
|
||
bool IsimpleProcedure(const Scope &scope) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misspelling
Note: formatting issues are still present. I’ll push another update tomorrow. |
- Removed `|| proc.IsSimple()` from the `if (proc.IsPure())` condition in check-expression.cpp. - Removed `Attr::Simple` from the isPureProcedureImpl helper in tools.cpp. - Fixed formatting issues.
Not that I think it matters in this instance, but one thing to mindful of is that llvm-project doesn't want you to force-push during the review process. (Or at least I think it does, I can't seem to find the docs for that). I think the reasoning is that github doesn't do as well will keeping comments linked to corresponding source locations across commits that have been force pushed. |
Wait, check-expression.cpp was marked for removal? What happened here? (If start having weird git issues, sometimes it's easier to create a new PR.) |
@eugeneepshteyn , Thank you for catching that, Eugene. The check-expression.cpp issue happened because I’m still learning the git workflow. I had originally edited it, but after feedback that the changes weren’t needed, I thought I could simply exclude it from my amend. That ended up looking like the file was removed. I will commit the missing file momentarily. I am also having issues with git clang-format on 4 files (tools.h, call.cpp, tools.cpp, resolve-names.cpp). It keeps reformatting more than I touched. |
I recommend using the latest clang-format that's built as part of your LLVM tree. You can set it for llvm-project via |
I’m already using the clang-format from my llvm-project build and double-checked that. I still struggle with the last 4 files being over-formatted. Once formatting noise enters a branch, git clang-format seems to keep it as part of the diff. I tried fresh checkouts, manual edits, and several git clang-format runs. Locally my diff looks clean, but online I keep seeing unexpected formatting. Please advise if these 4 files are acceptable as-is or if I should restart with a clean PR. Thank you :) |
@@ -0,0 +1,10 @@ | |||
! RUN: %flang_fc1 -fdebug-dump-parse-tree %s | FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add more tests here to check the messages you added to CheckProcCompatibility()
?
Superseded by #161285 due to formatting issues. |
If it's superseded, let's close it. |
This patch introduces the Fortran 2023
SIMPLE
procedure specifier intoFlang’s parser and attribute registration.
Changes include:
SIMPLE
toAttr
in Semantics/attr.hPrefixSpec
in the parse tree with aSimple
alternativeSIMPLE
keywordSIMPLE
IsSimple
,IsSimpleProcedure
) alongside existingPURE
support in EvaluateNote: This patch only covers attribute registration and parsing.
Semantic rules for
SIMPLE
will follow in a separate patch.Tests:
SIMPLE
.@klausler