Skip to content

Commit

Permalink
[fortran] Fix for function declarations in derived types (#834)
Browse files Browse the repository at this point in the history
## Summary of Changes
There was a bug discovered in the CTSM source PhotosynthesisMod.F90
where a function is declared in a derived type but defined in the outer
module. This PR updates the variable context to support this case.

Additionally resolves a bug where function declarations and definitions
can have different case sensitivity.

### Related issues

Resolves ???

---------

Co-authored-by: titomeister <[email protected]> 156e193
  • Loading branch information
github-actions[bot] committed Mar 4, 2024
1 parent 7d60fc5 commit 83db72a
Show file tree
Hide file tree
Showing 1,638 changed files with 221,087 additions and 242,275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ <h3>Instance variables</h3>
<h3>Methods</h3>
<dl>
<dt id="skema.img2mml.models.decoders.xfmer_decoder.Transformer_Decoder.create_pad_mask"><code class="name flex">
<span>def <span class="ident">create_pad_mask</span></span>(<span>self, matrix: <built-in method tensor of type object at 0x7f93e855b500>, pad_token: int) ‑> <built-in method tensor of type object at 0x7f93e855b500></span>
<span>def <span class="ident">create_pad_mask</span></span>(<span>self, matrix: <built-in method tensor of type object at 0x7f372515b500>, pad_token: int) ‑> <built-in method tensor of type object at 0x7f372515b500></span>
</code></dt>
<dd>
<div class="desc"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ <h3>Class variables</h3>
<h3>Methods</h3>
<dl>
<dt id="skema.img2mml.models.encoding.positional_encoding_for_xfmer.PositionalEncoding.forward"><code class="name flex">
<span>def <span class="ident">forward</span></span>(<span>self, x: <built-in method tensor of type object at 0x7f93e855b500>) ‑> <built-in method tensor of type object at 0x7f93e855b500></span>
<span>def <span class="ident">forward</span></span>(<span>self, x: <built-in method tensor of type object at 0x7f372515b500>) ‑> <built-in method tensor of type object at 0x7f372515b500></span>
</code></dt>
<dd>
<div class="desc"><p>Defines the computation performed at every call.</p>
Expand Down
156 changes: 78 additions & 78 deletions api/python/skema/program_analysis/CAST/fortran/ts2cast.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,7 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code
# TODO: Research the above
outer_body_nodes = get_children_by_types(root, [&#34;function&#34;, &#34;subroutine&#34;])
if len(outer_body_nodes) &gt; 0:
body = []
for body_node in outer_body_nodes:
child_cast = self.visit(body_node)
if isinstance(child_cast, List):
body.extend(child_cast)
elif isinstance(child_cast, AstNode):
body.append(child_cast)
body = self.generate_cast_body(outer_body_nodes)
modules.append(
Module(
name=None,
Expand Down Expand Up @@ -208,14 +202,8 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code
&#34;&#34;&#34;Visitor for program and module statement. Returns a Module object&#34;&#34;&#34;
self.variable_context.push_context()

program_body = []
for child in node.children[1:-1]: # Ignore the start and end program statement
child_cast = self.visit(child)
if isinstance(child_cast, List):
program_body.extend(child_cast)
elif isinstance(child_cast, AstNode):
program_body.append(child_cast)

program_body = self.generate_cast_body(node.children[1:-1])

self.variable_context.pop_context()

return Module(
Expand Down Expand Up @@ -260,7 +248,7 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code
# (function_result) - Optional
# (identifier)
# (body_node) ...

# Create a new variable context
self.variable_context.push_context()

Expand Down Expand Up @@ -345,6 +333,19 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code
# Pop variable context off of stack before leaving this scope
self.variable_context.pop_context()


# If this is a class function, we need to associate the function def with the class
# We should also return None here so we don&#39;t duplicate the function def
if self.variable_context.is_class_function(name.name):
self.variable_context.copy_class_function(name.name,
FunctionDef(
name=name,
func_args=func_args,
body=body,
source_refs=[self.node_helper.get_source_ref(node)],
))
return None

return FunctionDef(
name=name,
func_args=func_args,
Expand Down Expand Up @@ -1049,20 +1050,17 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code
# If we tell the variable context we are in a record definition, it will append the type name as a prefix to all defined variables.
self.variable_context.enter_record_definition(record_name)

# Note:
# Note: In derived type declarations, functions are only declared. The actual definition will be in the outer module.
funcs = []
derived_type_procedures_node = get_first_child_by_type(
if derived_type_procedures_node := get_first_child_by_type(
node, &#34;derived_type_procedures&#34;
)
if derived_type_procedures_node:
):
for procedure_node in get_children_by_types(
derived_type_procedures_node, [&#34;procedure_statement&#34;]
):
funcs.append(
self.visit_name(
get_first_child_by_type(procedure_node, &#34;method_name&#34;)
)
)
function_name = self.node_helper.get_identifier(get_first_child_by_type(procedure_node, &#34;method_name&#34;, recurse=True))
funcs.append(self.variable_context.register_module_function(function_name))


# A derived type can only have variable declarations in its body.
fields = []
Expand Down Expand Up @@ -1290,12 +1288,14 @@ <h1 class="title">Module <code>skema.program_analysis.CAST.fortran.ts2cast</code

def generate_cast_body(self, body_nodes: List):
body = []

for node in body_nodes:
cast = self.visit(node)

if isinstance(cast, AstNode):
body.append(cast)
elif isinstance(cast, List):
body.extend(cast)
body.extend([element for element in cast if element is not None])

# Gromet doesn&#39;t support empty bodies, so we should create a no_op instead
if len(body) == 0:
Expand Down Expand Up @@ -1368,13 +1368,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
# TODO: Research the above
outer_body_nodes = get_children_by_types(root, [&#34;function&#34;, &#34;subroutine&#34;])
if len(outer_body_nodes) &gt; 0:
body = []
for body_node in outer_body_nodes:
child_cast = self.visit(body_node)
if isinstance(child_cast, List):
body.extend(child_cast)
elif isinstance(child_cast, AstNode):
body.append(child_cast)
body = self.generate_cast_body(outer_body_nodes)
modules.append(
Module(
name=None,
Expand Down Expand Up @@ -1442,14 +1436,8 @@ <h2 class="section-title" id="header-classes">Classes</h2>
&#34;&#34;&#34;Visitor for program and module statement. Returns a Module object&#34;&#34;&#34;
self.variable_context.push_context()

program_body = []
for child in node.children[1:-1]: # Ignore the start and end program statement
child_cast = self.visit(child)
if isinstance(child_cast, List):
program_body.extend(child_cast)
elif isinstance(child_cast, AstNode):
program_body.append(child_cast)

program_body = self.generate_cast_body(node.children[1:-1])

self.variable_context.pop_context()

return Module(
Expand Down Expand Up @@ -1494,7 +1482,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
# (function_result) - Optional
# (identifier)
# (body_node) ...

# Create a new variable context
self.variable_context.push_context()

Expand Down Expand Up @@ -1579,6 +1567,19 @@ <h2 class="section-title" id="header-classes">Classes</h2>
# Pop variable context off of stack before leaving this scope
self.variable_context.pop_context()


# If this is a class function, we need to associate the function def with the class
# We should also return None here so we don&#39;t duplicate the function def
if self.variable_context.is_class_function(name.name):
self.variable_context.copy_class_function(name.name,
FunctionDef(
name=name,
func_args=func_args,
body=body,
source_refs=[self.node_helper.get_source_ref(node)],
))
return None

return FunctionDef(
name=name,
func_args=func_args,
Expand Down Expand Up @@ -2283,20 +2284,17 @@ <h2 class="section-title" id="header-classes">Classes</h2>
# If we tell the variable context we are in a record definition, it will append the type name as a prefix to all defined variables.
self.variable_context.enter_record_definition(record_name)

# Note:
# Note: In derived type declarations, functions are only declared. The actual definition will be in the outer module.
funcs = []
derived_type_procedures_node = get_first_child_by_type(
if derived_type_procedures_node := get_first_child_by_type(
node, &#34;derived_type_procedures&#34;
)
if derived_type_procedures_node:
):
for procedure_node in get_children_by_types(
derived_type_procedures_node, [&#34;procedure_statement&#34;]
):
funcs.append(
self.visit_name(
get_first_child_by_type(procedure_node, &#34;method_name&#34;)
)
)
function_name = self.node_helper.get_identifier(get_first_child_by_type(procedure_node, &#34;method_name&#34;, recurse=True))
funcs.append(self.variable_context.register_module_function(function_name))


# A derived type can only have variable declarations in its body.
fields = []
Expand Down Expand Up @@ -2524,12 +2522,14 @@ <h2 class="section-title" id="header-classes">Classes</h2>

def generate_cast_body(self, body_nodes: List):
body = []

for node in body_nodes:
cast = self.visit(node)

if isinstance(cast, AstNode):
body.append(cast)
elif isinstance(cast, List):
body.extend(cast)
body.extend([element for element in cast if element is not None])

# Gromet doesn&#39;t support empty bodies, so we should create a no_op instead
if len(body) == 0:
Expand Down Expand Up @@ -2568,12 +2568,14 @@ <h3>Methods</h3>
</summary>
<pre><code class="python">def generate_cast_body(self, body_nodes: List):
body = []

for node in body_nodes:
cast = self.visit(node)

if isinstance(cast, AstNode):
body.append(cast)
elif isinstance(cast, List):
body.extend(cast)
body.extend([element for element in cast if element is not None])

# Gromet doesn&#39;t support empty bodies, so we should create a no_op instead
if len(body) == 0:
Expand Down Expand Up @@ -2626,13 +2628,7 @@ <h3>Methods</h3>
# TODO: Research the above
outer_body_nodes = get_children_by_types(root, [&#34;function&#34;, &#34;subroutine&#34;])
if len(outer_body_nodes) &gt; 0:
body = []
for body_node in outer_body_nodes:
child_cast = self.visit(body_node)
if isinstance(child_cast, List):
body.extend(child_cast)
elif isinstance(child_cast, AstNode):
body.append(child_cast)
body = self.generate_cast_body(outer_body_nodes)
modules.append(
Module(
name=None,
Expand Down Expand Up @@ -2772,20 +2768,17 @@ <h3>Methods</h3>
# If we tell the variable context we are in a record definition, it will append the type name as a prefix to all defined variables.
self.variable_context.enter_record_definition(record_name)

# Note:
# Note: In derived type declarations, functions are only declared. The actual definition will be in the outer module.
funcs = []
derived_type_procedures_node = get_first_child_by_type(
if derived_type_procedures_node := get_first_child_by_type(
node, &#34;derived_type_procedures&#34;
)
if derived_type_procedures_node:
):
for procedure_node in get_children_by_types(
derived_type_procedures_node, [&#34;procedure_statement&#34;]
):
funcs.append(
self.visit_name(
get_first_child_by_type(procedure_node, &#34;method_name&#34;)
)
)
function_name = self.node_helper.get_identifier(get_first_child_by_type(procedure_node, &#34;method_name&#34;, recurse=True))
funcs.append(self.variable_context.register_module_function(function_name))


# A derived type can only have variable declarations in its body.
fields = []
Expand Down Expand Up @@ -3173,7 +3166,7 @@ <h3>Methods</h3>
# (function_result) - Optional
# (identifier)
# (body_node) ...

# Create a new variable context
self.variable_context.push_context()

Expand Down Expand Up @@ -3258,6 +3251,19 @@ <h3>Methods</h3>
# Pop variable context off of stack before leaving this scope
self.variable_context.pop_context()


# If this is a class function, we need to associate the function def with the class
# We should also return None here so we don&#39;t duplicate the function def
if self.variable_context.is_class_function(name.name):
self.variable_context.copy_class_function(name.name,
FunctionDef(
name=name,
func_args=func_args,
body=body,
source_refs=[self.node_helper.get_source_ref(node)],
))
return None

return FunctionDef(
name=name,
func_args=func_args,
Expand Down Expand Up @@ -3587,14 +3593,8 @@ <h3>Methods</h3>
&#34;&#34;&#34;Visitor for program and module statement. Returns a Module object&#34;&#34;&#34;
self.variable_context.push_context()

program_body = []
for child in node.children[1:-1]: # Ignore the start and end program statement
child_cast = self.visit(child)
if isinstance(child_cast, List):
program_body.extend(child_cast)
elif isinstance(child_cast, AstNode):
program_body.append(child_cast)

program_body = self.generate_cast_body(node.children[1:-1])

self.variable_context.pop_context()

return Module(
Expand Down
Loading

0 comments on commit 83db72a

Please sign in to comment.