[flang] work in progress

checkpoint: changes build, tests all pass

many fixes, ready to test more

Further CLASS(*) support

More fixes for CLASS(*)

Hide data members of DynamicType

implement PRESENT() intrinsic

Original-commit: flang-compiler/f18@044ba12c20f77c130e3c468914ddc2d6c8652c6e
Reviewed-on: https://github.com/flang-compiler/f18/pull/466
Tree-same-pre-rewrite: false
diff --git a/flang/lib/evaluate/intrinsics.cc b/flang/lib/evaluate/intrinsics.cc
index 521b29e..1957d0c 100644
--- a/flang/lib/evaluate/intrinsics.cc
+++ b/flang/lib/evaluate/intrinsics.cc
@@ -228,7 +228,7 @@
 // functions have more than one such pattern.  Besides the name
 // of the intrinsic function, each pattern has specifications for
 // the dummy arguments and for the result of the function.
-// The dummy argument patterns each have a name (this are from the
+// The dummy argument patterns each have a name (these are from the
 // standard, but rarely appear in actual code), a type and kind
 // pattern, allowable ranks, and optionality indicators.
 // Be advised, the default rank pattern is "elemental".
@@ -507,6 +507,7 @@
     {"product",
         {{"array", SameNumeric, Rank::array}, OptionalDIM, OptionalMASK},
         SameNumeric, Rank::dimReduced},
+    {"present", {{"a", Anything, Rank::anyOrAssumedRank}}, DefaultLogical},
     {"rank", {{"a", Anything, Rank::anyOrAssumedRank}}, DefaultInt},
     {"real", {{"a", AnyNumeric, Rank::elementalOrBOZ}, DefaultingKIND},
         KINDReal},
@@ -613,7 +614,7 @@
 //   COSHAPE
 // TODO: Object characteristic inquiry functions
 //   ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS,
-//   PRESENT, SAME_TYPE, STORAGE_SIZE
+//   SAME_TYPE, STORAGE_SIZE
 // TODO: Type inquiry intrinsic functions - these return constants
 //  BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT,
 //  NEW_LINE, PRECISION, RADIX, RANGE, TINY
@@ -906,7 +907,7 @@
             d.keyword);
       }
       return std::nullopt;
-    } else if (!d.typePattern.categorySet.test(type->category)) {
+    } else if (!d.typePattern.categorySet.test(type->category())) {
       messages.Say("Actual argument for '%s=' has bad type '%s'"_err_en_US,
           d.keyword, type->AsFortran());
       return std::nullopt;  // argument has invalid type category
@@ -919,29 +920,29 @@
       argOk = false;
       break;
     case KindCode::defaultIntegerKind:
-      argOk = type->kind == defaults.GetDefaultKind(TypeCategory::Integer);
+      argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Integer);
       break;
     case KindCode::defaultRealKind:
-      argOk = type->kind == defaults.GetDefaultKind(TypeCategory::Real);
+      argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Real);
       break;
     case KindCode::doublePrecision:
-      argOk = type->kind == defaults.doublePrecisionKind();
+      argOk = type->kind() == defaults.doublePrecisionKind();
       break;
     case KindCode::defaultCharKind:
-      argOk = type->kind == defaults.GetDefaultKind(TypeCategory::Character);
+      argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Character);
       break;
     case KindCode::defaultLogicalKind:
-      argOk = type->kind == defaults.GetDefaultKind(TypeCategory::Logical);
+      argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Logical);
       break;
     case KindCode::any: argOk = true; break;
     case KindCode::kindArg:
-      CHECK(type->category == TypeCategory::Integer);
+      CHECK(type->category() == TypeCategory::Integer);
       CHECK(kindArg == nullptr);
       kindArg = arg;
       argOk = true;
       break;
     case KindCode::dimArg:
-      CHECK(type->category == TypeCategory::Integer);
+      CHECK(type->category() == TypeCategory::Integer);
       hasDimArg = true;
       argOk = true;
       break;
