llvm/flang/lib/semantics/unparse-with-symbols.cc
Tim Keith 3133c9e7e2 [flang] Fix problems with extended derived types
When looking for a component name in a derived type, also look in the
parent type. Before adding a component to a derived type, report an
error if it already has one with that name. Check that components are
accessible when they are accessed.

Add the "parent component" to derived types (i.e. a component with the
same name as the parent type). The symbol is marked with the
`ParentComp` flag so we can avoid writing it to `.mod` files.

Add calls to `add_occurrence()` so that those particular instances of
`parser::Name` get their symbol set.

Change `DeclareObjectEntity` and `DeclareProcEntity` to use `SourceName` as
the name passed in rather than `parser::Name`.

Fix some problems in `unparse-with-symbols.cc` on statements that both
define and reference names.

Fixes flang-compiler/f18#187.

Original-commit: flang-compiler/f18@664b9627f2
Reviewed-on: https://github.com/flang-compiler/f18/pull/192
Tree-same-pre-rewrite: false
2018-09-20 14:08:59 -07:00

180 lines
6.8 KiB
C++

// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "unparse-with-symbols.h"
#include "symbol.h"
#include "../parser/parse-tree-visitor.h"
#include "../parser/parse-tree.h"
#include "../parser/unparse.h"
#include <map>
#include <ostream>
#include <set>
namespace Fortran::semantics {
// Walk the parse tree and collection information about which statements
// define and reference symbols. Then PrintSymbols outputs information
// by statement.
class SymbolDumpVisitor {
public:
// Write out symbols defined or referenced at this statement.
void PrintSymbols(const parser::CharBlock &stmt, std::ostream &, int) const;
template<typename T> bool Pre(const T &) { return true; }
template<typename T> void Post(const T &) {}
template<typename T> bool Pre(const parser::Statement<T> &stmt) {
currStmt_ = &stmt.source;
return true;
}
template<typename T> void Post(const parser::Statement<T> &) {
currStmt_ = nullptr;
}
void Post(const parser::Name &name) { ProcessName(name, isRef()); }
void Post(const parser::EndFunctionStmt &) { EndScope(); }
void Post(const parser::EndModuleStmt &) { EndScope(); }
void Post(const parser::EndMpSubprogramStmt &) { EndScope(); }
void Post(const parser::EndProgramStmt &) { EndScope(); }
void Post(const parser::EndSubmoduleStmt &) { EndScope(); }
void Post(const parser::EndSubroutineStmt &) { EndScope(); }
bool Pre(const parser::DataRef &) { return PreReference(); }
void Post(const parser::DataRef &) { PostReference(); }
bool Pre(const parser::Designator &) { return PreReference(); }
void Post(const parser::Designator &) { PostReference(); }
bool Pre(const parser::StructureComponent &) { return PreReference(); }
void Post(const parser::StructureComponent &) { PostReference(); }
bool Pre(const parser::ProcedureDesignator &) { return PreReference(); }
void Post(const parser::ProcedureDesignator &) { PostReference(); }
bool Pre(const parser::DerivedTypeSpec &) { return PreReference(); }
void Post(const parser::DerivedTypeSpec &) { PostReference(); }
bool Pre(const parser::Rename::Names &);
bool Pre(const parser::UseStmt &);
bool Pre(const parser::DerivedTypeStmt &);
bool Pre(const parser::ImportStmt &) { return PreReference(); }
void Post(const parser::ImportStmt &) { PostReference(); }
private:
using symbolMap = std::multimap<const char *, const Symbol *>;
const SourceName *currStmt_{nullptr}; // current statement we are processing
int isRef_{0}; // > 0 means in the context of a reference
symbolMap defs_; // statement location to symbol defined there
symbolMap refs_; // statement location to symbol referenced there
std::map<const Symbol *, const char *> symbolToStmt_; // symbol to def
void ProcessName(const parser::Name &, bool isRef);
void EndScope();
bool PreReference();
void PostReference();
bool isRef() const { return isRef_ > 0; }
void PrintSymbols(const parser::CharBlock &, std::ostream &, int,
const symbolMap &, bool) const;
void Indent(std::ostream &, int) const;
};
void SymbolDumpVisitor::PrintSymbols(
const parser::CharBlock &location, std::ostream &out, int indent) const {
PrintSymbols(location, out, indent, defs_, true);
PrintSymbols(location, out, indent, refs_, false);
}
void SymbolDumpVisitor::PrintSymbols(const parser::CharBlock &location,
std::ostream &out, int indent, const symbolMap &symbols, bool isDef) const {
std::set<const Symbol *> done; // used to prevent duplicates
auto range{symbols.equal_range(location.begin())};
for (auto it{range.first}; it != range.second; ++it) {
auto *symbol{it->second};
if (done.insert(symbol).second) {
Indent(out, indent);
out << '!' << (isDef ? "DEF"s : "REF"s) << ": ";
DumpForUnparse(out, *symbol, isDef);
out << '\n';
}
}
}
void SymbolDumpVisitor::Indent(std::ostream &out, int indent) const {
for (int i{0}; i < indent; ++i) {
out << ' ';
}
}
// Special handling for some nodes that both define and reference names.
bool SymbolDumpVisitor::Pre(const parser::Rename::Names &x) {
ProcessName(std::get<0>(x.t), /*isRef*/ false);
ProcessName(std::get<1>(x.t), /*isRef*/ true);
return false;
}
bool SymbolDumpVisitor::Pre(const parser::UseStmt &x) {
ProcessName(x.moduleName, /*isRef*/ true);
parser::Walk(x.u, *this);
return false;
}
bool SymbolDumpVisitor::Pre(const parser::DerivedTypeStmt &x) {
PreReference();
parser::Walk(std::get<0>(x.t), *this);
ProcessName(std::get<1>(x.t), /*isRef*/ false);
parser::Walk(std::get<2>(x.t), *this);
PostReference();
return false;
}
// If there is a symbol set in this name, add it to refs_ or symbolToStmt_.
void SymbolDumpVisitor::ProcessName(const parser::Name &name, bool isRef) {
if (const auto *symbol{name.symbol}) {
CHECK(currStmt_);
// If this is the first reference to an implicitly defined symbol,
// record it as a def.
bool isImplicit{symbol->test(Symbol::Flag::Implicit) &&
symbolToStmt_.find(symbol) == symbolToStmt_.end()};
if (isRef && !isImplicit) {
refs_.emplace(currStmt_->begin(), symbol);
} else {
symbolToStmt_.emplace(symbol, currStmt_->begin());
}
}
}
// Defs are initially saved in symbolToStmt_ so that a symbol defined across
// multiple statements is associated with only one (the first). Now that we
// are at the end of a scope, move them into defs_.
void SymbolDumpVisitor::EndScope() {
for (auto pair : symbolToStmt_) {
defs_.emplace(pair.second, pair.first);
}
symbolToStmt_.clear();
}
// {Pre,Post}Reference() are called around constructs that contains symbols
// references. Sometimes those are nested (e.g. DataRef inside Designator)
// so we need to maintain a count to know when we are back out.
bool SymbolDumpVisitor::PreReference() {
++isRef_;
return true;
}
void SymbolDumpVisitor::PostReference() {
CHECK(isRef_ > 0);
--isRef_;
}
void UnparseWithSymbols(std::ostream &out, const parser::Program &program,
parser::Encoding encoding) {
SymbolDumpVisitor visitor;
parser::Walk(program, visitor);
parser::preStatementType preStatement{
[&](const parser::CharBlock &location, std::ostream &out, int indent) {
visitor.PrintSymbols(location, out, indent);
}};
parser::Unparse(out, program, encoding, false, true, &preStatement);
}
} // namespace Fortran::semantics