This is an archive of the discontinued LLVM Phabricator instance.

[flang] Fix assert on constant folding of extended types
ClosedPublic

Authored by PeteSteinfeld on Sep 4 2020, 8:52 AM.

Details

Summary

When we define a derived type that extends another derived type, we can then
create a structure constructor that contains values for the fields of both the
child type and its parent. The compiler's internal representation of that
value contains the name of the parent type where a component name would
normally appear. This caused an assert during contant folding.

There are three cases for components that appear in structure constructors.
The first is the normal case of a component appearing in a structure
constructor for its type.

The second is a component of the parent (or grandparent) type appearing in a
structure constructor for the child type.

The third is the parent type component, which can appear in the structure
constructor of its child.

There are also cases where the component can be arrays.

I created the test case folding12.f90 that covers all of these cases and
modified the code to handle them.

Most of my changes were to the "Find()" method of the type
"StructureConstructor" where I added code to cover the second and third cases
described above. To handle these cases, I needed to create a
"StructureConstructor" for the parent type component and return it. To handle
returning a newly created "StructureConstructor", I changed the return type of
"Find()" to be "std::optional" rather than an ordinary pointer.

This change supersedes D86172.

Diff Detail

Event Timeline

PeteSteinfeld created this revision.Sep 4 2020, 8:52 AM
Herald added a project: Restricted Project. · View Herald Transcript
PeteSteinfeld requested review of this revision.Sep 4 2020, 8:52 AM
PeteSteinfeld added a project: Restricted Project.Sep 4 2020, 8:53 AM
klausler added inline comments.Sep 4 2020, 2:23 PM
flang/lib/Evaluate/expression.cpp
222

This loop seems to assume that the order of the containing structure constructor's values will be in 1-1 correspondence with the components of the parent constructor without gaps or rearrangement, and that the parent's parent component (if any) will appear only as a single leading component without a need to recurse to collect its components.

PeteSteinfeld added inline comments.Sep 4 2020, 2:46 PM
flang/lib/Evaluate/expression.cpp
222

Thanks for pointing this out. I already have one test that deals with a parent's parent component, but I'll check to see if it's adequate. I'll also write some tests with out-of-order named components in the structure constructor and look into gaps. Then I'll see what changes need to be made.

According to C799, every component in a structure constructor must have a
component-spec unless it has a default initialization. So, aside from
components with default initializations, I don't believe that it's possible to
have gaps in a structure constructor.

Also, it looks like the compiler puts the components in a structure constructor
into their declared order, regardless of the order in which they appear in the
source. The compiler also fills in components with default initializations.

I added test cases for structure constructors that depend on the values of
components with default initializers and for structure constructors that have
the components out of order in the source code. But because of the points
listed above, these new tests did not require any changes to the compiler
source.

I don't understand the comment about not needed to recurse to collect the
components of the parent component. In the current code, the function
StructureConstructor::Find() contains a recursive call to find components in a
parent component. This code gets exercised by the following test that's part
of folding12.f90:

type(child_type), parameter :: child_const9 = &
  child_type(parent_type(30), 31)
integer, parameter :: int_const10 = child_const9%parent_field
logical, parameter :: test_child5 = int_const10 == 30

Let me know if there's a form of structure constructor that this code doesn't
handle.

A structure constructor can contain a "parent component" as a nested structure constructor (child(parent=parent(a=1),b=2)) , or it can name the components of the parent component directly (child(a=1,b=2)).

A structure constructor can contain a "parent component" as a nested structure constructor (child(parent=parent(a=1),b=2)) , or it can name the components of the parent component directly (child(a=1,b=2)).

I believe that my latest addition to folding12.f90 covers both of these cases. Lines 141-144 declare a structure constructor that has a nested structure constructor for the parent component:

type(child_type), parameter :: child_const3 = &
  child_type(parent_type( &
    parent_field2 = 17.7, parent_field3 = .false., parent_field1 = 18), &
      child_field2 = .false., child_field1 = 19.9, child_field3 = 21)

Lines 152-155 contain a structure constructor that names the fields of the parent type without having an explicit structure constructor for the parent component:

type(child_type), parameter :: child_const4 = &
  child_type(parent_type( &
    parent_field3 = .true., parent_field1 = 22), &
    child_field1 = 23.4, child_field3 = 24)

Is there an aspect of structure constructors that this test doesn't handle?

Lines 152-155 contain a structure constructor that names the fields of the parent type without having an explicit structure constructor for the parent component:

type(child_type), parameter :: child_const4 = &
  child_type(parent_type( &
    parent_field3 = .true., parent_field1 = 22), &
    child_field1 = 23.4, child_field3 = 24)

That's a structure constructor there for parent_type(), no? A case with no structure constructor for the parent type would look like child_type(parent_field3=.true., parent_field1=22, child_field1=23.4, child_field3=24).

Lines 152-155 contain a structure constructor that names the fields of the parent type without having an explicit structure constructor for the parent component:

type(child_type), parameter :: child_const4 = &
  child_type(parent_type( &
    parent_field3 = .true., parent_field1 = 22), &
    child_field1 = 23.4, child_field3 = 24)

That's a structure constructor there for parent_type(), no? A case with no structure constructor for the parent type would look like child_type(parent_field3=.true., parent_field1=22, child_field1=23.4, child_field3=24).

Ahh.... There are structure constructors that name the parent fields directly in lines 106-110 and 130-133.

klausler accepted this revision.Sep 10 2020, 1:01 PM
This revision is now accepted and ready to land.Sep 10 2020, 1:01 PM
This revision was landed with ongoing or failed builds.Sep 10 2020, 2:38 PM
This revision was automatically updated to reflect the committed changes.