[flang] Run expression semantic analysis with rest of semantics.
authorpeter klausler <pklausler@nvidia.com>
Wed, 23 Jan 2019 00:30:32 +0000 (16:30 -0800)
committerpeter klausler <pklausler@nvidia.com>
Thu, 31 Jan 2019 17:59:28 +0000 (09:59 -0800)
checkpoint array constructor semantics work

checkpoint

array constructors of lengthless intrinsic types

checkpoint

Correct ambiguous substring refs misparsed as array elements

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

23 files changed:
flang/lib/common/fortran.h
flang/lib/common/template.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/fold.cc
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/evaluate/type.cc
flang/lib/evaluate/type.h
flang/lib/evaluate/variable.cc
flang/lib/evaluate/variable.h
flang/lib/parser/parse-tree.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/scope.h
flang/lib/semantics/semantics.cc
flang/lib/semantics/semantics.h
flang/lib/semantics/type.cc
flang/lib/semantics/type.h
flang/test/semantics/resolve30.f90
flang/test/semantics/resolve35.f90
flang/tools/f18/f18.cc

index 1bc9679..5dc8d77 100644 (file)
 
 namespace Fortran::common {
 
-// Fortran has five kinds of intrinsic data, and the derived types.
+// Fortran has five kinds of intrinsic data types, plus the derived types.
 ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived)
 
+constexpr bool IsNumericTypeCategory(TypeCategory category) {
+  return category == TypeCategory::Integer || category == TypeCategory::Real ||
+      category == TypeCategory::Complex;
+}
+
 // Kinds of IMPORT statements. Default means IMPORT or IMPORT :: names.
 ENUM_CLASS(ImportKind, Default, Only, None, All)
 