@@ -997,7 +998,7 @@
         if (rank == 1) {
           if (auto shape{GetShape(context, *arg)}) {
             if (auto constShape{AsConstantShape(*shape)}) {
-              shapeArgSize = (**constShape).ToInt64();
+              shapeArgSize = constShape->At(ConstantSubscripts{1}).ToInt64();
               CHECK(shapeArgSize >= 0);
               argOk = true;
             }
@@ -1099,10 +1100,10 @@
     case KindCode::same:
       CHECK(sameArg != nullptr);
       if (std::optional<DynamicType> aType{sameArg->GetType()}) {
-        if (result.categorySet.test(aType->category)) {
+        if (result.categorySet.test(aType->category())) {
           resultType = *aType;
         } else {
-          resultType = DynamicType{*category, aType->kind};
+          resultType = DynamicType{*category, aType->kind()};
         }
       }
       break;
@@ -1378,7 +1379,7 @@
   }
   parser::Messages *finalBuffer{context.messages().messages()};
   // Special case: NULL()
-  if (call.name.ToString() == "null") {
+  if (call.name == "null") {
     parser::Messages nullBuffer;
     parser::ContextualMessages nullErrors{
         call.name, finalBuffer ? &nullBuffer : nullptr};
@@ -1390,33 +1391,55 @@
     return result;
   }
   // Probe the specific intrinsic function table first.
-  parser::Messages specificBuffer;
-  parser::ContextualMessages specificErrors{
-      call.name, finalBuffer ? &specificBuffer : nullptr};
-  FoldingContext specificContext{context, specificErrors};
+  parser::Messages localBuffer, specificBuffer;
+  parser::ContextualMessages localMessages{
+      call.name, finalBuffer ? &localBuffer : nullptr};
+  FoldingContext localContext{context, localMessages};
   std::string name{call.name.ToString()};
   auto specificRange{specificFuncs_.equal_range(name)};
   for (auto iter{specificRange.first}; iter != specificRange.second; ++iter) {
     if (auto specificCall{
-            iter->second->Match(call, defaults_, arguments, specificContext)}) {
+            iter->second->Match(call, defaults_, arguments, localContext)}) {
       if (const char *genericName{iter->second->generic}) {
         specificCall->specificIntrinsic.name = genericName;
       }
       specificCall->specificIntrinsic.isRestrictedSpecific =
           iter->second->isRestrictedSpecific;
+      if (finalBuffer != nullptr) {
+        finalBuffer->Annex(std::move(localBuffer));
+      }
       return specificCall;
+    } else {
+      specificBuffer.Annex(std::move(localBuffer));
     }
   }
   // Probe the generic intrinsic function table next.
   parser::Messages genericBuffer;
-  parser::ContextualMessages genericErrors{
-      call.name, finalBuffer ? &genericBuffer : nullptr};
-  FoldingContext genericContext{context, genericErrors};
   auto genericRange{genericFuncs_.equal_range(name)};
   for (auto iter{genericRange.first}; iter != genericRange.second; ++iter) {
     if (auto specificCall{
-            iter->second->Match(call, defaults_, arguments, genericContext)}) {
+            iter->second->Match(call, defaults_, arguments, localContext)}) {
+      // Apply any semantic checks peculiar to the intrinsic
+      if (call.name == "present") {
+        bool ok{false};
+        if (const auto &arg{specificCall->arguments[0]}) {
+          if (const auto *expr{arg->GetExpr()}) {
+            if (const Symbol * symbol{GetLastSymbol(*expr)}) {
+              ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
+            }
+          }
+        }
+        if (!ok) {
+          localMessages.Say(
+              "Argument of PRESENT() must be the name of an OPTIONAL dummy argument"_err_en_US);
+        }
+      }
+      if (finalBuffer != nullptr) {
+        finalBuffer->Annex(std::move(localBuffer));
+      }
       return specificCall;
+    } else {
+      genericBuffer.Annex(std::move(localBuffer));
     }
   }
   // No match; report the right errors, if any