[flang] Type checking on values in structure components
authorpeter klausler <pklausler@nvidia.com>
Tue, 19 Feb 2019 23:38:55 +0000 (15:38 -0800)
committerpeter klausler <pklausler@nvidia.com>
Tue, 5 Mar 2019 00:30:22 +0000 (16:30 -0800)
Original-commit: flang-compiler/f18@bea98aeb9638b9d584488ae5faf72d53f1747a5f
Reviewed-on: https://github.com/flang-compiler/f18/pull/311
Tree-same-pre-rewrite: false

14 files changed:
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/semantics/expression.cc
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/scope.cc
flang/lib/semantics/scope.h
flang/lib/semantics/type.cc
flang/lib/semantics/type.h

index 5104ef9..637468c 100644 (file)
@@ -43,15 +43,18 @@ template<typename TO, TypeCategory FROMCAT>
 std::ostream &Convert<TO, FROMCAT>::AsFortran(std::ostream &o) const {
   static_assert(TO::category == TypeCategory::Integer ||
       TO::category == TypeCategory::Real ||
+      TO::category == TypeCategory::Character ||
       TO::category == TypeCategory::Logical || !"Convert<> to bad category!");
-  if constexpr (TO::category == TypeCategory::Integer) {
-    o << "int";
+  if constexpr (TO::category == TypeCategory::Character) {
+    this->left().AsFortran(o << "achar(iachar(") << ')';
+  } else if constexpr (TO::category == TypeCategory::Integer) {
+    this->left().AsFortran(o << "int(");
   } else if constexpr (TO::category == TypeCategory::Real) {
-    o << "real";
-  } else if constexpr (TO::category == TypeCategory::Logical) {
-    o << "logical";
+    this->left().AsFortran(o << "real(");
+  } else {
+    this->left().AsFortran(o << "logical(");
   }
-  return this->left().AsFortran(o << '(') << ",kind=" << TO::kind << ')';
+  return o << ",kind=" << TO::kind << ')';
 }
 
 template<typename A> std::ostream &Relational<A>::Infix(std::ostream &o) const {
@@ -160,6 +163,10 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
           },
           [](const ArrayConstructor<Result> &a) { return a.LEN(); },
           [](const Parentheses<Result> &x) { return x.left().LEN(); },
+          [](const Convert<Result> &x) {
+            return std::visit(
+                [&](const auto &kx) { return kx.LEN(); }, x.left().u);
+          },
           [](const Concat<KIND> &c) {
             return c.left().LEN() + c.right().LEN();
           },
index 235fde2..3a626da 100644 (file)
@@ -212,7 +212,7 @@ private:
 
 // Conversions to specific types from expressions of known category and
 // dynamic kind.
-template<typename TO, TypeCategory FROMCAT>
+template<typename TO, TypeCategory FROMCAT = TO::category>
 struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
   // 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.
@@ -221,6 +221,8 @@ struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
                      TO::category == TypeCategory::Real) &&
                     (FROMCAT == TypeCategory::Integer ||
                         FROMCAT == TypeCategory::Real)) ||
+      (TO::category == TypeCategory::Character &&
+          FROMCAT == TypeCategory::Character) ||
       (TO::category == TypeCategory::Logical &&
           FROMCAT == TypeCategory::Logical));
   using Result = TO;
@@ -572,7 +574,8 @@ public:
   Expr<SubscriptInteger> LEN() const;
 
   std::variant<Constant<Result>, ArrayConstructor<Result>, Designator<Result>,
-      FunctionRef<Result>, Parentheses<Result>, Concat<KIND>, Extremum<Result>>
+      FunctionRef<Result>, Parentheses<Result>, Convert<Result>, Concat<KIND>,
+      Extremum<Result>>
       u;
 };
 
@@ -641,8 +644,8 @@ public:
   explicit Expr(bool x) : u{Constant<Result>{x}} {}
 
 private:
-  using Operations = std::tuple<Convert<Result, TypeCategory::Logical>,
-      Parentheses<Result>, Not<KIND>, LogicalOperation<KIND>>;
+  using Operations = std::tuple<Convert<Result>, Parentheses<Result>, Not<KIND>,
+      LogicalOperation<KIND>>;
   using Relations = std::conditional_t<KIND == LogicalResult::kind,
       std::tuple<Relational<SomeType>>, std::tuple<>>;
   using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
