Skip to content

Commit

Permalink
[flang] Always incorporate parent types' special generic bindings
Browse files Browse the repository at this point in the history
The runtime type information table generator was broken when dealing
with an extension derived type that didn't include a special generic
procedure binding for ASSIGNMENT(=) or user-defined I/O, but one of
whose ancestor types did.  Ensure that the runtime derived type info
tables have complete subtables for all of these special bindings,
and respect any overrides that may have been defined.

Motivating example:
  type parent
   contains
    procedure :: dtWrite => dtWrite1
    generic :: write(formatted) => dtWrite
  end type
  type, extends(parent) :: extended
   contains
    procedure :: dtWrite => dtWrite2
  end type

The runtime derived type information table for "extended" must include
a special generic procedure entry for "write(formatted)" that points
to "dtWrite2" even though "extend" has no generic procedure for
"write(formatted)".

Differential Revision: https://reviews.llvm.org/D144148
  • Loading branch information
klausler committed Feb 16, 2023
1 parent 1538761 commit 7ed26ad
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 20 deletions.
66 changes: 48 additions & 18 deletions flang/lib/Semantics/runtime-type-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,16 @@ class RuntimeTableBuilder {
SomeExpr PackageIntValueExpr(const SomeExpr &genre, std::int64_t = 0) const;
std::vector<evaluate::StructureConstructor> DescribeBindings(
const Scope &dtScope, Scope &);
void DescribeGeneric(const GenericDetails &,
std::map<int, evaluate::StructureConstructor> &, const DerivedTypeSpec *);
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<GenericKind::DefinedIo>, const DerivedTypeSpec *);
std::optional<GenericKind::DefinedIo>, const Scope *,
const DerivedTypeSpec *) const;
void IncorporateDefinedIoGenericInterfaces(
std::map<int, evaluate::StructureConstructor> &, GenericKind::DefinedIo,
const Scope *, const DerivedTypeSpec *);
Expand Down Expand Up @@ -498,7 +503,6 @@ const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) {
if (!isPDTdefinitionWithKindParameters) {
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())};
Expand All @@ -518,8 +522,7 @@ const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) {
},
[&](const ProcBindingDetails &) { // handled in a later pass
},
[&](const GenericDetails &generic) {
DescribeGeneric(generic, specials, derivedTypeSpec);
[&](const GenericDetails &) { // ditto
},
[&](const auto &) {
common::die(
Expand Down Expand Up @@ -565,11 +568,15 @@ const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) {
evaluate::ConstantSubscripts{
static_cast<evaluate::ConstantSubscript>(bindings.size())}));
// Describe "special" bindings to defined assignments, FINAL subroutines,
// and user-defined derived type I/O subroutines.
// and user-defined derived type I/O subroutines. Defined assignments
// and I/O subroutines override any parent bindings; FINAL subroutines
// do not (the runtime will call all of them).
std::map<int, evaluate::StructureConstructor> specials{
DescribeSpecialGenerics(dtScope, dtScope, derivedTypeSpec)};
const DerivedTypeDetails &dtDetails{dtSymbol->get<DerivedTypeDetails>()};
for (const auto &pair : dtDetails.finals()) {
DescribeSpecialProc(specials, *pair.second, false /*!isAssignment*/,
true, std::nullopt, derivedTypeSpec);
true, std::nullopt, nullptr, derivedTypeSpec);
}
if (derivedTypeSpec) {
IncorporateDefinedIoGenericInterfaces(specials,
Expand Down Expand Up @@ -986,15 +993,33 @@ RuntimeTableBuilder::DescribeBindings(const Scope &dtScope, Scope &scope) {
return result;
}

void RuntimeTableBuilder::DescribeGeneric(const GenericDetails &generic,
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 DerivedTypeSpec *derivedTypeSpec) {
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, true,
false /*!final*/, std::nullopt, derivedTypeSpec);
false /*!final*/, std::nullopt, &dtScope,
derivedTypeSpec);
}
}
},
Expand All @@ -1006,7 +1031,7 @@ void RuntimeTableBuilder::DescribeGeneric(const GenericDetails &generic,
case GenericKind::DefinedIo::WriteUnformatted:
for (auto ref : generic.specificProcs()) {
DescribeSpecialProc(specials, *ref, false,
false /*!final*/, io, derivedTypeSpec);
false /*!final*/, io, &dtScope, derivedTypeSpec);
}
break;
}
Expand All @@ -1019,9 +1044,13 @@ void RuntimeTableBuilder::DescribeGeneric(const GenericDetails &generic,
void RuntimeTableBuilder::DescribeSpecialProc(
std::map<int, evaluate::StructureConstructor> &specials,
const Symbol &specificOrBinding, bool isAssignment, bool isFinal,
std::optional<GenericKind::DefinedIo> io,
const DerivedTypeSpec *derivedTypeSpec) {
std::optional<GenericKind::DefinedIo> io, const Scope *dtScope,
const DerivedTypeSpec *derivedTypeSpec) 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())}) {
Expand Down Expand Up @@ -1123,9 +1152,10 @@ void RuntimeTableBuilder::DescribeSpecialProc(
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
// index might already be present in the case of an override
specials.emplace(*index,
evaluate::StructureConstructor{
DEREF(specialSchema_.AsDerived()), std::move(values)});
}
}

Expand All @@ -1144,7 +1174,7 @@ void RuntimeTableBuilder::IncorporateDefinedIoGenericInterfaces(
definedIo);
for (auto ref : genericDetails.specificProcs()) {
DescribeSpecialProc(
specials, *ref, false, false, definedIo, derivedTypeSpec);
specials, *ref, false, false, definedIo, nullptr, derivedTypeSpec);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion flang/module/__fortran_type_info.f90
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
! "TBP" bindings appear first. Inherited bindings, with overrides already
! applied, appear in the initial entries in the same order as they
! appear in the parent type's bindings, if any. They are followed
! by new local bindings in alphabetic order of theing binding names.
! by new local bindings in alphabetic order of their binding names.
type(Binding), pointer, contiguous :: binding(:)
character(len=:), pointer :: name
integer(kind=int64) :: sizeInBytes
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/type-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ class DerivedType {

const DerivedType *GetParentType() const;

// Finds a data component by name in this derived type or tis ancestors.
// Finds a data component by name in this derived type or its ancestors.
const Component *FindDataComponent(
const char *name, std::size_t nameLen) const;

Expand Down
33 changes: 33 additions & 0 deletions flang/test/Semantics/typeinfo02.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
!RUN: bbc --dump-symbols %s | FileCheck %s
!RUN: %flang_fc1 -fdebug-dump-symbols %s | FileCheck %s

module m1
type base
contains
procedure :: wf => wf1
generic :: write(formatted) => wf
end type
type, extends(base) :: extended
contains
procedure :: wf => wf2
end type
contains
subroutine wf1(x,u,iot,v,iostat,iomsg)
class(base), intent(in) :: x
integer, intent(in) :: u
character(len=*), intent(in) :: iot
integer, intent(in) :: v(:)
integer, intent(out) :: iostat
character(len=*), intent(inout) :: iomsg
end subroutine
subroutine wf2(x,u,iot,v,iostat,iomsg)
class(extended), intent(in) :: x
integer, intent(in) :: u
character(len=*), intent(in) :: iot
integer, intent(in) :: v(:)
integer, intent(out) :: iostat
character(len=*), intent(inout) :: iomsg
end subroutine
end module
!CHECK: .s.base, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=1_1,proc=wf1)]
!CHECK: .s.extended, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=1_1,proc=wf2)]

0 comments on commit 7ed26ad

Please sign in to comment.