-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(Closes #2642, initial step towards #2643) Implement extends(type)
, procedure
in derived type, class
in declarations.
#2644
base: master
Are you sure you want to change the base?
Conversation
This will require me to edit the documentation, including (but maybe not limited to) https://psyclone-dev.readthedocs.io/en/latest/psyir_symbols.html#datatypes |
@arporter @sergisiso This works fine in practice on our project. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #2644 +/- ##
==========================================
- Coverage 99.88% 99.88% -0.01%
==========================================
Files 357 357
Lines 49823 50214 +391
==========================================
+ Hits 49767 50156 +389
- Misses 56 58 +2 ☔ View full report in Codecov by Sentry. |
@sergisiso @arporter Two remaining lines still to be tested for codecov (I’ll do this on Monday) but this is otherwise ready for review. |
Thanks @JulienRemy. I've set the integration tests going... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks for this Julien, it's a big change, especially since it impacts the metadata handling. In general I think it's good although there are a few things to discuss and I can't remember all of them now as it's taken me several days to get this first review done. However, it's all in the comments so we can work through them in the usual way.
src/psyclone/tests/psyir/frontend/fparser2_derived_type_test.py
Outdated
Show resolved
Hide resolved
" use kernel_mod, only : parent_type\n" | ||
" type, extends(parent_type) :: test_type\n" | ||
" integer :: i = 1\n" | ||
" contains\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please could you extend this test so that the type
contains a private
statement. The visibility of the test_code
procedure should then become private
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, it works :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite and unfortunately I think it's broken (see below) ! :-)
Thanks for your review @arporter :) |
if type(extends_symbol) is Symbol: | ||
extends_symbol.specialise(DataTypeSymbol) | ||
extends_symbol.datatype = StructureType() | ||
# If it is not in the symbol table, create a new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this comment into the else:
block as that's what it applies to. If we are creating a Symbol then it should be added to a SymbolTable. Given that the lookup
hasn't found it, it will have to have an Unresolved
interface. That means we know it exists somewhere but we don't know how it is brought into scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. This is done!
@@ -3268,6 +3339,10 @@ def _add_target_attribute(var_name, table): | |||
(e.g. a routine argument or an imported symbol). | |||
|
|||
''' | |||
# pylint: disable=import-outside-toplevel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to be a pain but please could you move the pylint disable down too.. In fact, could you also change it to be # pylint: disable-next=...
as that's even tighter.
:param tsymbol: the DataTypeSymbol representing the derived-type. | ||
:type tsymbol: :py:class:`psyclone.psyir.symbols.DataTypeSymbol` | ||
|
||
:returns: None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for a :returns: if it doesn't return anything :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
''' | ||
|
||
contains_blocks = walk(decl, Fortran2003.Type_Bound_Procedure_Part) | ||
if contains_blocks: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To reduce subsequent indentation, I suggest changing this to:
if not contains_blocks:
return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed :)
# `testkern_code` in `procedure :: code => testkern_code` | ||
if procedure.items[4] is not None: | ||
initial_value_name = procedure.items[4].string | ||
initial_value_symbol = parent.symbol_table.lookup( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think find_or_create
(https://psyclone-ref.readthedocs.io/en/latest/_static/html/classpsyclone_1_1psyir_1_1symbols_1_1symbol__table_1_1SymbolTable.html#ada706dd884505330c3d24b9b9a738042) would simplify things here. You can pass the symboltype
and data_type
that you want (if it doesn't exist) as arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, it's indeed clearer.
:raises TypeError: if the new value is not a Reference to a | ||
RoutineSymbol. | ||
|
||
:returns: None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
new_datatype, | ||
procedure_component.visibility, | ||
new_value) | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can a datatype contain more than one procedure with the same initial value? I would guess that it can. Same goes for the early return at L1216. Please also add a test for this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed about the early returns, there could be multiple procedure, ... => old_name
to replace in there. They're removed and I've added a test with multiple procedure components (supported and not).
or procedure_component.initial_value.name.lower() | ||
!= old_value_name.lower()): | ||
continue | ||
self._procedure_components[name] = self.ComponentType( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we made ComponentType mutable (i.e. not frozen) this would be a bit simpler - in case that swings the argument in your other PR that @sergisiso is reviewing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess keeping it frozen makes sense, otherwise we get a Symbol-equivalent that's mutable? Feels a bit dangerous.
src/psyclone/tests/psyGen_test.py
Outdated
''' Check that the CodedKern rename method renames the kernel in the PSyIR | ||
tree. ''' | ||
# pylint: disable=protected-access, too-many-statements | ||
_, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll give you that but they are old tests :-) It's:
psy, invoke_info = get_invoke("1_single_invoke.f90", "lfric", dist_mem=False)
assert isinstance(sym.datatype, DataTypeSymbol) | ||
assert sym.datatype.is_class is True | ||
assert sym.name == "urgh" | ||
assert sym.datatype.name == "*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in the implementation, I think this should either be UnsupportedFortranType (the easiest thing to do) or an UnresolvedFortranType with an is_class==True
attribute.
" end type test_type\n" in result) | ||
|
||
# private type that contains procedures | ||
# the `test_code` procedure should thus become private |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct. You've put the private
attribute on the declaration of test_type
so test_type
will be private - this says nothing about the visibility of the components within it. What I meant was:
type my_type
private
contains
procedure :: this_should_be_private
end type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I had misunderstood. Indeed, components should not inherit the derived type visibility and my implementation was plain wrong. Thanks for spotting this!
I checked the standard and implemented visibility for procedure components using a "standalone" private
in the contains part.
However, note that private
statements in the data components and procedure components parts are independent (see e.g. NOTE 4.44 p.74 of https://dci.dci-gitlab.cines.fr/webextranet/_downloads/91b30eb00b39f6896fae528e5d0d66fc/Fortran_2003_N1601.pdf) so that your code above would not modify the visibility of this_should_be_private
whereas this would:
type my_type
contains
private
procedure :: this_should_be_private
end type
I've fixed this and added tests where the standalone private
statement either does or does not impact the visibility of a procedure and of a data component.
" type, extends(parent_type), private :: test_type\n" | ||
" integer, public :: i = 1\n" | ||
" contains\n" | ||
" procedure, private :: test_code\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should remain public I think? Worth keeping this test in addition to one where you add a private
statement inside the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're totally right, see above.
" procedure (my_interface) :: test_code\n" | ||
" end type test_type\n" in result) | ||
|
||
# type that contains procedures with pass, nopass and deferred |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with other tests elsewhere, please could you break this one up into separate tests, as indicated by the comments that you have already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
" use kernel_mod, only : parent_type\n" | ||
" type, extends(parent_type) :: test_type\n" | ||
" integer :: i = 1\n" | ||
" contains\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite and unfortunately I think it's broken (see below) ! :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking pretty good now, thanks Julien.
We will need to update the frontend a little to handle class(*)
- I've suggested a couple of options.
Also, I think the visibility of members of a type needs a little work.
I'll set the integration tests running now in case that shows any other issues.
@arporter All edits except for those about |
@hiker and @sergisiso - we could do with your opinion on how to handle declarations of type |
This adds the following object- and inheritance-oriented features:
extends
attribute inStructureType
for inheritance of derived types, which avoids relying onUnsupportedFortranType
and itsdeclaration: str
attribute,procedure_components
attribute inStructureType
for procedures in acontains
block of a derived type, including visibility and initial value,class
(versustype
) in routine declarations, by introducing a boolean attribute inDataTypeSymbol
.Note that as this makes some derived types supported there is for now a workaround using
FortranWriter
in bothGOceanKernelMetadata
andLFRicKernelMetadata
, which rely onUnsupportedFortranType().declaration
and fparser, see #2643.