index 1ca68e8..ca68247 100644 (file)
@@ -366,6 +366,23 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
 
 // Unary operations
 
+template<typename TO, typename FROM> std::optional<TO> ConvertString(FROM &&s) {
+  if constexpr (std::is_same_v<TO, FROM>) {
+    return std::make_optional<TO>(std::move(s));
+  } else {
+    // Fortran character conversion is well defined between distinct kinds
+    // only when the actual characters are valid 7-bit ASCII.
+    TO str;
+    for (auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
+      if (static_cast<std::uint64_t>(*iter) > 127) {
+        return std::nullopt;
+      }
+      str.push_back(*iter);
+    }
+    return std::make_optional<TO>(std::move(str));
+  }
+}
+
 template<typename TO, TypeCategory FROMCAT>
 Expr<TO> FoldOperation(
     FoldingContext &context, Convert<TO, FROMCAT> &&convert) {
@@ -383,7 +400,7 @@ Expr<TO> FoldOperation(
                     "INTEGER(%d) to INTEGER(%d) conversion overflowed"_en_US,
                     Operand::kind, TO::kind);
               }
-              return Expr<TO>{Constant<TO>{std::move(converted.value)}};
+              return ScalarConstantToExpr(std::move(converted.value));
             } else if constexpr (Operand::category == TypeCategory::Real) {
               auto converted{value->template ToInteger<Scalar<TO>>()};
               if (converted.flags.test(RealFlag::InvalidArgument)) {
@@ -395,7 +412,7 @@ Expr<TO> FoldOperation(
                     "REAL(%d) to INTEGER(%d) conversion overflowed"_en_US,
                     Operand::kind, TO::kind);
               }
-              return Expr<TO>{Constant<TO>{std::move(converted.value)}};
+              return ScalarConstantToExpr(std::move(converted.value));
             }
           } else if constexpr (TO::category == TypeCategory::Real) {
             if constexpr (Operand::category == TypeCategory::Integer) {
@@ -406,7 +423,7 @@ Expr<TO> FoldOperation(
                     TO::kind);
                 RealFlagWarnings(context, converted.flags, buffer);
               }
-              return Expr<TO>{Constant<TO>{std::move(converted.value)}};
+              return ScalarConstantToExpr(std::move(converted.value));
             } else if constexpr (Operand::category == TypeCategory::Real) {
               auto converted{Scalar<TO>::Convert(*value)};
               if (!converted.flags.empty()) {
@@ -417,11 +434,16 @@ Expr<TO> FoldOperation(
               if (context.flushSubnormalsToZero()) {
                 converted.value = converted.value.FlushSubnormalToZero();
               }
-              return Expr<TO>{Constant<TO>{std::move(converted.value)}};
+              return ScalarConstantToExpr(std::move(converted.value));
+            }
+          } else if constexpr (TO::category == TypeCategory::Character &&
+              Operand::category == TypeCategory::Character) {
+            if (auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {
+              return ScalarConstantToExpr(std::move(*converted));
             }
           } else if constexpr (TO::category == TypeCategory::Logical &&
               Operand::category == TypeCategory::Logical) {
-            return Expr<TO>{Constant<TO>{value->IsTrue()}};
+            return Expr<TO>{value->IsTrue()};
           }
         }
         return Expr<TO>{std::move(convert)};
index abb2061..4d93f23 100644 (file)
@@ -522,12 +522,9 @@ std::optional<Expr<SomeType>> ConvertToType(
   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);
-      }
+    if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
+      return Expr<SomeType>{
+          ConvertToKind<TypeCategory::Character>(type.kind, std::move(*cx))};
     }
     break;
   case TypeCategory::Logical:
