[flang] Defer conversions to objects; fix some intrinsic table entries
authorpeter klausler <pklausler@nvidia.com>
Tue, 21 May 2019 23:58:46 +0000 (16:58 -0700)
committerpeter klausler <pklausler@nvidia.com>
Tue, 28 May 2019 20:29:29 +0000 (13:29 -0700)
more fixes

Access components of constant structures

Apply implicit typing to dummy args used in automatic array dimensions

SELECTED_INT_KIND and SELECTED_REAL_KIND

Finish SELECTED_{INT,REAL}_KIND and common cases of ALL()/ANY()

Original-commit: flang-compiler/f18@e9f8e53e55867863ca06461fb6edd965c86352c7
Reviewed-on: https://github.com/flang-compiler/f18/pull/472
Tree-same-pre-rewrite: false

28 files changed:
flang/lib/common/template.h
flang/lib/evaluate/call.cc
flang/lib/evaluate/call.h
flang/lib/evaluate/characteristics.h
flang/lib/evaluate/constant.cc
flang/lib/evaluate/constant.h
flang/lib/evaluate/descender.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/fold.cc
flang/lib/evaluate/fold.h
flang/lib/evaluate/formatting.cc
flang/lib/evaluate/intrinsics.cc
flang/lib/evaluate/shape.cc
flang/lib/evaluate/shape.h
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/evaluate/type.cc
flang/lib/evaluate/type.h
flang/lib/semantics/expression.cc
flang/lib/semantics/mod-file.cc
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/semantics.h
flang/lib/semantics/symbol.h
flang/lib/semantics/tools.cc
flang/test/semantics/CMakeLists.txt
flang/test/semantics/modfile26.f90 [new file with mode: 0644]
flang/tools/f18/f18.cc

index 48f31f2..471a15b 100644 (file)
@@ -295,25 +295,28 @@ std::optional<R> MapOptional(R (*f)(A &&...), std::optional<A> &&... x) {
 // SearchTypes will traverse the element types in the tuple in order
 // and invoke VISITOR::Test<T>() on each until it returns a value that
 // casts to true.  If no invocation of Test succeeds, SearchTypes will
-// return a default-constructed value VISITOR::Result{}.
+// return a default value.
 template<std::size_t J, typename VISITOR>
 common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
-    VISITOR &&visitor) {
+    VISITOR &&visitor, typename VISITOR::Result &&defaultResult) {
   using Tuple = typename VISITOR::Types;
   if constexpr (J < std::tuple_size_v<Tuple>) {
     if (auto result{visitor.template Test<std::tuple_element_t<J, Tuple>>()}) {
       return result;
     }
-    return SearchTypesHelper<J + 1, VISITOR>(std::move(visitor));
+    return SearchTypesHelper<J + 1, VISITOR>(std::move(visitor),
+        std::move(defaultResult));
   } else {
-    return typename VISITOR::Result{};
+    return std::move(defaultResult);
   }
 }
 
 template<typename VISITOR>
 common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes(
-    VISITOR &&visitor) {
-  return SearchTypesHelper<0, VISITOR>(std::move(visitor));
+    VISITOR &&visitor,
+    typename VISITOR::Result defaultResult = typename VISITOR::Result{}) {
+  return SearchTypesHelper<0, VISITOR>(
+      std::move(visitor), std::move(defaultResult));
 }
 }
 #endif  // FORTRAN_COMMON_TEMPLATE_H_
