1 //===-- lib/Semantics/runtime-type-info.cpp ---------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Semantics/runtime-type-info.h"
10 #include "mod-file.h"
11 #include "flang/Evaluate/fold-designator.h"
12 #include "flang/Evaluate/fold.h"
13 #include "flang/Evaluate/tools.h"
14 #include "flang/Evaluate/type.h"
15 #include "flang/Semantics/scope.h"
16 #include "flang/Semantics/tools.h"
17 #include <functional>
18 #include <list>
19 #include <map>
20 #include <string>
21 
22 namespace Fortran::semantics {
23 
24 static int FindLenParameterIndex(
25     const SymbolVector &parameters, const Symbol &symbol) {
26   int lenIndex{0};
27   for (SymbolRef ref : parameters) {
28     if (&*ref == &symbol) {
29       return lenIndex;
30     }
31     if (ref->get<TypeParamDetails>().attr() == common::TypeParamAttr::Len) {
32       ++lenIndex;
33     }
34   }
35   DIE("Length type parameter not found in parameter order");
36   return -1;
37 }
38 
39 class RuntimeTableBuilder {
40 public:
41   RuntimeTableBuilder(SemanticsContext &, RuntimeDerivedTypeTables &);
42   void DescribeTypes(Scope &scope, bool inSchemata);
43 
44 private:
45   const Symbol *DescribeType(Scope &);
46   const Symbol &GetSchemaSymbol(const char *) const;
47   const DeclTypeSpec &GetSchema(const char *) const;
48   SomeExpr GetEnumValue(const char *) const;
49   Symbol &CreateObject(const std::string &, const DeclTypeSpec &, Scope &);
50   // The names of created symbols are saved in and owned by the
51   // RuntimeDerivedTypeTables instance returned by
52   // BuildRuntimeDerivedTypeTables() so that references to those names remain
53   // valid for lowering.
54   SourceName SaveObjectName(const std::string &);
55   SomeExpr SaveNameAsPointerTarget(Scope &, const std::string &);
56   const SymbolVector *GetTypeParameters(const Symbol &);
57   evaluate::StructureConstructor DescribeComponent(const Symbol &,
58       const ObjectEntityDetails &, Scope &, Scope &,
59       const std::string &distinctName, const SymbolVector *parameters);
60   evaluate::StructureConstructor DescribeComponent(
61       const Symbol &, const ProcEntityDetails &, Scope &);
62   bool InitializeDataPointer(evaluate::StructureConstructorValues &,
63       const Symbol &symbol, const ObjectEntityDetails &object, Scope &scope,
64       Scope &dtScope, const std::string &distinctName);
65   evaluate::StructureConstructor PackageIntValue(
66       const SomeExpr &genre, std::int64_t = 0) const;
67   SomeExpr PackageIntValueExpr(const SomeExpr &genre, std::int64_t = 0) const;
68   std::vector<const Symbol *> CollectBindings(const Scope &dtScope) const;
69   std::vector<evaluate::StructureConstructor> DescribeBindings(
70       const Scope &dtScope, Scope &);
71   void DescribeGeneric(
72       const GenericDetails &, std::map<int, evaluate::StructureConstructor> &);
73   void DescribeSpecialProc(std::map<int, evaluate::StructureConstructor> &,
74       const Symbol &specificOrBinding, bool isAssignment, bool isFinal,
75       std::optional<GenericKind::DefinedIo>);
76   void IncorporateDefinedIoGenericInterfaces(
77       std::map<int, evaluate::StructureConstructor> &, GenericKind::DefinedIo,
78       const Scope *);
79 
80   // Instantiated for ParamValue and Bound
81   template <typename A>
82   evaluate::StructureConstructor GetValue(
83       const A &x, const SymbolVector *parameters) {
84     if (x.isExplicit()) {
85       return GetValue(x.GetExplicit(), parameters);
86     } else {
87       return PackageIntValue(deferredEnum_);
88     }
89   }
90 
91   // Specialization for optional<Expr<SomeInteger and SubscriptInteger>>
92   template <typename T>
93   evaluate::StructureConstructor GetValue(
94       const std::optional<evaluate::Expr<T>> &expr,
95       const SymbolVector *parameters) {
96     if (auto constValue{evaluate::ToInt64(expr)}) {
97       return PackageIntValue(explicitEnum_, *constValue);
98     }
99     if (expr) {
100       if (parameters) {
101         if (const Symbol * lenParam{evaluate::ExtractBareLenParameter(*expr)}) {
102           return PackageIntValue(
103               lenParameterEnum_, FindLenParameterIndex(*parameters, *lenParam));
104         }
105       }
106       context_.Say(location_,
107           "Specification expression '%s' is neither constant nor a length "
108           "type parameter"_err_en_US,
109           expr->AsFortran());
110     }
111     return PackageIntValue(deferredEnum_);
112   }
113 
114   SemanticsContext &context_;
115   RuntimeDerivedTypeTables &tables_;
116   std::map<const Symbol *, SymbolVector> orderedTypeParameters_;
117 
118   const DeclTypeSpec &derivedTypeSchema_; // TYPE(DerivedType)
119   const DeclTypeSpec &componentSchema_; // TYPE(Component)
120   const DeclTypeSpec &procPtrSchema_; // TYPE(ProcPtrComponent)
121   const DeclTypeSpec &valueSchema_; // TYPE(Value)
122   const DeclTypeSpec &bindingSchema_; // TYPE(Binding)
123   const DeclTypeSpec &specialSchema_; // TYPE(SpecialBinding)
124   SomeExpr deferredEnum_; // Value::Genre::Deferred
125   SomeExpr explicitEnum_; // Value::Genre::Explicit
126   SomeExpr lenParameterEnum_; // Value::Genre::LenParameter
127   SomeExpr scalarAssignmentEnum_; // SpecialBinding::Which::ScalarAssignment
128   SomeExpr
129       elementalAssignmentEnum_; // SpecialBinding::Which::ElementalAssignment
130   SomeExpr readFormattedEnum_; // SpecialBinding::Which::ReadFormatted
131   SomeExpr readUnformattedEnum_; // SpecialBinding::Which::ReadUnformatted
132   SomeExpr writeFormattedEnum_; // SpecialBinding::Which::WriteFormatted
133   SomeExpr writeUnformattedEnum_; // SpecialBinding::Which::WriteUnformatted
134   SomeExpr elementalFinalEnum_; // SpecialBinding::Which::ElementalFinal
135   SomeExpr assumedRankFinalEnum_; // SpecialBinding::Which::AssumedRankFinal
136   SomeExpr scalarFinalEnum_; // SpecialBinding::Which::ScalarFinal
137   parser::CharBlock location_;
138   std::set<const Scope *> ignoreScopes_;
139 };
140 
141 RuntimeTableBuilder::RuntimeTableBuilder(
142     SemanticsContext &c, RuntimeDerivedTypeTables &t)
143     : context_{c}, tables_{t}, derivedTypeSchema_{GetSchema("derivedtype")},
144       componentSchema_{GetSchema("component")}, procPtrSchema_{GetSchema(
145                                                     "procptrcomponent")},
146       valueSchema_{GetSchema("value")}, bindingSchema_{GetSchema("binding")},
147       specialSchema_{GetSchema("specialbinding")}, deferredEnum_{GetEnumValue(
148                                                        "deferred")},
149       explicitEnum_{GetEnumValue("explicit")}, lenParameterEnum_{GetEnumValue(
150                                                    "lenparameter")},
151       scalarAssignmentEnum_{GetEnumValue("scalarassignment")},
152       elementalAssignmentEnum_{GetEnumValue("elementalassignment")},
153       readFormattedEnum_{GetEnumValue("readformatted")},
154       readUnformattedEnum_{GetEnumValue("readunformatted")},
155       writeFormattedEnum_{GetEnumValue("writeformatted")},
156       writeUnformattedEnum_{GetEnumValue("writeunformatted")},
157       elementalFinalEnum_{GetEnumValue("elementalfinal")},
158       assumedRankFinalEnum_{GetEnumValue("assumedrankfinal")},
159       scalarFinalEnum_{GetEnumValue("scalarfinal")} {
160   ignoreScopes_.insert(tables_.schemata);
161 }
162 
163 void RuntimeTableBuilder::DescribeTypes(Scope &scope, bool inSchemata) {
164   inSchemata |= ignoreScopes_.find(&scope) != ignoreScopes_.end();
165   if (scope.IsDerivedType()) {
166     if (!inSchemata) { // don't loop trying to describe a schema
167       DescribeType(scope);
168     }
169   } else {
170     scope.InstantiateDerivedTypes();
171   }
172   for (Scope &child : scope.children()) {
173     DescribeTypes(child, inSchemata);
174   }
175 }
176 
177 // Returns derived type instantiation's parameters in declaration order
178 const SymbolVector *RuntimeTableBuilder::GetTypeParameters(
179     const Symbol &symbol) {
180   auto iter{orderedTypeParameters_.find(&symbol)};
181   if (iter != orderedTypeParameters_.end()) {
182     return &iter->second;
183   } else {
184     return &orderedTypeParameters_
185                 .emplace(&symbol, OrderParameterDeclarations(symbol))
186                 .first->second;
187   }
188 }
189 
190 static Scope &GetContainingNonDerivedScope(Scope &scope) {
191   Scope *p{&scope};
192   while (p->IsDerivedType()) {
193     p = &p->parent();
194   }
195   return *p;
196 }
197 
198 static const Symbol &GetSchemaField(
199     const DerivedTypeSpec &derived, const std::string &name) {
200   const Scope &scope{
201       DEREF(derived.scope() ? derived.scope() : derived.typeSymbol().scope())};
202   auto iter{scope.find(SourceName(name))};
203   CHECK(iter != scope.end());
204   return *iter->second;
205 }
206 
207 static const Symbol &GetSchemaField(
208     const DeclTypeSpec &derived, const std::string &name) {
209   return GetSchemaField(DEREF(derived.AsDerived()), name);
210 }
211 
212 static evaluate::StructureConstructorValues &AddValue(
213     evaluate::StructureConstructorValues &values, const DeclTypeSpec &spec,
214     const std::string &name, SomeExpr &&x) {
215   values.emplace(GetSchemaField(spec, name), std::move(x));
216   return values;
217 }
218 
219 static evaluate::StructureConstructorValues &AddValue(
220     evaluate::StructureConstructorValues &values, const DeclTypeSpec &spec,
221     const std::string &name, const SomeExpr &x) {
222   values.emplace(GetSchemaField(spec, name), x);
223   return values;
224 }
225 
226 static SomeExpr IntToExpr(std::int64_t n) {
227   return evaluate::AsGenericExpr(evaluate::ExtentExpr{n});
228 }
229 
230 static evaluate::StructureConstructor Structure(
231     const DeclTypeSpec &spec, evaluate::StructureConstructorValues &&values) {
232   return {DEREF(spec.AsDerived()), std::move(values)};
233 }
234 
235 static SomeExpr StructureExpr(evaluate::StructureConstructor &&x) {
236   return SomeExpr{evaluate::Expr<evaluate::SomeDerived>{std::move(x)}};
237 }
238 
239 static int GetIntegerKind(const Symbol &symbol) {
240   auto dyType{evaluate::DynamicType::From(symbol)};
241   CHECK(dyType && dyType->category() == TypeCategory::Integer);
242   return dyType->kind();
243 }
244 
245 static void SetReadOnlyCompilerCreatedFlags(Symbol &symbol) {
246   symbol.set(Symbol::Flag::CompilerCreated);
247   // Runtime type info symbols may have types that are incompatible with the
248   // PARAMETER attribute (the main issue is that they may be TARGET, and normal
249   // Fortran parameters cannot be TARGETs).
250   if (symbol.has<semantics::ObjectEntityDetails>() ||
251       symbol.has<semantics::ProcEntityDetails>()) {
252     symbol.set(Symbol::Flag::ReadOnly);
253   }
254 }
255 
256 // Save a rank-1 array constant of some numeric type as an
257 // initialized data object in a scope.
258 template <typename T>
259 static SomeExpr SaveNumericPointerTarget(
260     Scope &scope, SourceName name, std::vector<typename T::Scalar> &&x) {
261   if (x.empty()) {
262     return SomeExpr{evaluate::NullPointer{}};
263   } else {
264     ObjectEntityDetails object;
265     if (const auto *spec{scope.FindType(
266             DeclTypeSpec{NumericTypeSpec{T::category, KindExpr{T::kind}}})}) {
267       object.set_type(*spec);
268     } else {
269       object.set_type(scope.MakeNumericType(T::category, KindExpr{T::kind}));
270     }
271     auto elements{static_cast<evaluate::ConstantSubscript>(x.size())};
272     ArraySpec arraySpec;
273     arraySpec.push_back(ShapeSpec::MakeExplicit(Bound{0}, Bound{elements - 1}));
274     object.set_shape(arraySpec);
275     object.set_init(evaluate::AsGenericExpr(evaluate::Constant<T>{
276         std::move(x), evaluate::ConstantSubscripts{elements}}));
277     Symbol &symbol{*scope
278                         .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE},
279                             std::move(object))
280                         .first->second};
281     SetReadOnlyCompilerCreatedFlags(symbol);
282     return evaluate::AsGenericExpr(
283         evaluate::Expr<T>{evaluate::Designator<T>{symbol}});
284   }
285 }
286 
287 // Save an arbitrarily shaped array constant of some derived type
288 // as an initialized data object in a scope.
289 static SomeExpr SaveDerivedPointerTarget(Scope &scope, SourceName name,
290     std::vector<evaluate::StructureConstructor> &&x,
291     evaluate::ConstantSubscripts &&shape) {
292   if (x.empty()) {
293     return SomeExpr{evaluate::NullPointer{}};
294   } else {
295     const auto &derivedType{x.front().GetType().GetDerivedTypeSpec()};
296     ObjectEntityDetails object;
297     DeclTypeSpec typeSpec{DeclTypeSpec::TypeDerived, derivedType};
298     if (const DeclTypeSpec * spec{scope.FindType(typeSpec)}) {
299       object.set_type(*spec);
300     } else {
301       object.set_type(scope.MakeDerivedType(
302           DeclTypeSpec::TypeDerived, common::Clone(derivedType)));
303     }
304     if (!shape.empty()) {
305       ArraySpec arraySpec;
306       for (auto n : shape) {
307         arraySpec.push_back(ShapeSpec::MakeExplicit(Bound{0}, Bound{n - 1}));
308       }
309       object.set_shape(arraySpec);
310     }
311     object.set_init(
312         evaluate::AsGenericExpr(evaluate::Constant<evaluate::SomeDerived>{
313             derivedType, std::move(x), std::move(shape)}));
314     Symbol &symbol{*scope
315                         .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE},
316                             std::move(object))
317                         .first->second};
318     SetReadOnlyCompilerCreatedFlags(symbol);
319     return evaluate::AsGenericExpr(
320         evaluate::Designator<evaluate::SomeDerived>{symbol});
321   }
322 }
323 
324 static SomeExpr SaveObjectInit(
325     Scope &scope, SourceName name, const ObjectEntityDetails &object) {
326   Symbol &symbol{*scope
327                       .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE},
328                           ObjectEntityDetails{object})
329                       .first->second};
330   CHECK(symbol.get<ObjectEntityDetails>().init().has_value());
331   SetReadOnlyCompilerCreatedFlags(symbol);
332   return evaluate::AsGenericExpr(
333       evaluate::Designator<evaluate::SomeDerived>{symbol});
334 }
335 
336 template <int KIND> static SomeExpr IntExpr(std::int64_t n) {
337   return evaluate::AsGenericExpr(
338       evaluate::Constant<evaluate::Type<TypeCategory::Integer, KIND>>{n});
339 }
340 
341 static std::optional<std::string> GetSuffixIfTypeKindParameters(
342     const DerivedTypeSpec &derivedTypeSpec, const SymbolVector *parameters) {
343   if (parameters) {
344     std::optional<std::string> suffix;
345     for (SymbolRef ref : *parameters) {
346       const auto &tpd{ref->get<TypeParamDetails>()};
347       if (tpd.attr() == common::TypeParamAttr::Kind) {
348         if (const auto *pv{derivedTypeSpec.FindParameter(ref->name())}) {
349           if (pv->GetExplicit()) {
350             if (auto instantiatedValue{evaluate::ToInt64(*pv->GetExplicit())}) {
351               if (suffix.has_value()) {
352                 *suffix += "."s + std::to_string(*instantiatedValue);
353               } else {
354                 suffix = "."s + std::to_string(*instantiatedValue);
355               }
356             }
357           }
358         }
359       }
360     }
361     return suffix;
362   }
363   return std::nullopt;
364 }
365 
366 const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) {
367   if (const Symbol * info{dtScope.runtimeDerivedTypeDescription()}) {
368     return info;
369   }
370   const DerivedTypeSpec *derivedTypeSpec{dtScope.derivedTypeSpec()};
371   if (!derivedTypeSpec && !dtScope.IsDerivedTypeWithKindParameter() &&
372       dtScope.symbol()) {
373     // This derived type was declared (obviously, there's a Scope) but never
374     // used in this compilation (no instantiated DerivedTypeSpec points here).
375     // Create a DerivedTypeSpec now for it so that ComponentIterator
376     // will work. This covers the case of a derived type that's declared in
377     // a module but used only by clients and submodules, enabling the
378     // run-time "no initialization needed here" flag to work.
379     DerivedTypeSpec derived{dtScope.symbol()->name(), *dtScope.symbol()};
380     if (const SymbolVector *
381         lenParameters{GetTypeParameters(*dtScope.symbol())}) {
382       // Create dummy deferred values for the length parameters so that the
383       // DerivedTypeSpec is complete and can be used in helpers.
384       for (SymbolRef lenParam : *lenParameters) {
385         (void)lenParam;
386         derived.AddRawParamValue(
387             std::nullopt, ParamValue::Deferred(common::TypeParamAttr::Len));
388       }
389       derived.CookParameters(context_.foldingContext());
390     }
391     DeclTypeSpec &decl{
392         dtScope.MakeDerivedType(DeclTypeSpec::TypeDerived, std::move(derived))};
393     derivedTypeSpec = &decl.derivedTypeSpec();
394   }
395   const Symbol *dtSymbol{
396       derivedTypeSpec ? &derivedTypeSpec->typeSymbol() : dtScope.symbol()};
397   if (!dtSymbol) {
398     return nullptr;
399   }
400   auto locationRestorer{common::ScopedSet(location_, dtSymbol->name())};
401   // Check for an existing description that can be imported from a USE'd module
402   std::string typeName{dtSymbol->name().ToString()};
403   if (typeName.empty() ||
404       (typeName.front() == '.' && !context_.IsTempName(typeName))) {
405     return nullptr;
406   }
407   const SymbolVector *parameters{GetTypeParameters(*dtSymbol)};
408   std::string distinctName{typeName};
409   if (&dtScope != dtSymbol->scope() && derivedTypeSpec) {
410     // Only create new type descriptions for different kind parameter values.
411     // Type with different length parameters/same kind parameters can all
412     // share the same type description available in the current scope.
413     if (auto suffix{
414             GetSuffixIfTypeKindParameters(*derivedTypeSpec, parameters)}) {
415       distinctName += *suffix;
416     }
417   }
418   std::string dtDescName{".dt."s + distinctName};
419   Scope *dtSymbolScope{const_cast<Scope *>(dtSymbol->scope())};
420   Scope &scope{
421       GetContainingNonDerivedScope(dtSymbolScope ? *dtSymbolScope : dtScope)};
422   if (const auto it{scope.find(SourceName{dtDescName})}; it != scope.end()) {
423     dtScope.set_runtimeDerivedTypeDescription(*it->second);
424     return &*it->second;
425   }
426 
427   // Create a new description object before populating it so that mutual
428   // references will work as pointer targets.
429   Symbol &dtObject{CreateObject(dtDescName, derivedTypeSchema_, scope)};
430   dtScope.set_runtimeDerivedTypeDescription(dtObject);
431   evaluate::StructureConstructorValues dtValues;
432   AddValue(dtValues, derivedTypeSchema_, "name"s,
433       SaveNameAsPointerTarget(scope, typeName));
434   bool isPDTdefinitionWithKindParameters{
435       !derivedTypeSpec && dtScope.IsDerivedTypeWithKindParameter()};
436   if (!isPDTdefinitionWithKindParameters) {
437     auto sizeInBytes{static_cast<common::ConstantSubscript>(dtScope.size())};
438     if (auto alignment{dtScope.alignment().value_or(0)}) {
439       sizeInBytes += alignment - 1;
440       sizeInBytes /= alignment;
441       sizeInBytes *= alignment;
442     }
443     AddValue(
444         dtValues, derivedTypeSchema_, "sizeinbytes"s, IntToExpr(sizeInBytes));
445   }
446   bool isPDTinstantiation{derivedTypeSpec && &dtScope != dtSymbol->scope()};
447   if (isPDTinstantiation) {
448     // is PDT instantiation
449     const Symbol *uninstDescObject{
450         DescribeType(DEREF(const_cast<Scope *>(dtSymbol->scope())))};
451     AddValue(dtValues, derivedTypeSchema_, "uninstantiated"s,
452         evaluate::AsGenericExpr(evaluate::Expr<evaluate::SomeDerived>{
453             evaluate::Designator<evaluate::SomeDerived>{
454                 DEREF(uninstDescObject)}}));
455   } else {
456     AddValue(dtValues, derivedTypeSchema_, "uninstantiated"s,
457         SomeExpr{evaluate::NullPointer{}});
458   }
459   using Int8 = evaluate::Type<TypeCategory::Integer, 8>;
460   using Int1 = evaluate::Type<TypeCategory::Integer, 1>;
461   std::vector<Int8::Scalar> kinds;
462   std::vector<Int1::Scalar> lenKinds;
463   if (parameters) {
464     // Package the derived type's parameters in declaration order for
465     // each category of parameter.  KIND= type parameters are described
466     // by their instantiated (or default) values, while LEN= type
467     // parameters are described by their INTEGER kinds.
468     for (SymbolRef ref : *parameters) {
469       const auto &tpd{ref->get<TypeParamDetails>()};
470       if (tpd.attr() == common::TypeParamAttr::Kind) {
471         auto value{evaluate::ToInt64(tpd.init()).value_or(0)};
472         if (derivedTypeSpec) {
473           if (const auto *pv{derivedTypeSpec->FindParameter(ref->name())}) {
474             if (pv->GetExplicit()) {
475               if (auto instantiatedValue{
476                       evaluate::ToInt64(*pv->GetExplicit())}) {
477                 value = *instantiatedValue;
478               }
479             }
480           }
481         }
482         kinds.emplace_back(value);
483       } else { // LEN= parameter
484         lenKinds.emplace_back(GetIntegerKind(*ref));
485       }
486     }
487   }
488   AddValue(dtValues, derivedTypeSchema_, "kindparameter"s,
489       SaveNumericPointerTarget<Int8>(
490           scope, SaveObjectName(".kp."s + distinctName), std::move(kinds)));
491   AddValue(dtValues, derivedTypeSchema_, "lenparameterkind"s,
492       SaveNumericPointerTarget<Int1>(
493           scope, SaveObjectName(".lpk."s + distinctName), std::move(lenKinds)));
494   // Traverse the components of the derived type
495   if (!isPDTdefinitionWithKindParameters) {
496     std::vector<const Symbol *> dataComponentSymbols;
497     std::vector<evaluate::StructureConstructor> procPtrComponents;
498     std::map<int, evaluate::StructureConstructor> specials;
499     for (const auto &pair : dtScope) {
500       const Symbol &symbol{*pair.second};
501       auto locationRestorer{common::ScopedSet(location_, symbol.name())};
502       common::visit(
503           common::visitors{
504               [&](const TypeParamDetails &) {
505                 // already handled above in declaration order
506               },
507               [&](const ObjectEntityDetails &) {
508                 dataComponentSymbols.push_back(&symbol);
509               },
510               [&](const ProcEntityDetails &proc) {
511                 if (IsProcedurePointer(symbol)) {
512                   procPtrComponents.emplace_back(
513                       DescribeComponent(symbol, proc, scope));
514                 }
515               },
516               [&](const ProcBindingDetails &) { // handled in a later pass
517               },
518               [&](const GenericDetails &generic) {
519                 DescribeGeneric(generic, specials);
520               },
521               [&](const auto &) {
522                 common::die(
523                     "unexpected details on symbol '%s' in derived type scope",
524                     symbol.name().ToString().c_str());
525               },
526           },
527           symbol.details());
528     }
529     // Sort the data component symbols by offset before emitting them
530     std::sort(dataComponentSymbols.begin(), dataComponentSymbols.end(),
531         [](const Symbol *x, const Symbol *y) {
532           return x->offset() < y->offset();
533         });
534     std::vector<evaluate::StructureConstructor> dataComponents;
535     for (const Symbol *symbol : dataComponentSymbols) {
536       auto locationRestorer{common::ScopedSet(location_, symbol->name())};
537       dataComponents.emplace_back(
538           DescribeComponent(*symbol, symbol->get<ObjectEntityDetails>(), scope,
539               dtScope, distinctName, parameters));
540     }
541     AddValue(dtValues, derivedTypeSchema_, "component"s,
542         SaveDerivedPointerTarget(scope, SaveObjectName(".c."s + distinctName),
543             std::move(dataComponents),
544             evaluate::ConstantSubscripts{
545                 static_cast<evaluate::ConstantSubscript>(
546                     dataComponents.size())}));
547     AddValue(dtValues, derivedTypeSchema_, "procptr"s,
548         SaveDerivedPointerTarget(scope, SaveObjectName(".p."s + distinctName),
549             std::move(procPtrComponents),
550             evaluate::ConstantSubscripts{
551                 static_cast<evaluate::ConstantSubscript>(
552                     procPtrComponents.size())}));
553     // Compile the "vtable" of type-bound procedure bindings
554     std::vector<evaluate::StructureConstructor> bindings{
555         DescribeBindings(dtScope, scope)};
556     AddValue(dtValues, derivedTypeSchema_, "binding"s,
557         SaveDerivedPointerTarget(scope, SaveObjectName(".v."s + distinctName),
558             std::move(bindings),
559             evaluate::ConstantSubscripts{
560                 static_cast<evaluate::ConstantSubscript>(bindings.size())}));
561     // Describe "special" bindings to defined assignments, FINAL subroutines,
562     // and user-defined derived type I/O subroutines.
563     const DerivedTypeDetails &dtDetails{dtSymbol->get<DerivedTypeDetails>()};
564     for (const auto &pair : dtDetails.finals()) {
565       DescribeSpecialProc(
566           specials, *pair.second, false /*!isAssignment*/, true, std::nullopt);
567     }
568     IncorporateDefinedIoGenericInterfaces(
569         specials, GenericKind::DefinedIo::ReadFormatted, &scope);
570     IncorporateDefinedIoGenericInterfaces(
571         specials, GenericKind::DefinedIo::ReadUnformatted, &scope);
572     IncorporateDefinedIoGenericInterfaces(
573         specials, GenericKind::DefinedIo::WriteFormatted, &scope);
574     IncorporateDefinedIoGenericInterfaces(
575         specials, GenericKind::DefinedIo::WriteUnformatted, &scope);
576     // Pack the special procedure bindings in ascending order of their "which"
577     // code values, and compile a little-endian bit-set of those codes for
578     // use in O(1) look-up at run time.
579     std::vector<evaluate::StructureConstructor> sortedSpecials;
580     std::uint32_t specialBitSet{0};
581     for (auto &pair : specials) {
582       auto bit{std::uint32_t{1} << pair.first};
583       CHECK(!(specialBitSet & bit));
584       specialBitSet |= bit;
585       sortedSpecials.emplace_back(std::move(pair.second));
586     }
587     AddValue(dtValues, derivedTypeSchema_, "special"s,
588         SaveDerivedPointerTarget(scope, SaveObjectName(".s."s + distinctName),
589             std::move(sortedSpecials),
590             evaluate::ConstantSubscripts{
591                 static_cast<evaluate::ConstantSubscript>(specials.size())}));
592     AddValue(dtValues, derivedTypeSchema_, "specialbitset"s,
593         IntExpr<4>(specialBitSet));
594     // Note the presence/absence of a parent component
595     AddValue(dtValues, derivedTypeSchema_, "hasparent"s,
596         IntExpr<1>(dtScope.GetDerivedTypeParent() != nullptr));
597     // To avoid wasting run time attempting to initialize derived type
598     // instances without any initialized components, analyze the type
599     // and set a flag if there's nothing to do for it at run time.
600     AddValue(dtValues, derivedTypeSchema_, "noinitializationneeded"s,
601         IntExpr<1>(
602             derivedTypeSpec && !derivedTypeSpec->HasDefaultInitialization()));
603     // Similarly, a flag to short-circuit destruction when not needed.
604     AddValue(dtValues, derivedTypeSchema_, "nodestructionneeded"s,
605         IntExpr<1>(derivedTypeSpec && !derivedTypeSpec->HasDestruction()));
606     // Similarly, a flag to short-circuit finalization when not needed.
607     AddValue(dtValues, derivedTypeSchema_, "nofinalizationneeded"s,
608         IntExpr<1>(derivedTypeSpec && !IsFinalizable(*derivedTypeSpec)));
609   }
610   dtObject.get<ObjectEntityDetails>().set_init(MaybeExpr{
611       StructureExpr(Structure(derivedTypeSchema_, std::move(dtValues)))});
612   return &dtObject;
613 }
614 
615 static const Symbol &GetSymbol(const Scope &schemata, SourceName name) {
616   auto iter{schemata.find(name)};
617   CHECK(iter != schemata.end());
618   const Symbol &symbol{*iter->second};
619   return symbol;
620 }
621 
622 const Symbol &RuntimeTableBuilder::GetSchemaSymbol(const char *name) const {
623   return GetSymbol(
624       DEREF(tables_.schemata), SourceName{name, std::strlen(name)});
625 }
626 
627 const DeclTypeSpec &RuntimeTableBuilder::GetSchema(
628     const char *schemaName) const {
629   Scope &schemata{DEREF(tables_.schemata)};
630   SourceName name{schemaName, std::strlen(schemaName)};
631   const Symbol &symbol{GetSymbol(schemata, name)};
632   CHECK(symbol.has<DerivedTypeDetails>());
633   CHECK(symbol.scope());
634   CHECK(symbol.scope()->IsDerivedType());
635   const DeclTypeSpec *spec{nullptr};
636   if (symbol.scope()->derivedTypeSpec()) {
637     DeclTypeSpec typeSpec{
638         DeclTypeSpec::TypeDerived, *symbol.scope()->derivedTypeSpec()};
639     spec = schemata.FindType(typeSpec);
640   }
641   if (!spec) {
642     DeclTypeSpec typeSpec{
643         DeclTypeSpec::TypeDerived, DerivedTypeSpec{name, symbol}};
644     spec = schemata.FindType(typeSpec);
645   }
646   if (!spec) {
647     spec = &schemata.MakeDerivedType(
648         DeclTypeSpec::TypeDerived, DerivedTypeSpec{name, symbol});
649   }
650   CHECK(spec->AsDerived());
651   return *spec;
652 }
653 
654 SomeExpr RuntimeTableBuilder::GetEnumValue(const char *name) const {
655   const Symbol &symbol{GetSchemaSymbol(name)};
656   auto value{evaluate::ToInt64(symbol.get<ObjectEntityDetails>().init())};
657   CHECK(value.has_value());
658   return IntExpr<1>(*value);
659 }
660 
661 Symbol &RuntimeTableBuilder::CreateObject(
662     const std::string &name, const DeclTypeSpec &type, Scope &scope) {
663   ObjectEntityDetails object;
664   object.set_type(type);
665   auto pair{scope.try_emplace(SaveObjectName(name),
666       Attrs{Attr::TARGET, Attr::SAVE}, std::move(object))};
667   CHECK(pair.second);
668   Symbol &result{*pair.first->second};
669   SetReadOnlyCompilerCreatedFlags(result);
670   return result;
671 }
672 
673 SourceName RuntimeTableBuilder::SaveObjectName(const std::string &name) {
674   return *tables_.names.insert(name).first;
675 }
676 
677 SomeExpr RuntimeTableBuilder::SaveNameAsPointerTarget(
678     Scope &scope, const std::string &name) {
679   CHECK(!name.empty());
680   CHECK(name.front() != '.' || context_.IsTempName(name));
681   ObjectEntityDetails object;
682   auto len{static_cast<common::ConstantSubscript>(name.size())};
683   if (const auto *spec{scope.FindType(DeclTypeSpec{CharacterTypeSpec{
684           ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}}})}) {
685     object.set_type(*spec);
686   } else {
687     object.set_type(scope.MakeCharacterType(
688         ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}));
689   }
690   using evaluate::Ascii;
691   using AsciiExpr = evaluate::Expr<Ascii>;
692   object.set_init(evaluate::AsGenericExpr(AsciiExpr{name}));
693   Symbol &symbol{*scope
694                       .try_emplace(SaveObjectName(".n."s + name),
695                           Attrs{Attr::TARGET, Attr::SAVE}, std::move(object))
696                       .first->second};
697   SetReadOnlyCompilerCreatedFlags(symbol);
698   return evaluate::AsGenericExpr(
699       AsciiExpr{evaluate::Designator<Ascii>{symbol}});
700 }
701 
702 evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
703     const Symbol &symbol, const ObjectEntityDetails &object, Scope &scope,
704     Scope &dtScope, const std::string &distinctName,
705     const SymbolVector *parameters) {
706   evaluate::StructureConstructorValues values;
707   auto &foldingContext{context_.foldingContext()};
708   auto typeAndShape{evaluate::characteristics::TypeAndShape::Characterize(
709       symbol, foldingContext)};
710   CHECK(typeAndShape.has_value());
711   auto dyType{typeAndShape->type()};
712   const auto &shape{typeAndShape->shape()};
713   AddValue(values, componentSchema_, "name"s,
714       SaveNameAsPointerTarget(scope, symbol.name().ToString()));
715   AddValue(values, componentSchema_, "category"s,
716       IntExpr<1>(static_cast<int>(dyType.category())));
717   if (dyType.IsUnlimitedPolymorphic() ||
718       dyType.category() == TypeCategory::Derived) {
719     AddValue(values, componentSchema_, "kind"s, IntExpr<1>(0));
720   } else {
721     AddValue(values, componentSchema_, "kind"s, IntExpr<1>(dyType.kind()));
722   }
723   AddValue(values, componentSchema_, "offset"s, IntExpr<8>(symbol.offset()));
724   // CHARACTER length
725   auto len{typeAndShape->LEN()};
726   if (const semantics::DerivedTypeSpec *
727       pdtInstance{dtScope.derivedTypeSpec()}) {
728     auto restorer{foldingContext.WithPDTInstance(*pdtInstance)};
729     len = Fold(foldingContext, std::move(len));
730   }
731   if (dyType.category() == TypeCategory::Character && len) {
732     // Ignore IDIM(x) (represented as MAX(0, x))
733     if (const auto *clamped{evaluate::UnwrapExpr<
734             evaluate::Extremum<evaluate::SubscriptInteger>>(*len)}) {
735       if (clamped->ordering == evaluate::Ordering::Greater &&
736           clamped->left() == evaluate::Expr<evaluate::SubscriptInteger>{0}) {
737         len = clamped->right();
738       }
739     }
740     AddValue(values, componentSchema_, "characterlen"s,
741         evaluate::AsGenericExpr(GetValue(len, parameters)));
742   } else {
743     AddValue(values, componentSchema_, "characterlen"s,
744         PackageIntValueExpr(deferredEnum_));
745   }
746   // Describe component's derived type
747   std::vector<evaluate::StructureConstructor> lenParams;
748   if (dyType.category() == TypeCategory::Derived &&
749       !dyType.IsUnlimitedPolymorphic()) {
750     const DerivedTypeSpec &spec{dyType.GetDerivedTypeSpec()};
751     Scope *derivedScope{const_cast<Scope *>(
752         spec.scope() ? spec.scope() : spec.typeSymbol().scope())};
753     const Symbol *derivedDescription{DescribeType(DEREF(derivedScope))};
754     AddValue(values, componentSchema_, "derived"s,
755         evaluate::AsGenericExpr(evaluate::Expr<evaluate::SomeDerived>{
756             evaluate::Designator<evaluate::SomeDerived>{
757                 DEREF(derivedDescription)}}));
758     // Package values of LEN parameters, if any
759     if (const SymbolVector * specParams{GetTypeParameters(spec.typeSymbol())}) {
760       for (SymbolRef ref : *specParams) {
761         const auto &tpd{ref->get<TypeParamDetails>()};
762         if (tpd.attr() == common::TypeParamAttr::Len) {
763           if (const ParamValue * paramValue{spec.FindParameter(ref->name())}) {
764             lenParams.emplace_back(GetValue(*paramValue, parameters));
765           } else {
766             lenParams.emplace_back(GetValue(tpd.init(), parameters));
767           }
768         }
769       }
770     }
771   } else {
772     // Subtle: a category of Derived with a null derived type pointer
773     // signifies CLASS(*)
774     AddValue(values, componentSchema_, "derived"s,
775         SomeExpr{evaluate::NullPointer{}});
776   }
777   // LEN type parameter values for the component's type
778   if (!lenParams.empty()) {
779     AddValue(values, componentSchema_, "lenvalue"s,
780         SaveDerivedPointerTarget(scope,
781             SaveObjectName(
782                 ".lv."s + distinctName + "."s + symbol.name().ToString()),
783             std::move(lenParams),
784             evaluate::ConstantSubscripts{
785                 static_cast<evaluate::ConstantSubscript>(lenParams.size())}));
786   } else {
787     AddValue(values, componentSchema_, "lenvalue"s,
788         SomeExpr{evaluate::NullPointer{}});
789   }
790   // Shape information
791   int rank{evaluate::GetRank(shape)};
792   AddValue(values, componentSchema_, "rank"s, IntExpr<1>(rank));
793   if (rank > 0 && !IsAllocatable(symbol) && !IsPointer(symbol)) {
794     std::vector<evaluate::StructureConstructor> bounds;
795     evaluate::NamedEntity entity{symbol};
796     for (int j{0}; j < rank; ++j) {
797       bounds.emplace_back(
798           GetValue(std::make_optional(
799                        evaluate::GetRawLowerBound(foldingContext, entity, j)),
800               parameters));
801       bounds.emplace_back(GetValue(
802           evaluate::GetRawUpperBound(foldingContext, entity, j), parameters));
803     }
804     AddValue(values, componentSchema_, "bounds"s,
805         SaveDerivedPointerTarget(scope,
806             SaveObjectName(
807                 ".b."s + distinctName + "."s + symbol.name().ToString()),
808             std::move(bounds), evaluate::ConstantSubscripts{2, rank}));
809   } else {
810     AddValue(
811         values, componentSchema_, "bounds"s, SomeExpr{evaluate::NullPointer{}});
812   }
813   // Default component initialization
814   bool hasDataInit{false};
815   if (IsAllocatable(symbol)) {
816     AddValue(values, componentSchema_, "genre"s, GetEnumValue("allocatable"));
817   } else if (IsPointer(symbol)) {
818     AddValue(values, componentSchema_, "genre"s, GetEnumValue("pointer"));
819     hasDataInit = InitializeDataPointer(
820         values, symbol, object, scope, dtScope, distinctName);
821   } else if (IsAutomatic(symbol)) {
822     AddValue(values, componentSchema_, "genre"s, GetEnumValue("automatic"));
823   } else {
824     AddValue(values, componentSchema_, "genre"s, GetEnumValue("data"));
825     hasDataInit = object.init().has_value();
826     if (hasDataInit) {
827       AddValue(values, componentSchema_, "initialization"s,
828           SaveObjectInit(scope,
829               SaveObjectName(
830                   ".di."s + distinctName + "."s + symbol.name().ToString()),
831               object));
832     }
833   }
834   if (!hasDataInit) {
835     AddValue(values, componentSchema_, "initialization"s,
836         SomeExpr{evaluate::NullPointer{}});
837   }
838   return {DEREF(componentSchema_.AsDerived()), std::move(values)};
839 }
840 
841 evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
842     const Symbol &symbol, const ProcEntityDetails &proc, Scope &scope) {
843   evaluate::StructureConstructorValues values;
844   AddValue(values, procPtrSchema_, "name"s,
845       SaveNameAsPointerTarget(scope, symbol.name().ToString()));
846   AddValue(values, procPtrSchema_, "offset"s, IntExpr<8>(symbol.offset()));
847   if (auto init{proc.init()}; init && *init) {
848     AddValue(values, procPtrSchema_, "initialization"s,
849         SomeExpr{evaluate::ProcedureDesignator{**init}});
850   } else {
851     AddValue(values, procPtrSchema_, "initialization"s,
852         SomeExpr{evaluate::NullPointer{}});
853   }
854   return {DEREF(procPtrSchema_.AsDerived()), std::move(values)};
855 }
856 
857 // Create a static pointer object with the same initialization
858 // from whence the runtime can memcpy() the data pointer
859 // component initialization.
860 // Creates and interconnects the symbols, scopes, and types for
861 //   TYPE :: ptrDt
862 //     type, POINTER :: name
863 //   END TYPE
864 //   TYPE(ptrDt), TARGET, SAVE :: ptrInit = ptrDt(designator)
865 // and then initializes the original component by setting
866 //   initialization = ptrInit
867 // which takes the address of ptrInit because the type is C_PTR.
868 // This technique of wrapping the data pointer component into
869 // a derived type instance disables any reason for lowering to
870 // attempt to dereference the RHS of an initializer, thereby
871 // allowing the runtime to actually perform the initialization
872 // by means of a simple memcpy() of the wrapped descriptor in
873 // ptrInit to the data pointer component being initialized.
874 bool RuntimeTableBuilder::InitializeDataPointer(
875     evaluate::StructureConstructorValues &values, const Symbol &symbol,
876     const ObjectEntityDetails &object, Scope &scope, Scope &dtScope,
877     const std::string &distinctName) {
878   if (object.init().has_value()) {
879     SourceName ptrDtName{SaveObjectName(
880         ".dp."s + distinctName + "."s + symbol.name().ToString())};
881     Symbol &ptrDtSym{
882         *scope.try_emplace(ptrDtName, Attrs{}, UnknownDetails{}).first->second};
883     SetReadOnlyCompilerCreatedFlags(ptrDtSym);
884     Scope &ptrDtScope{scope.MakeScope(Scope::Kind::DerivedType, &ptrDtSym)};
885     ignoreScopes_.insert(&ptrDtScope);
886     ObjectEntityDetails ptrDtObj;
887     ptrDtObj.set_type(DEREF(object.type()));
888     ptrDtObj.set_shape(object.shape());
889     Symbol &ptrDtComp{*ptrDtScope
890                            .try_emplace(symbol.name(), Attrs{Attr::POINTER},
891                                std::move(ptrDtObj))
892                            .first->second};
893     DerivedTypeDetails ptrDtDetails;
894     ptrDtDetails.add_component(ptrDtComp);
895     ptrDtSym.set_details(std::move(ptrDtDetails));
896     ptrDtSym.set_scope(&ptrDtScope);
897     DeclTypeSpec &ptrDtDeclType{
898         scope.MakeDerivedType(DeclTypeSpec::Category::TypeDerived,
899             DerivedTypeSpec{ptrDtName, ptrDtSym})};
900     DerivedTypeSpec &ptrDtDerived{DEREF(ptrDtDeclType.AsDerived())};
901     ptrDtDerived.set_scope(ptrDtScope);
902     ptrDtDerived.CookParameters(context_.foldingContext());
903     ptrDtDerived.Instantiate(scope);
904     ObjectEntityDetails ptrInitObj;
905     ptrInitObj.set_type(ptrDtDeclType);
906     evaluate::StructureConstructorValues ptrInitValues;
907     AddValue(
908         ptrInitValues, ptrDtDeclType, symbol.name().ToString(), *object.init());
909     ptrInitObj.set_init(evaluate::AsGenericExpr(
910         Structure(ptrDtDeclType, std::move(ptrInitValues))));
911     AddValue(values, componentSchema_, "initialization"s,
912         SaveObjectInit(scope,
913             SaveObjectName(
914                 ".di."s + distinctName + "."s + symbol.name().ToString()),
915             ptrInitObj));
916     return true;
917   } else {
918     return false;
919   }
920 }
921 
922 evaluate::StructureConstructor RuntimeTableBuilder::PackageIntValue(
923     const SomeExpr &genre, std::int64_t n) const {
924   evaluate::StructureConstructorValues xs;
925   AddValue(xs, valueSchema_, "genre"s, genre);
926   AddValue(xs, valueSchema_, "value"s, IntToExpr(n));
927   return Structure(valueSchema_, std::move(xs));
928 }
929 
930 SomeExpr RuntimeTableBuilder::PackageIntValueExpr(
931     const SomeExpr &genre, std::int64_t n) const {
932   return StructureExpr(PackageIntValue(genre, n));
933 }
934 
935 std::vector<const Symbol *> RuntimeTableBuilder::CollectBindings(
936     const Scope &dtScope) const {
937   std::vector<const Symbol *> result;
938   std::map<SourceName, const Symbol *> localBindings;
939   // Collect local bindings
940   for (auto pair : dtScope) {
941     const Symbol &symbol{*pair.second};
942     if (symbol.has<ProcBindingDetails>()) {
943       localBindings.emplace(symbol.name(), &symbol);
944     }
945   }
946   if (const Scope * parentScope{dtScope.GetDerivedTypeParent()}) {
947     result = CollectBindings(*parentScope);
948     // Apply overrides from the local bindings of the extended type
949     for (auto iter{result.begin()}; iter != result.end(); ++iter) {
950       const Symbol &symbol{**iter};
951       auto overridden{localBindings.find(symbol.name())};
952       if (overridden != localBindings.end()) {
953         *iter = overridden->second;
954         localBindings.erase(overridden);
955       }
956     }
957   }
958   // Add remaining (non-overriding) local bindings in name order to the result
959   for (auto pair : localBindings) {
960     result.push_back(pair.second);
961   }
962   return result;
963 }
964 
965 std::vector<evaluate::StructureConstructor>
966 RuntimeTableBuilder::DescribeBindings(const Scope &dtScope, Scope &scope) {
967   std::vector<evaluate::StructureConstructor> result;
968   for (const Symbol *symbol : CollectBindings(dtScope)) {
969     evaluate::StructureConstructorValues values;
970     AddValue(values, bindingSchema_, "proc"s,
971         SomeExpr{evaluate::ProcedureDesignator{
972             symbol->get<ProcBindingDetails>().symbol()}});
973     AddValue(values, bindingSchema_, "name"s,
974         SaveNameAsPointerTarget(scope, symbol->name().ToString()));
975     result.emplace_back(DEREF(bindingSchema_.AsDerived()), std::move(values));
976   }
977   return result;
978 }
979 
980 void RuntimeTableBuilder::DescribeGeneric(const GenericDetails &generic,
981     std::map<int, evaluate::StructureConstructor> &specials) {
982   common::visit(common::visitors{
983                     [&](const GenericKind::OtherKind &k) {
984                       if (k == GenericKind::OtherKind::Assignment) {
985                         for (auto ref : generic.specificProcs()) {
986                           DescribeSpecialProc(specials, *ref, true,
987                               false /*!final*/, std::nullopt);
988                         }
989                       }
990                     },
991                     [&](const GenericKind::DefinedIo &io) {
992                       switch (io) {
993                       case GenericKind::DefinedIo::ReadFormatted:
994                       case GenericKind::DefinedIo::ReadUnformatted:
995                       case GenericKind::DefinedIo::WriteFormatted:
996                       case GenericKind::DefinedIo::WriteUnformatted:
997                         for (auto ref : generic.specificProcs()) {
998                           DescribeSpecialProc(
999                               specials, *ref, false, false /*!final*/, io);
1000                         }
1001                         break;
1002                       }
1003                     },
1004                     [](const auto &) {},
1005                 },
1006       generic.kind().u);
1007 }
1008 
1009 void RuntimeTableBuilder::DescribeSpecialProc(
1010     std::map<int, evaluate::StructureConstructor> &specials,
1011     const Symbol &specificOrBinding, bool isAssignment, bool isFinal,
1012     std::optional<GenericKind::DefinedIo> io) {
1013   const auto *binding{specificOrBinding.detailsIf<ProcBindingDetails>()};
1014   const Symbol &specific{*(binding ? &binding->symbol() : &specificOrBinding)};
1015   if (auto proc{evaluate::characteristics::Procedure::Characterize(
1016           specific, context_.foldingContext())}) {
1017     std::uint8_t isArgDescriptorSet{0};
1018     int argThatMightBeDescriptor{0};
1019     MaybeExpr which;
1020     if (isAssignment) {
1021       // Only type-bound asst's with the same type on both dummy arguments
1022       // are germane to the runtime, which needs only these to implement
1023       // component assignment as part of intrinsic assignment.
1024       // Non-type-bound generic INTERFACEs and assignments from distinct
1025       // types must not be used for component intrinsic assignment.
1026       CHECK(proc->dummyArguments.size() == 2);
1027       const auto t1{
1028           DEREF(std::get_if<evaluate::characteristics::DummyDataObject>(
1029                     &proc->dummyArguments[0].u))
1030               .type.type()};
1031       const auto t2{
1032           DEREF(std::get_if<evaluate::characteristics::DummyDataObject>(
1033                     &proc->dummyArguments[1].u))
1034               .type.type()};
1035       if (!binding || t1.category() != TypeCategory::Derived ||
1036           t2.category() != TypeCategory::Derived ||
1037           t1.IsUnlimitedPolymorphic() || t2.IsUnlimitedPolymorphic() ||
1038           t1.GetDerivedTypeSpec() != t2.GetDerivedTypeSpec()) {
1039         return;
1040       }
1041       which = proc->IsElemental() ? elementalAssignmentEnum_
1042                                   : scalarAssignmentEnum_;
1043       if (binding && binding->passName() &&
1044           *binding->passName() == proc->dummyArguments[1].name) {
1045         argThatMightBeDescriptor = 1;
1046         isArgDescriptorSet |= 2;
1047       } else {
1048         argThatMightBeDescriptor = 2; // the non-passed-object argument
1049         isArgDescriptorSet |= 1;
1050       }
1051     } else if (isFinal) {
1052       CHECK(binding == nullptr); // FINALs are not bindings
1053       CHECK(proc->dummyArguments.size() == 1);
1054       if (proc->IsElemental()) {
1055         which = elementalFinalEnum_;
1056       } else {
1057         const auto &typeAndShape{
1058             std::get<evaluate::characteristics::DummyDataObject>(
1059                 proc->dummyArguments.at(0).u)
1060                 .type};
1061         if (typeAndShape.attrs().test(
1062                 evaluate::characteristics::TypeAndShape::Attr::AssumedRank)) {
1063           which = assumedRankFinalEnum_;
1064           isArgDescriptorSet |= 1;
1065         } else {
1066           which = scalarFinalEnum_;
1067           if (int rank{evaluate::GetRank(typeAndShape.shape())}; rank > 0) {
1068             argThatMightBeDescriptor = 1;
1069             which = IntExpr<1>(ToInt64(which).value() + rank);
1070           }
1071         }
1072       }
1073     } else { // user defined derived type I/O
1074       CHECK(proc->dummyArguments.size() >= 4);
1075       if (binding) {
1076         isArgDescriptorSet |= 1;
1077       }
1078       switch (io.value()) {
1079       case GenericKind::DefinedIo::ReadFormatted:
1080         which = readFormattedEnum_;
1081         break;
1082       case GenericKind::DefinedIo::ReadUnformatted:
1083         which = readUnformattedEnum_;
1084         break;
1085       case GenericKind::DefinedIo::WriteFormatted:
1086         which = writeFormattedEnum_;
1087         break;
1088       case GenericKind::DefinedIo::WriteUnformatted:
1089         which = writeUnformattedEnum_;
1090         break;
1091       }
1092     }
1093     if (argThatMightBeDescriptor != 0 &&
1094         !proc->dummyArguments.at(argThatMightBeDescriptor - 1)
1095              .CanBePassedViaImplicitInterface()) {
1096       isArgDescriptorSet |= 1 << (argThatMightBeDescriptor - 1);
1097     }
1098     evaluate::StructureConstructorValues values;
1099     auto index{evaluate::ToInt64(which)};
1100     CHECK(index.has_value());
1101     AddValue(
1102         values, specialSchema_, "which"s, SomeExpr{std::move(which.value())});
1103     AddValue(values, specialSchema_, "isargdescriptorset"s,
1104         IntExpr<1>(isArgDescriptorSet));
1105     AddValue(values, specialSchema_, "proc"s,
1106         SomeExpr{evaluate::ProcedureDesignator{specific}});
1107     auto pair{specials.try_emplace(
1108         *index, DEREF(specialSchema_.AsDerived()), std::move(values))};
1109     CHECK(pair.second); // ensure not already present
1110   }
1111 }
1112 
1113 void RuntimeTableBuilder::IncorporateDefinedIoGenericInterfaces(
1114     std::map<int, evaluate::StructureConstructor> &specials,
1115     GenericKind::DefinedIo definedIo, const Scope *scope) {
1116   SourceName name{GenericKind::AsFortran(definedIo)};
1117   for (; !scope->IsGlobal(); scope = &scope->parent()) {
1118     if (auto asst{scope->find(name)}; asst != scope->end()) {
1119       const Symbol &generic{asst->second->GetUltimate()};
1120       const auto &genericDetails{generic.get<GenericDetails>()};
1121       CHECK(std::holds_alternative<GenericKind::DefinedIo>(
1122           genericDetails.kind().u));
1123       CHECK(std::get<GenericKind::DefinedIo>(genericDetails.kind().u) ==
1124           definedIo);
1125       for (auto ref : genericDetails.specificProcs()) {
1126         DescribeSpecialProc(specials, *ref, false, false, definedIo);
1127       }
1128     }
1129   }
1130 }
1131 
1132 RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables(
1133     SemanticsContext &context) {
1134   RuntimeDerivedTypeTables result;
1135   result.schemata = context.GetBuiltinModule(typeInfoBuiltinModule);
1136   if (result.schemata) {
1137     RuntimeTableBuilder builder{context, result};
1138     builder.DescribeTypes(context.globalScope(), false);
1139   }
1140   return result;
1141 }
1142 } // namespace Fortran::semantics
1143