@@ -538,7 +535,7 @@ std::optional<Expr<SomeType>> ConvertToType(
     break;
   case TypeCategory::Derived:
     if (auto fromType{x.GetType()}) {
-      if (type == fromType) {
+      if (type == *fromType) {
         return std::move(x);
       }
     }
@@ -549,6 +546,20 @@ std::optional<Expr<SomeType>> ConvertToType(
 }
 
 std::optional<Expr<SomeType>> ConvertToType(
+    const semantics::Symbol &symbol, Expr<SomeType> &&x) {
+  if (int xRank{x.Rank()}; xRank > 0) {
+    if (symbol.Rank() != xRank) {
+      return std::nullopt;
+    }
+  }
+  if (auto symType{GetSymbolType(symbol)}) {
+    // TODO pmk CHARACTER length
+    return ConvertToType(*symType, std::move(x));
+  }
+  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));
index 2aa4718..4f9b99e 100644 (file)
@@ -227,6 +227,8 @@ std::optional<Expr<SomeType>> ConvertToType(
     const DynamicType &, Expr<SomeType> &&);
 std::optional<Expr<SomeType>> ConvertToType(
     const DynamicType &, std::optional<Expr<SomeType>> &&);
+std::optional<Expr<SomeType>> ConvertToType(
+    const semantics::Symbol &, Expr<SomeType> &&);
 
 // Conversions to the type of another expression
 template<TypeCategory TC, int TK, typename FROM>
@@ -339,7 +341,7 @@ template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
   using Ty = TypeOf<A>;
   static_assert(
       std::is_same_v<Scalar<Ty>, std::decay_t<A>> || !"TypeOf<> is broken");
-  return {Constant<Ty>{x}};
+  return Expr<TypeOf<A>>{Constant<Ty>{x}};
 }
 
 // Combine two expressions of the same specific numeric type with an operation
