-
Notifications
You must be signed in to change notification settings - Fork 13
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
Draft: Proposal: resolution of the concrete procedure #340
base: main
Are you sure you want to change the base?
Changes from all commits
24564a0
fe2a819
312e990
2e9cd52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -34,7 +34,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from loki.expression.operations import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
StringConcat, ParenthesisedAdd, ParenthesisedMul, ParenthesisedDiv, ParenthesisedPow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from loki.expression import ExpressionDimensionsMapper, AttachScopes, AttachScopesMapper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from loki.expression import ExpressionDimensionsMapper, ExpressionTypeMapper, AttachScopes, AttachScopesMapper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from loki.logging import debug, perf, info, warning, error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from loki.tools import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
as_tuple, flatten, CaseInsensitiveDict, LazyNodeLookup, dict_override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -2483,6 +2483,42 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
arguments = tuple(arg for arg in arguments if not isinstance(arg, tuple)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
arguments, kwarguments = (), () | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Figure out the exact procedure being called if this is a call to a generic interface | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if name.type.dtype.is_generic: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# If the interface is imported, take its definition from a module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# TODO: handle interfaces defined in the same module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if name.type.imported: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
module = name.type.module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
interface = [i for i in module.interfaces if i.spec.name == name.name] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if len(interface) == 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
interface = interface[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+2492
to
+2494
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be able to shortcut this with
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Generic interface contains an abstract function definition and concrete function implementations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# We need to get rid of the abstract definition. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
concrete_symbols = [symbol for symbol in interface.symbols if not symbol.type.dtype.is_generic] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expr_type_mapper = ExpressionTypeMapper() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expr_dim_mapper = ExpressionDimensionsMapper() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
passed_arguments_types = [(expr_type_mapper(arg), expr_dim_mapper(arg)) for arg in arguments] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Try match passed arguments with one of the concrete functions from the interface | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for symbol in concrete_symbols: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
parameters = symbol.type.dtype.parameters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
declared_parameters_types = [(expr_type_mapper(param), expr_dim_mapper(param)) for param in parameters] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Find matching concrete function | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if declared_parameters_types == passed_arguments_types: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ptype: ProcedureType = name.type.dtype | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new_procedure_type = ProcedureType(name=name.name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
is_function=ptype.is_function, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
is_generic=ptype.is_generic, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return_type=ptype.return_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
concrete_procedure=symbol.type.dtype.procedure) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new_symbol_attribute = SymbolAttributes(dtype=new_procedure_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imported=name.type.imported, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
module=name.type.module) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# When the scope is name.scope, then the concrete_procedure takes always the same value ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# bug or my incomprehension?) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+2519
to
+2520
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is precisely the problem that I'm describing. Hence my alternative suggestion which ensures that you can attach the correct scope to the symbol. We will enforce a strict requirement for having a symbol scope in the future, hence this workaround would no longer work. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name = sym.ProcedureSymbol(name=name.name, scope=None, type=new_symbol_attribute) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+2486
to
+2521
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced that attaching this information to the procedure type will work as expected. The reason is, that all symbols in a scope with the same name share the same entry in the symbol table. Therefore, your example of an int and real variant of the same generic function will create a conflict. I think the above logic will have to be moved into the Lines 1039 to 1073 in b93a01d
This has also other benefits:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ir.CallStatement(name=name, arguments=arguments, kwarguments=kwarguments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
label=kwargs.get('label'), source=kwargs.get('source')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2066,3 +2066,57 @@ def test_import_of_private_symbols(here, frontend): | |||||||||
assert var.type.imported is True | ||||||||||
# Check if the symbol comes from the mod_public module | ||||||||||
assert var.type.module is mod_public | ||||||||||
|
||||||||||
|
||||||||||
@pytest.mark.parametrize('frontend', [FP]) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please parameterize tests always with |
||||||||||
def test_resolution_of_generic_procedures_ext_module(here, frontend): | ||||||||||
|
||||||||||
code_swap_module = """ | ||||||||||
module swap_module | ||||||||||
implicit none | ||||||||||
interface swap | ||||||||||
module procedure swap_int, swap_real | ||||||||||
end interface swap | ||||||||||
contains | ||||||||||
subroutine swap_int(a, b) | ||||||||||
integer, intent(inout) :: a, b | ||||||||||
integer :: temp | ||||||||||
temp = a | ||||||||||
a = b | ||||||||||
b = temp | ||||||||||
end subroutine swap_int | ||||||||||
|
||||||||||
subroutine swap_real(a, b) | ||||||||||
real, intent(inout) :: a, b | ||||||||||
real :: temp | ||||||||||
temp = a | ||||||||||
a = b | ||||||||||
b = temp | ||||||||||
end subroutine swap_real | ||||||||||
end module swap_module | ||||||||||
""" | ||||||||||
code_main_module = """ | ||||||||||
module main | ||||||||||
use swap_module, only: swap | ||||||||||
contains | ||||||||||
subroutine test() | ||||||||||
real :: r1, r2 | ||||||||||
integer :: i1, i2 | ||||||||||
r1 = 0.0 | ||||||||||
r2 = 3.0 | ||||||||||
call swap(r1, r2) | ||||||||||
i1 = 1 | ||||||||||
i2 = 3 | ||||||||||
call swap(i1, i2) | ||||||||||
end subroutine | ||||||||||
end module main | ||||||||||
""" | ||||||||||
mod_swap = Module.from_source(code_swap_module, frontend=frontend) | ||||||||||
mod_main = Module.from_source(code_main_module, frontend=frontend, definitions=[mod_swap]) | ||||||||||
Comment on lines
+2114
to
+2115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add the
Suggested change
|
||||||||||
# Procedures are defined in order: swap_int, swap_real | ||||||||||
procedure_symbols = [routine for routine in mod_swap.subroutines] | ||||||||||
test_routine = mod_main.subroutines[0] | ||||||||||
calls = FindNodes(ir.CallStatement).visit(test_routine.body) | ||||||||||
|
||||||||||
assert calls[0].procedure_type.concrete_procedure == procedure_symbols[1] # swap_real | ||||||||||
assert calls[1].procedure_type.concrete_procedure == procedure_symbols[0] # swap_int | ||||||||||
Comment on lines
+2121
to
+2122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to the above remark about moving the resolution to the
Suggested change
|
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 is very neat!
A small naming suggestion, since we implicitly make that distinction in the type system: I would call this
ExpressionDatatypeMapper
because "type" is what we use forSymbolAttributes
, which include Datatype (dtype
) as well as other declaration attributes.