| //===-- 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> |
| |
| // The symbols added by this code to various scopes in the program include: |
| // .b.TYPE.NAME - Bounds values for an array component |
| // .c.TYPE - TYPE(Component) descriptions for TYPE |
| // .di.TYPE.NAME - Data initialization for a component |
| // .dp.TYPE.NAME - Data pointer initialization for a component |
| // .dt.TYPE - TYPE(DerivedType) description for TYPE |
| // .kp.TYPE - KIND type parameter values for TYPE |
| // .lpk.TYPE - Integer kinds of LEN type parameter values |
| // .lv.TYPE.NAME - LEN type parameter values for a component's type |
| // .n.NAME - Character representation of a name |
| // .p.TYPE - TYPE(ProcPtrComponent) descriptions for TYPE |
| // .s.TYPE - TYPE(SpecialBinding) bindings for TYPE |
| // .v.TYPE - TYPE(Binding) bindings for TYPE |
| |
| 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<evaluate::StructureConstructor> DescribeBindings( |
| const Scope &dtScope, Scope &); |
| std::map<int, evaluate::StructureConstructor> DescribeSpecialGenerics( |
| const Scope &dtScope, const Scope &thisScope, |
| const DerivedTypeSpec *) const; |
| void DescribeSpecialGeneric(const GenericDetails &, |
| std::map<int, evaluate::StructureConstructor> &, const Scope &, |
| const DerivedTypeSpec *) const; |
| void DescribeSpecialProc(std::map<int, evaluate::StructureConstructor> &, |
| const Symbol &specificOrBinding, bool isAssignment, bool isFinal, |
| std::optional<common::DefinedIo>, const Scope *, const DerivedTypeSpec *, |
| bool isTypeBound) const; |
| void IncorporateDefinedIoGenericInterfaces( |
| std::map<int, evaluate::StructureConstructor> &, common::DefinedIo, |
| const Scope *, const DerivedTypeSpec *); |
| |
| // 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)); |
| } |
| } |
| // TODO: Replace a specification expression requiring actual operations |
| // with a reference to a new anonymous LEN type parameter whose default |
| // value captures the expression. This replacement must take place when |
| // the type is declared so that the new LEN type parameters appear in |
| // all instantiations and structure constructors. |
| context_.Say(location_, |
| "derived type specification expression '%s' that is neither constant nor a length type parameter"_todo_en_US, |
| expr->AsFortran()); |
| } |
| return PackageIntValue(deferredEnum_); |
| } |
| |
| SemanticsContext &context_; |
| RuntimeDerivedTypeTables &tables_; |
| std::map<const Symbol *, SymbolVector> orderedTypeParameters_; |
| |
| 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(bindingDescCompName)}, |
| 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); |
| } |
| |
| static void SetReadOnlyCompilerCreatedFlags(Symbol &symbol) { |
| symbol.set(Symbol::Flag::CompilerCreated); |
| // Runtime type info symbols may have types that are incompatible with the |
| // PARAMETER attribute (the main issue is that they may be TARGET, and normal |
| // Fortran parameters cannot be TARGETs). |
| if (symbol.has<semantics::ObjectEntityDetails>() || |
| symbol.has<semantics::ProcEntityDetails>()) { |
| symbol.set(Symbol::Flag::ReadOnly); |
| } |
| } |
| |
| // 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 { |
| auto dyType{x.front().GetType()}; |
| const auto &derivedType{dyType.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}; |
| SetReadOnlyCompilerCreatedFlags(symbol); |
| return evaluate::AsGenericExpr( |
| evaluate::Designator<evaluate::SomeDerived>{symbol}); |
| } |
| } |
| |
| 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) || |
| symbol.owner().context().HasError(symbol)); |
| return dyType && dyType->category() == TypeCategory::Integer |
| ? dyType->kind() |
| : symbol.owner().context().GetDefaultKind(TypeCategory::Integer); |
| } |
| |
| // 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}; |
| SetReadOnlyCompilerCreatedFlags(symbol); |
| return evaluate::AsGenericExpr( |
| evaluate::Expr<T>{evaluate::Designator<T>{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()); |
| SetReadOnlyCompilerCreatedFlags(symbol); |
| 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}); |
| } |
| |
| static std::optional<std::string> GetSuffixIfTypeKindParameters( |
| const DerivedTypeSpec &derivedTypeSpec, const SymbolVector *parameters) { |
| if (parameters) { |
| std::optional<std::string> suffix; |
| for (SymbolRef ref : *parameters) { |
| const auto &tpd{ref->get<TypeParamDetails>()}; |
| if (tpd.attr() == common::TypeParamAttr::Kind) { |
| if (const auto *pv{derivedTypeSpec.FindParameter(ref->name())}) { |
| if (pv->GetExplicit()) { |
| if (auto instantiatedValue{evaluate::ToInt64(*pv->GetExplicit())}) { |
| if (suffix.has_value()) { |
| *suffix += "."s + std::to_string(*instantiatedValue); |
| } else { |
| suffix = "."s + std::to_string(*instantiatedValue); |
| } |
| } |
| } |
| } |
| } |
| } |
| return suffix; |
| } |
| return std::nullopt; |
| } |
| |
| const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) { |
| if (const Symbol * info{dtScope.runtimeDerivedTypeDescription()}) { |
| return info; |
| } |
| const DerivedTypeSpec *derivedTypeSpec{dtScope.derivedTypeSpec()}; |
| if (!derivedTypeSpec && !dtScope.IsDerivedTypeWithKindParameter() && |
| 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()}; |
| if (const SymbolVector * |
| lenParameters{GetTypeParameters(*dtScope.symbol())}) { |
| // Create dummy deferred values for the length parameters so that the |
| // DerivedTypeSpec is complete and can be used in helpers. |
| for (SymbolRef lenParam : *lenParameters) { |
| (void)lenParam; |
| derived.AddRawParamValue( |
| nullptr, ParamValue::Deferred(common::TypeParamAttr::Len)); |
| } |
| derived.CookParameters(context_.foldingContext()); |
| } |
| 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.front() == '.' && !context_.IsTempName(typeName))) { |
| return nullptr; |
| } |
| bool isPDTDefinitionWithKindParameters{ |
| !derivedTypeSpec && dtScope.IsDerivedTypeWithKindParameter()}; |
| bool isPDTInstantiation{derivedTypeSpec && &dtScope != dtSymbol->scope()}; |
| const SymbolVector *parameters{GetTypeParameters(*dtSymbol)}; |
| std::string distinctName{typeName}; |
| if (isPDTInstantiation) { |
| // Only create new type descriptions for different kind parameter values. |
| // Type with different length parameters/same kind parameters can all |
| // share the same type description available in the current scope. |
| if (auto suffix{ |
| GetSuffixIfTypeKindParameters(*derivedTypeSpec, parameters)}) { |
| distinctName += *suffix; |
| } |
| } else if (isPDTDefinitionWithKindParameters) { |
| return nullptr; |
| } |
| std::string dtDescName{".dt."s + distinctName}; |
| Scope *dtSymbolScope{const_cast<Scope *>(dtSymbol->scope())}; |
| Scope &scope{ |
| GetContainingNonDerivedScope(dtSymbolScope ? *dtSymbolScope : dtScope)}; |
| if (const auto it{scope.find(SourceName{dtDescName})}; it != scope.end()) { |
| dtScope.set_runtimeDerivedTypeDescription(*it->second); |
| return &*it->second; |
| } |
| |
| // 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)); |
| if (!isPDTDefinitionWithKindParameters) { |
| 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)); |
| } |
| if (const Symbol * |
| uninstDescObject{isPDTInstantiation |
| ? DescribeType(DEREF(const_cast<Scope *>(dtSymbol->scope()))) |
| : nullptr}) { |
| 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; |
| 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) { |
| if (const auto *inst{dtScope.FindComponent(ref->name())}) { |
| const auto &tpd{inst->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(inst->name())}) { |
| if (pv->GetExplicit()) { |
| if (auto instantiatedValue{ |
| evaluate::ToInt64(*pv->GetExplicit())}) { |
| value = *instantiatedValue; |
| } |
| } |
| } |
| } |
| kinds.emplace_back(value); |
| } else { // LEN= parameter |
| lenKinds.emplace_back(GetIntegerKind(*inst)); |
| } |
| } |
| } |
| } |
| 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 (!isPDTDefinitionWithKindParameters) { |
| std::vector<const Symbol *> dataComponentSymbols; |
| std::vector<evaluate::StructureConstructor> procPtrComponents; |
| for (const auto &pair : dtScope) { |
| const Symbol &symbol{*pair.second}; |
| auto locationRestorer{common::ScopedSet(location_, symbol.name())}; |
| common::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 &) { // ditto |
| }, |
| [&](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, placing |
| // the parent component first if any. |
| std::sort(dataComponentSymbols.begin(), dataComponentSymbols.end(), |
| [](const Symbol *x, const Symbol *y) { |
| return x->test(Symbol::Flag::ParentComp) || 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::uint32_t specialBitSet{0}; |
| if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { |
| std::vector<evaluate::StructureConstructor> bindings{ |
| DescribeBindings(dtScope, scope)}; |
| AddValue(dtValues, derivedTypeSchema_, bindingDescCompName, |
| 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 defined derived type I/O subroutines. Defined assignments and I/O |
| // subroutines override any parent bindings, but FINAL subroutines do not |
| // (the runtime will call all of them). |
| std::map<int, evaluate::StructureConstructor> specials{ |
| DescribeSpecialGenerics(dtScope, dtScope, derivedTypeSpec)}; |
| if (derivedTypeSpec) { |
| for (auto &ref : FinalsForDerivedTypeInstantiation(*derivedTypeSpec)) { |
| DescribeSpecialProc(specials, *ref, /*isAssignment-*/ false, |
| /*isFinal=*/true, std::nullopt, nullptr, derivedTypeSpec, |
| /*isTypeBound=*/true); |
| } |
| IncorporateDefinedIoGenericInterfaces(specials, |
| common::DefinedIo::ReadFormatted, &scope, derivedTypeSpec); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| common::DefinedIo::ReadUnformatted, &scope, derivedTypeSpec); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| common::DefinedIo::WriteFormatted, &scope, derivedTypeSpec); |
| IncorporateDefinedIoGenericInterfaces(specials, |
| common::DefinedIo::WriteUnformatted, &scope, derivedTypeSpec); |
| } |
| // 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; |
| 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(false, false))); |
| // 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 && !MayRequireFinalization(*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}; |
| SetReadOnlyCompilerCreatedFlags(result); |
| 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() != '.' || context_.IsTempName(name)); |
| 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}; |
| SetReadOnlyCompilerCreatedFlags(symbol); |
| 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 = common::Clone(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())}; |
| if (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::GetRawLowerBound(foldingContext, entity, j)), |
| parameters)); |
| bounds.emplace_back(GetValue( |
| evaluate::GetRawUpperBound(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}; |
| SetReadOnlyCompilerCreatedFlags(ptrDtSym); |
| 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)); |
| } |
| |
| SymbolVector CollectBindings(const Scope &dtScope) { |
| SymbolVector result; |
| std::map<SourceName, Symbol *> localBindings; |
| // Collect local bindings |
| for (auto pair : dtScope) { |
| Symbol &symbol{const_cast<Symbol &>(*pair.second)}; |
| if (auto *binding{symbol.detailsIf<ProcBindingDetails>()}) { |
| localBindings.emplace(symbol.name(), &symbol); |
| binding->set_numPrivatesNotOverridden(0); |
| } |
| } |
| 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 overriderIter{localBindings.find(symbol.name())}; |
| if (overriderIter != localBindings.end()) { |
| Symbol &overrider{*overriderIter->second}; |
| if (symbol.attrs().test(Attr::PRIVATE) && |
| FindModuleContaining(symbol.owner()) != |
| FindModuleContaining(dtScope)) { |
| // Don't override inaccessible PRIVATE bindings |
| auto &binding{overrider.get<ProcBindingDetails>()}; |
| binding.set_numPrivatesNotOverridden( |
| binding.numPrivatesNotOverridden() + 1); |
| } else { |
| *iter = overrider; |
| localBindings.erase(overriderIter); |
| } |
| } |
| } |
| } |
| // 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 SymbolRef &ref : CollectBindings(dtScope)) { |
| evaluate::StructureConstructorValues values; |
| AddValue(values, bindingSchema_, procCompName, |
| SomeExpr{evaluate::ProcedureDesignator{ |
| ref.get().get<ProcBindingDetails>().symbol()}}); |
| AddValue(values, bindingSchema_, "name"s, |
| SaveNameAsPointerTarget(scope, ref.get().name().ToString())); |
| result.emplace_back(DEREF(bindingSchema_.AsDerived()), std::move(values)); |
| } |
| return result; |
| } |
| |
| std::map<int, evaluate::StructureConstructor> |
| RuntimeTableBuilder::DescribeSpecialGenerics(const Scope &dtScope, |
| const Scope &thisScope, const DerivedTypeSpec *derivedTypeSpec) const { |
| std::map<int, evaluate::StructureConstructor> specials; |
| if (const Scope * parentScope{dtScope.GetDerivedTypeParent()}) { |
| specials = |
| DescribeSpecialGenerics(*parentScope, thisScope, derivedTypeSpec); |
| } |
| for (auto pair : dtScope) { |
| const Symbol &symbol{*pair.second}; |
| if (const auto *generic{symbol.detailsIf<GenericDetails>()}) { |
| DescribeSpecialGeneric(*generic, specials, thisScope, derivedTypeSpec); |
| } |
| } |
| return specials; |
| } |
| |
| void RuntimeTableBuilder::DescribeSpecialGeneric(const GenericDetails &generic, |
| std::map<int, evaluate::StructureConstructor> &specials, |
| const Scope &dtScope, const DerivedTypeSpec *derivedTypeSpec) const { |
| common::visit( |
| common::visitors{ |
| [&](const GenericKind::OtherKind &k) { |
| if (k == GenericKind::OtherKind::Assignment) { |
| for (auto ref : generic.specificProcs()) { |
| DescribeSpecialProc(specials, *ref, /*isAssignment=*/true, |
| /*isFinal=*/false, std::nullopt, &dtScope, derivedTypeSpec, |
| /*isTypeBound=*/true); |
| } |
| } |
| }, |
| [&](const common::DefinedIo &io) { |
| switch (io) { |
| case common::DefinedIo::ReadFormatted: |
| case common::DefinedIo::ReadUnformatted: |
| case common::DefinedIo::WriteFormatted: |
| case common::DefinedIo::WriteUnformatted: |
| for (auto ref : generic.specificProcs()) { |
| DescribeSpecialProc(specials, *ref, /*isAssignment=*/false, |
| /*isFinal=*/false, io, &dtScope, derivedTypeSpec, |
| /*isTypeBound=*/true); |
| } |
| break; |
| } |
| }, |
| [](const auto &) {}, |
| }, |
| generic.kind().u); |
| } |
| |
| void RuntimeTableBuilder::DescribeSpecialProc( |
| std::map<int, evaluate::StructureConstructor> &specials, |
| const Symbol &specificOrBinding, bool isAssignment, bool isFinal, |
| std::optional<common::DefinedIo> io, const Scope *dtScope, |
| const DerivedTypeSpec *derivedTypeSpec, bool isTypeBound) const { |
| const auto *binding{specificOrBinding.detailsIf<ProcBindingDetails>()}; |
| if (binding && dtScope) { // use most recent override |
| binding = &DEREF(dtScope->FindComponent(specificOrBinding.name())) |
| .get<ProcBindingDetails>(); |
| } |
| const Symbol &specific{*(binding ? &binding->symbol() : &specificOrBinding)}; |
| if (auto proc{evaluate::characteristics::Procedure::Characterize( |
| specific, context_.foldingContext())}) { |
| std::uint8_t isArgDescriptorSet{0}; |
| std::uint8_t isArgContiguousSet{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 &dummyData{ |
| std::get<evaluate::characteristics::DummyDataObject>( |
| proc->dummyArguments.at(0).u)}; |
| const auto &typeAndShape{dummyData.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) { |
| which = IntExpr<1>(ToInt64(which).value() + rank); |
| if (dummyData.IsPassedByDescriptor(proc->IsBindC())) { |
| argThatMightBeDescriptor = 1; |
| } |
| if (!typeAndShape.attrs().test(evaluate::characteristics:: |
| TypeAndShape::Attr::AssumedShape) || |
| dummyData.attrs.test(evaluate::characteristics:: |
| DummyDataObject::Attr::Contiguous)) { |
| isArgContiguousSet |= 1; |
| } |
| } |
| } |
| } |
| } else { // defined derived type I/O |
| CHECK(proc->dummyArguments.size() >= 4); |
| const auto *ddo{std::get_if<evaluate::characteristics::DummyDataObject>( |
| &proc->dummyArguments[0].u)}; |
| if (!ddo) { |
| return; |
| } |
| if (derivedTypeSpec && |
| !ddo->type.type().IsTkCompatibleWith( |
| evaluate::DynamicType{*derivedTypeSpec})) { |
| // Defined I/O specific procedure is not for this derived type. |
| return; |
| } |
| if (ddo->type.type().IsPolymorphic()) { |
| isArgDescriptorSet |= 1; |
| } |
| switch (io.value()) { |
| case common::DefinedIo::ReadFormatted: |
| which = readFormattedEnum_; |
| break; |
| case common::DefinedIo::ReadUnformatted: |
| which = readUnformattedEnum_; |
| break; |
| case common::DefinedIo::WriteFormatted: |
| which = writeFormattedEnum_; |
| break; |
| case common::DefinedIo::WriteUnformatted: |
| which = writeUnformattedEnum_; |
| break; |
| } |
| } |
| if (argThatMightBeDescriptor != 0) { |
| if (const auto *dummyData{ |
| std::get_if<evaluate::characteristics::DummyDataObject>( |
| &proc->dummyArguments.at(argThatMightBeDescriptor - 1).u)}) { |
| if (dummyData->IsPassedByDescriptor(proc->IsBindC())) { |
| 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_, "istypebound"s, |
| IntExpr<1>(isTypeBound ? 1 : 0)); |
| AddValue(values, specialSchema_, "isargcontiguousset"s, |
| IntExpr<1>(isArgContiguousSet)); |
| AddValue(values, specialSchema_, procCompName, |
| SomeExpr{evaluate::ProcedureDesignator{specific}}); |
| // index might already be present in the case of an override |
| specials.emplace(*index, |
| evaluate::StructureConstructor{ |
| DEREF(specialSchema_.AsDerived()), std::move(values)}); |
| } |
| } |
| |
| void RuntimeTableBuilder::IncorporateDefinedIoGenericInterfaces( |
| std::map<int, evaluate::StructureConstructor> &specials, |
| common::DefinedIo definedIo, const Scope *scope, |
| const DerivedTypeSpec *derivedTypeSpec) { |
| SourceName name{GenericKind::AsFortran(definedIo)}; |
| for (; !scope->IsGlobal(); scope = &scope->parent()) { |
| if (auto asst{scope->find(name)}; asst != scope->end()) { |
| const Symbol &generic{asst->second->GetUltimate()}; |
| const auto &genericDetails{generic.get<GenericDetails>()}; |
| CHECK(std::holds_alternative<common::DefinedIo>(genericDetails.kind().u)); |
| CHECK(std::get<common::DefinedIo>(genericDetails.kind().u) == definedIo); |
| for (auto ref : genericDetails.specificProcs()) { |
| DescribeSpecialProc(specials, *ref, false, false, definedIo, nullptr, |
| derivedTypeSpec, false); |
| } |
| } |
| } |
| } |
| |
| RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables( |
| SemanticsContext &context) { |
| RuntimeDerivedTypeTables result; |
| // Do not attempt to read __fortran_type_info.mod when compiling |
| // the module on which it depends. |
| const auto &allSources{context.allCookedSources().allSources()}; |
| if (auto firstProv{allSources.GetFirstFileProvenance()}) { |
| if (const auto *srcFile{allSources.GetSourceFile(firstProv->start())}) { |
| if (srcFile->path().find("__fortran_builtins.f90") != std::string::npos) { |
| return result; |
| } |
| } |
| } |
| result.schemata = context.GetBuiltinModule(typeInfoBuiltinModule); |
| if (result.schemata) { |
| RuntimeTableBuilder builder{context, result}; |
| builder.DescribeTypes(context.globalScope(), false); |
| } |
| return result; |
| } |
| |
| // Find the type of a defined I/O procedure's interface's initial "dtv" |
| // dummy argument. Returns a non-null DeclTypeSpec pointer only if that |
| // dtv argument exists and is a derived type. |
| static const DeclTypeSpec *GetDefinedIoSpecificArgType(const Symbol &specific) { |
| const Symbol *interface{&specific.GetUltimate()}; |
| if (const auto *procEntity{specific.detailsIf<ProcEntityDetails>()}) { |
| interface = procEntity->procInterface(); |
| } |
| if (interface) { |
| if (const SubprogramDetails * |
| subprogram{interface->detailsIf<SubprogramDetails>()}; |
| subprogram && !subprogram->dummyArgs().empty()) { |
| if (const Symbol * dtvArg{subprogram->dummyArgs().at(0)}) { |
| if (const DeclTypeSpec * declType{dtvArg->GetType()}) { |
| return declType->AsDerived() ? declType : nullptr; |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| // Locate a particular scope's generic interface for a specific kind of |
| // defined I/O. |
| static const Symbol *FindGenericDefinedIo( |
| const Scope &scope, common::DefinedIo which) { |
| if (const Symbol * symbol{scope.FindSymbol(GenericKind::AsFortran(which))}) { |
| const Symbol &generic{symbol->GetUltimate()}; |
| const auto &genericDetails{generic.get<GenericDetails>()}; |
| CHECK(std::holds_alternative<common::DefinedIo>(genericDetails.kind().u)); |
| CHECK(std::get<common::DefinedIo>(genericDetails.kind().u) == which); |
| return &generic; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| std::multimap<const Symbol *, NonTbpDefinedIo> |
| CollectNonTbpDefinedIoGenericInterfaces( |
| const Scope &scope, bool useRuntimeTypeInfoEntries) { |
| std::multimap<const Symbol *, NonTbpDefinedIo> result; |
| if (!scope.IsTopLevel() && |
| (scope.GetImportKind() == Scope::ImportKind::All || |
| scope.GetImportKind() == Scope::ImportKind::Default)) { |
| result = CollectNonTbpDefinedIoGenericInterfaces( |
| scope.parent(), useRuntimeTypeInfoEntries); |
| } |
| if (scope.kind() != Scope::Kind::DerivedType) { |
| for (common::DefinedIo which : |
| {common::DefinedIo::ReadFormatted, common::DefinedIo::ReadUnformatted, |
| common::DefinedIo::WriteFormatted, |
| common::DefinedIo::WriteUnformatted}) { |
| if (const Symbol * generic{FindGenericDefinedIo(scope, which)}) { |
| for (auto specific : generic->get<GenericDetails>().specificProcs()) { |
| if (const DeclTypeSpec * |
| declType{GetDefinedIoSpecificArgType(*specific)}) { |
| const DerivedTypeSpec &derived{DEREF(declType->AsDerived())}; |
| if (const Symbol * |
| dtDesc{derived.scope() |
| ? derived.scope()->runtimeDerivedTypeDescription() |
| : nullptr}) { |
| if (useRuntimeTypeInfoEntries && |
| &derived.scope()->parent() == &generic->owner()) { |
| // This non-TBP defined I/O generic was defined in the |
| // same scope as the derived type, and it will be |
| // included in the derived type's special bindings |
| // by IncorporateDefinedIoGenericInterfaces(). |
| } else { |
| // Local scope's specific overrides host's for this type |
| bool updated{false}; |
| for (auto [iter, end]{result.equal_range(dtDesc)}; iter != end; |
| ++iter) { |
| NonTbpDefinedIo &nonTbp{iter->second}; |
| if (nonTbp.definedIo == which) { |
| nonTbp.subroutine = &*specific; |
| nonTbp.isDtvArgPolymorphic = declType->IsPolymorphic(); |
| updated = true; |
| } |
| } |
| if (!updated) { |
| result.emplace(dtDesc, |
| NonTbpDefinedIo{ |
| &*specific, which, declType->IsPolymorphic()}); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| // ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces() |
| // |
| // Returns a true result when a kind of defined I/O generic procedure |
| // has a type (from a symbol or a NAMELIST) such that |
| // (1) there is a specific procedure matching that type for a non-type-bound |
| // generic defined in the scope of the type, and |
| // (2) that specific procedure is unavailable or overridden in a particular |
| // local scope. |
| // Specific procedures of non-type-bound defined I/O generic interfaces |
| // declared in the scope of a derived type are identified as special bindings |
| // in the derived type's runtime type information, as if they had been |
| // type-bound. This predicate is meant to determine local situations in |
| // which those special bindings are not to be used. Its result is intended |
| // to be put into the "ignoreNonTbpEntries" flag of |
| // runtime::NonTbpDefinedIoTable and passed (negated) as the |
| // "useRuntimeTypeInfoEntries" argument of |
| // CollectNonTbpDefinedIoGenericInterfaces() above. |
| |
| static const Symbol *FindSpecificDefinedIo(const Scope &scope, |
| const evaluate::DynamicType &derived, common::DefinedIo which) { |
| if (const Symbol * generic{FindGenericDefinedIo(scope, which)}) { |
| for (auto ref : generic->get<GenericDetails>().specificProcs()) { |
| const Symbol &specific{*ref}; |
| if (const DeclTypeSpec * |
| thisType{GetDefinedIoSpecificArgType(specific)}) { |
| if (evaluate::DynamicType{DEREF(thisType->AsDerived()), true} |
| .IsTkCompatibleWith(derived)) { |
| return &specific.GetUltimate(); |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| bool ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| const Scope &scope, const DerivedTypeSpec *derived) { |
| if (!derived) { |
| return false; |
| } |
| const Symbol &typeSymbol{derived->typeSymbol()}; |
| const Scope &typeScope{typeSymbol.GetUltimate().owner()}; |
| evaluate::DynamicType dyType{*derived}; |
| for (common::DefinedIo which : |
| {common::DefinedIo::ReadFormatted, common::DefinedIo::ReadUnformatted, |
| common::DefinedIo::WriteFormatted, |
| common::DefinedIo::WriteUnformatted}) { |
| if (const Symbol * |
| specific{FindSpecificDefinedIo(typeScope, dyType, which)}) { |
| // There's a non-TBP defined I/O procedure in the scope of the type's |
| // definition that applies to this type. It will appear in the type's |
| // runtime information. Determine whether it still applies in the |
| // scope of interest. |
| if (FindSpecificDefinedIo(scope, dyType, which) != specific) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| const Scope &scope, const DeclTypeSpec *type) { |
| return type && |
| ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| scope, type->AsDerived()); |
| } |
| |
| bool ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| const Scope &scope, const Symbol *symbol) { |
| if (!symbol) { |
| return false; |
| } |
| return common::visit( |
| common::visitors{ |
| [&](const NamelistDetails &x) { |
| for (auto ref : x.objects()) { |
| if (ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| scope, &*ref)) { |
| return true; |
| } |
| } |
| return false; |
| }, |
| [&](const auto &) { |
| return ShouldIgnoreRuntimeTypeInfoNonTbpGenericInterfaces( |
| scope, symbol->GetType()); |
| }, |
| }, |
| symbol->GetUltimate().details()); |
| } |
| |
| } // namespace Fortran::semantics |