| //===-- lib/Semantics/runtime-type-info.cpp ---------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Semantics/runtime-type-info.h" |
| #include "mod-file.h" |
| #include "flang/Evaluate/fold-designator.h" |
| #include "flang/Evaluate/fold.h" |
| #include "flang/Evaluate/tools.h" |
| #include "flang/Evaluate/type.h" |
| #include "flang/Semantics/scope.h" |
| #include "flang/Semantics/tools.h" |
| #include <functional> |
| #include <list> |
| #include <map> |
| #include <string> |
| |
| namespace Fortran::semantics { |
| |
| static int FindLenParameterIndex( |
| const SymbolVector ¶meters, const Symbol &symbol) { |
| int lenIndex{0}; |
| for (SymbolRef ref : parameters) { |
| if (&*ref == &symbol) { |
| return lenIndex; |
| } |
| if (ref->get<TypeParamDetails>().attr() == common::TypeParamAttr::Len) { |
| ++lenIndex; |
| } |
| } |
| DIE("Length type parameter not found in parameter order"); |
| return -1; |
| } |
| |
| class RuntimeTableBuilder { |
| public: |
| RuntimeTableBuilder(SemanticsContext &, RuntimeDerivedTypeTables &); |
| void DescribeTypes(Scope &scope, bool inSchemata); |
| |
| private: |
| const Symbol *DescribeType(Scope &); |
| const Symbol &GetSchemaSymbol(const char *) const; |
| const DeclTypeSpec &GetSchema(const char *) const; |
| SomeExpr GetEnumValue(const char *) const; |
| Symbol &CreateObject(const std::string &, const DeclTypeSpec &, Scope &); |
| // The names of created symbols are saved in and owned by the |
| // RuntimeDerivedTypeTables instance returned by |
| // BuildRuntimeDerivedTypeTables() so that references to those names remain |
| // valid for lowering. |
| SourceName SaveObjectName(const std::string &); |
| SomeExpr SaveNameAsPointerTarget(Scope &, const std::string &); |
| const SymbolVector *GetTypeParameters(const Symbol &); |
| evaluate::StructureConstructor DescribeComponent(const Symbol &, |
| const ObjectEntityDetails &, Scope &, Scope &, |
| const std::string &distinctName, const SymbolVector *parameters); |
| evaluate::StructureConstructor DescribeComponent( |
| const Symbol &, const ProcEntityDetails &, Scope &); |
| bool InitializeDataPointer(evaluate::StructureConstructorValues &, |
| const Symbol &symbol, const ObjectEntityDetails &object, Scope &scope, |
| Scope &dtScope, const std::string &distinctName); |
| evaluate::StructureConstructor PackageIntValue( |
| const SomeExpr &genre, std::int64_t = 0) const; |
| SomeExpr PackageIntValueExpr(const SomeExpr &genre, std::int64_t = 0) const; |
| std::vector<const Symbol *> CollectBindings(const Scope &dtScope) const; |
| std::vector<evaluate::StructureConstructor> DescribeBindings( |
| const Scope &dtScope, Scope &); |
| void DescribeGeneric( |
| const GenericDetails &, std::map<int, evaluate::StructureConstructor> &); |
| void DescribeSpecialProc(std::map<int, evaluate::StructureConstructor> &, |
| const Symbol &specificOrBinding, bool isAssignment, bool isFinal, |
| std::optional<GenericKind::DefinedIo>); |
| void IncorporateDefinedIoGenericInterfaces( |
| std::map<int, evaluate::StructureConstructor> &, SourceName, |
| GenericKind::DefinedIo, const Scope *); |
| |
| // Instantiated for ParamValue and Bound |
| template <typename A> |
| evaluate::StructureConstructor GetValue( |
| const A &x, const SymbolVector *parameters) { |
| if (x.isExplicit()) { |
| return GetValue(x.GetExplicit(), parameters); |
| } else { |
| return PackageIntValue(deferredEnum_); |
| } |
| } |
| |
| // Specialization for optional<Expr<SomeInteger and SubscriptInteger>> |
| template <typename T> |
| evaluate::StructureConstructor GetValue( |
| const std::optional<evaluate::Expr<T>> &expr, |
| const SymbolVector *parameters) { |
| if (auto constValue{evaluate::ToInt64(expr)}) { |
| return PackageIntValue(explicitEnum_, *constValue); |
| } |
| if (expr) { |
| if (parameters) { |
| if (const Symbol * lenParam{evaluate::ExtractBareLenParameter(*expr)}) { |
| return PackageIntValue( |
| lenParameterEnum_, FindLenParameterIndex(*parameters, *lenParam)); |
| } |
| } |
| context_.Say(location_, |
| "Specification expression '%s' is neither constant nor a length " |
| "type parameter"_err_en_US, |
| expr->AsFortran()); |
| } |
| return PackageIntValue(deferredEnum_); |
| } |
| |
| SemanticsContext &context_; |
| RuntimeDerivedTypeTables &tables_; |
| std::map<const Symbol *, SymbolVector> orderedTypeParameters_; |
| int anonymousTypes_{0}; |
| |
| const DeclTypeSpec &derivedTypeSchema_; // TYPE(DerivedType) |
| const DeclTypeSpec &componentSchema_; // TYPE(Component) |
| const DeclTypeSpec &procPtrSchema_; // TYPE(ProcPtrComponent) |
| const DeclTypeSpec &valueSchema_; // TYPE(Value) |
| const DeclTypeSpec &bindingSchema_; // TYPE(Binding) |
| const DeclTypeSpec &specialSchema_; // TYPE(SpecialBinding) |
| SomeExpr deferredEnum_; // Value::Genre::Deferred |
| SomeExpr explicitEnum_; // Value::Genre::Explicit |
| SomeExpr lenParameterEnum_; // Value::Genre::LenParameter |
| SomeExpr scalarAssignmentEnum_; // SpecialBinding::Which::ScalarAssignment |
| SomeExpr |
| elementalAssignmentEnum_; // SpecialBinding::Which::ElementalAssignment |
| SomeExpr readFormattedEnum_; // SpecialBinding::Which::ReadFormatted |
| SomeExpr readUnformattedEnum_; // SpecialBinding::Which::ReadUnformatted |
| SomeExpr writeFormattedEnum_; // SpecialBinding::Which::WriteFormatted |
| SomeExpr writeUnformattedEnum_; // SpecialBinding::Which::WriteUnformatted |
| SomeExpr elementalFinalEnum_; // SpecialBinding::Which::ElementalFinal |
| SomeExpr assumedRankFinalEnum_; // SpecialBinding::Which::AssumedRankFinal |
| SomeExpr scalarFinalEnum_; // SpecialBinding::Which::ScalarFinal |
| parser::CharBlock location_; |
| std::set<const Scope *> ignoreScopes_; |
| }; |
| |
| RuntimeTableBuilder::RuntimeTableBuilder( |
| SemanticsContext &c, RuntimeDerivedTypeTables &t) |
| : context_{c}, tables_{t}, derivedTypeSchema_{GetSchema("derivedtype")}, |
| componentSchema_{GetSchema("component")}, procPtrSchema_{GetSchema( |
| "procptrcomponent")}, |
| valueSchema_{GetSchema("value")}, bindingSchema_{GetSchema("binding")}, |
| specialSchema_{GetSchema("specialbinding")}, deferredEnum_{GetEnumValue( |
| "deferred")}, |
| explicitEnum_{GetEnumValue("explicit")}, lenParameterEnum_{GetEnumValue( |
| "lenparameter")}, |
| scalarAssignmentEnum_{GetEnumValue("scalarassignment")}, |
| elementalAssignmentEnum_{GetEnumValue("elementalassignment")}, |
| readFormattedEnum_{GetEnumValue("readformatted")}, |
| readUnformattedEnum_{GetEnumValue("readunformatted")}, |
| writeFormattedEnum_{GetEnumValue("writeformatted")}, |
| writeUnformattedEnum_{GetEnumValue("writeunformatted")}, |
| elementalFinalEnum_{GetEnumValue("elementalfinal")}, |
| assumedRankFinalEnum_{GetEnumValue("assumedrankfinal")}, |
| scalarFinalEnum_{GetEnumValue("scalarfinal")} { |
| ignoreScopes_.insert(tables_.schemata); |
| } |
| |
| void RuntimeTableBuilder::DescribeTypes(Scope &scope, bool inSchemata) { |
| inSchemata |= ignoreScopes_.find(&scope) != ignoreScopes_.end(); |
| if (scope.IsDerivedType()) { |
| if (!inSchemata) { // don't loop trying to describe a schema |
| DescribeType(scope); |
| } |
| } else { |
| scope.InstantiateDerivedTypes(); |
| } |
| for (Scope &child : scope.children()) { |
| DescribeTypes(child, inSchemata); |
| } |
| } |
| |
| // Returns derived type instantiation's parameters in declaration order |
| const SymbolVector *RuntimeTableBuilder::GetTypeParameters( |
| const Symbol &symbol) { |
| auto iter{orderedTypeParameters_.find(&symbol)}; |
| if (iter != orderedTypeParameters_.end()) { |
| return &iter->second; |
| } else { |
| return &orderedTypeParameters_ |
| .emplace(&symbol, OrderParameterDeclarations(symbol)) |
| .first->second; |
| } |
| } |
| |
| static Scope &GetContainingNonDerivedScope(Scope &scope) { |
| Scope *p{&scope}; |
| while (p->IsDerivedType()) { |
| p = &p->parent(); |
| } |
| return *p; |
| } |
| |
| static const Symbol &GetSchemaField( |
| const DerivedTypeSpec &derived, const std::string &name) { |
| const Scope &scope{ |
| DEREF(derived.scope() ? derived.scope() : derived.typeSymbol().scope())}; |
| auto iter{scope.find(SourceName(name))}; |
| CHECK(iter != scope.end()); |
| return *iter->second; |
| } |
| |
| static const Symbol &GetSchemaField( |
| const DeclTypeSpec &derived, const std::string &name) { |
| return GetSchemaField(DEREF(derived.AsDerived()), name); |
| } |
| |
| static evaluate::StructureConstructorValues &AddValue( |
| evaluate::StructureConstructorValues &values, const DeclTypeSpec &spec, |
| const std::string &name, SomeExpr &&x) { |
| values.emplace(GetSchemaField(spec, name), std::move(x)); |
| return values; |
| } |
| |
| static evaluate::StructureConstructorValues &AddValue( |
| evaluate::StructureConstructorValues &values, const DeclTypeSpec &spec, |
| const std::string &name, const SomeExpr &x) { |
| values.emplace(GetSchemaField(spec, name), x); |
| return values; |
| } |
| |
| static SomeExpr IntToExpr(std::int64_t n) { |
| return evaluate::AsGenericExpr(evaluate::ExtentExpr{n}); |
| } |
| |
| static evaluate::StructureConstructor Structure( |
| const DeclTypeSpec &spec, evaluate::StructureConstructorValues &&values) { |
| return {DEREF(spec.AsDerived()), std::move(values)}; |
| } |
| |
| static SomeExpr StructureExpr(evaluate::StructureConstructor &&x) { |
| return SomeExpr{evaluate::Expr<evaluate::SomeDerived>{std::move(x)}}; |
| } |
| |
| static int GetIntegerKind(const Symbol &symbol) { |
| auto dyType{evaluate::DynamicType::From(symbol)}; |
| CHECK(dyType && dyType->category() == TypeCategory::Integer); |
| return dyType->kind(); |
| } |
| |
| // Save a rank-1 array constant of some numeric type as an |
| // initialized data object in a scope. |
| template <typename T> |
| static SomeExpr SaveNumericPointerTarget( |
| Scope &scope, SourceName name, std::vector<typename T::Scalar> &&x) { |
| if (x.empty()) { |
| return SomeExpr{evaluate::NullPointer{}}; |
| } else { |
| ObjectEntityDetails object; |
| if (const auto *spec{scope.FindType( |
| DeclTypeSpec{NumericTypeSpec{T::category, KindExpr{T::kind}}})}) { |
| object.set_type(*spec); |
| } else { |
| object.set_type(scope.MakeNumericType(T::category, KindExpr{T::kind})); |
| } |
| auto elements{static_cast<evaluate::ConstantSubscript>(x.size())}; |
| ArraySpec arraySpec; |
| arraySpec.push_back(ShapeSpec::MakeExplicit(Bound{0}, Bound{elements - 1})); |
| object.set_shape(arraySpec); |
| object.set_init(evaluate::AsGenericExpr(evaluate::Constant<T>{ |
| std::move(x), evaluate::ConstantSubscripts{elements}})); |
| Symbol &symbol{*scope |
| .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE}, |
| std::move(object)) |
| .first->second}; |
| symbol.set(Symbol::Flag::CompilerCreated); |
| return evaluate::AsGenericExpr( |
| evaluate::Expr<T>{evaluate::Designator<T>{symbol}}); |
| } |
| } |
| |
| // Save an arbitrarily shaped array constant of some derived type |
| // as an initialized data object in a scope. |
| static SomeExpr SaveDerivedPointerTarget(Scope &scope, SourceName name, |
| std::vector<evaluate::StructureConstructor> &&x, |
| evaluate::ConstantSubscripts &&shape) { |
| if (x.empty()) { |
| return SomeExpr{evaluate::NullPointer{}}; |
| } else { |
| const auto &derivedType{x.front().GetType().GetDerivedTypeSpec()}; |
| ObjectEntityDetails object; |
| DeclTypeSpec typeSpec{DeclTypeSpec::TypeDerived, derivedType}; |
| if (const DeclTypeSpec * spec{scope.FindType(typeSpec)}) { |
| object.set_type(*spec); |
| } else { |
| object.set_type(scope.MakeDerivedType( |
| DeclTypeSpec::TypeDerived, common::Clone(derivedType))); |
| } |
| if (!shape.empty()) { |
| ArraySpec arraySpec; |
| for (auto n : shape) { |
| arraySpec.push_back(ShapeSpec::MakeExplicit(Bound{0}, Bound{n - 1})); |
| } |
| object.set_shape(arraySpec); |
| } |
| object.set_init( |
| evaluate::AsGenericExpr(evaluate::Constant<evaluate::SomeDerived>{ |
| derivedType, std::move(x), std::move(shape)})); |
| Symbol &symbol{*scope |
| .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE}, |
| std::move(object)) |
| .first->second}; |
| symbol.set(Symbol::Flag::CompilerCreated); |
| return evaluate::AsGenericExpr( |
| evaluate::Designator<evaluate::SomeDerived>{symbol}); |
| } |
| } |
| |
| static SomeExpr SaveObjectInit( |
| Scope &scope, SourceName name, const ObjectEntityDetails &object) { |
| Symbol &symbol{*scope |
| .try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE}, |
| ObjectEntityDetails{object}) |
| .first->second}; |
| CHECK(symbol.get<ObjectEntityDetails>().init().has_value()); |
| symbol.set(Symbol::Flag::CompilerCreated); |
| return evaluate::AsGenericExpr( |
| evaluate::Designator<evaluate::SomeDerived>{symbol}); |
| } |
| |
| template <int KIND> static SomeExpr IntExpr(std::int64_t n) { |
| return evaluate::AsGenericExpr( |
| evaluate::Constant<evaluate::Type<TypeCategory::Integer, KIND>>{n}); |
| } |
| |
| const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) { |
| if (const Symbol * info{dtScope.runtimeDerivedTypeDescription()}) { |
| return info; |
| } |
| const DerivedTypeSpec *derivedTypeSpec{dtScope.derivedTypeSpec()}; |
| if (!derivedTypeSpec && !dtScope.IsParameterizedDerivedType() && |
| dtScope.symbol()) { |
| // This derived type was declared (obviously, there's a Scope) but never |
| // used in this compilation (no instantiated DerivedTypeSpec points here). |
| // Create a DerivedTypeSpec now for it so that ComponentIterator |
| // will work. This covers the case of a derived type that's declared in |
| // a module but used only by clients and submodules, enabling the |
| // run-time "no initialization needed here" flag to work. |
| DerivedTypeSpec derived{dtScope.symbol()->name(), *dtScope.symbol()}; |
| DeclTypeSpec &decl{ |
| dtScope.MakeDerivedType(DeclTypeSpec::TypeDerived, std::move(derived))}; |
| derivedTypeSpec = &decl.derivedTypeSpec(); |
| } |
| const Symbol *dtSymbol{ |
| derivedTypeSpec ? &derivedTypeSpec->typeSymbol() : dtScope.symbol()}; |
| if (!dtSymbol) { |
| return nullptr; |
| } |
| auto locationRestorer{common::ScopedSet(location_, dtSymbol->name())}; |
| // Check for an existing description that can be imported from a USE'd module |
| std::string typeName{dtSymbol->name().ToString()}; |
| if (typeName.empty() || typeName[0] == '.') { |
| return nullptr; |
| } |
| std::string distinctName{typeName}; |
| if (&dtScope != dtSymbol->scope()) { |
| distinctName += "."s + std::to_string(anonymousTypes_++); |
| } |
| std::string dtDescName{".dt."s + distinctName}; |
| Scope &scope{GetContainingNonDerivedScope(dtScope)}; |
| if (distinctName == typeName && scope.IsModule()) { |
| if (const Symbol * description{scope.FindSymbol(SourceName{dtDescName})}) { |
| dtScope.set_runtimeDerivedTypeDescription(*description); |
| return description; |
| } |
| } |
| // Create a new description object before populating it so that mutual |
| // references will work as pointer targets. |
| Symbol &dtObject{CreateObject(dtDescName, derivedTypeSchema_, scope)}; |
| dtScope.set_runtimeDerivedTypeDescription(dtObject); |
| evaluate::StructureConstructorValues dtValues; |
| AddValue(dtValues, derivedTypeSchema_, "name"s, |
| SaveNameAsPointerTarget(scope, typeName)); |
| bool isPDTdefinition{ |
| !derivedTypeSpec && dtScope.IsParameterizedDerivedType()}; |
| if (!isPDTdefinition) { |
| auto sizeInBytes{static_cast<common::ConstantSubscript>(dtScope.size())}; |
| if (auto alignment{dtScope.alignment().value_or(0)}) { |
| sizeInBytes += alignment - 1; |
| sizeInBytes /= alignment; |
| sizeInBytes *= alignment; |
| } |
| AddValue( |
| dtValues, derivedTypeSchema_, "sizeinbytes"s, IntToExpr(sizeInBytes)); |
| } |
| bool isPDTinstantiation{derivedTypeSpec && &dtScope != dtSymbol->scope()}; |
| if (isPDTinstantiation) { |
| // is PDT instantiation |
| const Symbol *uninstDescObject{ |
| DescribeType(DEREF(const_cast<Scope *>(dtSymbol->scope())))}; |
| AddValue(dtValues, derivedTypeSchema_, "uninstantiated"s, |
| evaluate::AsGenericExpr(evaluate::Expr<evaluate::SomeDerived>{ |
| evaluate::Designator<evaluate::SomeDerived>{ |
| DEREF(uninstDescObject)}})); |
| } else { |
| AddValue(dtValues, derivedTypeSchema_, "uninstantiated"s, |
| SomeExpr{evaluate::NullPointer{}}); |
| } |
| using Int8 = evaluate::Type<TypeCategory::Integer, 8>; |
| using Int1 = evaluate::Type<TypeCategory::Integer, 1>; |
| std::vector<Int8::Scalar> kinds; |
| std::vector<Int1::Scalar> lenKinds; |
| const SymbolVector *parameters{GetTypeParameters(*dtSymbol)}; |
| if (parameters) { |
| // Package the derived type's parameters in declaration order for |
| // each category of parameter. KIND= type parameters are described |
| // by their instantiated (or default) values, while LEN= type |
| // parameters are described by their INTEGER kinds. |
| for (SymbolRef ref : *parameters) { |
| const auto &tpd{ref->get<TypeParamDetails>()}; |
| if (tpd.attr() == common::TypeParamAttr::Kind) { |
| auto value{evaluate::ToInt64(tpd.init()).value_or(0)}; |
| if (derivedTypeSpec) { |
| if (const auto *pv{derivedTypeSpec->FindParameter(ref->name())}) { |
| if (pv->GetExplicit()) { |
| if (auto instantiatedValue{ |
| evaluate::ToInt64(*pv->GetExplicit())}) { |
| value = *instantiatedValue; |
| } |
| } |
| } |
| } |
| kinds.emplace_back(value); |
| } else { // LEN= parameter |
| lenKinds.emplace_back(GetIntegerKind(*ref)); |
| } |
| } |
| } |
| AddValue(dtValues, derivedTypeSchema_, "kindparameter"s, |
| SaveNumericPointerTarget<Int8>( |
| scope, SaveObjectName(".kp."s + distinctName), std::move(kinds))); |
| AddValue(dtValues, derivedTypeSchema_, "lenparameterkind"s, |
| SaveNumericPointerTarget<Int1>( |
| scope, SaveObjectName(".lpk."s + distinctName), std::move(lenKinds))); |
| // Traverse the components of the derived type |
| if (!isPDTdefinition) { |
| std::vector<const Symbol *> dataComponentSymbols; |
| std::vector<evaluate::StructureConstructor> procPtrComponents; |
| std::map<int, evaluate::StructureConstructor> specials; |
| for (const auto &pair : dtScope) { |
| const Symbol &symbol{*pair.second}; |
| auto locationRestorer{common::ScopedSet(location_, symbol.name())}; |
| std::visit( |
| common::visitors{ |
| [&](const TypeParamDetails &) { |
| // already handled above in declaration order |
| }, |
| [&](const ObjectEntityDetails &) { |
| dataComponentSymbols.push_back(&symbol); |
| }, |
| [&](const ProcEntityDetails &proc) { |
| if (IsProcedurePointer(symbol)) { |
| procPtrComponents.emplace_back( |
| DescribeComponent(symbol, proc, scope)); |
| } |
| }, |
| [&](const ProcBindingDetails &) { // handled in a later pass |
| }, |
| [&](const GenericDetails &generic) { |
| DescribeGeneric(generic, specials); |
| }, |
| [&](const auto &) { |
| common::die( |
| "unexpected details on symbol '%s' in derived type scope", |
| symbol.name().ToString().c_str()); |
| }, |
| }, |
| symbol.details()); |
| } |
| // Sort the data component symbols by offset before emitting them |
| std::sort(dataComponentSymbols.begin(), dataComponentSymbols.end(), |
| [](const Symbol *x, const Symbol *y) { |
| return x->offset() < y->offset(); |
| }); |
| std::vector<evaluate::StructureConstructor> dataComponents; |
| for (const Symbol *symbol : dataComponentSymbols) { |
| auto locationRestorer{common::ScopedSet(location_, symbol->name())}; |
| dataComponents.emplace_back( |
| DescribeComponent(*symbol, symbol->get<ObjectEntityDetails>(), scope, |
| dtScope, distinctName, parameters)); |
| } |
| AddValue(dtValues, derivedTypeSchema_, "component"s, |
| SaveDerivedPointerTarget(scope, SaveObjectName(".c."s + distinctName), |
| std::move(dataComponents), |
| evaluate::ConstantSubscripts{ |
| static_cast<evaluate::ConstantSubscript>( |
| dataComponents.size())})); |
| AddValue(dtValues, derivedTypeSchema_, "procptr"s, |
| SaveDerivedPointerTarget(scope, SaveObjectName(".p."s + distinctName), |
| std::move(procPtrComponents), |
| evaluate::ConstantSubscripts{ |
| static_cast<evaluate::ConstantSubscript>( |
| procPtrComponents.size())})); |
| // Compile the "vtable" of type-bound procedure bindings |
| std::vector<evaluate::StructureConstructor> bindings{ |
| DescribeBindings(dtScope, scope)}; |
| AddValue(dtValues, derivedTypeSchema_, "binding"s, |
| SaveDerivedPointerTarget(scope, SaveObjectName(".v."s + distinctName), |
| std::move(bindings), |
| evaluate::ConstantSubscripts{ |
| static_cast<evaluate::ConstantSubscript>(bindings.size())})); |
| // Describe "special" bindings to defined assignments, FINAL subroutines, |
| // and user-defined derived type I/O subroutines. |
| const DerivedTypeDetails &dtDetails{dtSymbol->get<DerivedTypeDetails>()}; |
| for (const auto &pair : dtDetails.finals()) { |
| DescribeSpecialProc( |
| specials, *pair.second, false /*!isAssignment*/, true, std::nullopt); |
| } |
| IncorporateDefinedIoGenericInterfaces(specials, |
| SourceName{"read(formatted)", 15}, |
| GenericKind::DefinedIo::ReadFormatted, &scope); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| SourceName{"read(unformatted)", 17}, |
| GenericKind::DefinedIo::ReadUnformatted, &scope); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| SourceName{"write(formatted)", 16}, |
| GenericKind::DefinedIo::WriteFormatted, &scope); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| SourceName{"write(unformatted)", 18}, |
| GenericKind::DefinedIo::WriteUnformatted, &scope); |
| // Pack the special procedure bindings in ascending order of their "which" |
| // code values, and compile a little-endian bit-set of those codes for |
| // use in O(1) look-up at run time. |
| std::vector<evaluate::StructureConstructor> sortedSpecials; |
| std::uint32_t specialBitSet{0}; |
| for (auto &pair : specials) { |
| auto bit{std::uint32_t{1} << pair.first}; |
| CHECK(!(specialBitSet & bit)); |
| specialBitSet |= bit; |
| sortedSpecials.emplace_back(std::move(pair.second)); |
| } |
| AddValue(dtValues, derivedTypeSchema_, "special"s, |
| SaveDerivedPointerTarget(scope, SaveObjectName(".s."s + distinctName), |
| std::move(sortedSpecials), |
| evaluate::ConstantSubscripts{ |
| static_cast<evaluate::ConstantSubscript>(specials.size())})); |
| AddValue(dtValues, derivedTypeSchema_, "specialbitset"s, |
| IntExpr<4>(specialBitSet)); |
| // Note the presence/absence of a parent component |
| AddValue(dtValues, derivedTypeSchema_, "hasparent"s, |
| IntExpr<1>(dtScope.GetDerivedTypeParent() != nullptr)); |
| // To avoid wasting run time attempting to initialize derived type |
| // instances without any initialized components, analyze the type |
| // and set a flag if there's nothing to do for it at run time. |
| AddValue(dtValues, derivedTypeSchema_, "noinitializationneeded"s, |
| IntExpr<1>( |
| derivedTypeSpec && !derivedTypeSpec->HasDefaultInitialization())); |
| // Similarly, a flag to short-circuit destruction when not needed. |
| AddValue(dtValues, derivedTypeSchema_, "nodestructionneeded"s, |
| IntExpr<1>(derivedTypeSpec && !derivedTypeSpec->HasDestruction())); |
| // Similarly, a flag to short-circuit finalization when not needed. |
| AddValue(dtValues, derivedTypeSchema_, "nofinalizationneeded"s, |
| IntExpr<1>(derivedTypeSpec && !IsFinalizable(*derivedTypeSpec))); |
| } |
| dtObject.get<ObjectEntityDetails>().set_init(MaybeExpr{ |
| StructureExpr(Structure(derivedTypeSchema_, std::move(dtValues)))}); |
| return &dtObject; |
| } |
| |
| static const Symbol &GetSymbol(const Scope &schemata, SourceName name) { |
| auto iter{schemata.find(name)}; |
| CHECK(iter != schemata.end()); |
| const Symbol &symbol{*iter->second}; |
| return symbol; |
| } |
| |
| const Symbol &RuntimeTableBuilder::GetSchemaSymbol(const char *name) const { |
| return GetSymbol( |
| DEREF(tables_.schemata), SourceName{name, std::strlen(name)}); |
| } |
| |
| const DeclTypeSpec &RuntimeTableBuilder::GetSchema( |
| const char *schemaName) const { |
| Scope &schemata{DEREF(tables_.schemata)}; |
| SourceName name{schemaName, std::strlen(schemaName)}; |
| const Symbol &symbol{GetSymbol(schemata, name)}; |
| CHECK(symbol.has<DerivedTypeDetails>()); |
| CHECK(symbol.scope()); |
| CHECK(symbol.scope()->IsDerivedType()); |
| const DeclTypeSpec *spec{nullptr}; |
| if (symbol.scope()->derivedTypeSpec()) { |
| DeclTypeSpec typeSpec{ |
| DeclTypeSpec::TypeDerived, *symbol.scope()->derivedTypeSpec()}; |
| spec = schemata.FindType(typeSpec); |
| } |
| if (!spec) { |
| DeclTypeSpec typeSpec{ |
| DeclTypeSpec::TypeDerived, DerivedTypeSpec{name, symbol}}; |
| spec = schemata.FindType(typeSpec); |
| } |
| if (!spec) { |
| spec = &schemata.MakeDerivedType( |
| DeclTypeSpec::TypeDerived, DerivedTypeSpec{name, symbol}); |
| } |
| CHECK(spec->AsDerived()); |
| return *spec; |
| } |
| |
| SomeExpr RuntimeTableBuilder::GetEnumValue(const char *name) const { |
| const Symbol &symbol{GetSchemaSymbol(name)}; |
| auto value{evaluate::ToInt64(symbol.get<ObjectEntityDetails>().init())}; |
| CHECK(value.has_value()); |
| return IntExpr<1>(*value); |
| } |
| |
| Symbol &RuntimeTableBuilder::CreateObject( |
| const std::string &name, const DeclTypeSpec &type, Scope &scope) { |
| ObjectEntityDetails object; |
| object.set_type(type); |
| auto pair{scope.try_emplace(SaveObjectName(name), |
| Attrs{Attr::TARGET, Attr::SAVE}, std::move(object))}; |
| CHECK(pair.second); |
| Symbol &result{*pair.first->second}; |
| result.set(Symbol::Flag::CompilerCreated); |
| return result; |
| } |
| |
| SourceName RuntimeTableBuilder::SaveObjectName(const std::string &name) { |
| return *tables_.names.insert(name).first; |
| } |
| |
| SomeExpr RuntimeTableBuilder::SaveNameAsPointerTarget( |
| Scope &scope, const std::string &name) { |
| CHECK(!name.empty()); |
| CHECK(name.front() != '.'); |
| ObjectEntityDetails object; |
| auto len{static_cast<common::ConstantSubscript>(name.size())}; |
| if (const auto *spec{scope.FindType(DeclTypeSpec{CharacterTypeSpec{ |
| ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}}})}) { |
| object.set_type(*spec); |
| } else { |
| object.set_type(scope.MakeCharacterType( |
| ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1})); |
| } |
| using evaluate::Ascii; |
| using AsciiExpr = evaluate::Expr<Ascii>; |
| object.set_init(evaluate::AsGenericExpr(AsciiExpr{name})); |
| Symbol &symbol{*scope |
| .try_emplace(SaveObjectName(".n."s + name), |
| Attrs{Attr::TARGET, Attr::SAVE}, std::move(object)) |
| .first->second}; |
| symbol.set(Symbol::Flag::CompilerCreated); |
| return evaluate::AsGenericExpr( |
| AsciiExpr{evaluate::Designator<Ascii>{symbol}}); |
| } |
| |
| evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent( |
| const Symbol &symbol, const ObjectEntityDetails &object, Scope &scope, |
| Scope &dtScope, const std::string &distinctName, |
| const SymbolVector *parameters) { |
| evaluate::StructureConstructorValues values; |
| auto &foldingContext{context_.foldingContext()}; |
| auto typeAndShape{evaluate::characteristics::TypeAndShape::Characterize( |
| symbol, foldingContext)}; |
| CHECK(typeAndShape.has_value()); |
| auto dyType{typeAndShape->type()}; |
| const auto &shape{typeAndShape->shape()}; |
| AddValue(values, componentSchema_, "name"s, |
| SaveNameAsPointerTarget(scope, symbol.name().ToString())); |
| AddValue(values, componentSchema_, "category"s, |
| IntExpr<1>(static_cast<int>(dyType.category()))); |
| if (dyType.IsUnlimitedPolymorphic() || |
| dyType.category() == TypeCategory::Derived) { |
| AddValue(values, componentSchema_, "kind"s, IntExpr<1>(0)); |
| } else { |
| AddValue(values, componentSchema_, "kind"s, IntExpr<1>(dyType.kind())); |
| } |
| AddValue(values, componentSchema_, "offset"s, IntExpr<8>(symbol.offset())); |
| // CHARACTER length |
| auto len{typeAndShape->LEN()}; |
| if (const semantics::DerivedTypeSpec * |
| pdtInstance{dtScope.derivedTypeSpec()}) { |
| auto restorer{foldingContext.WithPDTInstance(*pdtInstance)}; |
| len = Fold(foldingContext, std::move(len)); |
| } |
| if (dyType.category() == TypeCategory::Character && len) { |
| // Ignore IDIM(x) (represented as MAX(0, x)) |
| if (const auto *clamped{evaluate::UnwrapExpr< |
| evaluate::Extremum<evaluate::SubscriptInteger>>(*len)}) { |
| if (clamped->ordering == evaluate::Ordering::Greater && |
| clamped->left() == evaluate::Expr<evaluate::SubscriptInteger>{0}) { |
| len = clamped->right(); |
| } |
| } |
| AddValue(values, componentSchema_, "characterlen"s, |
| evaluate::AsGenericExpr(GetValue(len, parameters))); |
| } else { |
| AddValue(values, componentSchema_, "characterlen"s, |
| PackageIntValueExpr(deferredEnum_)); |
| } |
| // Describe component's derived type |
| std::vector<evaluate::StructureConstructor> lenParams; |
| if (dyType.category() == TypeCategory::Derived && |
| !dyType.IsUnlimitedPolymorphic()) { |
| const DerivedTypeSpec &spec{dyType.GetDerivedTypeSpec()}; |
| Scope *derivedScope{const_cast<Scope *>( |
| spec.scope() ? spec.scope() : spec.typeSymbol().scope())}; |
| const Symbol *derivedDescription{DescribeType(DEREF(derivedScope))}; |
| AddValue(values, componentSchema_, "derived"s, |
| evaluate::AsGenericExpr(evaluate::Expr<evaluate::SomeDerived>{ |
| evaluate::Designator<evaluate::SomeDerived>{ |
| DEREF(derivedDescription)}})); |
| // Package values of LEN parameters, if any |
| if (const SymbolVector * specParams{GetTypeParameters(spec.typeSymbol())}) { |
| for (SymbolRef ref : *specParams) { |
| const auto &tpd{ref->get<TypeParamDetails>()}; |
| if (tpd.attr() == common::TypeParamAttr::Len) { |
| if (const ParamValue * paramValue{spec.FindParameter(ref->name())}) { |
| lenParams.emplace_back(GetValue(*paramValue, parameters)); |
| } else { |
| lenParams.emplace_back(GetValue(tpd.init(), parameters)); |
| } |
| } |
| } |
| } |
| } else { |
| // Subtle: a category of Derived with a null derived type pointer |
| // signifies CLASS(*) |
| AddValue(values, componentSchema_, "derived"s, |
| SomeExpr{evaluate::NullPointer{}}); |
| } |
| // LEN type parameter values for the component's type |
| if (!lenParams.empty()) { |
| AddValue(values, componentSchema_, "lenvalue"s, |
| SaveDerivedPointerTarget(scope, |
| SaveObjectName( |
| ".lv."s + distinctName + "."s + symbol.name().ToString()), |
| std::move(lenParams), |
| evaluate::ConstantSubscripts{ |
| static_cast<evaluate::ConstantSubscript>(lenParams.size())})); |
| } else { |
| AddValue(values, componentSchema_, "lenvalue"s, |
| SomeExpr{evaluate::NullPointer{}}); |
| } |
| // Shape information |
| int rank{evaluate::GetRank(shape)}; |
| AddValue(values, componentSchema_, "rank"s, IntExpr<1>(rank)); |
| if (rank > 0 && !IsAllocatable(symbol) && !IsPointer(symbol)) { |
| std::vector<evaluate::StructureConstructor> bounds; |
| evaluate::NamedEntity entity{symbol}; |
| for (int j{0}; j < rank; ++j) { |
| bounds.emplace_back(GetValue(std::make_optional(evaluate::GetLowerBound( |
| foldingContext, entity, j)), |
| parameters)); |
| bounds.emplace_back(GetValue( |
| evaluate::GetUpperBound(foldingContext, entity, j), parameters)); |
| } |
| AddValue(values, componentSchema_, "bounds"s, |
| SaveDerivedPointerTarget(scope, |
| SaveObjectName( |
| ".b."s + distinctName + "."s + symbol.name().ToString()), |
| std::move(bounds), evaluate::ConstantSubscripts{2, rank})); |
| } else { |
| AddValue( |
| values, componentSchema_, "bounds"s, SomeExpr{evaluate::NullPointer{}}); |
| } |
| // Default component initialization |
| bool hasDataInit{false}; |
| if (IsAllocatable(symbol)) { |
| AddValue(values, componentSchema_, "genre"s, GetEnumValue("allocatable")); |
| } else if (IsPointer(symbol)) { |
| AddValue(values, componentSchema_, "genre"s, GetEnumValue("pointer")); |
| hasDataInit = InitializeDataPointer( |
| values, symbol, object, scope, dtScope, distinctName); |
| } else if (IsAutomatic(symbol)) { |
| AddValue(values, componentSchema_, "genre"s, GetEnumValue("automatic")); |
| } else { |
| AddValue(values, componentSchema_, "genre"s, GetEnumValue("data")); |
| hasDataInit = object.init().has_value(); |
| if (hasDataInit) { |
| AddValue(values, componentSchema_, "initialization"s, |
| SaveObjectInit(scope, |
| SaveObjectName( |
| ".di."s + distinctName + "."s + symbol.name().ToString()), |
| object)); |
| } |
| } |
| if (!hasDataInit) { |
| AddValue(values, componentSchema_, "initialization"s, |
| SomeExpr{evaluate::NullPointer{}}); |
| } |
| return {DEREF(componentSchema_.AsDerived()), std::move(values)}; |
| } |
| |
| evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent( |
| const Symbol &symbol, const ProcEntityDetails &proc, Scope &scope) { |
| evaluate::StructureConstructorValues values; |
| AddValue(values, procPtrSchema_, "name"s, |
| SaveNameAsPointerTarget(scope, symbol.name().ToString())); |
| AddValue(values, procPtrSchema_, "offset"s, IntExpr<8>(symbol.offset())); |
| if (auto init{proc.init()}; init && *init) { |
| AddValue(values, procPtrSchema_, "initialization"s, |
| SomeExpr{evaluate::ProcedureDesignator{**init}}); |
| } else { |
| AddValue(values, procPtrSchema_, "initialization"s, |
| SomeExpr{evaluate::NullPointer{}}); |
| } |
| return {DEREF(procPtrSchema_.AsDerived()), std::move(values)}; |
| } |
| |
| // Create a static pointer object with the same initialization |
| // from whence the runtime can memcpy() the data pointer |
| // component initialization. |
| // Creates and interconnects the symbols, scopes, and types for |
| // TYPE :: ptrDt |
| // type, POINTER :: name |
| // END TYPE |
| // TYPE(ptrDt), TARGET, SAVE :: ptrInit = ptrDt(designator) |
| // and then initializes the original component by setting |
| // initialization = ptrInit |
| // which takes the address of ptrInit because the type is C_PTR. |
| // This technique of wrapping the data pointer component into |
| // a derived type instance disables any reason for lowering to |
| // attempt to dereference the RHS of an initializer, thereby |
| // allowing the runtime to actually perform the initialization |
| // by means of a simple memcpy() of the wrapped descriptor in |
| // ptrInit to the data pointer component being initialized. |
| bool RuntimeTableBuilder::InitializeDataPointer( |
| evaluate::StructureConstructorValues &values, const Symbol &symbol, |
| const ObjectEntityDetails &object, Scope &scope, Scope &dtScope, |
| const std::string &distinctName) { |
| if (object.init().has_value()) { |
| SourceName ptrDtName{SaveObjectName( |
| ".dp."s + distinctName + "."s + symbol.name().ToString())}; |
| Symbol &ptrDtSym{ |
| *scope.try_emplace(ptrDtName, Attrs{}, UnknownDetails{}).first->second}; |
| ptrDtSym.set(Symbol::Flag::CompilerCreated); |
| Scope &ptrDtScope{scope.MakeScope(Scope::Kind::DerivedType, &ptrDtSym)}; |
| ignoreScopes_.insert(&ptrDtScope); |
| ObjectEntityDetails ptrDtObj; |
| ptrDtObj.set_type(DEREF(object.type())); |
| ptrDtObj.set_shape(object.shape()); |
| Symbol &ptrDtComp{*ptrDtScope |
| .try_emplace(symbol.name(), Attrs{Attr::POINTER}, |
| std::move(ptrDtObj)) |
| .first->second}; |
| DerivedTypeDetails ptrDtDetails; |
| ptrDtDetails.add_component(ptrDtComp); |
| ptrDtSym.set_details(std::move(ptrDtDetails)); |
| ptrDtSym.set_scope(&ptrDtScope); |
| DeclTypeSpec &ptrDtDeclType{ |
| scope.MakeDerivedType(DeclTypeSpec::Category::TypeDerived, |
| DerivedTypeSpec{ptrDtName, ptrDtSym})}; |
| DerivedTypeSpec &ptrDtDerived{DEREF(ptrDtDeclType.AsDerived())}; |
| ptrDtDerived.set_scope(ptrDtScope); |
| ptrDtDerived.CookParameters(context_.foldingContext()); |
| ptrDtDerived.Instantiate(scope); |
| ObjectEntityDetails ptrInitObj; |
| ptrInitObj.set_type(ptrDtDeclType); |
| evaluate::StructureConstructorValues ptrInitValues; |
| AddValue( |
| ptrInitValues, ptrDtDeclType, symbol.name().ToString(), *object.init()); |
| ptrInitObj.set_init(evaluate::AsGenericExpr( |
| Structure(ptrDtDeclType, std::move(ptrInitValues)))); |
| AddValue(values, componentSchema_, "initialization"s, |
| SaveObjectInit(scope, |
| SaveObjectName( |
| ".di."s + distinctName + "."s + symbol.name().ToString()), |
| ptrInitObj)); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| evaluate::StructureConstructor RuntimeTableBuilder::PackageIntValue( |
| const SomeExpr &genre, std::int64_t n) const { |
| evaluate::StructureConstructorValues xs; |
| AddValue(xs, valueSchema_, "genre"s, genre); |
| AddValue(xs, valueSchema_, "value"s, IntToExpr(n)); |
| return Structure(valueSchema_, std::move(xs)); |
| } |
| |
| SomeExpr RuntimeTableBuilder::PackageIntValueExpr( |
| const SomeExpr &genre, std::int64_t n) const { |
| return StructureExpr(PackageIntValue(genre, n)); |
| } |
| |
| std::vector<const Symbol *> RuntimeTableBuilder::CollectBindings( |
| const Scope &dtScope) const { |
| std::vector<const Symbol *> result; |
| std::map<SourceName, const Symbol *> localBindings; |
| // Collect local bindings |
| for (auto pair : dtScope) { |
| const Symbol &symbol{*pair.second}; |
| if (symbol.has<ProcBindingDetails>()) { |
| localBindings.emplace(symbol.name(), &symbol); |
| } |
| } |
| if (const Scope * parentScope{dtScope.GetDerivedTypeParent()}) { |
| result = CollectBindings(*parentScope); |
| // Apply overrides from the local bindings of the extended type |
| for (auto iter{result.begin()}; iter != result.end(); ++iter) { |
| const Symbol &symbol{**iter}; |
| auto overridden{localBindings.find(symbol.name())}; |
| if (overridden != localBindings.end()) { |
| *iter = overridden->second; |
| localBindings.erase(overridden); |
| } |
| } |
| } |
| // Add remaining (non-overriding) local bindings in name order to the result |
| for (auto pair : localBindings) { |
| result.push_back(pair.second); |
| } |
| return result; |
| } |
| |
| std::vector<evaluate::StructureConstructor> |
| RuntimeTableBuilder::DescribeBindings(const Scope &dtScope, Scope &scope) { |
| std::vector<evaluate::StructureConstructor> result; |
| for (const Symbol *symbol : CollectBindings(dtScope)) { |
| evaluate::StructureConstructorValues values; |
| AddValue(values, bindingSchema_, "proc"s, |
| SomeExpr{evaluate::ProcedureDesignator{ |
| symbol->get<ProcBindingDetails>().symbol()}}); |
| AddValue(values, bindingSchema_, "name"s, |
| SaveNameAsPointerTarget(scope, symbol->name().ToString())); |
| result.emplace_back(DEREF(bindingSchema_.AsDerived()), std::move(values)); |
| } |
| return result; |
| } |
| |
| void RuntimeTableBuilder::DescribeGeneric(const GenericDetails &generic, |
| std::map<int, evaluate::StructureConstructor> &specials) { |
| std::visit(common::visitors{ |
| [&](const GenericKind::OtherKind &k) { |
| if (k == GenericKind::OtherKind::Assignment) { |
| for (auto ref : generic.specificProcs()) { |
| DescribeSpecialProc(specials, *ref, true, |
| false /*!final*/, std::nullopt); |
| } |
| } |
| }, |
| [&](const GenericKind::DefinedIo &io) { |
| switch (io) { |
| case GenericKind::DefinedIo::ReadFormatted: |
| case GenericKind::DefinedIo::ReadUnformatted: |
| case GenericKind::DefinedIo::WriteFormatted: |
| case GenericKind::DefinedIo::WriteUnformatted: |
| for (auto ref : generic.specificProcs()) { |
| DescribeSpecialProc( |
| specials, *ref, false, false /*!final*/, io); |
| } |
| break; |
| } |
| }, |
| [](const auto &) {}, |
| }, |
| generic.kind().u); |
| } |
| |
| void RuntimeTableBuilder::DescribeSpecialProc( |
| std::map<int, evaluate::StructureConstructor> &specials, |
| const Symbol &specificOrBinding, bool isAssignment, bool isFinal, |
| std::optional<GenericKind::DefinedIo> io) { |
| const auto *binding{specificOrBinding.detailsIf<ProcBindingDetails>()}; |
| const Symbol &specific{*(binding ? &binding->symbol() : &specificOrBinding)}; |
| if (auto proc{evaluate::characteristics::Procedure::Characterize( |
| specific, context_.foldingContext())}) { |
| std::uint8_t isArgDescriptorSet{0}; |
| int argThatMightBeDescriptor{0}; |
| MaybeExpr which; |
| if (isAssignment) { |
| // Only type-bound asst's with the same type on both dummy arguments |
| // are germane to the runtime, which needs only these to implement |
| // component assignment as part of intrinsic assignment. |
| // Non-type-bound generic INTERFACEs and assignments from distinct |
| // types must not be used for component intrinsic assignment. |
| CHECK(proc->dummyArguments.size() == 2); |
| const auto t1{ |
| DEREF(std::get_if<evaluate::characteristics::DummyDataObject>( |
| &proc->dummyArguments[0].u)) |
| .type.type()}; |
| const auto t2{ |
| DEREF(std::get_if<evaluate::characteristics::DummyDataObject>( |
| &proc->dummyArguments[1].u)) |
| .type.type()}; |
| if (!binding || t1.category() != TypeCategory::Derived || |
| t2.category() != TypeCategory::Derived || |
| t1.IsUnlimitedPolymorphic() || t2.IsUnlimitedPolymorphic() || |
| t1.GetDerivedTypeSpec() != t2.GetDerivedTypeSpec()) { |
| return; |
| } |
| which = proc->IsElemental() ? elementalAssignmentEnum_ |
| : scalarAssignmentEnum_; |
| if (binding && binding->passName() && |
| *binding->passName() == proc->dummyArguments[1].name) { |
| argThatMightBeDescriptor = 1; |
| isArgDescriptorSet |= 2; |
| } else { |
| argThatMightBeDescriptor = 2; // the non-passed-object argument |
| isArgDescriptorSet |= 1; |
| } |
| } else if (isFinal) { |
| CHECK(binding == nullptr); // FINALs are not bindings |
| CHECK(proc->dummyArguments.size() == 1); |
| if (proc->IsElemental()) { |
| which = elementalFinalEnum_; |
| } else { |
| const auto &typeAndShape{ |
| std::get<evaluate::characteristics::DummyDataObject>( |
| proc->dummyArguments.at(0).u) |
| .type}; |
| if (typeAndShape.attrs().test( |
| evaluate::characteristics::TypeAndShape::Attr::AssumedRank)) { |
| which = assumedRankFinalEnum_; |
| isArgDescriptorSet |= 1; |
| } else { |
| which = scalarFinalEnum_; |
| if (int rank{evaluate::GetRank(typeAndShape.shape())}; rank > 0) { |
| argThatMightBeDescriptor = 1; |
| which = IntExpr<1>(ToInt64(which).value() + rank); |
| } |
| } |
| } |
| } else { // user defined derived type I/O |
| CHECK(proc->dummyArguments.size() >= 4); |
| if (binding) { |
| isArgDescriptorSet |= 1; |
| } |
| switch (io.value()) { |
| case GenericKind::DefinedIo::ReadFormatted: |
| which = readFormattedEnum_; |
| break; |
| case GenericKind::DefinedIo::ReadUnformatted: |
| which = readUnformattedEnum_; |
| break; |
| case GenericKind::DefinedIo::WriteFormatted: |
| which = writeFormattedEnum_; |
| break; |
| case GenericKind::DefinedIo::WriteUnformatted: |
| which = writeUnformattedEnum_; |
| break; |
| } |
| } |
| if (argThatMightBeDescriptor != 0 && |
| !proc->dummyArguments.at(argThatMightBeDescriptor - 1) |
| .CanBePassedViaImplicitInterface()) { |
| isArgDescriptorSet |= 1 << (argThatMightBeDescriptor - 1); |
| } |
| evaluate::StructureConstructorValues values; |
| auto index{evaluate::ToInt64(which)}; |
| CHECK(index.has_value()); |
| AddValue( |
| values, specialSchema_, "which"s, SomeExpr{std::move(which.value())}); |
| AddValue(values, specialSchema_, "isargdescriptorset"s, |
| IntExpr<1>(isArgDescriptorSet)); |
| AddValue(values, specialSchema_, "proc"s, |
| SomeExpr{evaluate::ProcedureDesignator{specific}}); |
| auto pair{specials.try_emplace( |
| *index, DEREF(specialSchema_.AsDerived()), std::move(values))}; |
| CHECK(pair.second); // ensure not already present |
| } |
| } |
| |
| void RuntimeTableBuilder::IncorporateDefinedIoGenericInterfaces( |
| std::map<int, evaluate::StructureConstructor> &specials, SourceName name, |
| GenericKind::DefinedIo definedIo, const Scope *scope) { |
| for (; !scope->IsGlobal(); scope = &scope->parent()) { |
| if (auto asst{scope->find(name)}; asst != scope->end()) { |
| const Symbol &generic{*asst->second}; |
| const auto &genericDetails{generic.get<GenericDetails>()}; |
| CHECK(std::holds_alternative<GenericKind::DefinedIo>( |
| genericDetails.kind().u)); |
| CHECK(std::get<GenericKind::DefinedIo>(genericDetails.kind().u) == |
| definedIo); |
| for (auto ref : genericDetails.specificProcs()) { |
| DescribeSpecialProc(specials, *ref, false, false, definedIo); |
| } |
| } |
| } |
| } |
| |
| RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables( |
| SemanticsContext &context) { |
| RuntimeDerivedTypeTables result; |
| result.schemata = context.GetBuiltinModule("__fortran_type_info"); |
| if (result.schemata) { |
| RuntimeTableBuilder builder{context, result}; |
| builder.DescribeTypes(context.globalScope(), false); |
| } |
| return result; |
| } |
| } // namespace Fortran::semantics |