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