index 0d71b34..6e92cfc 100644 (file)
@@ -89,9 +89,14 @@ bool IsDescriptor(const Symbol &symbol) {
 
 namespace Fortran::evaluate {
 
+template<typename A> bool PointeeComparison(const A *x, const A *y) {
+  return x == y || (x != nullptr && y != nullptr && *x == *y);
+}
+
 bool DynamicType::operator==(const DynamicType &that) const {
   return category == that.category && kind == that.kind &&
-      charLength == that.charLength && derived == that.derived;
+      PointeeComparison(charLength, that.charLength) &&
+      PointeeComparison(derived, that.derived);
 }
 
 std::optional<DynamicType> GetSymbolType(const semantics::Symbol *symbol) {
@@ -117,6 +122,14 @@ std::optional<DynamicType> GetSymbolType(const semantics::Symbol *symbol) {
   return std::nullopt;
 }
 
+std::optional<DynamicType> GetSymbolType(const semantics::Symbol *symbol) {
+  if (symbol != nullptr) {
+    return GetSymbolType(*symbol);
+  } else {
+    return std::nullopt;
+  }
+}
+
 std::string DynamicType::AsFortran() const {
   if (derived != nullptr) {
     CHECK(category == TypeCategory::Derived);
@@ -191,7 +204,7 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
 
 bool SomeKind<TypeCategory::Derived>::operator==(
     const SomeKind<TypeCategory::Derived> &that) const {
-  return spec_ == that.spec_;
+  return PointeeComparison(spec_, that.spec_);
 }
 
 std::string SomeDerived::AsFortran() const {
index fa83538..0ea3344 100644 (file)
@@ -84,6 +84,7 @@ struct DynamicType {
 
 // 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 &);
 std::optional<DynamicType> GetSymbolType(const semantics::Symbol *);
 
 template<TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
index 779998b..a931f5b 100644 (file)
@@ -638,10 +638,8 @@ template<typename T> const Symbol *Designator<T>::GetLastSymbol() const {
 template<typename T> std::optional<DynamicType> Designator<T>::GetType() const {
   if constexpr (IsLengthlessIntrinsicType<Result>) {
     return {Result::GetType()};
-  } else if (const Symbol * symbol{GetLastSymbol()}) {
-    return GetSymbolType(symbol);
   } else {
-    return std::nullopt;
+    return GetSymbolType(GetLastSymbol());
   }
 }
 
index b6b4654..666d089 100644 (file)
@@ -325,11 +325,10 @@ MaybeExpr TypedWrapper(const DynamicType &dyType, WRAPPED &&x) {
 }
 
 // Wraps a data reference in a typed Designator<>.
-static MaybeExpr Designate(DataRef &&dataRef) {
-  const Symbol &symbol{dataRef.GetLastSymbol()};
-  if (std::optional<DynamicType> dyType{GetSymbolType(&symbol)}) {
+static MaybeExpr Designate(DataRef &&ref) {
+  if (std::optional<DynamicType> dyType{GetSymbolType(ref.GetLastSymbol())}) {
     return TypedWrapper<Designator, DataRef>(
-        std::move(*dyType), std::move(dataRef));
+        std::move(*dyType), std::move(ref));
   }
   // TODO: graceful errors on CLASS(*) and TYPE(*) misusage
   return std::nullopt;
@@ -339,8 +338,7 @@ static MaybeExpr Designate(DataRef &&dataRef) {
 // 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 (std::optional<DynamicType> dyType{GetSymbolType(ref.GetLastSymbol())}) {
     if (dyType->category == TypeCategory::Character && ref.size() == 1) {
       DataRef base{std::visit([](auto &&y) { return DataRef{std::move(y)}; },
           std::move(ref.base()))};
@@ -855,7 +853,7 @@ static MaybeExpr AnalyzeExpr(
           std::optional<Expr<SubscriptInteger>> last{
               GetSubstringBound(context, std::get<1>(range.t))};
           const Symbol &symbol{checked->GetLastSymbol()};
-          if (std::optional<DynamicType> dynamicType{GetSymbolType(&symbol)}) {
+          if (std::optional<DynamicType> dynamicType{GetSymbolType(symbol)}) {
             if (dynamicType->category == TypeCategory::Character) {
               return WrapperHelper<TypeCategory::Character, Designator,
                   Substring>(dynamicType->kind,
@@ -1041,7 +1039,7 @@ static MaybeExpr AnalyzeExpr(
       }
       if (sym->detailsIf<semantics::TypeParamDetails>()) {
         if (auto *designator{UnwrapExpr<Designator<SomeDerived>>(*dtExpr)}) {
-          if (std::optional<DynamicType> dyType{GetSymbolType(sym)}) {
+          if (std::optional<DynamicType> dyType{GetSymbolType(*sym)}) {
             if (dyType->category == TypeCategory::Integer) {
               return AsMaybeExpr(
                   common::SearchTypes(TypeParamInquiryVisitor{dyType->kind,
@@ -1443,15 +1441,19 @@ static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context,
     if (symbol != nullptr) {
       if (symbol->has<semantics::TypeParamDetails>()) {
         context.Say(source,
-            "Type parameter '%s' cannot be a component of this structure constructor"_err_en_US,
+            "Type parameter '%s' cannot be a component of this structure "
+            "constructor"_err_en_US,
             symbol->name().ToString().data());
-      } else if (checkConflicts) {
+        continue;
+      }
+      if (checkConflicts) {
         auto componentIter{
             std::find(components.begin(), components.end(), symbol)};
         if (unavailable.find(symbol->name()) != unavailable.cend()) {
           // C797, C798
           context.Say(source,
-              "Component '%s' conflicts with another component earlier in this structure constructor"_err_en_US,
+              "Component '%s' conflicts with another component earlier in "
+              "this structure constructor"_err_en_US,
               symbol->name().ToString().data());
         } else if (symbol->test(Symbol::Flag::ParentComp)) {
           // Make earlier components unavailable once a whole parent appears.
@@ -1472,7 +1474,14 @@ static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context,
       if (MaybeExpr value{AnalyzeExpr(context, expr)}) {
         // TODO pmk: C7104, C7105 check that pointer components are
         // being initialized with data/procedure designators appropriately
-        result.Add(*symbol, std::move(*value));
+        if (MaybeExpr converted{ConvertToType(*symbol, std::move(*value))}) {
+          result.Add(*symbol, std::move(*converted));
+        } else {
+          if (auto *msg{context.Say(expr.source,
+                  "Structure constructor value is incompatible with component"_err_en_US)}) {
+            msg->Attach(symbol->name(), "Component declaration"_en_US);
+          }
+        }
       }
     }
   }
@@ -1488,7 +1497,8 @@ static MaybeExpr AnalyzeExpr(ExpressionAnalysisContext &context,
           result.Add(*symbol, common::Clone(*details->init()));
         } else {  // C799
           if (auto *msg{context.Say(typeName,
-                  "Structure constructor lacks a value for component '%s'"_err_en_US,
+                  "Structure constructor lacks a value for "
+                  "component '%s'"_err_en_US,
                   symbol->name().ToString().data())}) {
             msg->Attach(symbol->name(), "Absent component"_en_US);
           }
index 27a5f16..0340a06 100644 (file)
@@ -3291,6 +3291,7 @@ void DeclarationVisitor::Post(const parser::AllocateStmt &) {
 bool DeclarationVisitor::Pre(const parser::StructureConstructor &x) {
   auto &parsedType{std::get<parser::DerivedTypeSpec>(x.t)};
   const DeclTypeSpec *type{ProcessTypeSpec(parsedType)};
+
   if (type == nullptr) {
     return false;
   }
index 9be1c55..4ad0e3e 100644 (file)
@@ -120,14 +120,6 @@ const DeclTypeSpec &Scope::MakeDerivedType(
   return MakeDerivedType(std::move(spec), category);
 }
 
-const DeclTypeSpec &Scope::MakeDerivedType(DeclTypeSpec::Category category,
-    DerivedTypeSpec &&instance, SemanticsContext &semanticsContext) {
-  DeclTypeSpec &type{declTypeSpecs_.emplace_back(
-      category, DerivedTypeSpec{std::move(instance)})};
-  type.derivedTypeSpec().Instantiate(*this, semanticsContext);
-  return type;
-}
-
 DeclTypeSpec &Scope::MakeDerivedType(const Symbol &typeSymbol) {
   CHECK(typeSymbol.has<DerivedTypeDetails>());
   CHECK(typeSymbol.scope() != nullptr);
@@ -250,7 +242,10 @@ const DeclTypeSpec *Scope::FindInstantiatedDerivedType(
   if (typeIter != declTypeSpecs_.end()) {
     return &*typeIter;
   }
-  return nullptr;
+  if (&parent_ == this) {
+    return nullptr;
+  }
+  return parent_.FindInstantiatedDerivedType(spec, category);
 }
 
 const DeclTypeSpec &Scope::FindOrInstantiateDerivedType(DerivedTypeSpec &&spec,
index a04c9b2..cb107df 100644 (file)
@@ -144,8 +144,6 @@ public:
       ParamValue &&length, KindExpr &&kind = KindExpr{0});
   const DeclTypeSpec &MakeDerivedType(
       DeclTypeSpec::Category, DerivedTypeSpec &&);
-  const DeclTypeSpec &MakeDerivedType(
-      DeclTypeSpec::Category, DerivedTypeSpec &&, SemanticsContext &);
   DeclTypeSpec &MakeDerivedType(const Symbol &);
   DeclTypeSpec &MakeDerivedType(DerivedTypeSpec &&, DeclTypeSpec::Category);
   const DeclTypeSpec &MakeTypeStarType();
index 1bcbe9d..808674b 100644 (file)
@@ -42,10 +42,6 @@ void DerivedTypeSpec::set_scope(const Scope &scope) {
   scope_ = &scope;
 }
 
-bool DerivedTypeSpec::operator==(const DerivedTypeSpec &that) const {
-  return &typeSymbol_ == &that.typeSymbol_ && parameters_ == that.parameters_;
-}
-
 ParamValue &DerivedTypeSpec::AddParamValue(
     SourceName name, ParamValue &&value) {
   auto pair{parameters_.insert(std::make_pair(name, std::move(value)))};
@@ -227,10 +223,6 @@ void ParamValue::SetExplicit(SomeIntExpr &&x) {
   expr_ = std::move(x);
 }
 
-bool ParamValue::operator==(const ParamValue &that) const {
-  return category_ == that.category_ && expr_ == that.expr_;
-}
-
 std::ostream &operator<<(std::ostream &o, const ParamValue &x) {
   if (x.isAssumed()) {
     o << '*';
index 2ded0c4..20bd3a3 100644 (file)
@@ -101,7 +101,9 @@ public:
   bool isDeferred() const { return category_ == Category::Deferred; }
   const MaybeIntExpr &GetExplicit() const { return expr_; }
   void SetExplicit(SomeIntExpr &&);
-  bool operator==(const ParamValue &) const;
+  bool operator==(const ParamValue &that) const {
+    return category_ == that.category_ && expr_ == that.expr_;
+  }
 
 private:
   enum class Category { Explicit, Deferred, Assumed };
@@ -240,7 +242,9 @@ public:
   }
   void FoldParameterExpressions(evaluate::FoldingContext &);
   void Instantiate(Scope &, SemanticsContext &);
-  bool operator==(const DerivedTypeSpec &) const;  // for std::find()
+  bool operator==(const DerivedTypeSpec &that) const {
+    return &typeSymbol_ == &that.typeSymbol_ && parameters_ == that.parameters_;
+  }
 
 private:
   const Symbol &typeSymbol_;