This is an archive of the discontinued LLVM Phabricator instance.

[flang] Bug fix for ambiguous references to data and functions
ClosedPublic

Authored by PeteSteinfeld on Jun 30 2020, 12:05 PM.

Details

Summary

A program may erroneously reference the same name as both a data object and as a function. Some of these references were causing an internal error in expression analysis.

It was already the case that a symbol referenced in a parse tree for a call was changed from an Entity to a ProcEntity. I added code to detect when a symbol was referenced in a parse tree as an array element gets changed from an Entity to an ObjectEntity. Then, if an ObjectEntity gets called as a function or a ProcEntity gets referenced as a data object, errors get emitted.

This analysis was previously confined to the name resolution of the
specification part of a ProgramTree. I added a pass to the execution part of
a ProgramTree to catch names declared in blocks.

Diff Detail

Event Timeline

PeteSteinfeld created this revision.Jun 30 2020, 12:05 PM
Herald added a project: Restricted Project. · View Herald Transcript
PeteSteinfeld added a project: Restricted Project.Jun 30 2020, 12:06 PM

Would it be worthwhile adding flags to symbols to indicate that they are assumed to be functions or data based on their usage (as opposed to explicit declarations)?

Then we could give better error messages like "Cannot access 'foo' as an array as it was previously called as a function" and "Cannot call 'foo' as a function as it was previous accessed as an array".

flang/lib/Semantics/resolve-names.cpp
5893

Why is this change needed? When would FindSymbol find the wrong symbol?

6943

Why does this require an extra pass over the parse tree. Can't this check be done as part of ResolveExecutionParts where ArrayElement nodes are handled?

flang/test/Semantics/resolve93.f90
11

It's odd that this is the same situation as line 6 but we get a different error message.

PeteSteinfeld marked 3 inline comments as done.Jun 30 2020, 2:56 PM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
5893

Yes. The call to currScope() misses block scopes. So if the same name is declared in a block and in the global scope, the wrong one will be found.

6943

The problem I ran into was dealing with blocks and their associated scopes. When processing the specification parts, I couldn't find symbols declared in blocks. I tried eliminating the pass in ResolveSpecificationParts, but that broke processing of subprograms as dummy arguments.

flang/test/Semantics/resolve93.f90
11

In the first case, the problem is discovered during the first skimming code walk, where the name str is classified as an ObjectEntity because we find it in a parse tree that's an ArrayElement.

Then, the execution part is walked to resolve names. During this walk, a Pre() function for FunctionReference gets executed and it converts the name str2 to a ProcEntity. This happens before the code that I added. Then the skimming walk that I added gets executed. It sees the parse tree for the ArrayElement, but the name str2 has already been classified as a ProcEntity at that point. This causes the error message to be emitted.

tskeith added inline comments.Jun 30 2020, 4:00 PM
flang/lib/Semantics/resolve-names.cpp
5893

So you can't call FindSymbol if the name is in a block scope. But then how do you know it won't be called in that case? Is there a guarantee that every name in a block scope will be resolved by this point and so FindSymbol won't be called on it?

6943

I was referring to this call, not the one in ResolveSpecificationParts. Immediately above here we walk the execution parts. The will include the ArrayElement node. Why can't NoteExecutablePartArrayElement be done as part of that walk?

PeteSteinfeld marked 2 inline comments as done.Jun 30 2020, 5:03 PM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
5893

I'm not sure I understand your question. This code gets executed twice, once during processing of the specification part and then again at the end of processing of the execution part. In either case, we won't find names declared in block scopes. During the second pass, we won't find names that are declared in block scopes that have not had their symbol filled in previously. I'm not sure if this latter case is possible for an error-free symbol.

6943

Thanks for clarifying. It may be possible to do that. I'll investigate.

PeteSteinfeld edited the summary of this revision. (Show Details)Jul 1 2020, 8:08 AM
PeteSteinfeld marked an inline comment as done.Jul 1 2020, 10:26 AM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
6943

@tskeith , you were correct that we can handle detection of ArrayElement references in the main ResolveNames pass of the execution part. I'll be pushing a change soon with this implementation.

I eliminated the extra skimmer pass during name resolution of the execution
part. I also reverted the processing of calls to its original form.

tskeith added inline comments.Jul 1 2020, 11:48 AM
flang/lib/Semantics/resolve-names.cpp
6279

Do you have an example where this is needed?

PeteSteinfeld marked an inline comment as done.Jul 1 2020, 12:10 PM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
6279

You're right. It's not needed! Eliminating it has the result of always causing references to functions being recognized before references to array elements. This changes the error messages where the array reference precedes the function reference, but I think that's OK. I'll get rid of it.

Removed an unneeded visitor for ArrayElement.

tskeith added inline comments.Jul 2 2020, 11:34 AM
flang/lib/Semantics/resolve-names.cpp
5914

I think if you do this in ResolveDataRef when we see an ArrayElement you don't need to call FindScope. The name will be resolved if it can be.

tskeith added inline comments.Jul 2 2020, 11:38 AM
flang/lib/Semantics/resolve-names.cpp
5917

symbol->has<ProcEntityDetails>() is better.

PeteSteinfeld marked 2 inline comments as done.Jul 2 2020, 2:56 PM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
5914

Good thought. But I checked, and the symbol field of the parser::Name has not yet been filled in at the point. On the other hand, I realized that the lookup via the call to FindSymbol() always succeeds at the point because the current scope is correctly set. This let me simplify the code by eliminating the unnecessary reference to name.symbol.

5917

Thanks, Tim. I'll change it.

Simplified the code for getting the symbol of an ArrayElement.

tskeith added inline comments.Jul 2 2020, 4:09 PM
flang/lib/Semantics/resolve-names.cpp
5914

ResolveDataRef is where the symbol is filled in. I'm suggesting something like this for the ArrayElement case in ResolveDataRef:

[&](const Indirection<parser::ArrayElement> &y) {
  Walk(y.value().subscripts);
  const parser::Name *name{ResolveDataRef(y.value().base)};
  if (!name) {
  } else if (!name->symbol->has<ProcEntityDetails>()) {
    ConvertToObjectEntity(*name->symbol);
  } else if (!context().HasError(*name->symbol)) {
    SayWithDecl(*name, *name->symbol,
        "Cannot reference function '%s' as data"_err_en_US);
  }
  return name;
},
PeteSteinfeld marked an inline comment as done.Jul 2 2020, 8:19 PM
PeteSteinfeld added inline comments.
flang/lib/Semantics/resolve-names.cpp
5914

Ahhh. Thanks for leading me by the nose. This is much better than my implementation.

At Tim's suggestion, I moved the code to detect an illegal use of a
ProcEntity and to convert an Entity to an ObjectEntity to the existing
walk that processes an ArrayElement.

tskeith accepted this revision.Jul 4 2020, 10:26 AM
This revision is now accepted and ready to land.Jul 4 2020, 10:26 AM
This revision was automatically updated to reflect the committed changes.