index 873131a..1e7dd1a 100644 (file)
@@ -42,7 +42,7 @@ ActualArgument &ActualArgument::operator=(Expr<SomeType> &&expr) {
 }
 
 std::optional<DynamicType> ActualArgument::GetType() const {
-  if (const auto *expr{GetExpr()}) {
+  if (const auto *expr{UnwrapExpr()}) {
     return expr->GetType();
   } else {
     return std::nullopt;
@@ -50,7 +50,7 @@ std::optional<DynamicType> ActualArgument::GetType() const {
 }
 
 int ActualArgument::Rank() const {
-  if (const auto *expr{GetExpr()}) {
+  if (const auto *expr{UnwrapExpr()}) {
     return expr->Rank();
   } else {
     return std::get<AssumedType>(u_).Rank();
index 5bd38e9..74785da 100644 (file)
@@ -73,7 +73,7 @@ public:
   ~ActualArgument();
   ActualArgument &operator=(Expr<SomeType> &&);
 
-  Expr<SomeType> *GetExpr() {
+  Expr<SomeType> *UnwrapExpr() {
     if (auto *p{
             std::get_if<common::CopyableIndirection<Expr<SomeType>>>(&u_)}) {
       return &p->value();
@@ -81,7 +81,7 @@ public:
       return nullptr;
     }
   }
-  const Expr<SomeType> *GetExpr() const {
+  const Expr<SomeType> *UnwrapExpr() const {
     if (const auto *p{
             std::get_if<common::CopyableIndirection<Expr<SomeType>>>(&u_)}) {
       return &p->value();
index 47ab784..7d8757c 100644 (file)
@@ -56,7 +56,7 @@ public:
 
   bool operator==(const TypeAndShape &) const;
   bool IsAssumedRank() const { return isAssumedRank_; }
-  int Rank() const { return static_cast<int>(shape().size()); }
+  int Rank() const { return GetRank(shape_); }
   bool IsCompatibleWith(
       parser::ContextualMessages &, const TypeAndShape &) const;
 
index e1db2df..8be8c3c 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "constant.h"
 #include "expression.h"
+#include "shape.h"
 #include "type.h"
 #include <string>
 
@@ -30,9 +31,9 @@ std::size_t TotalElementCount(const ConstantSubscripts &shape) {
 
 bool IncrementSubscripts(
     ConstantSubscripts &indices, const ConstantSubscripts &shape) {
-  auto rank{shape.size()};
-  CHECK(indices.size() == rank);
-  for (std::size_t j{0}; j < rank; ++j) {
+  int rank{GetRank(shape)};
+  CHECK(GetRank(indices) == rank);
+  for (int j{0}; j < rank; ++j) {
     CHECK(indices[j] >= 1);
     if (++indices[j] <= shape[j]) {
       return true;
@@ -45,6 +46,13 @@ bool IncrementSubscripts(
 }
 
 template<typename RESULT, typename ELEMENT>
+ConstantBase<RESULT, ELEMENT>::ConstantBase(
+    std::vector<Element> &&x, ConstantSubscripts &&dims, Result res)
+  : result_{res}, values_(std::move(x)), shape_(std::move(dims)) {
+  CHECK(size() == TotalElementCount(shape_));
+}
+
+template<typename RESULT, typename ELEMENT>
 ConstantBase<RESULT, ELEMENT>::~ConstantBase() {}
 
 template<typename RESULT, typename ELEMENT>
@@ -54,7 +62,7 @@ bool ConstantBase<RESULT, ELEMENT>::operator==(const ConstantBase &that) const {
 
 static ConstantSubscript SubscriptsToOffset(
     const ConstantSubscripts &index, const ConstantSubscripts &shape) {
-  CHECK(index.size() == shape.size());
+  CHECK(GetRank(index) == GetRank(shape));
   ConstantSubscript stride{1}, offset{0};
   int dim{0};
   for (auto j : index) {
@@ -66,20 +74,25 @@ static ConstantSubscript SubscriptsToOffset(
   return offset;
 }
 
-static Constant<SubscriptInteger> ShapeAsConstant(
-    const ConstantSubscripts &shape) {
-  using IntType = Scalar<SubscriptInteger>;
-  std::vector<IntType> result;
-  for (auto dim : shape) {
-    result.emplace_back(dim);
-  }
-  return {std::move(result),
-      ConstantSubscripts{static_cast<std::int64_t>(shape.size())}};
+template<typename RESULT, typename ELEMENT>
+Constant<SubscriptInteger> ConstantBase<RESULT, ELEMENT>::SHAPE() const {
+  return AsConstantShape(shape_);
 }
 
 template<typename RESULT, typename ELEMENT>
-Constant<SubscriptInteger> ConstantBase<RESULT, ELEMENT>::SHAPE() const {
-  return ShapeAsConstant(shape_);
+auto ConstantBase<RESULT, ELEMENT>::Reshape(
+    const ConstantSubscripts &dims) const -> std::vector<Element> {
+  std::size_t n{TotalElementCount(dims)};
+  CHECK(!empty() || n == 0);
+  std::vector<Element> elements;
+  auto iter{values().cbegin()};
+  while (n-- > 0) {
+    elements.push_back(*iter);
+    if (++iter == values().cend()) {
+      iter = values().cbegin();
+    }
+  }
+  return elements;
 }
 
 template<typename T>
@@ -87,6 +100,11 @@ auto Constant<T>::At(const ConstantSubscripts &index) const -> Element {
   return Base::values_.at(SubscriptsToOffset(index, Base::shape_));
 }
 
+template<typename T>
+auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant {
+  return {Base::Reshape(dims), std::move(dims)};
+}
+
 // Constant<Type<TypeCategory::Character, KIND> specializations
 template<int KIND>
 Constant<Type<TypeCategory::Character, KIND>>::Constant(
@@ -102,6 +120,7 @@ template<int KIND>
 Constant<Type<TypeCategory::Character, KIND>>::Constant(std::int64_t len,
     std::vector<Scalar<Result>> &&strings, ConstantSubscripts &&dims)
   : length_{len}, shape_{std::move(dims)} {
+  CHECK(strings.size() == TotalElementCount(shape_));
   values_.assign(strings.size() * length_,
       static_cast<typename Scalar<Result>::value_type>(' '));
   std::int64_t at{0};
@@ -119,14 +138,6 @@ Constant<Type<TypeCategory::Character, KIND>>::Constant(std::int64_t len,
 
 template<int KIND> Constant<Type<TypeCategory::Character, KIND>>::~Constant() {}
 
-static ConstantSubscript ShapeElements(const ConstantSubscripts &shape) {
-  ConstantSubscript elements{1};
-  for (auto dim : shape) {
-    elements *= dim;
-  }
-  return elements;
-}
-
 template<int KIND>
 bool Constant<Type<TypeCategory::Character, KIND>>::empty() const {
   return size() == 0;
@@ -135,7 +146,7 @@ bool Constant<Type<TypeCategory::Character, KIND>>::empty() const {
 template<int KIND>
 std::size_t Constant<Type<TypeCategory::Character, KIND>>::size() const {
   if (length_ == 0) {
-    return ShapeElements(shape_);
+    return TotalElementCount(shape_);
   } else {
     return static_cast<std::int64_t>(values_.size()) / length_;
   }
@@ -149,9 +160,26 @@ auto Constant<Type<TypeCategory::Character, KIND>>::At(
 }
 
 template<int KIND>
+auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
+    ConstantSubscripts &&dims) const -> Constant<Result> {
+  std::size_t n{TotalElementCount(dims)};
+  CHECK(!empty() || n == 0);
+  std::vector<Element> elements;
+  std::int64_t at{0}, limit{static_cast<std::int64_t>(values_.size())};
+  while (n-- > 0) {
+    elements.push_back(values_.substr(at, length_));
+    at += length_;
+    if (at == limit) {  // subtle: at > limit somehow? substr() will catch it
+      at = 0;
+    }
+  }
+  return {length_, std::move(elements), std::move(dims)};
+}
+
+template<int KIND>
 Constant<SubscriptInteger>
 Constant<Type<TypeCategory::Character, KIND>>::SHAPE() const {
-  return ShapeAsConstant(shape_);
+  return AsConstantShape(shape_);
 }
 
 // Constant<SomeDerived> specialization
@@ -193,5 +221,10 @@ StructureConstructor Constant<SomeDerived>::At(
       values_.at(SubscriptsToOffset(index, shape_))};
 }
 
+auto Constant<SomeDerived>::Reshape(ConstantSubscripts &&dims) const
+    -> Constant {
+  return {result().derivedTypeSpec(), Base::Reshape(dims), std::move(dims)};
+}
+
 INSTANTIATE_CONSTANT_TEMPLATES
 }
index 064a774..8efa9d5 100644 (file)
@@ -19,6 +19,7 @@
 #include "type.h"
 #include <map>
 #include <ostream>
+#include <vector>
 
 namespace Fortran::evaluate {
 
@@ -36,6 +37,9 @@ template<typename> class Constant;
 // values as indices into constants, use a vector of integers.
 using ConstantSubscript = std::int64_t;
 using ConstantSubscripts = std::vector<ConstantSubscript>;
+inline int GetRank(const ConstantSubscripts &s) {
+  return static_cast<int>(s.size());
+}
 
 std::size_t TotalElementCount(const ConstantSubscripts &);
 
@@ -43,7 +47,7 @@ inline ConstantSubscripts InitialSubscripts(int rank) {
   return ConstantSubscripts(rank, 1);  // parens, not braces: "rank" copies of 1
 }
 inline ConstantSubscripts InitialSubscripts(const ConstantSubscripts &shape) {
-  return InitialSubscripts(static_cast<int>(shape.size()));
+  return InitialSubscripts(GetRank(shape));
 }
 
 // Increments a vector of subscripts in Fortran array order (first dimension
@@ -66,20 +70,18 @@ public:
   template<typename A, typename = common::NoLvalue<A>>
   ConstantBase(A &&x, Result res = Result{})
     : result_{res}, values_{std::move(x)} {}
-  ConstantBase(std::vector<Element> &&x, ConstantSubscripts &&dims,
-      Result res = Result{})
-    : result_{res}, values_(std::move(x)), shape_(std::move(dims)) {}
+  ConstantBase(
+      std::vector<Element> &&, ConstantSubscripts &&, Result = Result{});
 
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ConstantBase)
   ~ConstantBase();
 
-  int Rank() const { return static_cast<int>(shape_.size()); }
+  int Rank() const { return GetRank(shape_); }
   bool operator==(const ConstantBase &) const;
   bool empty() const { return values_.empty(); }
   std::size_t size() const { return values_.size(); }
   const std::vector<Element> &values() const { return values_; }
   const ConstantSubscripts &shape() const { return shape_; }
-  ConstantSubscripts &shape() { return shape_; }
   constexpr Result result() const { return result_; }
 
   constexpr DynamicType GetType() const { return result_.GetType(); }
@@ -87,6 +89,8 @@ public:
   std::ostream &AsFortran(std::ostream &) const;
 
 protected:
+  std::vector<Element> Reshape(const ConstantSubscripts &) const;
+
   Result result_;
   std::vector<Element> values_;
   ConstantSubscripts shape_;
@@ -111,6 +115,7 @@ public:
 
   // Apply 1-based subscripts
   Element At(const ConstantSubscripts &) const;
+  Constant Reshape(ConstantSubscripts &&) const;
 };
 
 template<int KIND> class Constant<Type<TypeCategory::Character, KIND>> {
@@ -124,14 +129,13 @@ public:
   Constant(std::int64_t, std::vector<Element> &&, ConstantSubscripts &&);
   ~Constant();
 
-  int Rank() const { return static_cast<int>(shape_.size()); }
+  int Rank() const { return GetRank(shape_); }
   bool operator==(const Constant &that) const {
     return shape_ == that.shape_ && values_ == that.values_;
   }
   bool empty() const;
   std::size_t size() const;
   const ConstantSubscripts &shape() const { return shape_; }
-  ConstantSubscripts &shape() { return shape_; }
 
   std::int64_t LEN() const { return length_; }
 
@@ -145,6 +149,7 @@ public:
 
   // Apply 1-based subscripts
   Scalar<Result> At(const ConstantSubscripts &) const;
+  Constant Reshape(ConstantSubscripts &&) const;
 
   Constant<SubscriptInteger> SHAPE() const;
   std::ostream &AsFortran(std::ostream &) const;
@@ -180,6 +185,7 @@ public:
 
   std::optional<StructureConstructor> GetScalarValue() const;
   StructureConstructor At(const ConstantSubscripts &) const;
+  Constant Reshape(ConstantSubscripts &&) const;
 };
 
 FOR_EACH_LENGTHLESS_INTRINSIC_KIND(extern template class ConstantBase, )
index f43fee6..40f45ba 100644 (file)
@@ -309,7 +309,7 @@ public:
   template<typename T> void Descend(Variable<T> &var) { Visit(var.u); }
 
   void Descend(const ActualArgument &arg) {
-    if (const auto *expr{arg.GetExpr()}) {
+    if (const auto *expr{arg.UnwrapExpr()}) {
       Visit(*expr);
     } else {
       const semantics::Symbol *aType{arg.GetAssumedTypeDummy()};
@@ -317,7 +317,7 @@ public:
     }
   }
   void Descend(ActualArgument &arg) {
-    if (auto *expr{arg.GetExpr()}) {
+    if (auto *expr{arg.UnwrapExpr()}) {
       Visit(*expr);
     } else {
       const semantics::Symbol *aType{arg.GetAssumedTypeDummy()};
index c1662d2..944f460 100644 (file)
@@ -142,6 +142,15 @@ bool StructureConstructor::operator==(const StructureConstructor &that) const {
 
 DynamicType StructureConstructor::GetType() const { return result_.GetType(); }
 
+const Expr<SomeType> *StructureConstructor::Find(
+    const Symbol *component) const {
+  if (auto iter{values_.find(component)}; iter != values_.end()) {
+    return &iter->second.value();
+  } else {
+    return nullptr;
+  }
+}
+
 StructureConstructor &StructureConstructor::Add(
     const Symbol &symbol, Expr<SomeType> &&expr) {
   values_.emplace(&symbol, std::move(expr));
index 7139341..808a894 100644 (file)
@@ -752,6 +752,8 @@ public:
     return values_.end();
   }
 
+  const Expr<SomeType> *Find(const Symbol *) const;  // can return null
+
   StructureConstructor &Add(const semantics::Symbol &, Expr<SomeType> &&);
   int Rank() const { return 0; }
   DynamicType GetType() const;
index 44ac74c..d60f5e0 100644 (file)
 
 namespace Fortran::evaluate {
 
+// FoldOperation() rewrites expression tree nodes.
+// If there is any possibility that the rewritten node will
+// not have the same representation type, the result of
+// FoldOperation() will be packaged in an Expr<> of the same
+// specific type.
+
 // no-op base case
 template<typename A>
 common::IfNoLvalue<Expr<ResultType<A>>, A> FoldOperation(
     FoldingContext &, A &&x) {
+  static_assert(!std::is_same_v<A, Expr<ResultType<A>>> &&
+      "call Fold() instead for Expr<>");
   return Expr<ResultType<A>>{std::move(x)};
 }
 
 // Forward declarations of overloads, template instantiations, and template
 // specializations of FoldOperation() to enable mutual recursion between them.
-BaseObject FoldOperation(FoldingContext &, BaseObject &&);
-Component FoldOperation(FoldingContext &, Component &&);
-Triplet FoldOperation(FoldingContext &, Triplet &&);
-Subscript FoldOperation(FoldingContext &, Subscript &&);
-ArrayRef FoldOperation(FoldingContext &, ArrayRef &&);
-CoarrayRef FoldOperation(FoldingContext &, CoarrayRef &&);
-DataRef FoldOperation(FoldingContext &, DataRef &&);
-Substring FoldOperation(FoldingContext &, Substring &&);
-ComplexPart FoldOperation(FoldingContext &, ComplexPart &&);
+static Component FoldOperation(FoldingContext &, Component &&);
+static Triplet FoldOperation(
+    FoldingContext &, Triplet &&, const Symbol &, int dim);
+static Subscript FoldOperation(
+    FoldingContext &, Subscript &&, const Symbol &, int dim);
+static ArrayRef FoldOperation(FoldingContext &, ArrayRef &&);
+static CoarrayRef FoldOperation(FoldingContext &, CoarrayRef &&);
+static DataRef FoldOperation(FoldingContext &, DataRef &&);
+static Substring FoldOperation(FoldingContext &, Substring &&);
+static ComplexPart FoldOperation(FoldingContext &, ComplexPart &&);
 template<int KIND>
 Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
     FoldingContext &context, FunctionRef<Type<TypeCategory::Integer, KIND>> &&);
@@ -75,27 +84,37 @@ template<typename T> Expr<T> FoldOperation(FoldingContext &, Designator<T> &&);
 template<int KIND>
 Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
     FoldingContext &, TypeParamInquiry<KIND> &&);
+static Expr<ImpliedDoIndex::Result> FoldOperation(
+    FoldingContext &context, ImpliedDoIndex &&);
 template<typename T>
 Expr<T> FoldOperation(FoldingContext &, ArrayConstructor<T> &&);
-Expr<SomeDerived> FoldOperation(FoldingContext &, StructureConstructor &&);
+static Expr<SomeDerived> FoldOperation(
+    FoldingContext &, StructureConstructor &&);
 
 // Overloads, instantiations, and specializations of FoldOperation().
 
-BaseObject FoldOperation(FoldingContext &, BaseObject &&object) {
-  return std::move(object);
-}
-
 Component FoldOperation(FoldingContext &context, Component &&component) {
   return {FoldOperation(context, std::move(component.base())),
       component.GetLastSymbol()};
 }
 
-Triplet FoldOperation(FoldingContext &context, Triplet &&triplet) {
-  return {Fold(context, triplet.lower()), Fold(context, triplet.upper()),
-      Fold(context, common::Clone(triplet.stride()))};
+Triplet FoldOperation(
+    FoldingContext &context, Triplet &&triplet, const Symbol &symbol, int dim) {
+  MaybeExtentExpr lower{triplet.lower()};
+  if (!lower.has_value()) {
+    lower = GetLowerBound(context, symbol, dim);
+  }
+  MaybeExtentExpr upper{triplet.upper()};
+  if (!upper.has_value()) {
+    upper = GetUpperBound(
+        context, common::Clone(lower), GetExtent(context, symbol, dim));
+  }
+  return {Fold(context, std::move(lower)), Fold(context, std::move(upper)),
+      Fold(context, triplet.stride())};
 }
 
-Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
+Subscript FoldOperation(FoldingContext &context, Subscript &&subscript,
+    const Symbol &symbol, int dim) {
   return std::visit(
       common::visitors{
           [&](IndirectSubscriptIntegerExpr &&expr) {
@@ -103,15 +122,18 @@ Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
             return Subscript(std::move(expr));
           },
           [&](Triplet &&triplet) {
-            return Subscript(FoldOperation(context, std::move(triplet)));
+            return Subscript(
+                FoldOperation(context, std::move(triplet), symbol, dim));
           },
       },
       std::move(subscript.u));
 }
 
 ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
+  const Symbol &symbol{arrayRef.GetLastSymbol()};
+  int dim{0};
   for (Subscript &subscript : arrayRef.subscript()) {
-    subscript = FoldOperation(context, std::move(subscript));
+    subscript = FoldOperation(context, std::move(subscript), symbol, dim++);
   }
   return std::visit(
       common::visitors{
@@ -127,9 +149,11 @@ ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
 }
 
 CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) {
+  const Symbol &symbol{coarrayRef.GetLastSymbol()};
   std::vector<Subscript> subscript;
+  int dim{0};
   for (Subscript x : coarrayRef.subscript()) {
-    subscript.emplace_back(FoldOperation(context, std::move(x)));
+    subscript.emplace_back(FoldOperation(context, std::move(x), symbol, dim++));
   }
   std::vector<Expr<SubscriptInteger>> cosubscript;
   for (Expr<SubscriptInteger> x : coarrayRef.cosubscript()) {
@@ -194,8 +218,8 @@ static inline Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
   static_assert(
       (... && IsSpecificIntrinsicType<TA>));  // TODO derived types for MERGE?
   static_assert(sizeof...(TA) > 0);
-  std::tuple<const Constant<TA> *...> args{
-      GetConstantValue<TA>(*funcRef.arguments()[I].value().GetExpr())...};
+  std::tuple<const Constant<TA> *...> args{UnwrapExpr<Constant<TA>>(
+      *funcRef.arguments()[I].value().UnwrapExpr())...};
   if ((... && (std::get<I>(args) != nullptr))) {
     // Compute the shape of the result based on shapes of arguments
     ConstantSubscripts shape;
@@ -215,13 +239,13 @@ static inline Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
             // same. Shouldn't this be checked elsewhere so that this is also
             // checked for non constexpr call to elemental intrinsics function?
             context.messages().Say(
-                "arguments in elemental intrinsic function are not conformable"_err_en_US);
+                "Arguments in elemental intrinsic function are not conformable"_err_en_US);
             return Expr<TR>{std::move(funcRef)};
           }
         }
       }
     }
-    CHECK(rank == static_cast<int>(shape.size()));
+    CHECK(rank == GetRank(shape));
 
     // Compute all the scalar values of the results
     std::vector<Scalar<TR>> results;
@@ -254,34 +278,35 @@ static inline Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
 }
 
 template<typename TR, typename... TA>
-static Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
+Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
     FunctionRef<TR> &&funcRef, ScalarFunc<TR, TA...> func) {
   return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(
       context, std::move(funcRef), func, std::index_sequence_for<TA...>{});
 }
 template<typename TR, typename... TA>
-static Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
+Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
     FunctionRef<TR> &&funcRef, ScalarFuncWithContext<TR, TA...> func) {
   return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(
       context, std::move(funcRef), func, std::index_sequence_for<TA...>{});
 }
 
-template<typename T>
-static Expr<T> *UnwrapArgument(std::optional<ActualArgument> &arg) {
-  if (arg.has_value()) {
-    if (Expr<SomeType> * expr{arg->GetExpr()}) {
-      return UnwrapExpr<Expr<T>>(*expr);
-    }
+static std::optional<std::int64_t> GetInt64Arg(
+    const std::optional<ActualArgument> &arg) {
+  if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(arg)}) {
+    return ToInt64(*intExpr);
+  } else {
+    return std::nullopt;
   }
-  return nullptr;
 }
 
-static BOZLiteralConstant *UnwrapBozArgument(
-    std::optional<ActualArgument> &arg) {
-  if (auto *expr{UnwrapArgument<SomeType>(arg)}) {
-    return std::get_if<BOZLiteralConstant>(&expr->u);
+static std::optional<std::int64_t> GetInt64ArgOr(
+    const std::optional<ActualArgument> &arg, std::int64_t defaultValue) {
+  if (!arg.has_value()) {
+    return defaultValue;
+  } else if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(arg)}) {
+    return ToInt64(*intExpr);
   } else {
-    return nullptr;
+    return std::nullopt;
   }
 }
 
@@ -291,8 +316,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
   using T = Type<TypeCategory::Integer, KIND>;
   ActualArguments &args{funcRef.arguments()};
   for (std::optional<ActualArgument> &arg : args) {
-    if (auto *expr{UnwrapArgument<SomeType>(arg)}) {
-      *expr = FoldOperation(context, std::move(*expr));
+    if (auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
+      *expr = Fold(context, std::move(*expr));
     }
   }
   if (auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}) {
@@ -313,7 +338,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "dshiftl" || name == "dshiftr") {
       // convert boz
       for (int i{0}; i <= 1; ++i) {
-        if (auto *x{UnwrapBozArgument(args[i])}) {
+        if (auto *x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
           *args[i] =
               AsGenericExpr(Fold(context, ConvertToType<T>(std::move(*x))));
         }
@@ -321,7 +346,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
       // Third argument can be of any kind. However, it must be smaller or equal
       // than BIT_SIZE. It can be converted to Int4 to simplify.
       using Int4 = Type<TypeCategory::Integer, 4>;
-      if (auto *n{UnwrapArgument<SomeInteger>(args[2])}) {
+      if (auto *n{UnwrapExpr<Expr<SomeInteger>>(args[2])}) {
         *args[2] =
             AsGenericExpr(Fold(context, ConvertToType<Int4>(std::move(*n))));
       }
@@ -335,7 +360,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
                     fptr, i, j, static_cast<int>(shift.ToInt64()));
               }));
     } else if (name == "exponent") {
-      if (auto *sx{UnwrapArgument<SomeReal>(args[0])}) {
+      if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
         return std::visit(
             [&funcRef, &context](const auto &x) -> Expr<T> {
               using TR = typename std::decay_t<decltype(x)>::Result;
@@ -349,7 +374,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "iand" || name == "ior" || name == "ieor") {
       // convert boz
       for (int i{0}; i <= 1; ++i) {
-        if (auto *x{UnwrapBozArgument(args[i])}) {
+        if (auto *x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
           *args[i] =
               AsGenericExpr(Fold(context, ConvertToType<T>(std::move(*x))));
         }
@@ -370,7 +395,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
       // Second argument can be of any kind. However, it must be smaller or
       // equal than BIT_SIZE. It can be converted to Int4 to simplify.
       using Int4 = Type<TypeCategory::Integer, 4>;
-      if (auto *n{UnwrapArgument<SomeInteger>(args[1])}) {
+      if (auto *n{UnwrapExpr<Expr<SomeInteger>>(args[1])}) {
         *args[1] =
             AsGenericExpr(Fold(context, ConvertToType<Int4>(std::move(*n))));
       }
@@ -395,7 +420,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
             return std::invoke(fptr, i, static_cast<int>(pos.ToInt64()));
           }));
     } else if (name == "int") {
-      if (auto *expr{args[0].value().GetExpr()}) {
+      if (auto *expr{args[0].value().UnwrapExpr()}) {
         return std::visit(
             [&](auto &&x) -> Expr<T> {
               using From = std::decay_t<decltype(x)>;
@@ -415,7 +440,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
       }
     } else if (name == "leadz" || name == "trailz" || name == "poppar" ||
         name == "popcnt") {
-      if (auto *sn{UnwrapArgument<SomeInteger>(args[0])}) {
+      if (auto *sn{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
         return std::visit(
             [&funcRef, &context, &name](const auto &n) -> Expr<T> {
               using TI = typename std::decay_t<decltype(n)>::Result;
@@ -446,7 +471,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
         common::die("leadz argument must be integer");
       }
     } else if (name == "len") {
-      if (auto *charExpr{UnwrapArgument<SomeCharacter>(args[0])}) {
+      if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
         return std::visit(
             [&](auto &kx) { return Fold(context, ConvertToType<T>(kx.LEN())); },
             charExpr->u);
@@ -457,7 +482,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
       // Argument can be of any kind but value has to be smaller than bit_size.
       // It can be safely converted to Int4 to simplify.
       using Int4 = Type<TypeCategory::Integer, 4>;
-      if (auto *n{UnwrapArgument<SomeInteger>(args[0])}) {
+      if (auto *n{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
         *args[0] =
             AsGenericExpr(Fold(context, ConvertToType<Int4>(std::move(*n))));
       }
@@ -469,7 +494,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "merge_bits") {
       // convert boz
       for (int i{0}; i <= 2; ++i) {
-        if (auto *x{UnwrapBozArgument(args[i])}) {
+        if (auto *x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
           *args[i] =
               AsGenericExpr(Fold(context, ConvertToType<T>(std::move(*x))));
         }
@@ -479,6 +504,18 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "rank") {
       // TODO assumed-rank dummy argument
       return Expr<T>{args[0].value().Rank()};
+    } else if (name == "selected_int_kind") {
+      if (auto p{GetInt64Arg(args[0])}) {
+        return Expr<T>{SelectedIntKind(*p)};
+      }
+    } else if (name == "selected_real_kind") {
+      if (auto p{GetInt64ArgOr(args[0], 0)}) {
+        if (auto r{GetInt64ArgOr(args[1], 0)}) {
+          if (auto radix{GetInt64ArgOr(args[2], 2)}) {
+            return Expr<T>{SelectedRealKind(*p, *r, *radix)};
+          }
+        }
+      }
     } else if (name == "shape") {
       if (auto shape{GetShape(context, args[0].value())}) {
         if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
@@ -488,18 +525,16 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "size") {
       if (auto shape{GetShape(context, args[0].value())}) {
         if (auto &dimArg{args[1]}) {  // DIM= is present, get one extent
-          if (auto *expr{dimArg->GetExpr()}) {
-            if (auto dim{ToInt64(*expr)}) {
-              std::int64_t rank = shape->size();
-              if (*dim >= 1 && *dim <= rank) {
-                if (auto &extent{shape->at(*dim - 1)}) {
-                  return Fold(context, ConvertToType<T>(std::move(*extent)));
-                }
-              } else {
-                context.messages().Say(
-                    "size(array,dim=%jd) dimension is out of range for rank-%d array"_en_US,
-                    static_cast<std::intmax_t>(*dim), static_cast<int>(rank));
+          if (auto dim{GetInt64Arg(args[1])}) {
+            int rank = GetRank(*shape);
+            if (*dim >= 1 && *dim <= rank) {
+              if (auto &extent{shape->at(*dim - 1)}) {
+                return Fold(context, ConvertToType<T>(std::move(*extent)));
               }
+            } else {
+              context.messages().Say(
+                  "size(array,dim=%jd) dimension is out of range for rank-%d array"_en_US,
+                  static_cast<std::intmax_t>(*dim), static_cast<int>(rank));
             }
           }
         } else if (auto extents{
@@ -518,7 +553,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(FoldingContext &context,
     // findloc, floor, iachar, iall, iany, iparity, ibits, ichar, image_status,
     // index, ishftc, lbound, len_trim, matmul, max, maxloc, maxval, merge, min,
     // minloc, minval, mod, modulo, nint, not, pack, product, reduce, reshape,
-    // scan, selected_char_kind, selected_int_kind, selected_real_kind,
+    // scan, selected_char_kind,
     // sign, spread, sum, transfer, transpose, ubound, unpack, verify
   }
   return Expr<T>{std::move(funcRef)};
@@ -562,8 +597,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
   ActualArguments &args{funcRef.arguments()};
   for (std::optional<ActualArgument> &arg : args) {
     if (arg.has_value()) {
-      if (auto *expr{arg->GetExpr()}) {
-        *expr = FoldOperation(context, std::move(*expr));
+      if (auto *expr{arg->UnwrapExpr()}) {
+        *expr = Fold(context, std::move(*expr));
       }
     }
   }
@@ -605,7 +640,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
       if (args.size() == 2) {  // elemental
         // runtime functions use int arg
         using Int4 = Type<TypeCategory::Integer, 4>;
-        if (auto *n{UnwrapArgument<SomeInteger>(args[0])}) {
+        if (auto *n{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
           *args[0] =
               AsGenericExpr(Fold(context, ConvertToType<Int4>(std::move(*n))));
         }
@@ -622,10 +657,10 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
       }
     } else if (name == "abs") {
       // Argument can be complex or real
-      if (auto *x{UnwrapArgument<SomeReal>(args[0])}) {
+      if (auto *x{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
         return FoldElementalIntrinsic<T, T>(
             context, std::move(funcRef), &Scalar<T>::ABS);
-      } else if (auto *z{UnwrapArgument<SomeComplex>(args[0])}) {
+      } else if (auto *z{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
         if (auto callable{
                 context.hostIntrinsicsLibrary()
                     .GetHostProcedureWrapper<Scalar, T, ComplexT>("abs")}) {
@@ -643,7 +678,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
           context, std::move(funcRef), &Scalar<ComplexT>::AIMAG);
     } else if (name == "aint") {
       // Convert argument to the requested kind before calling aint
-      if (auto *x{UnwrapArgument<SomeReal>(args[0])}) {
+      if (auto *x{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
         *args[0] =
             AsGenericExpr(Fold(context, ConvertToType<T>(std::move(*x))));
       }
@@ -657,8 +692,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
             return y.value;
           }));
     } else if (name == "dprod") {
-      if (auto *x{UnwrapArgument<SomeReal>(args[0])}) {
-        if (auto *y{UnwrapArgument<SomeReal>(args[1])}) {
+      if (auto *x{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
+        if (auto *y{UnwrapExpr<Expr<SomeReal>>(args[1])}) {
           return Fold(context,
               Expr<T>{Multiply<T>{ConvertToType<T>(std::move(*x)),
                   ConvertToType<T>(std::move(*y))}});
@@ -668,7 +703,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldOperation(FoldingContext &context,
     } else if (name == "epsilon") {
       return Expr<T>{Constant<T>{Scalar<T>::EPSILON()}};
     } else if (name == "real") {
-      if (auto *expr{args[0].value().GetExpr()}) {
+      if (auto *expr{args[0].value().UnwrapExpr()}) {
         return ToReal<KIND>(context, std::move(*expr));
       }
     }
@@ -688,8 +723,8 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldOperation(FoldingContext &context,
   ActualArguments &args{funcRef.arguments()};
   for (std::optional<ActualArgument> &arg : args) {
     if (arg.has_value()) {
-      if (auto *expr{arg->GetExpr()}) {
-        *expr = FoldOperation(context, std::move(*expr));
+      if (auto *expr{arg->UnwrapExpr()}) {
+        *expr = Fold(context, std::move(*expr));
       }
     }
   }
@@ -712,7 +747,7 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldOperation(FoldingContext &context,
           context, std::move(funcRef), &Scalar<T>::CONJG);
     } else if (name == "cmplx") {
       if (args.size() == 2) {
-        if (auto *x{UnwrapArgument<SomeComplex>(args[0])}) {
+        if (auto *x{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
           return Fold(context, ConvertToType<T>(std::move(*x)));
         } else {
           common::die("x must be complex in cmplx(x[, kind])");
@@ -720,9 +755,9 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldOperation(FoldingContext &context,
       } else {
         CHECK(args.size() == 3);
         using Part = typename T::Part;
-        Expr<SomeType> re{std::move(*args[0].value().GetExpr())};
+        Expr<SomeType> re{std::move(*args[0].value().UnwrapExpr())};
         Expr<SomeType> im{args[1].has_value()
-                ? std::move(*args[1].value().GetExpr())
+                ? std::move(*args[1].value().UnwrapExpr())
                 : AsGenericExpr(Constant<Part>{Scalar<Part>{}})};
         return Fold(context,
             Expr<T>{
@@ -743,24 +778,51 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(FoldingContext &context,
   ActualArguments &args{funcRef.arguments()};
   for (std::optional<ActualArgument> &arg : args) {
     if (arg.has_value()) {
-      if (auto *expr{arg->GetExpr()}) {
-        *expr = FoldOperation(context, std::move(*expr));
+      if (auto *expr{arg->UnwrapExpr()}) {
+        *expr = Fold(context, std::move(*expr));
       }
     }
   }
   if (auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}) {
     std::string name{intrinsic->name};
-    if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
+    if (name == "all") {
+      if (!args[1].has_value()) {  // TODO: ALL(x,DIM=d)
+        if (const auto *constant{UnwrapConstantValue<T>(args[0])}) {
+          bool result{true};
+          for (const auto &element : constant->values()) {
+            if (!element.IsTrue()) {
+              result = false;
+              break;
+            }
+          }
+          return Expr<T>{result};
+        }
+      }
+    } else if (name == "any") {
+      if (!args[1].has_value()) {  // TODO: ANY(x,DIM=d)
+        if (const auto *constant{UnwrapConstantValue<T>(args[0])}) {
+          bool result{false};
+          for (const auto &element : constant->values()) {
+            if (element.IsTrue()) {
+              result = true;
+              break;
+            }
+          }
+          return Expr<T>{result};
+        }
+      }
+    } else if (name == "bge" || name == "bgt" || name == "ble" ||
+        name == "blt") {
       using LargestInt = Type<TypeCategory::Integer, 16>;
       static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>);
       // Arguments do not have to be of the same integer type. Convert all
       // arguments to the biggest integer type before comparing them to
       // simplify.
       for (int i{0}; i <= 1; ++i) {
-        if (auto *x{UnwrapArgument<SomeInteger>(args[i])}) {
+        if (auto *x{UnwrapExpr<Expr<SomeInteger>>(args[i])}) {
           *args[i] = AsGenericExpr(
               Fold(context, ConvertToType<LargestInt>(std::move(*x))));
-        } else if (auto *x{UnwrapBozArgument(args[i])}) {
+        } else if (auto *x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
           *args[i] = AsGenericExpr(Constant<LargestInt>{std::move(*x)});
         }
       }
@@ -783,7 +845,7 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(FoldingContext &context,
                 return Scalar<T>{std::invoke(fptr, i, j)};
               }));
     }
-    // TODO: all, any, btest, cshift, dot_product, eoshift, is_iostat_end,
+    // TODO: btest, cshift, dot_product, eoshift, is_iostat_end,
     // is_iostat_eor, lge, lgt, lle, llt, logical, matmul, merge, out_of_range,
     // pack, parity, reduce, reshape, spread, transfer, transpose, unpack
   }
@@ -792,20 +854,23 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(FoldingContext &context,
 
 // Get the value of a PARAMETER
 template<typename T>
-static std::optional<Expr<T>> GetParameterValue(
+std::optional<Expr<T>> GetParameterValue(
     FoldingContext &context, const Symbol *symbol) {
   CHECK(symbol != nullptr);
   if (symbol->attrs().test(semantics::Attr::PARAMETER)) {
     if (const auto *object{
             symbol->detailsIf<semantics::ObjectEntityDetails>()}) {
+      if (object->initWasValidated()) {
+        const auto *constant{UnwrapConstantValue<T>(object->init())};
+        CHECK(constant != nullptr);
+        return Expr<T>{*constant};
+      }
       if (const auto &init{object->init()}) {
-        if (const auto *constant{UnwrapExpr<Constant<T>>(*init)}) {
-          return Expr<T>{*constant};
-        }
         if (auto dyType{DynamicType::From(*symbol)}) {
-          auto converted{ConvertToType(*dyType, common::Clone(*init))};
           semantics::ObjectEntityDetails *mutableObject{
               const_cast<semantics::ObjectEntityDetails *>(object)};
+          auto converted{
+              ConvertToType(*dyType, std::move(mutableObject->init().value()))};
           // Reset expression now to prevent infinite loops if the init
           // expression depends on symbol itself.
           mutableObject->set_init(std::nullopt);
@@ -813,9 +878,34 @@ static std::optional<Expr<T>> GetParameterValue(
             *converted = Fold(context, std::move(*converted));
             auto *unwrapped{UnwrapExpr<Expr<T>>(*converted)};
             CHECK(unwrapped != nullptr);
-            if (auto constant{GetScalarConstantValue<T>(*unwrapped)}) {
+            if (auto *constant{UnwrapConstantValue<T>(*unwrapped)}) {
+              if (constant->Rank() == 0 && symbol->Rank() > 0) {
+                // scalar expansion
+                if (auto symShape{GetShape(context, *symbol)}) {
+                  if (auto extents{AsConstantExtents(*symShape)}) {
+                    *constant = constant->Reshape(std::move(*extents));
+                    CHECK(constant->Rank() == symbol->Rank());
+                  }
+                }
+              }
               mutableObject->set_init(AsGenericExpr(Expr<T>{*constant}));
-              return std::move(*unwrapped);
+              if (auto constShape{GetShape(context, *constant)}) {
+                if (auto symShape{GetShape(context, *symbol)}) {
+                  if (CheckConformance(context.messages(), *constShape,
+                          *symShape, "initialization expression",
+                          "PARAMETER")) {
+                    mutableObject->set_initWasValidated();
+                    return std::move(*unwrapped);
+                  }
+                } else {
+                  context.messages().Say(symbol->name(),
+                      "Could not determine the shape of the PARAMETER"_err_en_US);
+                }
+              } else {
+                context.messages().Say(symbol->name(),
+                    "Could not determine the shape of the initialization expression"_err_en_US);
+              }
+              mutableObject->set_init(std::nullopt);
             } else {
               std::stringstream ss;
               unwrapped->AsFortran(ss);
@@ -839,9 +929,9 @@ static std::optional<Expr<T>> GetParameterValue(
 
 // Apply subscripts to a constant array
 
-std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
-    FoldingContext &context, Subscript &ss) {
-  ss = FoldOperation(context, std::move(ss));
+static std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
+    FoldingContext &context, Subscript &ss, const Symbol &symbol, int dim) {
+  ss = FoldOperation(context, std::move(ss), symbol, dim);
   return std::visit(
       common::visitors{
           [](IndirectSubscriptIntegerExpr &expr)
@@ -886,8 +976,8 @@ std::optional<Constant<T>> ApplySubscripts(parser::ContextualMessages &messages,
     const Constant<T> &array,
     const std::vector<Constant<SubscriptInteger>> &subscripts) {
   const auto &shape{array.shape()};
-  std::size_t rank{shape.size()};
-  CHECK(rank == subscripts.size());
+  int rank{GetRank(shape)};
+  CHECK(rank == static_cast<int>(subscripts.size()));
   std::size_t elements{1};
   ConstantSubscripts resultShape;
   for (const auto &ss : subscripts) {
@@ -901,12 +991,12 @@ std::optional<Constant<T>> ApplySubscripts(parser::ContextualMessages &messages,
   std::vector<Scalar<T>> values;
   while (elements-- > 0) {
     bool increment{true};
-    std::size_t k{0};
-    for (std::size_t j{0}; j < rank; ++j) {
+    int k{0};
+    for (int j{0}; j < rank; ++j) {
       if (subscripts[j].Rank() == 0) {
         at[j] = subscripts[j].GetScalarValue().value().ToInt64();
       } else {
-        CHECK(k < resultShape.size());
+        CHECK(k < GetRank(resultShape));
         tmp[0] = ssAt[j] + 1;
         at[j] = subscripts[j].At(tmp).ToInt64();
         if (increment) {
@@ -927,7 +1017,7 @@ std::optional<Constant<T>> ApplySubscripts(parser::ContextualMessages &messages,
     }
     values.emplace_back(array.At(at));
     CHECK(!increment || elements == 0);
-    CHECK(k == resultShape.size());
+    CHECK(k == GetRank(resultShape));
   }
   if constexpr (T::category == TypeCategory::Character) {
     return Constant<T>{array.LEN(), std::move(values), std::move(resultShape)};
@@ -940,20 +1030,23 @@ std::optional<Constant<T>> ApplySubscripts(parser::ContextualMessages &messages,
 }
 
 template<typename T>
-static std::optional<Constant<T>> ApplyConstantSubscripts(
+std::optional<Constant<T>> ApplyConstantSubscripts(
     FoldingContext &context, ArrayRef &aRef) {
+  const Symbol &symbol{aRef.GetLastSymbol()};
   std::vector<Constant<SubscriptInteger>> subscripts;
+  int dim{0};
   for (Subscript &ss : aRef.subscript()) {
-    if (auto constant{GetConstantSubscript(context, ss)}) {
+    if (auto constant{GetConstantSubscript(context, ss, symbol, dim++)}) {
       subscripts.emplace_back(std::move(*constant));
     } else {
       return std::nullopt;
     }
   }
+  // TODO pmk generalize to component base too
   if (const Symbol *const *symbol{std::get_if<const Symbol *>(&aRef.base())}) {
     if (auto value{GetParameterValue<T>(context, *symbol)}) {
       Expr<T> folded{Fold(context, std::move(*value))};
-      if (const auto *array{GetConstantValue<T>(folded)}) {
+      if (const auto *array{UnwrapConstantValue<T>(folded)}) {
         if (auto result{
                 ApplySubscripts(context.messages(), *array, subscripts)}) {
           return result;
@@ -965,6 +1058,28 @@ static std::optional<Constant<T>> ApplyConstantSubscripts(
 }
 
 template<typename T>
+std::optional<Constant<T>> GetConstantComponent(
+    FoldingContext &context, Component &component) {
+  // TODO pmk generalize to array ref and component bases too
+  if (const Symbol *const *symbol{
+          std::get_if<const Symbol *>(&component.base().u)}) {
+    if (auto value{GetParameterValue<SomeDerived>(context, *symbol)}) {
+      Expr<SomeDerived> folded{Fold(context, std::move(*value))};
+      if (const auto *structure{UnwrapConstantValue<SomeDerived>(folded)}) {
+        if (auto scalar{structure->GetScalarValue()}) {
+          if (auto *expr{scalar->Find(&component.GetLastSymbol())}) {
+            if (const auto *value{UnwrapConstantValue<T>(*expr)}) {
+              return *value;
+            }
+          }
+        }
+      }
+    }
+  }
+  return std::nullopt;
+}
+
+template<typename T>
 Expr<T> FoldOperation(FoldingContext &context, Designator<T> &&designator) {
   if constexpr (T::category == TypeCategory::Character) {
     if (auto *substring{common::Unwrap<Substring>(designator.u)}) {
@@ -990,11 +1105,19 @@ Expr<T> FoldOperation(FoldingContext &context, Designator<T> &&designator) {
             }
           },
           [&](ArrayRef &&aRef) {
+            aRef = FoldOperation(context, std::move(aRef));
             if (auto c{ApplyConstantSubscripts<T>(context, aRef)}) {
               return Expr<T>{std::move(*c)};
             } else {
-              return Expr<T>{
-                  Designator<T>{FoldOperation(context, std::move(aRef))}};
+              return Expr<T>{Designator<T>{std::move(aRef)}};
+            }
+          },
+          [&](Component &&component) {
+            component = FoldOperation(context, std::move(component));
+            if (auto c{GetConstantComponent<T>(context, component)}) {
+              return Expr<T>{std::move(*c)};
+            } else {
+              return Expr<T>{Designator<T>{std::move(component)}};
             }
           },
           [&](auto &&x) {
@@ -1020,6 +1143,7 @@ public:
   explicit ArrayConstructorFolder(const FoldingContext &c) : context_{c} {}
 
   Expr<T> FoldArray(ArrayConstructor<T> &&array) {
+    // Calls FoldArray(const ArrayConstructorValues<T> &) below
     if (FoldArray(array)) {
       auto n{static_cast<std::int64_t>(elements_.size())};
       if constexpr (std::is_same_v<T, SomeDerived>) {
@@ -1042,11 +1166,11 @@ public:
 private:
   bool FoldArray(const common::CopyableIndirection<Expr<T>> &expr) {
     Expr<T> folded{Fold(context_, common::Clone(expr.value()))};
-    if (const auto *c{GetConstantValue<T>(folded)}) {
+    if (const auto *c{UnwrapConstantValue<T>(folded)}) {
       // Copy elements in Fortran array element order
       ConstantSubscripts shape{c->shape()};
       int rank{c->Rank()};
-      ConstantSubscripts index(shape.size(), 1);
+      ConstantSubscripts index(GetRank(shape), 1);
       for (std::size_t n{c->size()}; n-- > 0;) {
         elements_.emplace_back(c->At(index));
         for (int d{0}; d < rank; ++d) {
@@ -1109,9 +1233,7 @@ private:
 
 template<typename T>
 Expr<T> FoldOperation(FoldingContext &context, ArrayConstructor<T> &&array) {
-  ArrayConstructorFolder<T> folder{context};
-  Expr<T> result{folder.FoldArray(std::move(array))};
-  return result;
+  return ArrayConstructorFolder<T>{context}.FoldArray(std::move(array));
 }
 
 Expr<SomeDerived> FoldOperation(
@@ -1180,7 +1302,7 @@ bool ArrayConstructorIsFlat(const ArrayConstructorValues<T> &values) {
 
 template<typename T>
 std::optional<Expr<T>> AsFlatArrayConstructor(const Expr<T> &expr) {
-  if (const auto *c{GetConstantValue<T>(expr)}) {
+  if (const auto *c{UnwrapConstantValue<T>(expr)}) {
     ArrayConstructor<T> result{expr};
     if (c->size() > 0) {
       ConstantSubscripts at{InitialSubscripts(c->shape())};
@@ -1200,8 +1322,9 @@ std::optional<Expr<T>> AsFlatArrayConstructor(const Expr<T> &expr) {
 }
 
 template<TypeCategory CAT>
-std::optional<Expr<SomeKind<CAT>>> AsFlatArrayConstructor(
-    const Expr<SomeKind<CAT>> &expr) {
+std::enable_if_t<CAT != TypeCategory::Derived,
+    std::optional<Expr<SomeKind<CAT>>>>
+AsFlatArrayConstructor(const Expr<SomeKind<CAT>> &expr) {
   return std::visit(
       [&](const auto &kindExpr) -> std::optional<Expr<SomeKind<CAT>>> {
         if (auto flattened{AsFlatArrayConstructor(kindExpr)}) {
@@ -1222,8 +1345,8 @@ Expr<T> FromArrayConstructor(FoldingContext &context,
     ArrayConstructor<T> &&values, std::optional<ConstantSubscripts> &&shape) {
   Expr<T> result{Fold(context, Expr<T>{std::move(values)})};
   if (shape.has_value()) {
-    if (auto *constant{GetConstantValue<T>(result)}) {
-      constant->shape() = std::move(*shape);
+    if (auto *constant{UnwrapConstantValue<T>(result)}) {
+      return Expr<T>{constant->Reshape(std::move(*shape))};
     }
   }
   return result;
@@ -1249,8 +1372,7 @@ Expr<RESULT> MapOperation(FoldingContext &context,
           auto &aConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
           for (auto &acValue : aConst) {
             auto &scalar{std::get<Expr<kindType>>(acValue.u)};
-            result.Push(
-                FoldOperation(context, f(Expr<OPERAND>{std::move(scalar)})));
+            result.Push(Fold(context, f(Expr<OPERAND>{std::move(scalar)})));
           }
         },
         std::move(values.u));
@@ -1258,7 +1380,7 @@ Expr<RESULT> MapOperation(FoldingContext &context,
     auto &aConst{std::get<ArrayConstructor<OPERAND>>(values.u)};
     for (auto &acValue : aConst) {
       auto &scalar{std::get<Expr<OPERAND>>(acValue.u)};
-      result.Push(FoldOperation(context, f(std::move(scalar))));
+      result.Push(Fold(context, f(std::move(scalar))));
     }
   }
   return FromArrayConstructor(
@@ -1283,7 +1405,7 @@ Expr<RESULT> MapOperation(FoldingContext &context,
             CHECK(rightIter != rightArrConst.end());
             auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
             auto &rightScalar{std::get<Expr<kindType>>(rightIter->u)};
-            result.Push(FoldOperation(context,
+            result.Push(Fold(context,
                 f(std::move(leftScalar), Expr<RIGHT>{std::move(rightScalar)})));
             ++rightIter;
           }
@@ -1296,8 +1418,8 @@ Expr<RESULT> MapOperation(FoldingContext &context,
       CHECK(rightIter != rightArrConst.end());
       auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
       auto &rightScalar{std::get<Expr<RIGHT>>(rightIter->u)};
-      result.Push(FoldOperation(
-          context, f(std::move(leftScalar), std::move(rightScalar))));
+      result.Push(
+          Fold(context, f(std::move(leftScalar), std::move(rightScalar))));
       ++rightIter;
     }
   }
@@ -1315,8 +1437,8 @@ Expr<RESULT> MapOperation(FoldingContext &context,
   auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
   for (auto &leftValue : leftArrConst) {
     auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
-    result.Push(FoldOperation(
-        context, f(std::move(leftScalar), Expr<RIGHT>{rightScalar})));
+    result.Push(
+        Fold(context, f(std::move(leftScalar), Expr<RIGHT>{rightScalar})));
   }
   return FromArrayConstructor(
       context, std::move(result), AsConstantExtents(shape));
@@ -1336,7 +1458,7 @@ Expr<RESULT> MapOperation(FoldingContext &context,
           auto &rightArrConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
           for (auto &rightValue : rightArrConst) {
             auto &rightScalar{std::get<Expr<kindType>>(rightValue.u)};
-            result.Push(FoldOperation(context,
+            result.Push(Fold(context,
                 f(Expr<LEFT>{leftScalar},
                     Expr<RIGHT>{std::move(rightScalar)})));
           }
@@ -1346,8 +1468,8 @@ Expr<RESULT> MapOperation(FoldingContext &context,
     auto &rightArrConst{std::get<ArrayConstructor<RIGHT>>(rightValues.u)};
     for (auto &rightValue : rightArrConst) {
       auto &rightScalar{std::get<Expr<RIGHT>>(rightValue.u)};
-      result.Push(FoldOperation(
-          context, f(Expr<LEFT>{leftScalar}, std::move(rightScalar))));
+      result.Push(
+          Fold(context, f(Expr<LEFT>{leftScalar}, std::move(rightScalar))));
     }
   }
   return FromArrayConstructor(
index 0ea56bf..72dbe47 100644 (file)
@@ -30,7 +30,7 @@ namespace Fortran::evaluate {
 using namespace Fortran::parser::literals;
 
 // Fold() rewrites an expression and returns it.  When the rewritten expression
-// is a constant, GetConstantValue() and GetScalarConstantValue() below will
+// is a constant, UnwrapConstantValue() and GetScalarConstantValue() below will
 // be able to extract it.
 // Note the rvalue reference argument: the rewrites are performed in place
 // for efficiency.
@@ -48,29 +48,18 @@ std::optional<Expr<T>> Fold(
   }
 }
 
-// GetConstantValue() isolates the known constant value of
-// an expression, if it has one.  The value can be parenthesized.
+// UnwrapConstantValue() isolates the known constant value of
+// an expression, if it has one.  It returns a pointer, which is
+// const-qualified when the expression is so.  The value can be
+// parenthesized.
 template<typename T, typename EXPR>
-const Constant<T> *GetConstantValue(const EXPR &expr) {
-  if (const auto *c{UnwrapExpr<Constant<T>>(expr)}) {
-    return c;
-  } else {
-    if constexpr (!std::is_same_v<T, SomeDerived>) {
-      if (auto *parens{UnwrapExpr<Parentheses<T>>(expr)}) {
-        return GetConstantValue<T>(parens->left());
-      }
-    }
-    return nullptr;
-  }
-}
-
-template<typename T, typename EXPR> Constant<T> *GetConstantValue(EXPR &expr) {
+auto UnwrapConstantValue(EXPR &expr) -> common::Constify<Constant<T>, EXPR> * {
   if (auto *c{UnwrapExpr<Constant<T>>(expr)}) {
     return c;
   } else {
     if constexpr (!std::is_same_v<T, SomeDerived>) {
       if (auto *parens{UnwrapExpr<Parentheses<T>>(expr)}) {
-        return GetConstantValue<T>(parens->left());
+        return UnwrapConstantValue<T>(parens->left());
       }
     }
     return nullptr;
@@ -81,7 +70,7 @@ template<typename T, typename EXPR> Constant<T> *GetConstantValue(EXPR &expr) {
 // an expression, if it has one.  The value can be parenthesized.
 template<typename T, typename EXPR>
 auto GetScalarConstantValue(const EXPR &expr) -> std::optional<Scalar<T>> {
-  if (const Constant<T> *constant{GetConstantValue<T>(expr)}) {
+  if (const Constant<T> *constant{UnwrapConstantValue<T>(expr)}) {
     return constant->GetScalarValue();
   } else {
     return std::nullopt;
index 5eb0c29..ed63691 100644 (file)
@@ -24,7 +24,7 @@
 namespace Fortran::evaluate {
 
 static void ShapeAsFortran(std::ostream &o, const ConstantSubscripts &shape) {
-  if (shape.size() > 1) {
+  if (GetRank(shape) > 1) {
     o << ",shape=";
     char ch{'['};
     for (auto dim : shape) {
@@ -112,7 +112,7 @@ std::ostream &ActualArgument::AsFortran(std::ostream &o) const {
   if (isAlternateReturn) {
     o << '*';
   }
-  if (const auto *expr{GetExpr()}) {
+  if (const auto *expr{UnwrapExpr()}) {
     return expr->AsFortran(o);
   } else {
     return std::get<AssumedType>(u_).AsFortran(o);
index bd242c9..320ff33 100644 (file)
@@ -301,7 +301,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"cshift",
         {{"array", SameType, Rank::array}, {"shift", AnyInt, Rank::dimRemoved},
             OptionalDIM},
-        SameType, Rank::array},
+        SameType, Rank::conformable},
     {"dble", {{"a", AnyNumeric, Rank::elementalOrBOZ}}, DoublePrecision},
     {"dim", {{"x", SameIntOrReal}, {"y", SameIntOrReal}}, SameIntOrReal},
     {"dot_product",
@@ -333,12 +333,12 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
             {"boundary", SameIntrinsic, Rank::dimRemoved,
                 Optionality::optional},
             OptionalDIM},
-        SameIntrinsic, Rank::array},
+        SameIntrinsic, Rank::conformable},
     {"eoshift",
         {{"array", SameDerivedType, Rank::array},
             {"shift", AnyInt, Rank::dimRemoved},
             {"boundary", SameDerivedType, Rank::dimRemoved}, OptionalDIM},
-        SameDerivedType, Rank::array},
+        SameDerivedType, Rank::conformable},
     {"erf", {{"x", SameReal}}, SameReal},
     {"erfc", {{"x", SameReal}}, SameReal},
     {"erfc_scaled", {{"x", SameReal}}, SameReal},
@@ -401,13 +401,13 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"is_iostat_eor", {{"i", AnyInt}}, DefaultLogical},
     {"kind", {{"x", AnyIntrinsic}}, DefaultInt},
     {"lbound",
-        {{"array", Anything, Rank::anyOrAssumedRank}, SubscriptDefaultKIND},
-        KINDInt, Rank::vector},
-    {"lbound",
         {{"array", Anything, Rank::anyOrAssumedRank},
             {"dim", {IntType, KindCode::dimArg}, Rank::scalar},
             SubscriptDefaultKIND},
         KINDInt, Rank::scalar},
+    {"lbound",
+        {{"array", Anything, Rank::anyOrAssumedRank}, SubscriptDefaultKIND},
+        KINDInt, Rank::vector},
     {"leadz", {{"i", AnyInt}}, DefaultInt},
     {"len", {{"string", AnyChar}, SubscriptDefaultKIND}, KINDInt},
     {"len_trim", {{"string", AnyChar}, SubscriptDefaultKIND}, KINDInt},
@@ -590,13 +590,13 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"transpose", {{"matrix", SameType, Rank::matrix}}, SameType, Rank::matrix},
     {"trim", {{"string", SameChar, Rank::scalar}}, SameChar, Rank::scalar},
     {"ubound",
-        {{"array", Anything, Rank::anyOrAssumedRank}, SubscriptDefaultKIND},
-        KINDInt, Rank::vector},
-    {"ubound",
         {{"array", Anything, Rank::anyOrAssumedRank},
             {"dim", {IntType, KindCode::dimArg}, Rank::scalar},
             SubscriptDefaultKIND},
         KINDInt, Rank::scalar},
+    {"ubound",
+        {{"array", Anything, Rank::anyOrAssumedRank}, SubscriptDefaultKIND},
+        KINDInt, Rank::vector},
     {"unpack",
         {{"vector", SameType, Rank::vector}, {"mask", AnyLogical, Rank::array},
             {"field", SameType, Rank::conformable}},
@@ -890,7 +890,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
     std::optional<DynamicType> type{arg->GetType()};
     if (!type.has_value()) {
       CHECK(arg->Rank() == 0);
-      const Expr<SomeType> *expr{arg->GetExpr()};
+      const Expr<SomeType> *expr{arg->UnwrapExpr()};
       CHECK(expr != nullptr);
       if (std::holds_alternative<BOZLiteralConstant>(expr->u)) {
         if (d.typePattern.kindCode == KindCode::typeless ||
@@ -1111,7 +1111,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
       CHECK(kindDummyArg != nullptr);
       CHECK(result.categorySet == CategorySet{*category});
       if (kindArg != nullptr) {
-        if (auto *expr{kindArg->GetExpr()}) {
+        if (auto *expr{kindArg->UnwrapExpr()}) {
           CHECK(expr->Rank() == 0);
           if (auto code{ToInt64(*expr)}) {
             if (IsValidKindOfIntrinsicType(*category, *code)) {
@@ -1215,7 +1215,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
   for (std::size_t j{0}; j < dummies; ++j) {
     const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
     if (const auto &arg{rearranged[j]}) {
-      const Expr<SomeType> *expr{arg->GetExpr()};
+      const Expr<SomeType> *expr{arg->UnwrapExpr()};
       CHECK(expr != nullptr);
       std::optional<characteristics::TypeAndShape> typeAndShape;
       if (auto type{expr->GetType()}) {
@@ -1318,7 +1318,7 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
       context.messages().Say("Unknown argument '%s' to NULL()"_err_en_US,
           arguments[0]->keyword->ToString());
     } else {
-      if (Expr<SomeType> * mold{arguments[0]->GetExpr()}) {
+      if (Expr<SomeType> * mold{arguments[0]->UnwrapExpr()}) {
         if (IsAllocatableOrPointer(*mold)) {
           characteristics::DummyArguments args;
           std::optional<characteristics::FunctionResult> fResult;
@@ -1423,7 +1423,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
       if (call.name == "present") {
         bool ok{false};
         if (const auto &arg{specificCall->arguments[0]}) {
-          if (const auto *expr{arg->GetExpr()}) {
+          if (const auto *expr{arg->UnwrapExpr()}) {
             if (const Symbol * symbol{IsWholeSymbolDataRef(*expr)}) {
               ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
             }
index 3df551e..f5bd03f 100644 (file)
 
 namespace Fortran::evaluate {
 
+bool IsImpliedShape(const Symbol &symbol) {
+  if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
+    if (symbol.attrs().test(semantics::Attr::PARAMETER) &&
+        details->init().has_value()) {
+      for (const semantics::ShapeSpec &ss : details->shape()) {
+        if (!ss.ubound().isDeferred()) {
+          // ss.isDeferred() can't be used because the lower bounds are
+          // implicitly set to 1 in the symbol table.
+          return false;
+        }
+      }
+      return !details->shape().empty();
+    }
+  }
+  return false;
+}
+
+bool IsExplicitShape(const Symbol &symbol) {
+  if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
+    for (const semantics::ShapeSpec &ss : details->shape()) {
+      if (!ss.isExplicit()) {
+        return false;
+      }
+    }
+    return true;  // even if scalar
+  } else {
+    return false;
+  }
+}
+
 Shape AsShape(const Constant<ExtentType> &arrayConstant) {
   CHECK(arrayConstant.Rank() == 1);
   Shape result;
   std::size_t dimensions{arrayConstant.size()};
   for (std::size_t j{0}; j < dimensions; ++j) {
     Scalar<ExtentType> extent{arrayConstant.values().at(j)};
-    result.emplace_back(MaybeExtent{ExtentExpr{extent}});
+    result.emplace_back(MaybeExtentExpr{ExtentExpr{extent}});
   }
   return result;
 }
@@ -37,7 +67,7 @@ Shape AsShape(const Constant<ExtentType> &arrayConstant) {
 std::optional<Shape> AsShape(FoldingContext &context, ExtentExpr &&arrayExpr) {
   // Flatten any array expression into an array constructor if possible.
   arrayExpr = Fold(context, std::move(arrayExpr));
-  if (const auto *constArray{GetConstantValue<ExtentType>(arrayExpr)}) {
+  if (const auto *constArray{UnwrapConstantValue<ExtentType>(arrayExpr)}) {
     return AsShape(*constArray);
   }
   if (auto *constructor{UnwrapExpr<ArrayConstructor<ExtentType>>(arrayExpr)}) {
@@ -72,13 +102,22 @@ std::optional<Constant<ExtentType>> AsConstantShape(const Shape &shape) {
   if (auto shapeArray{AsExtentArrayExpr(shape)}) {
     FoldingContext noFoldingContext;
     auto folded{Fold(noFoldingContext, std::move(*shapeArray))};
-    if (auto *p{GetConstantValue<ExtentType>(folded)}) {
+    if (auto *p{UnwrapConstantValue<ExtentType>(folded)}) {
       return std::move(*p);
     }
   }
   return std::nullopt;
 }
 
+Constant<SubscriptInteger> AsConstantShape(const ConstantSubscripts &shape) {
+  using IntType = Scalar<SubscriptInteger>;
+  std::vector<IntType> result;
+  for (auto dim : shape) {
+    result.emplace_back(dim);
+  }
+  return {std::move(result), ConstantSubscripts{GetRank(shape)}};
+}
+
 ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &shape) {
   ConstantSubscripts result;
   for (const auto &extent : shape.values()) {
@@ -119,13 +158,13 @@ ExtentExpr CountTrips(const ExtentExpr &lower, const ExtentExpr &upper,
       common::Clone(lower), common::Clone(upper), common::Clone(stride));
 }
 
-MaybeExtent CountTrips(
-    MaybeExtent &&lower, MaybeExtent &&upper, MaybeExtent &&stride) {
+MaybeExtentExpr CountTrips(MaybeExtentExpr &&lower, MaybeExtentExpr &&upper,
+    MaybeExtentExpr &&stride) {
   return common::MapOptional(
       ComputeTripCount, std::move(lower), std::move(upper), std::move(stride));
 }
 
-MaybeExtent GetSize(Shape &&shape) {
+MaybeExtentExpr GetSize(Shape &&shape) {
   ExtentExpr extent{1};
   for (auto &&dim : std::move(shape)) {
     if (dim.has_value()) {
@@ -146,14 +185,14 @@ bool ContainsAnyImpliedDoIndex(const ExtentExpr &expr) {
   return Visitor<MyVisitor>{0}.Traverse(expr);
 }
 
-MaybeExtent GetShapeHelper::GetLowerBound(
-    const Symbol &symbol, const Component *component, int dimension) {
+MaybeExtentExpr GetLowerBound(FoldingContext &context, const Symbol &symbol,
+    int dimension, const Component *component) {
   if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
     int j{0};
     for (const auto &shapeSpec : details->shape()) {
       if (j++ == dimension) {
         if (const auto &bound{shapeSpec.lbound().GetExplicit()}) {
-          return *bound;
+          return Fold(context, common::Clone(*bound));
         } else if (component != nullptr) {
           return ExtentExpr{DescriptorInquiry{
               *component, DescriptorInquiry::Field::LowerBound, dimension}};
@@ -167,41 +206,27 @@ MaybeExtent GetShapeHelper::GetLowerBound(
   return std::nullopt;
 }
 
-static bool IsImpliedShape(const Symbol &symbol) {
+MaybeExtentExpr GetExtent(FoldingContext &context, const Symbol &symbol,
+    int dimension, const Component *component) {
+  CHECK(dimension >= 0);
   if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
-    if (symbol.attrs().test(semantics::Attr::PARAMETER) &&
-        details->init().has_value()) {
-      for (const semantics::ShapeSpec &ss : details->shape()) {
-        if (ss.isExplicit()) {
-          return false;
-        }
-      }
-      return true;
+    if (IsImpliedShape(symbol)) {
+      Shape shape{GetShape(context, symbol).value()};
+      return std::move(shape.at(dimension));
     }
-  }
-  return false;
-}
-
-MaybeExtent GetShapeHelper::GetExtent(
-    const Symbol &symbol, const Component *component, int dimension) {
-  if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
     int j{0};
     for (const auto &shapeSpec : details->shape()) {
       if (j++ == dimension) {
         if (shapeSpec.isExplicit()) {
           if (const auto &ubound{shapeSpec.ubound().GetExplicit()}) {
-            FoldingContext noFoldingContext;
             if (const auto &lbound{shapeSpec.lbound().GetExplicit()}) {
-              return Fold(noFoldingContext,
+              return Fold(context,
                   common::Clone(ubound.value()) -
                       common::Clone(lbound.value()) + ExtentExpr{1});
             } else {
-              return Fold(noFoldingContext, common::Clone(ubound.value()));
+              return Fold(context, common::Clone(ubound.value()));
             }
           }
-        } else if (IsImpliedShape(symbol)) {
-          Shape shape{GetShape(symbol).value()};
-          return std::move(shape.at(dimension));
         } else if (details->IsAssumedSize() && j == symbol.Rank()) {
           return std::nullopt;
         } else if (component != nullptr) {
@@ -217,26 +242,26 @@ MaybeExtent GetShapeHelper::GetExtent(
   return std::nullopt;
 }
 
-MaybeExtent GetShapeHelper::GetExtent(const Subscript &subscript,
-    const Symbol &symbol, const Component *component, int dimension) {
+MaybeExtentExpr GetExtent(FoldingContext &context, const Subscript &subscript,
+    const Symbol &symbol, int dimension, const Component *component) {
   return std::visit(
       common::visitors{
-          [&](const Triplet &triplet) -> MaybeExtent {
-            MaybeExtent upper{triplet.upper()};
+          [&](const Triplet &triplet) -> MaybeExtentExpr {
+            MaybeExtentExpr upper{triplet.upper()};
             if (!upper.has_value()) {
-              upper = GetExtent(symbol, component, dimension);
+              upper = GetExtent(context, symbol, dimension, component);
             }
-            MaybeExtent lower{triplet.lower()};
+            MaybeExtentExpr lower{triplet.lower()};
             if (!lower.has_value()) {
-              lower = GetLowerBound(symbol, component, dimension);
+              lower = GetLowerBound(context, symbol, dimension, component);
             }
             return CountTrips(std::move(lower), std::move(upper),
-                MaybeExtent{triplet.stride()});
+                MaybeExtentExpr{triplet.stride()});
           },
-          [&](const IndirectSubscriptIntegerExpr &subs) -> MaybeExtent {
-            if (auto shape{GetShape(subs.value())}) {
-              if (shape->size() > 0) {
-                CHECK(shape->size() == 1);  // vector-valued subscript
+          [&](const IndirectSubscriptIntegerExpr &subs) -> MaybeExtentExpr {
+            if (auto shape{GetShape(context, subs.value())}) {
+              if (GetRank(*shape) > 0) {
+                CHECK(GetRank(*shape) == 1);  // vector-valued subscript
                 return std::move(shape->at(0));
               }
             }
@@ -246,6 +271,16 @@ MaybeExtent GetShapeHelper::GetExtent(const Subscript &subscript,
       subscript.u);
 }
 
+MaybeExtentExpr GetUpperBound(FoldingContext &context, MaybeExtentExpr &&lower,
+    MaybeExtentExpr &&extent) {
+  if (lower.has_value() && extent.has_value()) {
+    return Fold(
+        context, std::move(*extent) - std::move(*lower) + ExtentExpr{1});
+  } else {
+    return std::nullopt;
+  }
+}
+
 std::optional<Shape> GetShapeHelper::GetShape(
     const Symbol &symbol, const Component *component) {
   if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
@@ -255,7 +290,7 @@ std::optional<Shape> GetShapeHelper::GetShape(
       Shape result;
       int n{static_cast<int>(details->shape().size())};
       for (int dimension{0}; dimension < n; ++dimension) {
-        result.emplace_back(GetExtent(symbol, component, dimension++));
+        result.emplace_back(GetExtent(context_, symbol, dimension, component));
       }
       return result;
     }
@@ -300,7 +335,7 @@ std::optional<Shape> GetShapeHelper::GetShape(const ArrayRef &arrayRef) {
   int dimension{0};
   for (const Subscript &ss : arrayRef.subscript()) {
     if (ss.Rank() > 0) {
-      shape.emplace_back(GetExtent(ss, symbol, component, dimension));
+      shape.emplace_back(GetExtent(context_, ss, symbol, dimension, component));
     }
     ++dimension;
   }
@@ -319,7 +354,7 @@ std::optional<Shape> GetShapeHelper::GetShape(const CoarrayRef &coarrayRef) {
   int dimension{0};
   for (const Subscript &ss : coarrayRef.subscript()) {
     if (ss.Rank() > 0) {
-      shape.emplace_back(GetExtent(ss, symbol, component, dimension));
+      shape.emplace_back(GetExtent(context_, ss, symbol, dimension, component));
     }
     ++dimension;
   }
@@ -347,7 +382,7 @@ std::optional<Shape> GetShapeHelper::GetShape(const ComplexPart &part) {
 }
 
 std::optional<Shape> GetShapeHelper::GetShape(const ActualArgument &arg) {
-  if (const auto *expr{arg.GetExpr()}) {
+  if (const auto *expr{arg.UnwrapExpr()}) {
     return GetShape(*expr);
   } else {
     const Symbol *aType{arg.GetAssumedTypeDummy()};
@@ -379,13 +414,13 @@ std::optional<Shape> GetShapeHelper::GetShape(const ProcedureRef &call) {
                  std::get_if<SpecificIntrinsic>(&call.proc().u)}) {
     if (intrinsic->name == "shape" || intrinsic->name == "lbound" ||
         intrinsic->name == "ubound") {
-      const auto *expr{call.arguments().front().value().GetExpr()};
+      const auto *expr{call.arguments().front().value().UnwrapExpr()};
       CHECK(expr != nullptr);
-      return Shape{MaybeExtent{ExtentExpr{expr->Rank()}}};
+      return Shape{MaybeExtentExpr{ExtentExpr{expr->Rank()}}};
     } else if (intrinsic->name == "reshape") {
       if (call.arguments().size() >= 2 && call.arguments().at(1).has_value()) {
         // SHAPE(RESHAPE(array,shape)) -> shape
-        const auto *shapeExpr{call.arguments().at(1).value().GetExpr()};
+        const auto *shapeExpr{call.arguments().at(1).value().UnwrapExpr()};
         CHECK(shapeExpr != nullptr);
         Expr<SomeInteger> shape{std::get<Expr<SomeInteger>>(shapeExpr->u)};
         return AsShape(context_, ConvertToType<ExtentType>(std::move(shape)));
@@ -425,8 +460,8 @@ std::optional<Shape> GetShapeHelper::GetShape(const NullPointer &) {
 bool CheckConformance(parser::ContextualMessages &messages, const Shape &left,
     const Shape &right, const char *leftDesc, const char *rightDesc) {
   if (!left.empty() && !right.empty()) {
-    int n{static_cast<int>(left.size())};
-    int rn{static_cast<int>(right.size())};
+    int n{GetRank(left)};
+    int rn{GetRank(right)};
     if (n != rn) {
       messages.Say("Rank of %s is %d, but %s has rank %d"_err_en_US, leftDesc,
           n, rightDesc, rn);
index a7fa80a..9a49f82 100644 (file)
@@ -35,27 +35,46 @@ class FoldingContext;
 
 using ExtentType = SubscriptInteger;
 using ExtentExpr = Expr<ExtentType>;
-using MaybeExtent = std::optional<ExtentExpr>;
-using Shape = std::vector<MaybeExtent>;
+using MaybeExtentExpr = std::optional<ExtentExpr>;
+using Shape = std::vector<MaybeExtentExpr>;
+
+bool IsImpliedShape(const Symbol &);
+bool IsExplicitShape(const Symbol &);
 
 // Conversions between various representations of shapes.
-Shape AsShape(const Constant<ExtentType> &arrayConstant);
-std::optional<Shape> AsShape(FoldingContext &, ExtentExpr &&arrayExpr);
+Shape AsShape(const Constant<ExtentType> &);
+std::optional<Shape> AsShape(FoldingContext &, ExtentExpr &&);
+
 std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &);
+
 std::optional<Constant<ExtentType>> AsConstantShape(const Shape &);
+Constant<ExtentType> AsConstantShape(const ConstantSubscripts &);
+
 ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &);
 std::optional<ConstantSubscripts> AsConstantExtents(const Shape &);
 
+inline int GetRank(const Shape &s) { return static_cast<int>(s.size()); }
+
+// The dimension here is zero-based, unlike DIM= arguments to many intrinsics.
+MaybeExtentExpr GetLowerBound(FoldingContext &, const Symbol &, int dimension,
+    const Component * = nullptr);
+MaybeExtentExpr GetExtent(FoldingContext &, const Symbol &, int dimension,
+    const Component * = nullptr);
+MaybeExtentExpr GetExtent(FoldingContext &, const Subscript &, const Symbol &,
+    int dimension, const Component * = nullptr);
+MaybeExtentExpr GetUpperBound(
+    FoldingContext &, MaybeExtentExpr &&lower, MaybeExtentExpr &&extent);
+
 // Compute an element count for a triplet or trip count for a DO.
 ExtentExpr CountTrips(
     ExtentExpr &&lower, ExtentExpr &&upper, ExtentExpr &&stride);
 ExtentExpr CountTrips(
     const ExtentExpr &lower, const ExtentExpr &upper, const ExtentExpr &stride);
-MaybeExtent CountTrips(
-    MaybeExtent &&lower, MaybeExtent &&upper, MaybeExtent &&stride);
+MaybeExtentExpr CountTrips(
+    MaybeExtentExpr &&lower, MaybeExtentExpr &&upper, MaybeExtentExpr &&stride);
 
 // Computes SIZE() == PRODUCT(shape)
-MaybeExtent GetSize(Shape &&);
+MaybeExtentExpr GetSize(Shape &&);
 
 // Utility predicate: does an expression reference any implied DO index?
 bool ContainsAnyImpliedDoIndex(const ExtentExpr &);
@@ -127,7 +146,7 @@ public:
 
   template<typename T>
   std::optional<Shape> GetShape(const ArrayConstructor<T> &aconst) {
-    return Shape{GetExtent(aconst)};
+    return Shape{GetArrayConstructorExtent(aconst)};
   }
 
   template<typename... A>
@@ -151,10 +170,11 @@ public:
 
 private:
   template<typename T>
-  MaybeExtent GetExtent(const ArrayConstructorValue<T> &value) {
+  MaybeExtentExpr GetArrayConstructorValueExtent(
+      const ArrayConstructorValue<T> &value) {
     return std::visit(
         common::visitors{
-            [&](const Expr<T> &x) -> MaybeExtent {
+            [&](const Expr<T> &x) -> MaybeExtentExpr {
               if (std::optional<Shape> xShape{GetShape(x)}) {
                 // Array values in array constructors get linearized.
                 return GetSize(std::move(*xShape));
@@ -162,13 +182,13 @@ private:
                 return std::nullopt;
               }
             },
-            [&](const ImpliedDo<T> &ido) -> MaybeExtent {
+            [&](const ImpliedDo<T> &ido) -> MaybeExtentExpr {
               // Don't be heroic and try to figure out triangular implied DO
               // nests.
               if (!ContainsAnyImpliedDoIndex(ido.lower()) &&
                   !ContainsAnyImpliedDoIndex(ido.upper()) &&
                   !ContainsAnyImpliedDoIndex(ido.stride())) {
-                if (auto nValues{GetExtent(ido.values())}) {
+                if (auto nValues{GetArrayConstructorExtent(ido.values())}) {
                   return std::move(*nValues) *
                       CountTrips(ido.lower(), ido.upper(), ido.stride());
                 }
@@ -180,10 +200,11 @@ private:
   }
 
   template<typename T>
-  MaybeExtent GetExtent(const ArrayConstructorValues<T> &values) {
+  MaybeExtentExpr GetArrayConstructorExtent(
+      const ArrayConstructorValues<T> &values) {
     ExtentExpr result{0};
     for (const auto &value : values) {
-      if (MaybeExtent n{GetExtent(value)}) {
+      if (MaybeExtentExpr n{GetArrayConstructorValueExtent(value)}) {
         result = std::move(result) + std::move(*n);
       } else {
         return std::nullopt;
@@ -192,12 +213,6 @@ private:
     return result;
   }
 
-  // The dimension here is zero-based, unlike DIM= intrinsic arguments.
-  MaybeExtent GetLowerBound(const Symbol &, const Component *, int dimension);
-  MaybeExtent GetExtent(const Symbol &, const Component *, int dimension);
-  MaybeExtent GetExtent(
-      const Subscript &, const Symbol &, const Component *, int dimension);
-
   FoldingContext &context_;
 };
 
index f88f306..bdedebc 100644 (file)
@@ -614,7 +614,7 @@ bool IsAssumedRank(const semantics::Symbol &symbol) {
 }
 
 bool IsAssumedRank(const ActualArgument &arg) {
-  if (const auto *expr{arg.GetExpr()}) {
+  if (const auto *expr{arg.UnwrapExpr()}) {
     return IsAssumedRank(*expr);
   } else {
     const semantics::Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()};
index cf10aa2..9fcace3 100644 (file)
@@ -144,22 +144,26 @@ template<typename A> constexpr bool IsNumericCategoryExpr() {
 }
 
 // Specializing extractor.  If an Expr wraps some type of object, perhaps
-// in several layers, return a pointer to it; otherwise null.
+// in several layers, return a pointer to it; otherwise null.  Also works
+// with ActualArgument.
 template<typename A, typename B>
 auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
   using Ty = std::decay_t<B>;
   if constexpr (std::is_same_v<A, Ty>) {
     return &x;
-  } else if constexpr (common::HasMember<Ty, TypelessExpression>) {
-    return nullptr;
-  } else if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>>) {
-    return common::Unwrap<A>(x.u);
-  } else if constexpr (std::is_same_v<Ty, Expr<SomeType>> ||
-      std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
+  } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
+    if (auto *expr{x.UnwrapExpr()}) {
+      return UnwrapExpr<A>(*expr);
+    }
+  } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
     return std::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
-  } else {
-    return nullptr;
+  } else if constexpr (!common::HasMember<A, TypelessExpression>) {
+    if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> ||
+        std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
+      return std::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
+    }
   }
+  return nullptr;
 }
 
 template<typename A, typename B>
index 4578385..db6c6f9 100644 (file)
@@ -16,6 +16,7 @@
 #include "expression.h"
 #include "fold.h"
 #include "../common/idioms.h"
+#include "../common/template.h"
 #include "../semantics/scope.h"
 #include "../semantics/symbol.h"
 #include "../semantics/tools.h"
@@ -219,4 +220,79 @@ bool SomeKind<TypeCategory::Derived>::operator==(
     const SomeKind<TypeCategory::Derived> &that) const {
   return PointeeComparison(derivedTypeSpec_, that.derivedTypeSpec_);
 }
+
+static constexpr double LogBaseTenOfTwo{0.301029995664};
+
+class SelectedIntKindVisitor {
+public:
+  explicit SelectedIntKindVisitor(std::int64_t p) : precision_{p} {}
+  using Result = std::optional<int>;
+  using Types = IntegerTypes;
+  template<typename T> Result Test() const {
+    if ((Scalar<T>::bits - 1) * LogBaseTenOfTwo > precision_) {
+      return T::kind;
+    } else {
+      return std::nullopt;
+    }
+  }
+
+private:
+  std::int64_t precision_;
+};
+
+int SelectedIntKind(std::int64_t precision) {
+  if (auto kind{common::SearchTypes(SelectedIntKindVisitor{precision})}) {
+    return *kind;
+  } else {
+    return -1;
+  }
+}
+
+class SelectedRealKindVisitor {
+public:
+  explicit SelectedRealKindVisitor(std::int64_t p, std::int64_t r)
+    : precision_{p}, range_{r} {}
+  using Result = std::optional<int>;
+  using Types = RealTypes;
+  template<typename T> Result Test() const {
+    if ((Scalar<T>::precision - 1) * LogBaseTenOfTwo > precision_ &&
+        (Scalar<T>::exponentBias - 1) * LogBaseTenOfTwo > range_) {
+      return {T::kind};
+    } else {
+      return std::nullopt;
+    }
+  }
+
+private:
+  std::int64_t precision_, range_;
+};
+
+int SelectedRealKind(
+    std::int64_t precision, std::int64_t range, std::int64_t radix) {
+  if (radix != 2) {
+    return -5;
+  }
+  if (auto kind{
+          common::SearchTypes(SelectedRealKindVisitor{precision, range})}) {
+    return *kind;
+  }
+  // No kind has both sufficient precision and sufficient range.
+  // The negative return value encodes whether any kinds exist that
+  // could satisfy either constraint independently.
+  bool pOK{common::SearchTypes(SelectedRealKindVisitor{precision, 0})};
+  bool rOK{common::SearchTypes(SelectedRealKindVisitor{0, range})};
+  if (pOK) {
+    if (rOK) {
+      return -4;
+    } else {
+      return -2;
+    }
+  } else {
+    if (rOK) {
+      return -1;
+    } else {
+      return -3;
+    }
+  }
+}
 }
index c46a7c8..77bac30 100644 (file)
@@ -60,6 +60,7 @@ using LargestReal = Type<TypeCategory::Real, 16>;
 // A predicate that is true when a kind value is a kind that could possibly
 // be supported for an intrinsic type category on some target instruction
 // set architecture.
+// TODO: specialize for the actual target architecture
 static constexpr bool IsValidKindOfIntrinsicType(
     TypeCategory category, std::int64_t kind) {
   switch (category) {
@@ -410,6 +411,10 @@ template<typename CONST> struct TypeOfHelper {
 
 template<typename CONST> using TypeOf = typename TypeOfHelper<CONST>::type;
 
+int SelectedIntKind(std::int64_t precision = 0);
+int SelectedRealKind(
+    std::int64_t precision = 0, std::int64_t range = 0, std::int64_t radix = 2);
+
 // For generating "[extern] template class", &c. boilerplate
 #define EXPAND_FOR_EACH_INTEGER_KIND(M, P, S) \
   M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8) M(P, S, 16)
index c5dd39a..d90e455 100644 (file)
@@ -30,7 +30,7 @@
 #include <optional>
 #include <set>
 
-// #define DUMP_ON_FAILURE 1
+#define DUMP_ON_FAILURE 1  // pmk
 // #define CRASH_ON_FAILURE
 #if DUMP_ON_FAILURE
 #include "../parser/dump-parse-tree.h"
@@ -170,6 +170,7 @@ MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
   if (subscripts != symbolRank) {
     Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US,
         symbolRank, symbol.name(), subscripts);
+    return std::nullopt;
   } else if (subscripts == 0) {
     // nothing to check
   } else if (Component * component{std::get_if<Component>(&ref.base())}) {
@@ -183,6 +184,7 @@ MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
         Say("Subscripts of component '%s' of rank-%d derived type "
             "array have rank %d but must all be scalar"_err_en_US,
             symbol.name(), baseRank, subscriptRank);
+        return std::nullopt;
       }
     }
   } else if (const auto *details{
@@ -193,6 +195,7 @@ MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
         Say("Assumed-size array '%s' must have explicit final "
             "subscript upper bound value"_err_en_US,
             symbol.name());
+        return std::nullopt;
       }
     }
   }
@@ -612,8 +615,6 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
       // A bare reference to a derived type parameter (within a parameterized
       // derived type definition)
       return AsMaybeExpr(MakeBareTypeParamInquiry(&ultimate));
-    } else if (MaybeExpr result{Designate(DataRef{ultimate})}) {
-      return result;
     } else {
       return Designate(DataRef{*n.symbol});
     }
index 50980e6..dbe9d9d 100644 (file)
@@ -30,14 +30,12 @@ namespace Fortran::semantics {
 
 using namespace parser::literals;
 
-// The extension used for module files.
-static constexpr auto extension{".mod"};
 // The initial characters of a file that identify it as a .mod file.
 static constexpr auto magic{"!mod$ v1 sum:"};
 
 static const SourceName *GetSubmoduleParent(const parser::Program &);
-static std::string ModFilePath(
-    const std::string &, const SourceName &, const std::string &);
+static std::string ModFilePath(const std::string &dir, const SourceName &,
+    const std::string &ancestor, const std::string &suffix);
 static std::vector<const Symbol *> CollectSymbols(const Scope &);
 static void PutEntity(std::ostream &, const Symbol &);
 static void PutObjectEntity(std::ostream &, const Symbol &);
@@ -126,8 +124,8 @@ void ModFileWriter::WriteOne(const Scope &scope) {
 void ModFileWriter::Write(const Symbol &symbol) {
   auto *ancestor{symbol.get<ModuleDetails>().ancestor()};
   auto ancestorName{ancestor ? ancestor->name().ToString() : ""s};
-  auto path{
-      ModFilePath(context_.moduleDirectory(), symbol.name(), ancestorName)};
+  auto path{ModFilePath(context_.moduleDirectory(), symbol.name(), ancestorName,
+      context_.moduleFileSuffix())};
   PutSymbols(*symbol.scope());
   if (!WriteFile(path, GetAsString(symbol))) {
     context_.Say(symbol.name(), "Error writing %s: %s"_err_en_US, path,
@@ -723,7 +721,8 @@ std::optional<std::string> ModFileReader::FindModFile(
     const SourceName &name, const std::string &ancestor) {
   parser::Messages attachments;
   for (auto &dir : context_.searchDirectories()) {
-    std::string path{ModFilePath(dir, name, ancestor)};
+    std::string path{
+        ModFilePath(dir, name, ancestor, context_.moduleFileSuffix())};
     std::ifstream ifstream{path};
     if (!ifstream.good()) {
       attachments.Say(name, "%s: %s"_en_US, path, std::strerror(errno));
@@ -764,7 +763,7 @@ static const SourceName *GetSubmoduleParent(const parser::Program &program) {
 
 // Construct the path to a module file. ancestorName not empty means submodule.
 static std::string ModFilePath(const std::string &dir, const SourceName &name,
-    const std::string &ancestorName) {
+    const std::string &ancestorName, const std::string &suffix) {
   std::stringstream path;
   if (dir != "."s) {
     path << dir << '/';
@@ -772,7 +771,7 @@ static std::string ModFilePath(const std::string &dir, const SourceName &name,
   if (!ancestorName.empty()) {
     PutLower(path, ancestorName) << '-';
   }
-  PutLower(path, name.ToString()) << extension;
+  PutLower(path, name.ToString()) << suffix;
   return path.str();
 }
 
index 68ba1cd..fbe2881 100644 (file)
@@ -1565,9 +1565,10 @@ void ScopeHandler::PushScope(Scope &scope) {
   }
 }
 void ScopeHandler::PopScope() {
+  // Entities that are not yet classified as objects or procedures are now
+  // assumed to be objects.
   for (auto &pair : currScope()) {
-    auto &symbol{*pair.second};
-    ConvertToObjectEntity(symbol);  // if not a proc by now, it is an object
+    ConvertToObjectEntity(*pair.second);
   }
   SetScope(currScope_->parent());
 }
@@ -4252,10 +4253,13 @@ const parser::Name *DeclarationVisitor::ResolveVariable(
 // If implicit types are allowed, ensure name is in the symbol table.
 // Otherwise, report an error if it hasn't been declared.
 const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
-  if (FindSymbol(name)) {
+  if (Symbol * symbol{FindSymbol(name)}) {
     if (CheckUseError(name)) {
       return nullptr;  // reported an error
     }
+    if (symbol->IsDummy()) {
+      ApplyImplicitRules(*symbol);
+    }
     return &name;
   }
   if (isImplicitNoneType()) {
@@ -4643,7 +4647,20 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
   for (auto &child : node.children()) {
     ResolveSpecificationParts(child);
   }
-  PopScope();
+  // Subtlety: PopScope() is not called here because we want to defer
+  // conversions of uncategorized entities into objects until after
+  // we have traversed the executable part of the subprogram.
+  // Function results, however, are converted now so that they can
+  // be used in executable parts.
+  if (Symbol * symbol{currScope().symbol()}) {
+    if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
+      if (details->isFunction()) {
+        Symbol &result{const_cast<Symbol &>(details->result())};
+        ConvertToObjectEntity(result);
+      }
+    }
+  }
+  SetScope(currScope().parent());
 }
 
 // Add SubprogramNameDetails symbols for contained subprograms
@@ -4686,6 +4703,7 @@ void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) {
   if (const auto *exec{node.exec()}) {
     Walk(*exec);
   }
+  PopScope();  // converts unclassified entities into objects
   for (const auto &child : node.children()) {
     ResolveExecutionParts(child);
   }
index b4ddf47..fe2232e 100644 (file)
@@ -53,6 +53,7 @@ public:
     return searchDirectories_;
   }
   const std::string &moduleDirectory() const { return moduleDirectory_; }
+  const std::string &moduleFileSuffix() const { return moduleFileSuffix_; }
   bool warnOnNonstandardUsage() const { return warnOnNonstandardUsage_; }
   bool warningsAreErrors() const { return warningsAreErrors_; }
   const evaluate::IntrinsicProcTable &intrinsics() const { return intrinsics_; }
@@ -72,6 +73,10 @@ public:
     moduleDirectory_ = x;
     return *this;
   }
+  SemanticsContext &set_moduleFileSuffix(const std::string &x) {
+    moduleFileSuffix_ = x;
+    return *this;
+  }
   SemanticsContext &set_warnOnNonstandardUsage(bool x) {
     warnOnNonstandardUsage_ = x;
     return *this;
@@ -113,6 +118,7 @@ private:
   const parser::CharBlock *location_{nullptr};
   std::vector<std::string> searchDirectories_;
   std::string moduleDirectory_{"."s};
+  std::string moduleFileSuffix_{".mod"};
   bool warnOnNonstandardUsage_{false};
   bool warningsAreErrors_{false};
   const evaluate::IntrinsicProcTable intrinsics_;
index f9d3206..bd5f61e 100644 (file)
@@ -148,6 +148,8 @@ public:
   MaybeExpr &init() { return init_; }
   const MaybeExpr &init() const { return init_; }
   void set_init(MaybeExpr &&expr) { init_ = std::move(expr); }
+  bool initWasValidated() const { return initWasValidated_; }
+  void set_initWasValidated(bool yes = true) { initWasValidated_ = yes; }
   ArraySpec &shape() { return shape_; }
   const ArraySpec &shape() const { return shape_; }
   ArraySpec &coshape() { return coshape_; }
@@ -179,6 +181,7 @@ public:
 
 private:
   MaybeExpr init_;
+  bool initWasValidated_{false};
   ArraySpec shape_;
   ArraySpec coshape_;
   const Symbol *commonBlock_{nullptr};  // common block this object is in
index ef6d5d7..7f38da2 100644 (file)
@@ -175,6 +175,7 @@ bool IsProcedure(const Symbol &symbol) {
           [](const SubprogramNameDetails &) { return true; },
           [](const ProcEntityDetails &) { return true; },
           [](const GenericDetails &) { return true; },
+          [](const ProcBindingDetails &) { return true; },
           [](const UseDetails &x) { return IsProcedure(x.symbol()); },
           [](const auto &) { return false; },
       },
index 108efc6..01bf061 100644 (file)
@@ -173,6 +173,7 @@ set(MODFILE_TESTS
   modfile23.f90
   modfile24.f90
   modfile25.f90
+  modfile26.f90
 )
 
 set(LABEL_TESTS
diff --git a/flang/test/semantics/modfile26.f90 b/flang/test/semantics/modfile26.f90
new file mode 100644 (file)
index 0000000..9bf5196
--- /dev/null
@@ -0,0 +1,62 @@
+! Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+!
+! Licensed under the Apache License, Version 2.0 (the "License");
+! you may not use this file except in compliance with the License.
+! You may obtain a copy of the License at
+!
+!     http://www.apache.org/licenses/LICENSE-2.0
+!
+! Unless required by applicable law or agreed to in writing, software
+! distributed under the License is distributed on an "AS IS" BASIS,
+! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+! See the License for the specific language governing permissions and
+! limitations under the License.
+
+! SELECTED_INT_KIND and SELECTED_REAL_KIND
+
+module m1
+  ! INTEGER(KIND=1)  handles  0 <= P < 3
+  ! INTEGER(KIND=2)  handles  3 <= P < 5
+  ! INTEGER(KIND=4)  handles  5 <= P < 10
+  ! INTEGER(KIND=8)  handles 10 <= P < 19
+  ! INTEGER(KIND=16) handles 19 <= P < 38
+  integer, parameter :: intpvals(:) = [0, 2, 3, 4, 5, 9, 10, 18, 19, 38, 39]
+  integer, parameter :: intpkinds(:) = &
+    [(selected_int_kind(intpvals(j)),j=1,size(intpvals))]
+  logical, parameter :: ipcheck = &
+    all([1, 1, 2, 2, 4, 4, 8, 8, 16, 16, -1] == intpkinds)
+  ! REAL(KIND=2)  handles  0 <= P < 4  (if available)
+  ! REAL(KIND=4)  handles  4 <= P < 7
+  ! REAL(KIND=8)  handles  7 <= P < 16
+  ! REAL(KIND=10) handles 16 <= P < 19 (if available; ifort is KIND=16)
+  ! REAL(KIND=16) handles 19 <= P < 34 (32 with Power double/double)
+  integer, parameter :: realpvals(:) = [0, 3, 4, 6, 7, 15, 16, 18, 19, 33, 34]
+  integer, parameter :: realpkinds(:) = &
+    [(selected_real_kind(realpvals(j),0),j=1,size(realpvals))]
+  logical, parameter :: realpcheck = &
+    all([2, 2, 4, 4, 8, 8, 10, 10, 16, 16, -1] == realpkinds)
+  ! REAL(KIND=2)  handles  0 <= R < 5 (if available)
+  ! REAL(KIND=3)  handles  5 <= R < 38 (if available, same range as KIND=4)
+  ! 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)
+  integer, parameter :: realrvals(:) = &
+    [0, 4, 5, 37, 38, 307, 308, 4931, 4932, 9863, 9864]
+  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)
+end module m1
+!Expect: m1.mod
+!module m1
+!integer(4),parameter::intpvals(1_8:)=[Integer(4)::0_4,2_4,3_4,4_4,5_4,9_4,10_4,18_4,19_4,38_4,39_4]
+!integer(4),parameter::intpkinds(1_8:)=[Integer(4)::1_4,1_4,2_4,2_4,4_4,4_4,8_4,8_4,16_4,16_4,-1_4]
+!logical(4),parameter::ipcheck=.true._4
+!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::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]
+!logical(4),parameter::realrcheck=.true._4
+!end
index 20713f7..d419bc1 100644 (file)
@@ -86,6 +86,7 @@ struct DriverOptions {
   std::string outputPath;  // -o path
   std::vector<std::string> searchDirectories{"."s};  // -I dir
   std::string moduleDirectory{"."s};  // -module dir
+  std::string moduleFileSuffix{".mod"};  // -moduleSuffix suff
   bool forcedForm{false};  // -Mfixed or -Mfree appeared
   bool warnOnNonstandardUsage{false};  // -Mstandard
   bool warningsAreErrors{false};  // -Werror
@@ -452,6 +453,12 @@ int main(int argc, char *const argv[]) {
       defaultKinds.set_defaultIntegerKind(8);
     } else if (arg == "-fno-large-arrays") {
       defaultKinds.set_subscriptIntegerKind(4);
+    } else if (arg == "-module") {
+      driver.moduleDirectory = args.front();
+      args.pop_front();
+    } else if (arg == "-module-suffix") {
+      driver.moduleFileSuffix = args.front();
+      args.pop_front();
     } else if (arg == "-help" || arg == "--help" || arg == "-?") {
       std::cerr
           << "f18 options:\n"
@@ -465,6 +472,7 @@ int main(int argc, char *const argv[]) {
           << "  -Werror              treat warnings as errors\n"
           << "  -ed                  enable fixed form D lines\n"
           << "  -E                   prescan & preprocess only\n"
+          << "  -module dir          module output directory (default .)\n"
           << "  -fparse-only         parse only, no output except messages\n"
           << "  -funparse            parse & reformat only, no code "
              "generation\n"
@@ -495,10 +503,6 @@ int main(int argc, char *const argv[]) {
         args.pop_front();
       } else if (arg.substr(0, 2) == "-I") {
         driver.searchDirectories.push_back(arg.substr(2));
-      } else if (arg == "-module") {
-        driver.moduleDirectory = args.front();
-        driver.pgf90Args.push_back(driver.moduleDirectory);
-        args.pop_front();
       } else if (arg == "-Mx,125,4") {  // PGI "all Kanji" mode
         options.encoding = Fortran::parser::Encoding::EUC_JP;
       }
@@ -513,14 +517,14 @@ int main(int argc, char *const argv[]) {
           Fortran::parser::LanguageFeature::BackslashEscapes)) {
     driver.pgf90Args.push_back("-Mbackslash");
   }
-  if (options.features.IsEnabled(
-          Fortran::parser::LanguageFeature::OpenMP)) {
+  if (options.features.IsEnabled(Fortran::parser::LanguageFeature::OpenMP)) {
     driver.pgf90Args.push_back("-mp");
   }
 
   Fortran::semantics::SemanticsContext semanticsContext{
       defaultKinds, options.features};
   semanticsContext.set_moduleDirectory(driver.moduleDirectory)
+      .set_moduleFileSuffix(driver.moduleFileSuffix)
       .set_searchDirectories(driver.searchDirectories)
       .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage)
       .set_warningsAreErrors(driver.warningsAreErrors);