index 7f3f4d6..aaa7721 100644 (file)
@@ -29,8 +29,7 @@ namespace Fortran::common {
 // SearchTypeList<PREDICATE, TYPES...> scans a list of types.  The zero-based
 // index of the first type T in the list for which PREDICATE<T>::value() is
 // true is returned, or -1 if the predicate is false for every type in the list.
-// This is a compile-time operation; see SearchDynamicTypes below for a
-// run-time form.
+// This is a compile-time operation; see SearchTypes below for a run-time form.
 template<int N, template<typename> class PREDICATE, typename TUPLE>
 struct SearchTypeListHelper {
   static constexpr int value() {
@@ -245,28 +244,29 @@ std::optional<R> MapOptional(
 // Given a VISITOR class of the general form
 //   struct VISITOR {
 //     using Result = ...;
-//     static constexpr std::size_t Types{...};
-//     template<std::size_t J> static Result Test();
+//     using Types = std::tuple<...>;
+//     template<typename T> Result Test() { ... }
 //   };
-// SearchDynamicTypes will traverse the indices 0 .. (Types-1) and
-// invoke VISITOR::Test<J>() until it returns a value that casts
-// to true.  If no invocation of Test succeeds, it returns a
-// default-constructed Result.
+// 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{}.
 template<std::size_t J, typename VISITOR>
-typename VISITOR::Result SearchDynamicTypesHelper(VISITOR &&visitor) {
-  if constexpr (J < VISITOR::Types) {
-    if (auto result{visitor.template Test<J>()}) {
+typename VISITOR::Result SearchTypesHelper(VISITOR &&visitor) {
+  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 SearchDynamicTypesHelper<J + 1, VISITOR>(std::move(visitor));
+    return SearchTypesHelper<J + 1, VISITOR>(std::move(visitor));
   } else {
     return typename VISITOR::Result{};
   }
 }
 
 template<typename VISITOR>
-typename VISITOR::Result SearchDynamicTypes(VISITOR &&visitor) {
-  return SearchDynamicTypesHelper<0, VISITOR>(std::move(visitor));
+typename VISITOR::Result SearchTypes(VISITOR &&visitor) {
+  return SearchTypesHelper<0, VISITOR>(std::move(visitor));
 }
 }
 #endif  // FORTRAN_COMMON_TEMPLATE_H_
index ef78008..f4e4ff9 100644 (file)
@@ -21,6 +21,7 @@
 #include "../parser/characters.h"
 #include "../parser/message.h"
 #include <ostream>
+#include <sstream>
 #include <string>
 #include <type_traits>
 
@@ -107,15 +108,15 @@ template<typename T>
 std::ostream &Emit(std::ostream &o, const CopyableIndirection<Expr<T>> &expr) {
   return expr->AsFortran(o);
 }
+
 template<typename T>
 std::ostream &Emit(std::ostream &, const ArrayConstructorValues<T> &);
 
-template<typename ITEM, typename INT>
-std::ostream &Emit(std::ostream &o, const ImpliedDo<ITEM, INT> &implDo) {
+template<typename T>
+std::ostream &Emit(std::ostream &o, const ImpliedDo<T> &implDo) {
   o << '(';
   Emit(o, *implDo.values);
-  o << ',' << INT::AsFortran() << "::";
-  o << implDo.controlVariableName.ToString();
+  o << ',' << ImpliedDoIndex::Result::AsFortran() << "::";
   o << '=';
   implDo.lower->AsFortran(o) << ',';
   implDo.upper->AsFortran(o) << ',';
@@ -136,8 +137,18 @@ std::ostream &Emit(std::ostream &o, const ArrayConstructorValues<T> &values) {
 
 template<typename T>
 std::ostream &ArrayConstructor<T>::AsFortran(std::ostream &o) const {
-  o << '[' << result.AsFortran() << "::";
-  Emit(o, *this);
+  o << '[' << GetType().AsFortran() << "::";
+  Emit(o, values);
+  return o << ']';
+}
+
+template<int KIND>
+std::ostream &ArrayConstructor<Type<TypeCategory::Character, KIND>>::AsFortran(
+    std::ostream &o) const {
+  std::stringstream len;
+  length->AsFortran(len);
+  o << '[' << GetType().AsFortran(len.str()) << "::";
+  Emit(o, values);
   return o << ']';
 }
 
@@ -149,17 +160,13 @@ std::ostream &ExpressionBase<RESULT>::AsFortran(std::ostream &o) const {
             o << "z'" << x.Hexadecimal() << "'";
           },
           [&](const CopyableIndirection<Substring> &s) { s->AsFortran(o); },
+          [&](const ImpliedDoIndex &i) { o << i.name.ToString(); },
           [&](const auto &x) { x.AsFortran(o); },
       },
       derived().u);
   return o;
 }
 
-template<typename T> Expr<SubscriptInteger> ArrayConstructor<T>::LEN() const {
-  // TODO pmk: extract from type spec in array constructor
-  return AsExpr(Constant<SubscriptInteger>{0});  // TODO placeholder
-}
-
 template<int KIND>
 Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
   return std::visit(
@@ -184,11 +191,6 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
 
 Expr<SomeType>::~Expr() {}
 
-template<typename T> DynamicType ArrayConstructor<T>::GetType() const {
-  // TODO: pmk: parameterized derived types, CHARACTER length
-  return result.GetType();
-}
-
 #if defined(__APPLE__) && defined(__GNUC__)
 template<typename A>
 typename ExpressionBase<A>::Derived &ExpressionBase<A>::derived() {
@@ -231,10 +233,17 @@ template<typename A> int ExpressionBase<A>::Rank() const {
       derived().u);
 }
 
+template<int KIND>
+ArrayConstructor<Type<TypeCategory::Character, KIND>>::~ArrayConstructor() {}
+
 // Equality testing for classes without EVALUATE_UNION_CLASS_BOILERPLATE()
 
-template<typename V, typename O>
-bool ImpliedDo<V, O>::operator==(const ImpliedDo<V, O> &that) const {
+bool ImpliedDoIndex::operator==(const ImpliedDoIndex &that) const {
+  return name == that.name;
+}
+
+template<typename T>
+bool ImpliedDo<T>::operator==(const ImpliedDo<T> &that) const {
   return controlVariableName == that.controlVariableName &&
       lower == that.lower && upper == that.upper && stride == that.stride &&
       values == that.values;
@@ -248,8 +257,13 @@ bool ArrayConstructorValues<R>::operator==(
 
 template<typename R>
 bool ArrayConstructor<R>::operator==(const ArrayConstructor<R> &that) const {
-  return *static_cast<const ArrayConstructorValues<R> *>(this) == that &&
-      result == that.result && typeParameterValues == that.typeParameterValues;
+  return type == that.type && values == that.values;
+}
+
+template<int KIND>
+bool ArrayConstructor<Type<TypeCategory::Character, KIND>>::operator==(
+    const ArrayConstructor<Type<TypeCategory::Character, KIND>> &that) const {
+  return length == that.length && values == that.values;
 }
 
 bool GenericExprWrapper::operator==(const GenericExprWrapper &that) const {
index 59e1566..a4071ac 100644 (file)
@@ -31,6 +31,7 @@
 #include "../lib/parser/char-block.h"
 #include "../lib/parser/message.h"
 #include <algorithm>
+#include <list>
 #include <ostream>
 #include <tuple>
 #include <type_traits>
@@ -58,7 +59,7 @@ using common::RelationalOperator;
 // Everything that can appear in, or as, a valid Fortran expression must be
 // represented with an instance of some class containing a Result typedef that
 // maps to some instantiation of Type<CATEGORY, KIND>, SomeKind<CATEGORY>,
-// or SomeType.
+// or SomeType.  (Exception: BOZ literal constants in generic Expr<SomeType>.)
 template<typename A> using ResultType = typename std::decay_t<A>::Result;
 
 // Common Expr<> behaviors: every Expr<T> derives from ExpressionBase<T>.
@@ -212,7 +213,8 @@ private:
 // dynamic kind.
 template<typename TO, TypeCategory FROMCAT>
 struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
-  // Fortran doesn't have conversions between kinds of CHARACTER.
+  // Fortran doesn't have conversions between kinds of CHARACTER apart from
+  // assignments, and in those the data must be convertible to/from 7-bit ASCII.
   // Conversions between kinds of COMPLEX are represented piecewise.
   static_assert(((TO::category == TypeCategory::Integer ||
                      TO::category == TypeCategory::Real) &&
@@ -392,47 +394,67 @@ struct LogicalOperation
 
 template<typename RESULT> struct ArrayConstructorValues;
 
-template<typename VALUES, typename OPERAND> struct ImpliedDo {
-  using Values = VALUES;
-  using Operand = OPERAND;
-  using Result = ResultType<Values>;
-  static_assert(Operand::category == TypeCategory::Integer);
+struct ImpliedDoIndex {
+  using Result = SubscriptInteger;
+  bool operator==(const ImpliedDoIndex &) const;
+  static constexpr int Rank() { return 0; }
+  parser::CharBlock name;  // nested implied DOs must use distinct names
+};
+
+template<typename RESULT> struct ImpliedDo {
+  using Result = RESULT;
   bool operator==(const ImpliedDo &) const;
   parser::CharBlock controlVariableName;
-  CopyableIndirection<Expr<Operand>> lower, upper, stride;
-  CopyableIndirection<Values> values;
+  CopyableIndirection<Expr<ResultType<ImpliedDoIndex>>> lower, upper, stride;
+  CopyableIndirection<ArrayConstructorValues<RESULT>> values;
 };
 
 template<typename RESULT> struct ArrayConstructorValue {
   using Result = RESULT;
   EVALUATE_UNION_CLASS_BOILERPLATE(ArrayConstructorValue)
-  template<typename INT>
-  using ImpliedDo = ImpliedDo<ArrayConstructorValues<Result>, INT>;
-  common::CombineVariants<std::variant<CopyableIndirection<Expr<Result>>>,
-      common::MapTemplate<ImpliedDo, IntegerTypes>>
-      u;
+  std::variant<CopyableIndirection<Expr<Result>>, ImpliedDo<Result>> u;
 };
 
 template<typename RESULT> struct ArrayConstructorValues {
   using Result = RESULT;
-  CLASS_BOILERPLATE(ArrayConstructorValues)
-  template<typename A> void Push(A &&x) { values.emplace_back(std::move(x)); }
+  DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ArrayConstructorValues)
+  ArrayConstructorValues() {}
   bool operator==(const ArrayConstructorValues &) const;
+  template<typename A> void Push(A &&x) { values.emplace_back(std::move(x)); }
   std::vector<ArrayConstructorValue<Result>> values;
 };
 
-template<typename RESULT>
-struct ArrayConstructor : public ArrayConstructorValues<RESULT> {
+template<typename RESULT> struct ArrayConstructor {
   using Result = RESULT;
-  using ArrayConstructorValues<Result>::ArrayConstructorValues;
-  DynamicType GetType() const;
+  CLASS_BOILERPLATE(ArrayConstructor)
+  ArrayConstructor(Result &&t, ArrayConstructorValues<Result> &&v)
+    : type{std::move(t)}, values{std::move(v)} {
+    CHECK(type.category != TypeCategory::Character);
+  }
+  bool operator==(const ArrayConstructor<RESULT> &) const;
+  DynamicType GetType() const { return type.GetType(); }
+  static constexpr int Rank() { return 1; }
+  std::ostream &AsFortran(std::ostream &) const;
+  Result type;
+  ArrayConstructorValues<Result> values;
+};
+
+template<int KIND>
+struct ArrayConstructor<Type<TypeCategory::Character, KIND>> {
+  using Result = Type<TypeCategory::Character, KIND>;
+  CLASS_BOILERPLATE(ArrayConstructor)
+  ArrayConstructor(
+      ArrayConstructorValues<Result> &&v, Expr<SubscriptInteger> &&len)
+    : values{std::move(v)}, length{std::move(len)} {}
+  ~ArrayConstructor();
+  bool operator==(const ArrayConstructor<Result> &) const;
+  static constexpr DynamicType GetType() { return Result::GetType(); }
   static constexpr int Rank() { return 1; }
-  Expr<SubscriptInteger> LEN() const;
-  bool operator==(const ArrayConstructor &) const;
   std::ostream &AsFortran(std::ostream &) const;
+  const Expr<SubscriptInteger> &LEN() const { return *length; }
 
-  Result result;
-  std::vector<Expr<SubscriptInteger>> typeParameterValues;
+  ArrayConstructorValues<Result> values;
+  CopyableIndirection<Expr<SubscriptInteger>> length;
 };
 
 // Expression representations for each type category.
@@ -450,16 +472,20 @@ public:
     : u{Constant<Result>{n}} {}
 
 private:
-  using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
+  using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
       Convert<Result, TypeCategory::Real>>;
-  using Operations = std::variant<Parentheses<Result>, Negate<Result>,
+  using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
       Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
       Power<Result>, Extremum<Result>>;
-  using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
+  using Indices = std::conditional_t<KIND == ImpliedDoIndex::Result::kind,
+      std::tuple<ImpliedDoIndex>, std::tuple<>>;
+  using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
       TypeParamInquiry<KIND>, Designator<Result>, FunctionRef<Result>>;
 
 public:
-  common::CombineVariants<Operations, Conversions, Others> u;
+  common::TupleToVariant<
+      common::CombineTuples<Operations, Conversions, Indices, Others>>
+      u;
 };
 
 template<int KIND>
@@ -592,15 +618,16 @@ public:
   explicit Expr(bool x) : u{Constant<Result>{x}} {}
 
 private:
-  using Operations = std::variant<Convert<Result, TypeCategory::Logical>,
+  using Operations = std::tuple<Convert<Result, TypeCategory::Logical>,
       Parentheses<Result>, Not<KIND>, LogicalOperation<KIND>>;
   using Relations = std::conditional_t<KIND == LogicalResult::kind,
-      std::variant<Relational<SomeType>>, std::variant<>>;
-  using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
+      std::tuple<Relational<SomeType>>, std::tuple<>>;
+  using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
       Designator<Result>, FunctionRef<Result>>;
 
 public:
-  common::CombineVariants<Operations, Relations, Others> u;
+  common::TupleToVariant<common::CombineTuples<Operations, Relations, Others>>
+      u;
 };
 
 FOR_EACH_LOGICAL_KIND(extern template class Expr)
index c9b4c2f..32e9fe8 100644 (file)
@@ -68,7 +68,7 @@ Component FoldOperation(FoldingContext &context, Component &&component) {
 
 Triplet FoldOperation(FoldingContext &context, Triplet &&triplet) {
   return {Fold(context, triplet.lower()), Fold(context, triplet.upper()),
-      Fold(context, triplet.stride())};
+      Fold(context, Expr<SubscriptInteger>{triplet.stride()})};
 }
 
 Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
@@ -660,14 +660,16 @@ bool IsConstExpr(ConstExprContext &, const Symbol *symbol) {
   return symbol->attrs().test(semantics::Attr::PARAMETER);
 }
 bool IsConstExpr(ConstExprContext &, const CoarrayRef &) { return false; }
+bool IsConstExpr(ConstExprContext &, const ImpliedDoIndex &) {
+  return true;  // only tested when bounds are constant
+}
 
 // Prototypes for mutual recursion
 template<typename D, typename R, typename O1>
 bool IsConstExpr(ConstExprContext &, const Operation<D, R, O1> &);
 template<typename D, typename R, typename O1, typename O2>
 bool IsConstExpr(ConstExprContext &, const Operation<D, R, O1, O2> &);
-template<typename V, typename O>
-bool IsConstExpr(ConstExprContext &, const ImpliedDo<V, O> &);
+template<typename V> bool IsConstExpr(ConstExprContext &, const ImpliedDo<V> &);
 template<typename A>
 bool IsConstExpr(ConstExprContext &, const ArrayConstructorValue<A> &);
 template<typename A>
@@ -709,8 +711,8 @@ bool IsConstExpr(
   return IsConstExpr(context, operation.left()) &&
       IsConstExpr(context, operation.right());
 }
-template<typename V, typename O>
-bool IsConstExpr(ConstExprContext &context, const ImpliedDo<V, O> &impliedDo) {
+template<typename V>
+bool IsConstExpr(ConstExprContext &context, const ImpliedDo<V> &impliedDo) {
   if (!IsConstExpr(context, impliedDo.lower) ||
       !IsConstExpr(context, impliedDo.upper) ||
       !IsConstExpr(context, impliedDo.stride)) {
@@ -732,8 +734,7 @@ bool IsConstExpr(
 }
 template<typename A>
 bool IsConstExpr(ConstExprContext &context, const ArrayConstructor<A> &array) {
-  return IsConstExpr(context, array.values) &&
-      IsConstExpr(context, array.typeParameterValues);
+  return IsConstExpr(context, array.values);
 }
 bool IsConstExpr(ConstExprContext &context, const BaseObject &base) {
   return IsConstExpr(context, base.u);
index c511750..abb2061 100644 (file)
@@ -494,4 +494,66 @@ Expr<SomeLogical> BinaryLogicalOperation(
       },
       AsSameKindExprs(std::move(x), std::move(y)));
 }
+
+template<TypeCategory TO>
+std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
+  static_assert(common::IsNumericTypeCategory(TO));
+  return std::visit(
+      [=](auto &&cx) -> std::optional<Expr<SomeType>> {
+        using cxType = std::decay_t<decltype(cx)>;
+        if constexpr (!std::is_same_v<cxType, BOZLiteralConstant>) {
+          if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) {
+            return std::make_optional(
+                Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))});
+          }
+        }
+        return std::nullopt;
+      },
+      std::move(x.u));
+}
+
+std::optional<Expr<SomeType>> ConvertToType(
+    const DynamicType &type, Expr<SomeType> &&x) {
+  switch (type.category) {
+  case TypeCategory::Integer:
+    return ConvertToNumeric<TypeCategory::Integer>(type.kind, std::move(x));
+  case TypeCategory::Real:
+    return ConvertToNumeric<TypeCategory::Real>(type.kind, std::move(x));
+  case TypeCategory::Complex:
+    return ConvertToNumeric<TypeCategory::Complex>(type.kind, std::move(x));
+  case TypeCategory::Character:
+    if (auto fromType{x.GetType()}) {
+      if (fromType->category == TypeCategory::Character &&
+          fromType->kind == type.kind) {
+        // TODO pmk: adjusting CHARACTER length via conversion
+        return std::move(x);
+      }
+    }
+    break;
+  case TypeCategory::Logical:
+    if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) {
+      return Expr<SomeType>{
+          ConvertToKind<TypeCategory::Logical>(type.kind, std::move(*cx))};
+    }
+    break;
+  case TypeCategory::Derived:
+    if (auto fromType{x.GetType()}) {
+      if (type == fromType) {
+        return std::move(x);
+      }
+    }
+    break;
+  default: CRASH_NO_CASE;
+  }
+  return std::nullopt;
+}
+
+std::optional<Expr<SomeType>> ConvertToType(
+    const DynamicType &type, std::optional<Expr<SomeType>> &&x) {
+  if (x.has_value()) {
+    return ConvertToType(type, std::move(*x));
+  } else {
+    return std::nullopt;
+  }
+}
 }
index de924ed..67cde04 100644 (file)
@@ -164,6 +164,16 @@ Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
       Scalar<Part> zero;
       return Expr<TO>{ComplexConstructor<TO::kind>{
           ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
+    } else if constexpr (FROMCAT == TypeCategory::Complex) {
+      // Extract and convert the real component of a complex value
+      return std::visit(
+          [&](auto &&z) {
+            using ZType = ResultType<decltype(z)>;
+            using Part = typename ZType::Part;
+            return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
+                Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
+          },
+          std::move(x.u));
     } else {
       return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
     }
@@ -194,6 +204,11 @@ Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
   }
 }
 
+template<typename TO, TypeCategory FROMCAT, int FROMKIND>
+Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
+  return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
+}
+
 template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
   static_assert(IsSpecificIntrinsicType<TO>);
   using Value = typename Constant<TO>::Value;
@@ -206,21 +221,20 @@ template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
   }
 }
 
-template<TypeCategory TC, int TK, TypeCategory FC>
-Expr<Type<TC, TK>> ConvertTo(
-    const Expr<Type<TC, TK>> &, Expr<SomeKind<FC>> &&x) {
-  return ConvertToType<Type<TC, TK>>(std::move(x));
-}
+// Conversions to dynamic types
+std::optional<Expr<SomeType>> ConvertToType(
+    const DynamicType &, Expr<SomeType> &&);
+std::optional<Expr<SomeType>> ConvertToType(
+    const DynamicType &, std::optional<Expr<SomeType>> &&);
 
-template<TypeCategory TC, int TK, TypeCategory FC, int FK>
-Expr<Type<TC, TK>> ConvertTo(
-    const Expr<Type<TC, TK>> &, Expr<Type<FC, FK>> &&x) {
-  return AsExpr(ConvertToType<Type<TC, TK>>(AsCategoryExpr(std::move(x))));
+// Conversions to the type of another expression
+template<TypeCategory TC, int TK, typename FROM>
+Expr<Type<TC, TK>> ConvertTo(const Expr<Type<TC, TK>> &, FROM &&x) {
+  return ConvertToType<Type<TC, TK>>(std::move(x));
 }
 
-template<TypeCategory TC, TypeCategory FC>
-Expr<SomeKind<TC>> ConvertTo(
-    const Expr<SomeKind<TC>> &to, Expr<SomeKind<FC>> &&from) {
+template<TypeCategory TC, typename FROM>
+Expr<SomeKind<TC>> ConvertTo(const Expr<SomeKind<TC>> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toKindExpr) {
         using KindExpr = std::decay_t<decltype(toKindExpr)>;
@@ -230,14 +244,8 @@ Expr<SomeKind<TC>> ConvertTo(
       to.u);
 }
 
-template<TypeCategory TC, TypeCategory FC, int FK>
-Expr<SomeKind<TC>> ConvertTo(
-    const Expr<SomeKind<TC>> &to, Expr<Type<FC, FK>> &&from) {
-  return ConvertTo(to, AsCategoryExpr(std::move(from)));
-}
-
-template<typename FT>
-Expr<SomeType> ConvertTo(const Expr<SomeType> &to, Expr<FT> &&from) {
+template<typename FROM>
+Expr<SomeType> ConvertTo(const Expr<SomeType> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toCatExpr) {
         return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
@@ -245,28 +253,16 @@ Expr<SomeType> ConvertTo(const Expr<SomeType> &to, Expr<FT> &&from) {
       to.u);
 }
 
-template<TypeCategory CAT>
-Expr<SomeKind<CAT>> ConvertTo(
-    const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
-  return std::visit(
-      [&](const auto &tok) {
-        using Ty = ResultType<decltype(tok)>;
-        return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
-      },
-      to.u);
-}
-
 // Convert an expression of some known category to a dynamically chosen
 // kind of some category (usually but not necessarily distinct).
 template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
   using Result = std::optional<Expr<SomeKind<TOCAT>>>;
-  static constexpr std::size_t Types{std::tuple_size_v<CategoryTypes<TOCAT>>};
+  using Types = CategoryTypes<TOCAT>;
   ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
-  template<std::size_t J> Result Test() {
-    using Ty = std::tuple_element_t<J, CategoryTypes<TOCAT>>;
-    if (kind == Ty::kind) {
+  template<typename T> Result Test() {
+    if (kind == T::kind) {
       return std::make_optional(
-          AsCategoryExpr(ConvertToType<Ty>(std::move(value))));
+          AsCategoryExpr(ConvertToType<T>(std::move(value))));
     }
     return std::nullopt;
   }
@@ -276,7 +272,7 @@ template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
 
 template<TypeCategory TOCAT, typename VALUE>
 Expr<SomeKind<TOCAT>> ConvertToKind(int kind, VALUE &&x) {
-  return common::SearchDynamicTypes(
+  return common::SearchTypes(
       ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})
       .value();
 }
@@ -501,21 +497,20 @@ Expr<SomeKind<CAT>> operator/(
   return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y));
 }
 
-// A utility for use with common::SearchDynamicTypes to create generic
-// expressions when an intrinsic type category for (say) a variable is known
+// A utility for use with common::SearchTypes to create generic expressions
+// when an intrinsic type category for (say) a variable is known
 // but the kind parameter value is not.
 template<TypeCategory CAT, template<typename> class TEMPLATE, typename VALUE>
 struct TypeKindVisitor {
   using Result = std::optional<Expr<SomeType>>;
-  static constexpr std::size_t Types{std::tuple_size_v<CategoryTypes<CAT>>};
+  using Types = CategoryTypes<CAT>;
 
   TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
   TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
 
-  template<std::size_t J> Result Test() {
-    using Ty = std::tuple_element_t<J, CategoryTypes<CAT>>;
-    if (kind == Ty::kind) {
-      return AsGenericExpr(TEMPLATE<Ty>{std::move(value)});
+  template<typename T> Result Test() {
+    if (kind == T::kind) {
+      return AsGenericExpr(TEMPLATE<T>{std::move(value)});
     }
     return std::nullopt;
   }
index 79ba994..ee45eb1 100644 (file)
@@ -118,15 +118,23 @@ std::optional<DynamicType> GetSymbolType(const semantics::Symbol *symbol) {
 }
 
 std::string DynamicType::AsFortran() const {
-  if (category == TypeCategory::Derived) {
-    // TODO: derived type parameters
+  if (derived != nullptr) {
+    CHECK(category == TypeCategory::Derived);
     return "TYPE("s + derived->typeSymbol().name().ToString() + ')';
   } else {
-    // TODO: CHARACTER length
     return EnumToString(category) + '(' + std::to_string(kind) + ')';
   }
 }
 
+std::string DynamicType::AsFortran(std::string &&charLenExpr) const {
+  if (!charLenExpr.empty() && category == TypeCategory::Character) {
+    return "CHARACTER(KIND=" + std::to_string(kind) +
+        ",len=" + std::move(charLenExpr) + ')';
+  } else {
+    return AsFortran();
+  }
+}
+
 DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
   switch (category) {
   case TypeCategory::Integer:
index c2fbaad..678618a 100644 (file)
@@ -44,27 +44,35 @@ namespace Fortran::evaluate {
 
 using common::TypeCategory;
 
+// Specific intrinsic types are represented by specializations of
+// this class template Type<CATEGORY, KIND>.
+template<TypeCategory CATEGORY, int KIND = 0> class Type;
+
+using SubscriptInteger = Type<TypeCategory::Integer, 8>;
+using LogicalResult = Type<TypeCategory::Logical, 1>;
+using LargestReal = Type<TypeCategory::Real, 16>;
+
 // DynamicType is suitable for use as the result type for
-// GetType() functions and member functions.
+// GetType() functions and member functions.  It does *not*
+// hold CHARACTER length type parameter expressions -- those
+// must be derived via LEN() member functions or packaged
+// elsewhere (e.g. as in ArrayConstructor).
 struct DynamicType {
-  bool operator==(const DynamicType &that) const;
+  bool operator==(const DynamicType &) const;
   std::string AsFortran() const;
+  std::string AsFortran(std::string &&charLenExpr) const;
   DynamicType ResultTypeForMultiply(const DynamicType &) const;
 
   TypeCategory category;
   int kind{0};  // set only for intrinsic types
-  const semantics::DerivedTypeSpec *derived{nullptr};
-  const semantics::Symbol *descriptor{nullptr};
+  const semantics::DerivedTypeSpec *derived{nullptr};  // TYPE(T), CLASS(T)
+  const semantics::Symbol *descriptor{nullptr};  // CHARACTER, CLASS(T/*)
 };
 
 // Result will be missing when a symbol is absent or
 // has an erroneous type, e.g., REAL(KIND=666).
 std::optional<DynamicType> GetSymbolType(const semantics::Symbol *);
 
-// Specific intrinsic types are represented by specializations of
-// this class template Type<CATEGORY, KIND>.
-template<TypeCategory CATEGORY, int KIND = 0> class Type;
-
 template<TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
   static constexpr TypeCategory category{CATEGORY};
   static constexpr int kind{KIND};
@@ -172,10 +180,6 @@ template<typename T> using Scalar = typename std::decay_t<T>::Scalar;
 template<TypeCategory CATEGORY, typename T>
 using SameKind = Type<CATEGORY, std::decay_t<T>::kind>;
 
-using SubscriptInteger = Type<TypeCategory::Integer, 8>;
-using LogicalResult = Type<TypeCategory::Logical, 1>;
-using LargestReal = Type<TypeCategory::Real, 16>;
-
 // Many expressions, including subscripts, CHARACTER lengths, array bounds,
 // and effective type parameter values, are of a maximal kind of INTEGER.
 using IndirectSubscriptIntegerExpr =
index ac95368..b6af28c 100644 (file)
@@ -29,17 +29,17 @@ namespace Fortran::evaluate {
 
 // Constructors, accessors, mutators
 
+Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
+
 Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,
     std::optional<Expr<SubscriptInteger>> &&u,
-    std::optional<Expr<SubscriptInteger>> &&s) {
+    std::optional<Expr<SubscriptInteger>> &&s)
+  : stride_{s.has_value() ? std::move(*s) : Expr<SubscriptInteger>{1}} {
   if (l.has_value()) {
-    lower_ = IndirectSubscriptIntegerExpr::Make(std::move(*l));
+    lower_.emplace(std::move(*l));
   }
   if (u.has_value()) {
-    upper_ = IndirectSubscriptIntegerExpr::Make(std::move(*u));
-  }
-  if (s.has_value()) {
-    stride_ = IndirectSubscriptIntegerExpr::Make(std::move(*s));
+    upper_.emplace(std::move(*u));
   }
 }
 
@@ -57,11 +57,14 @@ std::optional<Expr<SubscriptInteger>> Triplet::upper() const {
   return std::nullopt;
 }
 
-std::optional<Expr<SubscriptInteger>> Triplet::stride() const {
-  if (stride_) {
-    return {**stride_};
+const Expr<SubscriptInteger> &Triplet::stride() const { return *stride_; }
+
+bool Triplet::IsStrideOne() const {
+  if (auto stride{ToInt64(*stride_)}) {
+    return stride == 1;
+  } else {
+    return false;
   }
-  return std::nullopt;
 }
 
 CoarrayRef::CoarrayRef(std::vector<const Symbol *> &&c,
@@ -90,13 +93,13 @@ std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
 
 CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
   CHECK(IsVariable(v));
-  stat_ = CopyableIndirection<Expr<SomeInteger>>::Make(std::move(v));
+  stat_.emplace(std::move(v));
   return *this;
 }
 
 CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
   CHECK(IsVariable(v));
-  team_ = CopyableIndirection<Expr<SomeInteger>>::Make(std::move(v));
+  team_.emplace(std::move(v));
   teamIsTeamNumber_ = isTeamNumber;
   return *this;
 }
@@ -104,10 +107,10 @@ CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
 void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,
     std::optional<Expr<SubscriptInteger>> &upper) {
   if (lower.has_value()) {
-    lower_ = IndirectSubscriptIntegerExpr::Make(std::move(*lower));
+    lower_.emplace(std::move(*lower));
   }
   if (upper.has_value()) {
-    upper_ = IndirectSubscriptIntegerExpr::Make(std::move(*upper));
+    upper_.emplace(std::move(*upper));
   }
 }
 
@@ -156,8 +159,12 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
     std::optional<std::int64_t> length;
     if (literal != nullptr) {
       length = (*literal)->data().size();
-    } else {
-      // TODO pmk: get max character length from symbol
+    } else if (const Symbol * symbol{GetLastSymbol()}) {
+      if (const semantics::DeclTypeSpec * type{symbol->GetType()}) {
+        if (type->category() == semantics::DeclTypeSpec::Character) {
+          length = ToInt64(type->characterTypeSpec().length().GetExplicit());
+        }
+      }
     }
     if (*ubi < 1 || (lbi.has_value() && *ubi < *lbi)) {
       // Zero-length string: canonicalize
@@ -298,9 +305,7 @@ std::ostream &Component::AsFortran(std::ostream &o) const {
 std::ostream &Triplet::AsFortran(std::ostream &o) const {
   Emit(o, lower_) << ':';
   Emit(o, upper_);
-  if (stride_) {
-    Emit(o << ':', stride_);
-  }
+  Emit(o << ':', *stride_);
   return o;
 }
 
@@ -657,7 +662,7 @@ bool TypeParamInquiry<KIND>::operator==(
 }
 bool Triplet::operator==(const Triplet &that) const {
   return lower_ == that.lower_ && upper_ == that.upper_ &&
-      stride_ == that.stride_;
+      *stride_ == *that.stride_;
 }
 bool ArrayRef::operator==(const ArrayRef &that) const {
   return u == that.u && subscript == that.subscript;
index 81463e7..1da5df0 100644 (file)
@@ -21,6 +21,8 @@
 // Fortran 2018 language standard (q.v.) and uses strong typing to ensure
 // that only admissable combinations can be constructed.
 
+// TODO pmk: convert remaining structs to classes
+
 #include "call.h"
 #include "common.h"
 #include "static-data.h"
@@ -120,19 +122,21 @@ EXPAND_FOR_EACH_INTEGER_KIND(
 // R921 subscript-triplet
 class Triplet {
 public:
-  Triplet() {}
+  Triplet();
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Triplet)
   Triplet(std::optional<Expr<SubscriptInteger>> &&,
       std::optional<Expr<SubscriptInteger>> &&,
       std::optional<Expr<SubscriptInteger>> &&);
   std::optional<Expr<SubscriptInteger>> lower() const;
   std::optional<Expr<SubscriptInteger>> upper() const;
-  std::optional<Expr<SubscriptInteger>> stride() const;
+  const Expr<SubscriptInteger> &stride() const;
   bool operator==(const Triplet &) const;
+  bool IsStrideOne() const;
   std::ostream &AsFortran(std::ostream &) const;
 
 private:
-  std::optional<IndirectSubscriptIntegerExpr> lower_, upper_, stride_;
+  std::optional<IndirectSubscriptIntegerExpr> lower_, upper_;
+  IndirectSubscriptIntegerExpr stride_;
 };
 
 // R919 subscript when rank 0, R923 vector-subscript when rank 1
index d94f2da..e3e409b 100644 (file)
@@ -60,6 +60,7 @@ CLASS_TRAIT(TupleTrait)
 // here.
 namespace Fortran::semantics {
 class Symbol;
+class DeclTypeSpec;
 }
 
 // Expressions in the parse tree have owning pointers that can be set to
@@ -700,6 +701,7 @@ struct DerivedTypeSpec {
 // R702 type-spec -> intrinsic-type-spec | derived-type-spec
 struct TypeSpec {
   UNION_CLASS_BOILERPLATE(TypeSpec);
+  mutable const semantics::DeclTypeSpec *declTypeSpec{nullptr};
   std::variant<IntrinsicTypeSpec, DerivedTypeSpec> u;
 };
 
@@ -1693,9 +1695,9 @@ struct Expr {
   explicit Expr(Designator &&);
   explicit Expr(FunctionReference &&);
 
-  // Filled in later during semantic analysis of the expression.
-  // TODO: May be temporary; remove if caching no longer required.
+  // Filled in after successful semantic analysis of the expression.
   mutable common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
+
   CharBlock source;
 
   std::variant<common::Indirection<CharLiteralConstantSubstring>,
index 9d70913..f43be6e 100644 (file)
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "expression.h"
-#include "dump-parse-tree.h"  // TODO pmk temporary
 #include "scope.h"
 #include "semantics.h"
 #include "symbol.h"
 #include <functional>
 #include <optional>
 
-#include <iostream>  // TODO pmk rm
+// TODO pmk remove when scaffolding is obsolete
+#define PMKDEBUG 1
+#if PMKDEBUG
+#include "dump-parse-tree.h"
+#include <iostream>
+#endif
 
 // Typedef for optional generic expressions (ubiquitous in this file)
 using MaybeExpr =
@@ -109,9 +113,50 @@ struct CallAndArguments {
   ActualArguments arguments;
 };
 
+struct DynamicTypeWithLength : public DynamicType {
+  std::optional<Expr<SubscriptInteger>> length;
+};
+
+std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
+    ExpressionAnalysisContext &context,
+    const std::optional<parser::TypeSpec> &spec) {
+  if (spec.has_value()) {
+    if (const semantics::DeclTypeSpec * typeSpec{spec->declTypeSpec}) {
+      // Name resolution sets TypeSpec::declTypeSpec only when it's valid
+      // (viz., an intrinsic type with valid known kind or a non-polymorphic
+      // & non-ABSTRACT derived type).
+      if (const semantics::IntrinsicTypeSpec *
+          intrinsic{typeSpec->AsIntrinsic()}) {
+        TypeCategory category{intrinsic->category()};
+        if (auto kind{ToInt64(intrinsic->kind())}) {
+          DynamicTypeWithLength result{category, static_cast<int>(*kind)};
+          if (category == TypeCategory::Character) {
+            const semantics::CharacterTypeSpec &cts{
+                typeSpec->characterTypeSpec()};
+            const semantics::ParamValue len{cts.length()};
+            // N.B. CHARACTER(LEN=*) is allowed in type-specs in ALLOCATE() &
+            // type guards, but not in array constructors.
+            if (len.GetExplicit().has_value()) {
+              Expr<SomeInteger> copy{*len.GetExplicit()};
+              result.length = ConvertToType<SubscriptInteger>(std::move(copy));
+            }
+          }
+          return result;
+        }
+      } else if (const semantics::DerivedTypeSpec *
+          derived{typeSpec->AsDerived()}) {
+        return DynamicTypeWithLength{TypeCategory::Derived, 0, derived};
+      }
+    }
+  }
+  return std::nullopt;
+}
+
 // Forward declarations of additional AnalyzeExpr specializations and overloads
 template<typename... As>
 MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &, const std::variant<As...> &);
+template<typename A>
+MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &, const std::optional<A> &);
 static MaybeExpr AnalyzeExpr(
     ExpressionAnalysisContext &, const parser::Designator &);
 static MaybeExpr AnalyzeExpr(
@@ -217,12 +262,21 @@ MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context, const A &x) {
 // Definitions of AnalyzeExpr() specializations follow.
 // Helper subroutines are intermixed.
 
-// Variants are silently traversed by AnalyzeExpr().
+// Variants and optionals are silently traversed by AnalyzeExpr().
 template<typename... As>
 MaybeExpr AnalyzeExpr(
     ExpressionAnalysisContext &context, const std::variant<As...> &u) {
   return std::visit([&](const auto &x) { return AnalyzeExpr(context, x); }, u);
 }
+template<typename A>
+MaybeExpr AnalyzeExpr(
+    ExpressionAnalysisContext &context, const std::optional<A> &x) {
+  if (x.has_value()) {
+    return AnalyzeExpr(context, *x);
+  } else {
+    return std::nullopt;
+  }
+}
 
 // Wraps a object in an explicitly typed representation (e.g., Designator<>
 // or FunctionRef<>) that has been instantiated on a dynamically chosen type.
@@ -230,7 +284,7 @@ MaybeExpr AnalyzeExpr(
 template<TypeCategory CATEGORY, template<typename> typename WRAPPER,
     typename WRAPPED>
 MaybeExpr WrapperHelper(int kind, WRAPPED &&x) {
-  return common::SearchDynamicTypes(
+  return common::SearchTypes(
       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
 }
 
@@ -269,8 +323,44 @@ static MaybeExpr Designate(DataRef &&dataRef) {
   return std::nullopt;
 }
 
+// Catch and resolve the ambiguous parse of a substring reference
+// that looks like a 1-D array element or section.
+static MaybeExpr ResolveAmbiguousSubstring(
+    ExpressionAnalysisContext &context, ArrayRef &&ref) {
+  const Symbol &symbol{ref.GetLastSymbol()};
+  if (std::optional<DynamicType> dyType{GetSymbolType(&symbol)}) {
+    if (dyType->category == TypeCategory::Character &&
+        ref.subscript.size() == 1) {
+      DataRef base{std::visit(
+          [](auto &&y) { return DataRef{std::move(y)}; }, std::move(ref.u))};
+      std::optional<Expr<SubscriptInteger>> lower, upper;
+      if (std::visit(
+              common::visitors{
+                  [&](IndirectSubscriptIntegerExpr &&x) {
+                    lower = std::move(*x);
+                    return true;
+                  },
+                  [&](Triplet &&triplet) {
+                    lower = triplet.lower();
+                    upper = triplet.upper();
+                    return triplet.IsStrideOne();
+                  },
+              },
+              std::move(ref.subscript[0].u))) {
+        return WrapperHelper<TypeCategory::Character, Designator, Substring>(
+            dyType->kind,
+            Substring{std::move(base), std::move(lower), std::move(upper)});
+      }
+    }
+  }
+
+  return std::nullopt;
+}
+
 // Some subscript semantic checks must be deferred until all of the
-// subscripts are in hand.
+// subscripts are in hand.  This is also where we can catch the
+// ambiguous parse of a substring reference that looks like a 1-D array
+// element or section.
 static MaybeExpr CompleteSubscripts(
     ExpressionAnalysisContext &context, ArrayRef &&ref) {
   const Symbol &symbol{ref.GetLastSymbol()};
@@ -283,7 +373,11 @@ static MaybeExpr CompleteSubscripts(
   }
   int subscripts = ref.subscript.size();
   if (subscripts != symbolRank) {
-    context.Say("reference to rank-%d object '%s' has %d subscripts"_err_en_US,
+    if (MaybeExpr substring{
+            ResolveAmbiguousSubstring(context, std::move(ref))}) {
+      return substring;
+    }
+    context.Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US,
         symbolRank, symbol.name().ToString().data(), subscripts);
   } else if (subscripts == 0) {
     // nothing to check
@@ -292,8 +386,8 @@ static MaybeExpr CompleteSubscripts(
     if (baseRank > 0) {
       int rank{ref.Rank()};
       if (rank > 0) {
-        context.Say(
-            "subscripts of rank-%d component reference have rank %d, but must all be scalar"_err_en_US,
+        context.Say("Subscripts of rank-%d component reference have rank %d, "
+                    "but must all be scalar"_err_en_US,
             baseRank, rank);
       }
     }
@@ -302,8 +396,8 @@ static MaybeExpr CompleteSubscripts(
     // C928 & C1002
     if (Triplet * last{std::get_if<Triplet>(&ref.subscript.back().u)}) {
       if (!last->upper().has_value() && details->IsAssumedSize()) {
-        context.Say(
-            "assumed-size array '%s' must have explicit final subscript upper bound value"_err_en_US,
+        context.Say("Assumed-size array '%s' must have explicit final "
+                    "subscript upper bound value"_err_en_US,
             symbol.name().ToString().data());
       }
     }
@@ -433,7 +527,7 @@ MaybeExpr IntLiteralConstant(
       AnalyzeKindParam(context, std::get<std::optional<parser::KindParam>>(x.t),
           context.GetDefaultKind(TypeCategory::Integer))};
   auto value{std::get<0>(x.t)};  // std::(u)int64_t
-  auto result{common::SearchDynamicTypes(
+  auto result{common::SearchTypes(
       TypeKindVisitor<TypeCategory::Integer, Constant, std::int64_t>{
           kind, static_cast<std::int64_t>(value)})};
   if (!result.has_value()) {
@@ -468,15 +562,14 @@ Constant<TYPE> ReadRealLiteral(
 
 struct RealTypeVisitor {
   using Result = std::optional<Expr<SomeReal>>;
-  static constexpr std::size_t Types{std::tuple_size_v<RealTypes>};
+  using Types = RealTypes;
 
   RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx)
     : kind{k}, literal{lit}, context{ctx} {}
 
-  template<std::size_t J> Result Test() {
-    using Ty = std::tuple_element_t<J, RealTypes>;
-    if (kind == Ty::kind) {
-      return {AsCategoryExpr(ReadRealLiteral<Ty>(literal, context))};
+  template<typename T> Result Test() {
+    if (kind == T::kind) {
+      return {AsCategoryExpr(ReadRealLiteral<T>(literal, context))};
     }
     return std::nullopt;
   }
@@ -520,7 +613,7 @@ static MaybeExpr AnalyzeExpr(
     context.Say(
         "explicit kind parameter on real constant disagrees with exponent letter"_en_US);
   }
-  auto result{common::SearchDynamicTypes(
+  auto result{common::SearchTypes(
       RealTypeVisitor{kind, x.real.source, context.GetFoldingContext()})};
   if (!result.has_value()) {
     context.Say("unsupported REAL(KIND=%d)"_err_en_US, kind);
@@ -610,7 +703,7 @@ static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context,
       AnalyzeKindParam(context, std::get<std::optional<parser::KindParam>>(x.t),
           context.GetDefaultKind(TypeCategory::Logical))};
   bool value{std::get<bool>(x.t)};
-  auto result{common::SearchDynamicTypes(
+  auto result{common::SearchTypes(
       TypeKindVisitor<TypeCategory::Logical, Constant, bool>{
           kind, std::move(value)})};
   if (!result.has_value()) {
@@ -645,19 +738,17 @@ static MaybeExpr AnalyzeExpr(
   return {AsGenericExpr(std::move(value.value))};
 }
 
-// For use with SearchDynamicTypes to create a TypeParamInquiry with the
+// For use with SearchTypes to create a TypeParamInquiry with the
 // right integer kind.
 struct TypeParamInquiryVisitor {
   using Result = std::optional<Expr<SomeInteger>>;
-  static constexpr std::size_t Types{
-      std::tuple_size_v<CategoryTypes<TypeCategory::Integer>>};
+  using Types = IntegerTypes;
   TypeParamInquiryVisitor(int k, SymbolOrComponent &&b, const Symbol &param)
     : kind{k}, base{std::move(b)}, parameter{param} {}
-  template<std::size_t J> Result Test() {
-    using Ty = std::tuple_element_t<J, CategoryTypes<TypeCategory::Integer>>;
-    if (kind == Ty::kind) {
+  template<typename T> Result Test() {
+    if (kind == T::kind) {
       return Expr<SomeInteger>{
-          Expr<Ty>{TypeParamInquiry<Ty::kind>{std::move(base), parameter}}};
+          Expr<T>{TypeParamInquiry<T::kind>{std::move(base), parameter}}};
     }
     return std::nullopt;
   }
@@ -670,7 +761,7 @@ static std::optional<Expr<SomeInteger>> MakeTypeParamInquiry(
     const Symbol *symbol) {
   if (std::optional<DynamicType> dyType{GetSymbolType(symbol)}) {
     if (dyType->category == TypeCategory::Integer) {
-      return common::SearchDynamicTypes(TypeParamInquiryVisitor{
+      return common::SearchTypes(TypeParamInquiryVisitor{
           dyType->kind, SymbolOrComponent{nullptr}, *symbol});
     }
   }
@@ -680,7 +771,10 @@ static std::optional<Expr<SomeInteger>> MakeTypeParamInquiry(
 // Names and named constants
 static MaybeExpr AnalyzeExpr(
     ExpressionAnalysisContext &context, const parser::Name &n) {
-  if (n.symbol == nullptr) {
+  if (std::optional<int> kind{context.IsAcImpliedDo(n.source)}) {
+    return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>(
+        *kind, AsExpr(ImpliedDoIndex{n.source})));
+  } else if (n.symbol == nullptr) {
     context.Say(
         n.source, "TODO INTERNAL: name was not resolved to a symbol"_err_en_US);
   } else if (n.symbol->attrs().test(semantics::Attr::PARAMETER)) {
@@ -944,7 +1038,7 @@ static MaybeExpr AnalyzeExpr(
           CHECK(dyType.has_value());
           CHECK(dyType->category == TypeCategory::Integer);
           return AsMaybeExpr(
-              common::SearchDynamicTypes(TypeParamInquiryVisitor{dyType->kind,
+              common::SearchTypes(TypeParamInquiryVisitor{dyType->kind,
                   IgnoreAnySubscripts(std::move(*designator)), *sym}));
         } else {
           context.Say(name,
@@ -1015,9 +1109,221 @@ static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context,
   return std::nullopt;
 }
 
-static MaybeExpr AnalyzeExpr(
-    ExpressionAnalysisContext &context, const parser::ArrayConstructor &) {
-  context.Say("TODO: ArrayConstructor unimplemented"_en_US);
+static int IntegerTypeSpecKind(
+    ExpressionAnalysisContext &context, const parser::IntegerTypeSpec &spec) {
+  Expr<SubscriptInteger> value{context.Analyze(TypeCategory::Integer, spec.v)};
+  if (auto kind{ToInt64(value)}) {
+    return static_cast<int>(*kind);
+  }
+  context.SayAt(spec, "Constant INTEGER kind value required here"_err_en_US);
+  return context.GetDefaultKind(TypeCategory::Integer);
+}
+
+template<int KIND, typename A>
+std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr(
+    ExpressionAnalysisContext &context, const A &x) {
+  if (MaybeExpr y{AnalyzeExpr(context, x)}) {
+    Expr<SomeInteger> *intExpr{UnwrapExpr<Expr<SomeInteger>>(*y)};
+    CHECK(intExpr != nullptr);
+    return ConvertToType<Type<TypeCategory::Integer, KIND>>(
+        std::move(*intExpr));
+  }
+  return std::nullopt;
+}
+
+// Array constructors
+
+struct ArrayConstructorContext {
+  void Push(MaybeExpr &&);
+  void Add(const parser::AcValue &);
+  ExpressionAnalysisContext &exprContext;
+  std::optional<DynamicTypeWithLength> &type;
+  bool typesMustMatch{false};
+  ArrayConstructorValues<SomeType> values;
+};
+
+void ArrayConstructorContext::Push(MaybeExpr &&x) {
+  if (x.has_value()) {
+    DynamicTypeWithLength xType;
+    if (auto dyType{x->GetType()}) {
+      *static_cast<DynamicType *>(&xType) = *dyType;
+    }
+    if (Expr<SomeCharacter> * charExpr{UnwrapExpr<Expr<SomeCharacter>>(*x)}) {
+      CHECK(xType.category == TypeCategory::Character);
+      xType.length =
+          std::visit([](const auto &kc) { return kc.LEN(); }, charExpr->u);
+    }
+    if (!type.has_value()) {
+      // If there is no explicit type-spec in an array constructor, the type
+      // of the array is the declared type of all of the elements, which must
+      // be well-defined.
+      // TODO: Possible language extension: use the most general type of
+      // the values as the type of a numeric constructed array, convert all
+      // of the other values to that type.  Alternative: let the first value
+      // determine the type, and convert the others to that type.
+      type = std::move(xType);
+      values.Push(std::move(*x));
+    } else if (typesMustMatch) {
+      if (static_cast<const DynamicType &>(*type) ==
+          static_cast<const DynamicType &>(xType)) {
+        values.Push(std::move(*x));
+      } else {
+        exprContext.Say(
+            "Values in array constructor must have the same declared type when no explicit type appears"_err_en_US);
+      }
+    } else {
+      if (auto cast{ConvertToType(*type, std::move(*x))}) {
+        values.Push(std::move(*cast));
+      } else {
+        exprContext.Say(
+            "Value in array constructor could not be converted to the type of the array"_err_en_US);
+      }
+    }
+  }
+}
+
+void ArrayConstructorContext::Add(const parser::AcValue &x) {
+  using IntType = ResultType<ImpliedDoIndex>;
+  std::visit(
+      common::visitors{
+          [&](const parser::AcValue::Triplet &triplet) {
+            // Transform l:u(:s) into (_,_=l,u(,s)) with an anonymous index '_'
+            std::optional<Expr<IntType>> lower{
+                GetSpecificIntExpr<IntType::kind>(
+                    exprContext, std::get<0>(triplet.t))};
+            std::optional<Expr<IntType>> upper{
+                GetSpecificIntExpr<IntType::kind>(
+                    exprContext, std::get<1>(triplet.t))};
+            std::optional<Expr<IntType>> stride{
+                GetSpecificIntExpr<IntType::kind>(
+                    exprContext, std::get<2>(triplet.t))};
+            if (lower.has_value() && upper.has_value()) {
+              if (!stride.has_value()) {
+                stride = Expr<IntType>{1};
+              }
+              if (!type.has_value()) {
+                type = DynamicTypeWithLength{IntType::GetType()};
+              }
+              ArrayConstructorContext nested{exprContext, type, typesMustMatch};
+              parser::CharBlock name;
+              nested.Push(Expr<SomeType>{
+                  Expr<SomeInteger>{Expr<IntType>{ImpliedDoIndex{name}}}});
+              values.Push(ImpliedDo<SomeType>{name, std::move(*lower),
+                  std::move(*upper), std::move(*stride),
+                  std::move(nested.values)});
+            }
+          },
+          [&](const common::Indirection<parser::Expr> &expr) {
+            if (MaybeExpr v{exprContext.Analyze(*expr)}) {
+              Push(std::move(*v));
+            }
+          },
+          [&](const common::Indirection<parser::AcImpliedDo> &impliedDo) {
+            const auto &control{
+                std::get<parser::AcImpliedDoControl>(impliedDo->t)};
+            const auto &bounds{
+                std::get<parser::LoopBounds<parser::ScalarIntExpr>>(control.t)};
+            parser::CharBlock name{bounds.name.thing.thing.source};
+            int kind{IntType::kind};
+            if (auto &its{std::get<std::optional<parser::IntegerTypeSpec>>(
+                    control.t)}) {
+              kind = IntegerTypeSpecKind(exprContext, *its);
+            }
+            bool inserted{exprContext.AddAcImpliedDo(name, kind)};
+            if (!inserted) {
+              exprContext.SayAt(name,
+                  "Implied DO index is active in surrounding implied DO loop and cannot have the same name"_err_en_US);
+            }
+            std::optional<Expr<IntType>> lower{
+                GetSpecificIntExpr<IntType::kind>(exprContext, bounds.lower)};
+            std::optional<Expr<IntType>> upper{
+                GetSpecificIntExpr<IntType::kind>(exprContext, bounds.upper)};
+            std::optional<Expr<IntType>> stride{
+                GetSpecificIntExpr<IntType::kind>(exprContext, bounds.step)};
+            ArrayConstructorContext nested{exprContext, type, typesMustMatch};
+            for (const auto &value :
+                std::get<std::list<parser::AcValue>>(impliedDo->t)) {
+              nested.Add(value);
+            }
+            if (lower.has_value() && upper.has_value()) {
+              if (!stride.has_value()) {
+                stride = Expr<IntType>{1};
+              }
+              values.Push(ImpliedDo<SomeType>{name, std::move(*lower),
+                  std::move(*upper), std::move(*stride),
+                  std::move(nested.values)});
+            }
+            if (inserted) {
+              exprContext.RemoveAcImpliedDo(name);
+            }
+          },
+      },
+      x.u);
+}
+
+// Inverts a collection of generic ArrayConstructorValues<SomeType> that
+// all happen to have or be convertible to the same actual type T into
+// one ArrayConstructor<T>.
+template<typename T>
+ArrayConstructorValues<T> MakeSpecific(
+    ArrayConstructorValues<SomeType> &&from) {
+  ArrayConstructorValues<T> to;
+  for (ArrayConstructorValue<SomeType> &x : from.values) {
+    std::visit(
+        common::visitors{
+            [&](CopyableIndirection<Expr<SomeType>> &&expr) {
+              auto *typed{UnwrapExpr<Expr<T>>(*expr)};
+              CHECK(typed != nullptr);
+              to.Push(std::move(*typed));
+            },
+            [&](ImpliedDo<SomeType> &&impliedDo) {
+              to.Push(ImpliedDo<T>{impliedDo.controlVariableName,
+                  std::move(*impliedDo.lower), std::move(*impliedDo.upper),
+                  std::move(*impliedDo.stride),
+                  MakeSpecific<T>(std::move(*impliedDo.values))});
+            },
+        },
+        std::move(x.u));
+  }
+  return to;
+}
+
+struct ArrayConstructorTypeVisitor {
+  using Result = MaybeExpr;
+  using Types = LengthlessIntrinsicTypes;
+  template<typename T> Result Test() {
+    if (type.category == T::category && type.kind == T::kind) {
+      if constexpr (T::category == TypeCategory::Character) {
+        CHECK(type.length.has_value());
+        return AsMaybeExpr(ArrayConstructor<T>{
+            MakeSpecific<T>(std::move(values)), std::move(*type.length)});
+      } else {
+        return AsMaybeExpr(
+            ArrayConstructor<T>{T{}, MakeSpecific<T>(std::move(values))});
+      }
+    } else {
+      return std::nullopt;
+    }
+  }
+  DynamicTypeWithLength type;
+  ArrayConstructorValues<SomeType> values;
+};
+
+static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &exprContext,
+    const parser::ArrayConstructor &array) {
+  const parser::AcSpec &acSpec{array.v};
+  std::optional<DynamicTypeWithLength> type{
+      AnalyzeTypeSpec(exprContext, acSpec.type)};
+  bool typesMustMatch{!type.has_value()};
+  ArrayConstructorContext context{exprContext, type, typesMustMatch};
+  for (const parser::AcValue &value : acSpec.values) {
+    context.Add(value);
+  }
+  if (type.has_value()) {
+    ArrayConstructorTypeVisitor visitor{
+        std::move(*type), std::move(context.values)};
+    return common::SearchTypes(std::move(visitor));
+  }
   return std::nullopt;
 }
 
@@ -1502,6 +1808,28 @@ DynamicType ExpressionAnalysisContext::GetDefaultKindOfType(
     common::TypeCategory category) {
   return {category, GetDefaultKind(category)};
 }
+
+bool ExpressionAnalysisContext::AddAcImpliedDo(
+    parser::CharBlock name, int kind) {
+  return acImpliedDos_.insert(std::make_pair(name, kind)).second;
+}
+
+void ExpressionAnalysisContext::RemoveAcImpliedDo(parser::CharBlock name) {
+  auto iter{acImpliedDos_.find(name)};
+  if (iter != acImpliedDos_.end()) {
+    acImpliedDos_.erase(iter);
+  }
+}
+
+std::optional<int> ExpressionAnalysisContext::IsAcImpliedDo(
+    parser::CharBlock name) const {
+  auto iter{acImpliedDos_.find(name)};
+  if (iter != acImpliedDos_.cend()) {
+    return {iter->second};
+  } else {
+    return std::nullopt;
+  }
+}
 }
 
 namespace Fortran::semantics {
@@ -1517,12 +1845,16 @@ public:
   bool Pre(const parser::Expr &expr) {
     if (expr.typedExpr.get() == nullptr) {
       if (MaybeExpr checked{AnalyzeExpr(context_, expr)}) {
-        // checked->AsFortran(std::cout << "pmk: checked expression: ") << '\n';
+#if PMKDEBUG
+//      checked->AsFortran(std::cout << "checked expression: ") << '\n';
+#endif
         expr.typedExpr.reset(
             new evaluate::GenericExprWrapper{std::move(*checked)});
       } else {
+#if PMKDEBUG
         std::cout << "TODO: expression analysis failed for this expression: ";
         DumpTree(std::cout, expr);
+#endif
       }
     }
     return false;
index d7e3a56..9a55bf7 100644 (file)
 #include "../evaluate/expression.h"
 #include "../evaluate/tools.h"
 #include "../evaluate/type.h"
+#include "../parser/char-block.h"
 #include "../parser/parse-tree-visitor.h"
 #include "../parser/parse-tree.h"
+#include <map>
 #include <optional>
 #include <variant>
 
@@ -98,8 +100,14 @@ public:
   int GetDefaultKind(common::TypeCategory);
   DynamicType GetDefaultKindOfType(common::TypeCategory);
 
+  // Manage a set of active array constructor implied DO loops.
+  bool AddAcImpliedDo(parser::CharBlock, int);
+  void RemoveAcImpliedDo(parser::CharBlock);
+  std::optional<int> IsAcImpliedDo(parser::CharBlock) const;
+
 private:
   semantics::SemanticsContext &context_;
+  std::map<parser::CharBlock, int> acImpliedDos_;  // values are INTEGER kinds
 };
 
 template<typename PARSED>
index db91b48..4cd025c 100644 (file)
@@ -267,7 +267,7 @@ public:
   void Post(const parser::DeclarationTypeSpec::TypeStar &);
   bool Pre(const parser::TypeGuardStmt &);
   void Post(const parser::TypeGuardStmt &);
-  bool Pre(const parser::AcSpec &);
+  void Post(const parser::TypeSpec &);
 
 protected:
   struct State {
@@ -687,10 +687,14 @@ public:
 protected:
   bool BeginDecl();
   void EndDecl();
-  // Declare a construct or statement entity. If there isn't a type specified
+  // Declare a construct entity. If there isn't a type specified
   // it comes from the entity in the containing scope, or implicit rules.
   // Return pointer to the new symbol, or nullptr on error.
   Symbol *DeclareConstructEntity(const parser::Name &);
+  // Declare a statement entity (e.g., an implied DO loop index).
+  // If there isn't a type specified, implicit rules apply.
+  // Return pointer to the new symbol, or nullptr on error.
+  Symbol *DeclareStatementEntity(const parser::Name &);
   bool CheckUseError(const parser::Name &);
   void CheckAccessibility(const parser::Name &, bool, const Symbol &);
 
@@ -774,9 +778,11 @@ public:
   bool Pre(const parser::LocalitySpec::Local &);
   bool Pre(const parser::LocalitySpec::LocalInit &);
   bool Pre(const parser::LocalitySpec::Shared &);
+  bool Pre(const parser::AcSpec &);
+  bool Pre(const parser::AcImpliedDo &);
   bool Pre(const parser::DataImpliedDo &);
-  bool Pre(const parser::DataStmt &);
-  void Post(const parser::DataStmt &);
+  bool Pre(const parser::DataStmtSet &);
+  void Post(const parser::DataStmtSet &);
   bool Pre(const parser::DoConstruct &);
   void Post(const parser::DoConstruct &);
   void Post(const parser::ConcurrentControl &);
@@ -846,7 +852,7 @@ private:
   }
   bool CheckDef(const std::optional<parser::Name> &);
   void CheckRef(const std::optional<parser::Name> &);
-  void CheckIntegerType(const Symbol &);
+  void CheckScalarIntegerType(const Symbol &);
   const DeclTypeSpec &ToDeclTypeSpec(evaluate::DynamicType &&);
   const DeclTypeSpec &ToDeclTypeSpec(
       evaluate::DynamicType &&, SubscriptIntExpr &&length);
@@ -1150,15 +1156,27 @@ void DeclTypeSpecVisitor::Post(const parser::TypeGuardStmt &) {
   EndDeclTypeSpec();
 }
 
-bool DeclTypeSpecVisitor::Pre(const parser::AcSpec &x) {
-  // AcSpec can occur within a TypeDeclarationStmt: save and restore state
-  auto savedState{SetDeclTypeSpecState({})};
-  BeginDeclTypeSpec();
-  Walk(x.type);
-  Walk(x.values);
-  EndDeclTypeSpec();
-  SetDeclTypeSpecState(savedState);
-  return false;
+void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
+  // Record the resolved DeclTypeSpec in the parse tree for use by
+  // expression semantics if the DeclTypeSpec is a valid TypeSpec.
+  // The grammar ensures that it's an intrinsic or derived type spec,
+  // not TYPE(*) or CLASS(*) or CLASS(T).
+  if (const DeclTypeSpec * spec{state_.declTypeSpec}) {
+    switch (spec->category()) {
+    case DeclTypeSpec::Numeric:
+    case DeclTypeSpec::Logical:
+    case DeclTypeSpec::Character: typeSpec.declTypeSpec = spec; break;
+    case DeclTypeSpec::TypeDerived:
+      if (const DerivedTypeSpec * derived{spec->AsDerived()}) {
+        if (derived->typeSymbol().attrs().test(Attr::ABSTRACT)) {
+          Say("ABSTRACT derived type may not be used here"_err_en_US);
+        }
+        typeSpec.declTypeSpec = spec;
+      }
+      break;
+    default: CRASH_NO_CASE;
+    }
+  }
 }
 
 void DeclTypeSpecVisitor::Post(
@@ -2985,6 +3003,26 @@ Symbol *DeclarationVisitor::DeclareConstructEntity(const parser::Name &name) {
   return &symbol;
 }
 
+Symbol *DeclarationVisitor::DeclareStatementEntity(const parser::Name &name) {
+  if (auto *prev{FindSymbol(name)}) {
+    if (prev->owner() == currScope()) {
+      SayAlreadyDeclared(name, *prev);
+      return nullptr;
+    }
+    name.symbol = nullptr;
+  }
+  Symbol &symbol{DeclareEntity<ObjectEntityDetails>(name, {})};
+  if (symbol.has<ObjectEntityDetails>()) {
+    if (auto *type{GetDeclTypeSpec()}) {
+      SetType(name, *type);
+    } else {
+      ApplyImplicitRules(symbol);
+    }
+    return Resolve(name, &symbol);
+  }
+  return nullptr;
+}
+
 // Set the type of an entity or report an error.
 void DeclarationVisitor::SetType(
     const parser::Name &name, const DeclTypeSpec &type) {
@@ -3173,6 +3211,39 @@ bool ConstructVisitor::Pre(const parser::LocalitySpec::Shared &x) {
   return false;
 }
 
+bool ConstructVisitor::Pre(const parser::AcSpec &x) {
+  // AcSpec can occur within a TypeDeclarationStmt: save and restore state
+  auto savedState{SetDeclTypeSpecState({})};
+  BeginDeclTypeSpec();
+  Walk(x.type);
+  EndDeclTypeSpec();
+  SetDeclTypeSpecState(savedState);
+  PushScope(Scope::Kind::ImpliedDos, nullptr);
+  Walk(x.values);
+  PopScope();
+  return false;
+}
+
+bool ConstructVisitor::Pre(const parser::AcImpliedDo &x) {
+  auto &values{std::get<std::list<parser::AcValue>>(x.t)};
+  auto &control{std::get<parser::AcImpliedDoControl>(x.t)};
+  auto &type{std::get<std::optional<parser::IntegerTypeSpec>>(control.t)};
+  auto &bounds{std::get<parser::LoopBounds<parser::ScalarIntExpr>>(control.t)};
+  if (type) {
+    BeginDeclTypeSpec();
+    DeclarationVisitor::Post(*type);
+  }
+  if (auto *symbol{DeclareStatementEntity(bounds.name.thing.thing)}) {
+    CheckScalarIntegerType(*symbol);
+  }
+  if (type) {
+    EndDeclTypeSpec();
+  }
+  Walk(bounds);
+  Walk(values);
+  return false;
+}
+
 bool ConstructVisitor::Pre(const parser::DataImpliedDo &x) {
   auto &objects{std::get<std::list<parser::DataIDoObject>>(x.t)};
   auto &type{std::get<std::optional<parser::IntegerTypeSpec>>(x.t)};
@@ -3182,8 +3253,8 @@ bool ConstructVisitor::Pre(const parser::DataImpliedDo &x) {
     BeginDeclTypeSpec();
     DeclarationVisitor::Post(*type);
   }
-  if (auto *symbol{DeclareConstructEntity(bounds.name.thing.thing)}) {
-    CheckIntegerType(*symbol);
+  if (auto *symbol{DeclareStatementEntity(bounds.name.thing.thing)}) {
+    CheckScalarIntegerType(*symbol);
   }
   if (type) {
     EndDeclTypeSpec();
@@ -3193,11 +3264,11 @@ bool ConstructVisitor::Pre(const parser::DataImpliedDo &x) {
   return false;
 }
 
-bool ConstructVisitor::Pre(const parser::DataStmt &) {
-  PushScope(Scope::Kind::Block, nullptr);
+bool ConstructVisitor::Pre(const parser::DataStmtSet &) {
+  PushScope(Scope::Kind::ImpliedDos, nullptr);
   return true;
 }
-void ConstructVisitor::Post(const parser::DataStmt &) { PopScope(); }
+void ConstructVisitor::Post(const parser::DataStmtSet &) { PopScope(); }
 
 bool ConstructVisitor::Pre(const parser::DoConstruct &x) {
   if (x.IsDoConcurrent()) {
@@ -3214,7 +3285,7 @@ void ConstructVisitor::Post(const parser::DoConstruct &x) {
 void ConstructVisitor::Post(const parser::ConcurrentControl &x) {
   auto &name{std::get<parser::Name>(x.t)};
   if (auto *symbol{DeclareConstructEntity(name)}) {
-    CheckIntegerType(*symbol);
+    CheckScalarIntegerType(*symbol);
   }
 }
 
@@ -3334,10 +3405,17 @@ void ConstructVisitor::CheckRef(const std::optional<parser::Name> &x) {
   }
 }
 
-void ConstructVisitor::CheckIntegerType(const Symbol &symbol) {
+void ConstructVisitor::CheckScalarIntegerType(const Symbol &symbol) {
+  if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
+    if (details->IsArray()) {
+      Say(symbol.name(), "Variable '%s' is not scalar"_err_en_US);
+      return;
+    }
+  }
   if (auto *type{symbol.GetType()}) {
     if (!type->IsNumeric(TypeCategory::Integer)) {
-      Say(symbol.name(), "Variable '%s' is not scalar integer"_err_en_US);
+      Say(symbol.name(), "Variable '%s' is not integer"_err_en_US);
+      return;
     }
   }
 }
index 8a80773..63f3c47 100644 (file)
@@ -38,7 +38,7 @@ class Scope {
 
 public:
   ENUM_CLASS(Kind, System, Global, Module, MainProgram, Subprogram, DerivedType,
-      Block, Forall)
+      Block, Forall, ImpliedDos)
   using ImportKind = common::ImportKind;
 
   // Create the Global scope -- the root of the scope tree
index 7f72ad6..9af2cbc 100644 (file)
@@ -71,9 +71,6 @@ bool Semantics::Perform() {
   if (AnyFatalError()) {
     return false;
   }
-  if (AnyFatalError()) {
-    return false;
-  }
   CheckDoConcurrentConstraints(context_.messages(), program_);
   if (AnyFatalError()) {
     return false;
@@ -83,10 +80,8 @@ bool Semantics::Perform() {
   if (AnyFatalError()) {
     return false;
   }
-  if (context_.debugExpressions()) {
-    AnalyzeExpressions(program_, context_);
-    AnalyzeAssignments(program_, context_);
-  }
+  AnalyzeExpressions(program_, context_);
+  AnalyzeAssignments(program_, context_);
   return !AnyFatalError();
 }
 
index de974b5..994ddd7 100644 (file)
@@ -46,7 +46,6 @@ public:
   }
   const std::string &moduleDirectory() const { return moduleDirectory_; }
   const bool warningsAreErrors() const { return warningsAreErrors_; }
-  const bool debugExpressions() const { return debugExpressions_; }
   const evaluate::IntrinsicProcTable &intrinsics() const { return intrinsics_; }
   Scope &globalScope() { return globalScope_; }
   parser::Messages &messages() { return messages_; }
@@ -64,10 +63,6 @@ public:
     warningsAreErrors_ = x;
     return *this;
   }
-  SemanticsContext &set_debugExpressions(bool x) {
-    debugExpressions_ = x;
-    return *this;
-  }
 
   const DeclTypeSpec &MakeNumericType(TypeCategory, int kind = 0);
   const DeclTypeSpec &MakeLogicalType(int kind = 0);
@@ -82,7 +77,6 @@ private:
   std::vector<std::string> searchDirectories_;
   std::string moduleDirectory_{"."s};
   bool warningsAreErrors_{false};
-  bool debugExpressions_{false};
   const evaluate::IntrinsicProcTable intrinsics_;
   Scope globalScope_;
   parser::Messages messages_;
index ce34065..655448c 100644 (file)
@@ -276,10 +276,6 @@ const LogicalTypeSpec &DeclTypeSpec::logicalTypeSpec() const {
   CHECK(category_ == Logical);
   return std::get<LogicalTypeSpec>(typeSpec_);
 }
-const CharacterTypeSpec &DeclTypeSpec::characterTypeSpec() const {
-  CHECK(category_ == Character);
-  return std::get<CharacterTypeSpec>(typeSpec_);
-}
 const DerivedTypeSpec &DeclTypeSpec::derivedTypeSpec() const {
   CHECK(category_ == TypeDerived || category_ == ClassDerived);
   return std::get<DerivedTypeSpec>(typeSpec_);
index 5223408..d81a6e6 100644 (file)
@@ -149,7 +149,7 @@ public:
   CharacterTypeSpec(ParamValue &&length, KindExpr &&kind)
     : IntrinsicTypeSpec(TypeCategory::Character, std::move(kind)),
       length_{std::move(length)} {}
-  const ParamValue length() const { return length_; }
+  const ParamValue &length() const { return length_; }
 
 private:
   ParamValue length_;
@@ -280,7 +280,10 @@ public:
   bool IsNumeric(TypeCategory) const;
   const NumericTypeSpec &numericTypeSpec() const;
   const LogicalTypeSpec &logicalTypeSpec() const;
-  const CharacterTypeSpec &characterTypeSpec() const;
+  const CharacterTypeSpec &characterTypeSpec() const {
+    CHECK(category_ == Character);
+    return std::get<CharacterTypeSpec>(typeSpec_);
+  }
   const DerivedTypeSpec &derivedTypeSpec() const;
   DerivedTypeSpec &derivedTypeSpec();
 
index 00fe442..b6cc7aa 100644 (file)
@@ -28,16 +28,3 @@ subroutine s2
     y = 1
   end block
 end
-
-subroutine s3
-  integer j
-  block
-    import, only: j
-    type t
-      !ERROR: 'i' from host scoping unit is not accessible due to IMPORT
-      real :: x(10) = [(i, &
-        !ERROR: 'i' from host scoping unit is not accessible due to IMPORT
-        i=1,10)]
-    end type
-  end block
-end subroutine
index 9832676..db12a9b 100644 (file)
@@ -45,14 +45,19 @@ end
 subroutine s4
   real :: a(10), b(10)
   complex :: x
-  !ERROR: Variable 'x' is not scalar integer
+  integer :: i(2)
+  !ERROR: Variable 'x' is not integer
   forall(x=1:10)
     a(x) = b(x)
   end forall
-  !ERROR: Variable 'y' is not scalar integer
+  !ERROR: Variable 'y' is not integer
   forall(y=1:10)
     a(y) = b(y)
   end forall
+  !ERROR: Variable 'i' is not scalar
+  forall(i=1:10)
+    a(i) = b(i)
+  end forall
 end
 
 subroutine s5
@@ -68,7 +73,7 @@ subroutine s6
   real, dimension(n) :: x
   data(x(i), i=1, n) / n * 0.0 /
   !ERROR: Index name 't' conflicts with existing identifier
-  data(x(t), t=1, n) / n * 0.0 /
+  forall(t=1:n) x(t) = 0.0
 contains
   subroutine t
   end
index b095989..29dbcc0 100644 (file)
@@ -92,7 +92,6 @@ struct DriverOptions {
   bool dumpUnparseWithSymbols{false};
   bool dumpParseTree{false};
   bool dumpSymbols{false};
-  bool debugExpressions{false};
   bool debugResolveNames{false};
   bool debugSemantics{false};
   bool measureTree{false};
@@ -213,7 +212,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
   }
   // TODO: Change this predicate to just "if (!driver.debugNoSemantics)"
   if (driver.debugSemantics || driver.debugResolveNames || driver.dumpSymbols ||
-      driver.dumpUnparseWithSymbols || driver.debugExpressions) {
+      driver.dumpUnparseWithSymbols) {
     Fortran::semantics::Semantics semantics{
         semanticsContext, parseTree, parsing.cooked()};
     semantics.Perform();
@@ -392,8 +391,6 @@ int main(int argc, char *const argv[]) {
       driver.dumpParseTree = true;
     } else if (arg == "-fdebug-dump-symbols") {
       driver.dumpSymbols = true;
-    } else if (arg == "-fdebug-expressions") {
-      driver.debugExpressions = true;
     } else if (arg == "-fdebug-resolve-names") {
       driver.debugResolveNames = true;
     } else if (arg == "-fdebug-measure-parse-tree") {
@@ -494,8 +491,7 @@ int main(int argc, char *const argv[]) {
   Fortran::semantics::SemanticsContext semanticsContext{defaultKinds};
   semanticsContext.set_moduleDirectory(driver.moduleDirectory)
       .set_searchDirectories(driver.searchDirectories)
-      .set_warningsAreErrors(driver.warningsAreErrors)
-      .set_debugExpressions(driver.debugExpressions);
+      .set_warningsAreErrors(driver.warningsAreErrors);
 
   if (!anyFiles) {
     driver.measureTree = true;