(Closes #1734 #2975 #1258 #2949) Add stricter checking for symbols#3358
(Closes #1734 #2975 #1258 #2949) Add stricter checking for symbols#3358
Conversation
|
Despite what it says above, if I go to CodeCov then it says there are 0 indirect coverage changes. |
| if module in self._symtab.symbols: | ||
| mod_sym_tab = self._symtab |
There was a problem hiding this comment.
Seems odd that the find_symbol_table(self._symtab.node) below doesn't already search the given scope as the parameter is :param node: the PSyIR node from which to search. but the implementation starts with a node.scope, wich means the ancestor scope.
Could you check if including the given scope in the search would break other things. I think this would match the expected behaviour of this method name. And even if it breaks other things should those other calls then be ...find_symbol_table(parent) instead?
There was a problem hiding this comment.
That is odd. I've just checked node.scope though and it does:
node = self.ancestor(ScopingNode, include_self=True)so it will return its own table first. I think the problem is that I hacked this code in towards the end because I hit situations where self._symtab.node was None. I'll refactor it.
| # A mapping of 'i_def' etc. to the corresponding DataSymbol | ||
| _precision_map = {} | ||
|
|
||
| def __init__(self, node=None, default_visibility=Symbol.Visibility.PUBLIC): |
There was a problem hiding this comment.
Always happy to see methods deleted from this class, as we have a good strategy to do DSL nodes (by uplifting-lowering), but subclassing other parts of PSyclone for API-specific logic is problematic (discussed here: #1954).
This PR is big enough, but do you think we could fully remove this class? This is now just helpers to create symbols with the LFRic precisions, but the generic find_or_create already accepts a datatype argument to specify these.
There was a problem hiding this comment.
Yes we could. I started doing it and it was going well but it got quite big so I decided to do the Right Thing and break it into #3366.
| _precision_map = {} | ||
|
|
||
| def __init__(self, node=None, default_visibility=Symbol.Visibility.PUBLIC): | ||
| super().__init__(node, default_visibility) |
There was a problem hiding this comment.
Arguably, #1258 could already be closed but I'll add it to the list for this PR. Unfortunately this PR doesn't do anything to address the root of #2659 - that is a harder problem. (Although it will improve the consistency of a tree as soon as it is copied.) #2975 will be closed by this (thanks for spotting).
| # Take care in case we've found an existing ContainerSymbol and | ||
| # it's in an outer scope. | ||
| actual_table = container.find_symbol_table(symbol_table.node) |
There was a problem hiding this comment.
I am a bit confused about why do we need here, is it to "declare the new imported symbols in the same scope as container they come from"? If thats the case this may be a better comment.
| # Reorganise the symbol table construction to occur before we add | ||
| # the children. | ||
| self._symbol_table = other.symbol_table.deep_copy(self) | ||
| self._symbol_table = other.symbol_table.deep_copy(new_node=self) |
There was a problem hiding this comment.
Not the fault of this PR but I always found that argument name strange. Could we replace "new_node" to "attached_to"? Since the symtab method that does the same is call "attach"
There was a problem hiding this comment.
Oh yes, that's a good idea. I also realised that we didn't check the type of the supplied node so I've added that plus test.
| new_sym.interface.container_symbol = new_st.find_or_create( | ||
| name, symbol_type=ContainerSymbol) |
There was a problem hiding this comment.
It is a bit unexpected that deep_copy creates new symbols or changes the name of symbols (find_or_create behaviour). But I understand this is necessary if the symbol has a dependency to an outer scope AND we change the associated node, which could make a lookup go wrong. Is this the reason?
I will be happier if we extend the second paragraph of the docstring to explain this behaviour as a consequence of providing a different associated scope node.
There was a problem hiding this comment.
Agreed. Note that it won't change the name of a Symbol but it will add a new ContainerSymbol if none is found. I tried removing this and ~70 tests broke, primarily because of the way we create a temporary copy of a SymbolTable within LFRicKern.reference_accesses(). That's already the subject of #2874 so I've put in a few more TODOs. I've extended the paragraph as you suggested.
However, I've also realised that perhaps I should be stricter about imported Symbols. As things are now, I check that a suitable ContainerSymbol is in scope when a Symbol with ImportInterface is added to a table. We could tighten this further to require that the ContainerSymbol is in the same table as the imported symbol. What do you think?
| other_sym.name, other_table=other_table)) | ||
| self.update_import_interface(isym) | ||
|
|
||
| def update_symbol_dependencies(self): |
There was a problem hiding this comment.
"thinking out loud" comment, no need to agree, but I am not sure if "update_" is too generic to find out what these two methods do without reading the docstrings but I am not sure of good alternatives. What I could think of is:
- localise_symbol_dependencies
- bind_symbol_dependencies_to_scope
same for the next one where I also wonder if adding "_of" would help make clear is the argument that we update, .e.g. "localise_import_interface_of(symbol)"
There was a problem hiding this comment.
Agreed. I've gone for localise_all_symbol_dependencies and localise_import_interface_of.
| refers to a Container in this or an ancestor scope. | ||
|
|
||
| If no ContainerSymbol is found with the name of the one in the | ||
| ImportInterface of the Symbol, then the ContainerSymboll in the |
| with pytest.raises(symbols.SymbolError) as err: | ||
| table.update_symbol_dependencies() | ||
| assert ("which is not of the same type as the original dependency" | ||
| in str(err.value)) |
There was a problem hiding this comment.
If I am not wrong, this test also needs a case with an outer symbol that has been shadowed in the inner scope, but the "update" should not change the interface of an import in the inner scope (something the implementation already accounts for if I understand the comments correctly)
There was a problem hiding this comment.
Good point. Test added - let me know if it isn't quite what you had in mind.
| try: | ||
| constants_container = symtab.lookup(const_mod) | ||
| except KeyError: | ||
| # TODO Once #696 is done, we should *always* have a |
There was a problem hiding this comment.
Another good spot - #696 is done so I've closed it. I've just grepped and found no remaining TODOs for it.
|
Ready for another look now @sergisiso. I've fired off the ITs again... |
I've broken this out of the work on #1823 - that work revealed issues with our Symbol handling so I thought I might as well do the job properly.
We now no longer add a wildcard import of
constants_modin LFRic generated code. This revealed a few bugs/misssing symbols that I've now fixed.