llvm-project/flang/lib/Semantics/scope.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

442 lines
13 KiB
C++
Raw Normal View History

//===-- lib/Semantics/scope.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Semantics/scope.h"
#include "flang/Parser/characters.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/type.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
[flang] Partial implementation of Symbols and Scopes. A Symbol consists of a common part (in class Symbol) containing name, owner, attributes. Information for a specific kind of symbol is in a variant containing one of the *Details classes. So the kind of symbol is determined by the type of details class stored in the details_ variant. For scopes there is a single Scope class with an enum indicating the kind. So far there isn't a need for extra kind-specific details as with Symbols but that could change. Symbols defined in a Scope are stored there in a simple map. resolve-names.cc is a partial implementation of a parse-tree walker that resolves names to Symbols. Currently is only handles functions (which introduce a new Scope) and entity-decls. The test-type executable was reused as a driver for this to avoid the need for a new one. Sample output is below. When each "end function" is encountered the scope is dumped, which shows the symbols defined in it. $ cat a.f90 pure integer(8) function foo(arg1, arg2) result(res) integer :: arg1 real :: arg2 contains function bar(arg1) real :: bar real :: arg1 end function end function $ Debug/tools/f18/test-type a.f90 Subprogram scope: 0 children arg1: Entity type: REAL bar: Entity type: REAL Subprogram scope: 1 children arg1: Entity type: INTEGER arg2: Entity type: REAL bar: Subprogram (arg1) foo: Subprogram (arg1, arg2) result(res) res: Entity type: INTEGER(8) Original-commit: flang-compiler/f18@1cd2fbc04da1d6bb2ef5bc1cf07c808460ea7547 Reviewed-on: https://github.com/flang-compiler/f18/pull/30 Tree-same-pre-rewrite: false
2018-03-23 08:08:20 +08:00
#include <memory>
namespace Fortran::semantics {
[flang] Partial implementation of Symbols and Scopes. A Symbol consists of a common part (in class Symbol) containing name, owner, attributes. Information for a specific kind of symbol is in a variant containing one of the *Details classes. So the kind of symbol is determined by the type of details class stored in the details_ variant. For scopes there is a single Scope class with an enum indicating the kind. So far there isn't a need for extra kind-specific details as with Symbols but that could change. Symbols defined in a Scope are stored there in a simple map. resolve-names.cc is a partial implementation of a parse-tree walker that resolves names to Symbols. Currently is only handles functions (which introduce a new Scope) and entity-decls. The test-type executable was reused as a driver for this to avoid the need for a new one. Sample output is below. When each "end function" is encountered the scope is dumped, which shows the symbols defined in it. $ cat a.f90 pure integer(8) function foo(arg1, arg2) result(res) integer :: arg1 real :: arg2 contains function bar(arg1) real :: bar real :: arg1 end function end function $ Debug/tools/f18/test-type a.f90 Subprogram scope: 0 children arg1: Entity type: REAL bar: Entity type: REAL Subprogram scope: 1 children arg1: Entity type: INTEGER arg2: Entity type: REAL bar: Subprogram (arg1) foo: Subprogram (arg1, arg2) result(res) res: Entity type: INTEGER(8) Original-commit: flang-compiler/f18@1cd2fbc04da1d6bb2ef5bc1cf07c808460ea7547 Reviewed-on: https://github.com/flang-compiler/f18/pull/30 Tree-same-pre-rewrite: false
2018-03-23 08:08:20 +08:00
[flang] Change how memory for Symbol instances is managed. With this change, all instances Symbol are stored in class Symbols. Scope.symbols_, which used to own the symbol memory, now maps names to Symbol* instead. This causes a bunch of reference-to-pointer changes because of the change in type of key-value pairs. It also requires a default constructor for Symbol, which means owner_ can't be a reference. Symbols manages Symbol instances by allocating a block of them at a time and returning the next one when needed. They are never freed. The reason for the change is that there are a few cases where we need to have a two symbols with the same name, so they can't both live in the map in Scope. Those are: 1. When there is an erroneous redeclaration of a name we may delete the first symbol and replace it with a new one. If we have saved a pointer to the first one it is now dangling. This can be seen by running `f18 -fdebug-dump-symbols -fparse-only test/semantics/resolve19.f90` under valgrind. Subroutine s is declared twice: each results in a scope that contains a pointer back to the symbol for the subroutine. After the second symbol for s is created the first is gone so the pointer in the scope is invalid. 2. A generic and one of its specifics can have the same name. We currently handle that by moving the symbol for the specific into a unique_ptr in the generic. So in that case the symbol is owned by another symbol instead of by the scope. It is simpler if we only have to deal with moving the raw pointer around. 3. A generic and a derived type can have the same name. This case isn't handled yet, but it can be done like flang-compiler/f18#2 above. It's more complicated because the derived type and the generic can be declared in either order. Original-commit: flang-compiler/f18@55a68cf0235c8a3ac855de7dc0e2b08690866be0 Reviewed-on: https://github.com/flang-compiler/f18/pull/107
2018-06-20 07:06:41 +08:00
Symbols<1024> Scope::allSymbols;
bool EquivalenceObject::operator==(const EquivalenceObject &that) const {
return symbol == that.symbol && subscripts == that.subscripts &&
substringStart == that.substringStart;
}
bool EquivalenceObject::operator<(const EquivalenceObject &that) const {
return &symbol < &that.symbol ||
(&symbol == &that.symbol &&
(subscripts < that.subscripts ||
(subscripts == that.subscripts &&
substringStart < that.substringStart)));
}
std::string EquivalenceObject::AsFortran() const {
std::string buf;
llvm::raw_string_ostream ss{buf};
ss << symbol.name().ToString();
if (!subscripts.empty()) {
char sep{'('};
for (auto subscript : subscripts) {
ss << sep << subscript;
sep = ',';
}
ss << ')';
}
if (substringStart) {
ss << '(' << *substringStart << ":)";
}
return ss.str();
}
Scope &Scope::MakeScope(Kind kind, Symbol *symbol) {
return children_.emplace_back(*this, kind, symbol, context_);
[flang] Partial implementation of Symbols and Scopes. A Symbol consists of a common part (in class Symbol) containing name, owner, attributes. Information for a specific kind of symbol is in a variant containing one of the *Details classes. So the kind of symbol is determined by the type of details class stored in the details_ variant. For scopes there is a single Scope class with an enum indicating the kind. So far there isn't a need for extra kind-specific details as with Symbols but that could change. Symbols defined in a Scope are stored there in a simple map. resolve-names.cc is a partial implementation of a parse-tree walker that resolves names to Symbols. Currently is only handles functions (which introduce a new Scope) and entity-decls. The test-type executable was reused as a driver for this to avoid the need for a new one. Sample output is below. When each "end function" is encountered the scope is dumped, which shows the symbols defined in it. $ cat a.f90 pure integer(8) function foo(arg1, arg2) result(res) integer :: arg1 real :: arg2 contains function bar(arg1) real :: bar real :: arg1 end function end function $ Debug/tools/f18/test-type a.f90 Subprogram scope: 0 children arg1: Entity type: REAL bar: Entity type: REAL Subprogram scope: 1 children arg1: Entity type: INTEGER arg2: Entity type: REAL bar: Subprogram (arg1) foo: Subprogram (arg1, arg2) result(res) res: Entity type: INTEGER(8) Original-commit: flang-compiler/f18@1cd2fbc04da1d6bb2ef5bc1cf07c808460ea7547 Reviewed-on: https://github.com/flang-compiler/f18/pull/30 Tree-same-pre-rewrite: false
2018-03-23 08:08:20 +08:00
}
template <typename T>
static std::vector<common::Reference<T>> GetSortedSymbols(
std::map<SourceName, MutableSymbolRef> symbols) {
std::vector<common::Reference<T>> result;
result.reserve(symbols.size());
for (auto &pair : symbols) {
result.push_back(*pair.second);
}
std::sort(result.begin(), result.end(), SymbolSourcePositionCompare{});
return result;
}
MutableSymbolVector Scope::GetSymbols() {
return GetSortedSymbols<Symbol>(symbols_);
}
SymbolVector Scope::GetSymbols() const {
return GetSortedSymbols<const Symbol>(symbols_);
}
[flang] Name resolution for derived types. This consists of: - a new kind of symbols to represent them with DerivedTypeDetails - creating symbols for derived types when they are declared - creating a new kind of scope for the type to hold component symbols - resolving entity declarations of objects of derived type - resolving references to objects of derived type and to components - handling derived types with same name as generic Type parameters are not yet implemented. Refactor DeclTypeSpec to be a value class wrapping an IntrinsicTypeSpec or a DerivedTypeSpec (or neither in the TypeStar and ClassStar cases). Store DerivedTypeSpec objects in a new structure the current scope MakeDerivedTypeSpec so that DeclTypeSpec can just contain a pointer to them, as it currently does for intrinsic types. In GenericDetails, add derivedType field to handle case where generic and derived type have the same name. The generic is in the scope and the derived type is referenced from the generic, similar to the case where a generic and specific have the same name. When one of these names is mis-recognized, we sometimes have to fix up the 'occurrences' lists of the symbols. Assign implicit types as soon as an entity is encountered that requires one. Otherwise implicit derived types won't work. When we see 'x%y' we have to know the type of x in order to resolve y. Add an Implicit flag to mark symbols that were implicitly typed For symbols that introduce a new scope, include a pointer back to that scope. Add CurrNonTypeScope() for the times when we want the current scope but ignoring derived type scopes. For example, that happens when looking for types or parameters, or creating implicit symbols. Original-commit: flang-compiler/f18@9bd16da020b64b78ed3928e0244765cd2e2d8068 Reviewed-on: https://github.com/flang-compiler/f18/pull/109
2018-06-22 23:21:19 +08:00
Scope::iterator Scope::find(const SourceName &name) {
[flang] Change when symbol is set in parser::Name Rework how `parser::Name` is resolved to contain a `Symbol`. so that constants in types can be evaluated. For example: ``` integer, parameter :: k = 8 integer(k) :: i ``` The old approach of collecting the symbols at the end of name resolution and filling in the `parser::Name` does not work because the type of `i` needs to be set in the symbol table. The symbol field in `parser::Name` is now mutable so that we can set it during name resolution. `RewriteParseTree` no longer needs to do that (it still warns about unresolved ones), so it does not need to collect symbols and fill them in. Consequently, we can eliminate "occurrences" from symbols -- we just need the name where each is first defined. This requires a lot of refactoring in `resolve-names.cc` to pass around `parser::Name` rather than `SourceName` so that we can resolve the name to a symbol. Fix some bugs where we stored `SourceName *` instead of `SourceName` in the symbol table. The pointers were into the parse tree, so they were only valid as long as the parse tree was around. The symbol table needs to remain valid longer than that, so the names need to be copied. `parser::Name` is not used in the symbol table. Eliminate `GenericSpec`. Currently all we need to do is to resolve the kinds of GenericSpec that contain names. Add `ScopeName` kind of `MiscDetails` for when we need a symbol in the scope to match the name of the scope. For example, `module m` cannot contain a declaration of a new `m`. Subprograms need real details because they can be called recursively. Fix output of partially resolved modules where we know it is a submodule but have not yet resolved the ancestor. Original-commit: flang-compiler/f18@5c1a4b99d2421f5b32e83426488d3fdf7951cfba Reviewed-on: https://github.com/flang-compiler/f18/pull/238 Tree-same-pre-rewrite: false
2018-11-17 04:43:08 +08:00
return symbols_.find(name);
[flang] Name resolution for derived types. This consists of: - a new kind of symbols to represent them with DerivedTypeDetails - creating symbols for derived types when they are declared - creating a new kind of scope for the type to hold component symbols - resolving entity declarations of objects of derived type - resolving references to objects of derived type and to components - handling derived types with same name as generic Type parameters are not yet implemented. Refactor DeclTypeSpec to be a value class wrapping an IntrinsicTypeSpec or a DerivedTypeSpec (or neither in the TypeStar and ClassStar cases). Store DerivedTypeSpec objects in a new structure the current scope MakeDerivedTypeSpec so that DeclTypeSpec can just contain a pointer to them, as it currently does for intrinsic types. In GenericDetails, add derivedType field to handle case where generic and derived type have the same name. The generic is in the scope and the derived type is referenced from the generic, similar to the case where a generic and specific have the same name. When one of these names is mis-recognized, we sometimes have to fix up the 'occurrences' lists of the symbols. Assign implicit types as soon as an entity is encountered that requires one. Otherwise implicit derived types won't work. When we see 'x%y' we have to know the type of x in order to resolve y. Add an Implicit flag to mark symbols that were implicitly typed For symbols that introduce a new scope, include a pointer back to that scope. Add CurrNonTypeScope() for the times when we want the current scope but ignoring derived type scopes. For example, that happens when looking for types or parameters, or creating implicit symbols. Original-commit: flang-compiler/f18@9bd16da020b64b78ed3928e0244765cd2e2d8068 Reviewed-on: https://github.com/flang-compiler/f18/pull/109
2018-06-22 23:21:19 +08:00
}
Scope::size_type Scope::erase(const SourceName &name) {
auto it{symbols_.find(name)};
[flang] Name resolution for derived types. This consists of: - a new kind of symbols to represent them with DerivedTypeDetails - creating symbols for derived types when they are declared - creating a new kind of scope for the type to hold component symbols - resolving entity declarations of objects of derived type - resolving references to objects of derived type and to components - handling derived types with same name as generic Type parameters are not yet implemented. Refactor DeclTypeSpec to be a value class wrapping an IntrinsicTypeSpec or a DerivedTypeSpec (or neither in the TypeStar and ClassStar cases). Store DerivedTypeSpec objects in a new structure the current scope MakeDerivedTypeSpec so that DeclTypeSpec can just contain a pointer to them, as it currently does for intrinsic types. In GenericDetails, add derivedType field to handle case where generic and derived type have the same name. The generic is in the scope and the derived type is referenced from the generic, similar to the case where a generic and specific have the same name. When one of these names is mis-recognized, we sometimes have to fix up the 'occurrences' lists of the symbols. Assign implicit types as soon as an entity is encountered that requires one. Otherwise implicit derived types won't work. When we see 'x%y' we have to know the type of x in order to resolve y. Add an Implicit flag to mark symbols that were implicitly typed For symbols that introduce a new scope, include a pointer back to that scope. Add CurrNonTypeScope() for the times when we want the current scope but ignoring derived type scopes. For example, that happens when looking for types or parameters, or creating implicit symbols. Original-commit: flang-compiler/f18@9bd16da020b64b78ed3928e0244765cd2e2d8068 Reviewed-on: https://github.com/flang-compiler/f18/pull/109
2018-06-22 23:21:19 +08:00
if (it != end()) {
symbols_.erase(it);
return 1;
} else {
return 0;
}
}
[flang] Change when symbol is set in parser::Name Rework how `parser::Name` is resolved to contain a `Symbol`. so that constants in types can be evaluated. For example: ``` integer, parameter :: k = 8 integer(k) :: i ``` The old approach of collecting the symbols at the end of name resolution and filling in the `parser::Name` does not work because the type of `i` needs to be set in the symbol table. The symbol field in `parser::Name` is now mutable so that we can set it during name resolution. `RewriteParseTree` no longer needs to do that (it still warns about unresolved ones), so it does not need to collect symbols and fill them in. Consequently, we can eliminate "occurrences" from symbols -- we just need the name where each is first defined. This requires a lot of refactoring in `resolve-names.cc` to pass around `parser::Name` rather than `SourceName` so that we can resolve the name to a symbol. Fix some bugs where we stored `SourceName *` instead of `SourceName` in the symbol table. The pointers were into the parse tree, so they were only valid as long as the parse tree was around. The symbol table needs to remain valid longer than that, so the names need to be copied. `parser::Name` is not used in the symbol table. Eliminate `GenericSpec`. Currently all we need to do is to resolve the kinds of GenericSpec that contain names. Add `ScopeName` kind of `MiscDetails` for when we need a symbol in the scope to match the name of the scope. For example, `module m` cannot contain a declaration of a new `m`. Subprograms need real details because they can be called recursively. Fix output of partially resolved modules where we know it is a submodule but have not yet resolved the ancestor. Original-commit: flang-compiler/f18@5c1a4b99d2421f5b32e83426488d3fdf7951cfba Reviewed-on: https://github.com/flang-compiler/f18/pull/238 Tree-same-pre-rewrite: false
2018-11-17 04:43:08 +08:00
Symbol *Scope::FindSymbol(const SourceName &name) const {
auto it{find(name)};
if (it != end()) {
return &*it->second;
} else if (CanImport(name)) {
return parent_.FindSymbol(name);
} else {
return nullptr;
}
}
Symbol *Scope::FindComponent(SourceName name) const {
CHECK(IsDerivedType());
auto found{find(name)};
if (found != end()) {
return &*found->second;
} else if (const Scope * parent{GetDerivedTypeParent()}) {
return parent->FindComponent(name);
} else {
return nullptr;
}
}
bool Scope::Contains(const Scope &that) const {
for (const Scope *scope{&that};; scope = &scope->parent()) {
if (*scope == *this) {
return true;
}
if (scope->IsGlobal()) {
return false;
}
}
}
Symbol *Scope::CopySymbol(const Symbol &symbol) {
auto pair{try_emplace(symbol.name(), symbol.attrs())};
if (!pair.second) {
return nullptr; // already exists
} else {
Symbol &result{*pair.first->second};
result.flags() = symbol.flags();
result.set_details(common::Clone(symbol.details()));
return &result;
}
}
void Scope::add_equivalenceSet(EquivalenceSet &&set) {
equivalenceSets_.emplace_back(std::move(set));
}
void Scope::add_crayPointer(const SourceName &name, Symbol &pointer) {
CHECK(pointer.test(Symbol::Flag::CrayPointer));
crayPointers_.emplace(name, pointer);
}
Symbol &Scope::MakeCommonBlock(const SourceName &name) {
const auto it{commonBlocks_.find(name)};
if (it != commonBlocks_.end()) {
return *it->second;
} else {
Symbol &symbol{MakeSymbol(name, Attrs{}, CommonBlockDetails{})};
commonBlocks_.emplace(name, symbol);
return symbol;
}
}
Symbol *Scope::FindCommonBlock(const SourceName &name) const {
const auto it{commonBlocks_.find(name)};
return it != commonBlocks_.end() ? &*it->second : nullptr;
}
Scope *Scope::FindSubmodule(const SourceName &name) const {
auto it{submodules_.find(name)};
if (it == submodules_.end()) {
return nullptr;
} else {
return &*it->second;
}
}
bool Scope::AddSubmodule(const SourceName &name, Scope &submodule) {
return submodules_.emplace(name, submodule).second;
}
const DeclTypeSpec *Scope::FindType(const DeclTypeSpec &type) const {
auto it{std::find(declTypeSpecs_.begin(), declTypeSpecs_.end(), type)};
return it != declTypeSpecs_.end() ? &*it : nullptr;
}
const DeclTypeSpec &Scope::MakeNumericType(
TypeCategory category, KindExpr &&kind) {
return MakeLengthlessType(NumericTypeSpec{category, std::move(kind)});
}
const DeclTypeSpec &Scope::MakeLogicalType(KindExpr &&kind) {
return MakeLengthlessType(LogicalTypeSpec{std::move(kind)});
}
[flang] Resolve names in ASSOCIATE and SELECT TYPE Create `AssocEntityDetails` for symbols that represent entities identified by the associate-name in ASSOCIATE and SELECT TYPE constructs. For ASSOCIATE, create a new scope for the associated entity. For SELECT TYPE, create a new scope for each of type guard blocks. Each one contains an associated entity with the appropriate type. For SELECT TYPE, also create a place-holder symbol for the associate-name in the SELECT TYPE statement. The real symbols are in the new scopes and none of them is uniquely identified with the associate-name. Handling of `Selector` is common between these, with `associate-name => expr | variable` recorded in `ConstructVisitor::association_`. When the selector is an expression, derive the type of the associated entity from the type of the expression. This required some refactoring of how `DeclTypeSpec`s are created. The `DerivedTypeSpec` that comes from and expression is const so we can only create const `DeclTypeSpec`s from it. But there were times during name resolution when we needed to set type parameters in the current `DeclTypeSpec`. Now the non-const `DerivedTypeSpec` is saved separately from the const `DeclTypeSpec` while we are processing a declaration type spec. This makes it unnecessary to save the derived type name. Add a type alias for `common::Indirection` to reduce verbosity. Original-commit: flang-compiler/f18@b7668cebe49a122ea23c89c81eafdeba243bbfaf Reviewed-on: https://github.com/flang-compiler/f18/pull/261 Tree-same-pre-rewrite: false
2019-01-16 08:59:20 +08:00
const DeclTypeSpec &Scope::MakeTypeStarType() {
return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::TypeStar});
}
[flang] Resolve names in ASSOCIATE and SELECT TYPE Create `AssocEntityDetails` for symbols that represent entities identified by the associate-name in ASSOCIATE and SELECT TYPE constructs. For ASSOCIATE, create a new scope for the associated entity. For SELECT TYPE, create a new scope for each of type guard blocks. Each one contains an associated entity with the appropriate type. For SELECT TYPE, also create a place-holder symbol for the associate-name in the SELECT TYPE statement. The real symbols are in the new scopes and none of them is uniquely identified with the associate-name. Handling of `Selector` is common between these, with `associate-name => expr | variable` recorded in `ConstructVisitor::association_`. When the selector is an expression, derive the type of the associated entity from the type of the expression. This required some refactoring of how `DeclTypeSpec`s are created. The `DerivedTypeSpec` that comes from and expression is const so we can only create const `DeclTypeSpec`s from it. But there were times during name resolution when we needed to set type parameters in the current `DeclTypeSpec`. Now the non-const `DerivedTypeSpec` is saved separately from the const `DeclTypeSpec` while we are processing a declaration type spec. This makes it unnecessary to save the derived type name. Add a type alias for `common::Indirection` to reduce verbosity. Original-commit: flang-compiler/f18@b7668cebe49a122ea23c89c81eafdeba243bbfaf Reviewed-on: https://github.com/flang-compiler/f18/pull/261 Tree-same-pre-rewrite: false
2019-01-16 08:59:20 +08:00
const DeclTypeSpec &Scope::MakeClassStarType() {
return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::ClassStar});
}
// Types that can't have length parameters can be reused without having to
// compare length expressions. They are stored in the global scope.
const DeclTypeSpec &Scope::MakeLengthlessType(DeclTypeSpec &&type) {
const auto *found{FindType(type)};
return found ? *found : declTypeSpecs_.emplace_back(std::move(type));
}
const DeclTypeSpec &Scope::MakeCharacterType(
ParamValue &&length, KindExpr &&kind) {
return declTypeSpecs_.emplace_back(
CharacterTypeSpec{std::move(length), std::move(kind)});
}
DeclTypeSpec &Scope::MakeDerivedType(
DeclTypeSpec::Category category, DerivedTypeSpec &&spec) {
return declTypeSpecs_.emplace_back(category, std::move(spec));
}
const DeclTypeSpec *Scope::GetType(const SomeExpr &expr) {
if (auto dyType{expr.GetType()}) {
if (dyType->IsAssumedType()) {
return &MakeTypeStarType();
} else if (dyType->IsUnlimitedPolymorphic()) {
return &MakeClassStarType();
} else {
switch (dyType->category()) {
case TypeCategory::Integer:
case TypeCategory::Real:
case TypeCategory::Complex:
return &MakeNumericType(dyType->category(), KindExpr{dyType->kind()});
case TypeCategory::Character:
if (const ParamValue * lenParam{dyType->charLengthParamValue()}) {
return &MakeCharacterType(
ParamValue{*lenParam}, KindExpr{dyType->kind()});
} else {
auto lenExpr{dyType->GetCharLength()};
if (!lenExpr) {
lenExpr =
std::get<evaluate::Expr<evaluate::SomeCharacter>>(expr.u).LEN();
}
if (lenExpr) {
return &MakeCharacterType(
ParamValue{SomeIntExpr{std::move(*lenExpr)},
common::TypeParamAttr::Len},
KindExpr{dyType->kind()});
}
}
break;
case TypeCategory::Logical:
return &MakeLogicalType(KindExpr{dyType->kind()});
case TypeCategory::Derived:
return &MakeDerivedType(dyType->IsPolymorphic()
? DeclTypeSpec::ClassDerived
: DeclTypeSpec::TypeDerived,
DerivedTypeSpec{dyType->GetDerivedTypeSpec()});
}
}
}
return nullptr;
}
Scope::ImportKind Scope::GetImportKind() const {
if (importKind_) {
return *importKind_;
}
if (symbol_ && !symbol_->attrs().test(Attr::MODULE)) {
if (auto *details{symbol_->detailsIf<SubprogramDetails>()}) {
if (details->isInterface()) {
return ImportKind::None; // default for non-mod-proc interface body
}
}
}
return ImportKind::Default;
}
std::optional<parser::MessageFixedText> Scope::SetImportKind(ImportKind kind) {
if (!importKind_) {
importKind_ = kind;
return std::nullopt;
}
bool hasNone{kind == ImportKind::None || *importKind_ == ImportKind::None};
bool hasAll{kind == ImportKind::All || *importKind_ == ImportKind::All};
// Check C8100 and C898: constraints on multiple IMPORT statements
if (hasNone || hasAll) {
return hasNone
? "IMPORT,NONE must be the only IMPORT statement in a scope"_err_en_US
: "IMPORT,ALL must be the only IMPORT statement in a scope"_err_en_US;
} else if (kind != *importKind_ &&
(kind != ImportKind::Only || kind != ImportKind::Only)) {
return "Every IMPORT must have ONLY specifier if one of them does"_err_en_US;
} else {
return std::nullopt;
}
}
[flang] Change when symbol is set in parser::Name Rework how `parser::Name` is resolved to contain a `Symbol`. so that constants in types can be evaluated. For example: ``` integer, parameter :: k = 8 integer(k) :: i ``` The old approach of collecting the symbols at the end of name resolution and filling in the `parser::Name` does not work because the type of `i` needs to be set in the symbol table. The symbol field in `parser::Name` is now mutable so that we can set it during name resolution. `RewriteParseTree` no longer needs to do that (it still warns about unresolved ones), so it does not need to collect symbols and fill them in. Consequently, we can eliminate "occurrences" from symbols -- we just need the name where each is first defined. This requires a lot of refactoring in `resolve-names.cc` to pass around `parser::Name` rather than `SourceName` so that we can resolve the name to a symbol. Fix some bugs where we stored `SourceName *` instead of `SourceName` in the symbol table. The pointers were into the parse tree, so they were only valid as long as the parse tree was around. The symbol table needs to remain valid longer than that, so the names need to be copied. `parser::Name` is not used in the symbol table. Eliminate `GenericSpec`. Currently all we need to do is to resolve the kinds of GenericSpec that contain names. Add `ScopeName` kind of `MiscDetails` for when we need a symbol in the scope to match the name of the scope. For example, `module m` cannot contain a declaration of a new `m`. Subprograms need real details because they can be called recursively. Fix output of partially resolved modules where we know it is a submodule but have not yet resolved the ancestor. Original-commit: flang-compiler/f18@5c1a4b99d2421f5b32e83426488d3fdf7951cfba Reviewed-on: https://github.com/flang-compiler/f18/pull/238 Tree-same-pre-rewrite: false
2018-11-17 04:43:08 +08:00
void Scope::add_importName(const SourceName &name) {
importNames_.insert(name);
}
// true if name can be imported or host-associated from parent scope.
bool Scope::CanImport(const SourceName &name) const {
if (IsTopLevel() || parent_.IsTopLevel()) {
return false;
}
switch (GetImportKind()) {
SWITCH_COVERS_ALL_CASES
case ImportKind::None:
return false;
case ImportKind::All:
case ImportKind::Default:
return true;
case ImportKind::Only:
return importNames_.count(name) > 0;
}
}
const Scope *Scope::FindScope(parser::CharBlock source) const {
return const_cast<Scope *>(this)->FindScope(source);
}
Scope *Scope::FindScope(parser::CharBlock source) {
bool isContained{sourceRange_.Contains(source)};
if (!isContained && !IsTopLevel() && !IsModuleFile()) {
return nullptr;
}
for (auto &child : children_) {
if (auto *scope{child.FindScope(source)}) {
return scope;
}
}
return isContained && !IsTopLevel() ? this : nullptr;
}
void Scope::AddSourceRange(const parser::CharBlock &source) {
for (auto *scope{this}; !scope->IsGlobal(); scope = &scope->parent()) {
[flang] [OpenMP] Name Resolution for OpenMP constructs (flang-compiler/f18#940) This is an extended framework based on the previous work that addresses the NR on OpenMP directives/clauses (b2ea520). In this change: * New `OmpVisitor` is created (ResolveNamesVisitor derives from it) to create necessary scopes for certain OpenMP constructs. This is along with the regular Fortran NR process. * Old `OmpVisitor` is adjusted and converted to a standalone visitor-- `OmpAttributeVisitor`. This is used to walk through the OpenMP constructs and do the NR for variables on the OpenMP directives or data references within the OpenMP constructs. "Do the NR" here means that based on the NR results of the regular Fortran NR, fix the symbols of `Names` related to the OpenMP constructs. Note that there is an `OmpContext` in this visitor (similar to the one in `OmpStructureChecker`), this is necessary when dealing with the nested OpenMP constructs in the future. Given an OpenMP code: ``` real*8 a, b a = 1. b = 2. !$omp parallel private(a) a = 3. b = 4. !$omp end parallel print *, a, b end ``` w/o -fopenmp: ``` real*8 a, b !REF: /MainProgram1/a a = 1. !REF: /MainProgram1/b b = 2. !!!! OMP parallel !REF: /MainProgram1/a a = 3. !REF: /MainProgram1/b b = 4. !!!! OMP end parallel !REF: /MainProgram1/a !REF: /MainProgram1/b print *, a, b end ``` w/ -fopenmp: ``` real*8 a, b !REF: /MainProgram1/a a = 1. !REF: /MainProgram1/b b = 2. !$omp parallel private(a) <-- new Symbol for 'a' created !DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(8) a = 3. <-- fix the old symbol with new Symbol in parallel scope !REF: /MainProgram1/b b = 4. <-- do nothing because by default it is shared in this scope !$omp end parallel !REF: /MainProgram1/a !REF: /MainProgram1/b print *, a, b end ``` Please note that this is a framework update, there are still many things on the TODO list for finishing the NR for OpenMP (based on the `OpenMP-semantics.md` design doc), which will be on top of this framework. Some TODO items: - Create a generic function to go through all the rules for deciding `predetermined`, `explicitly determined`, and `implicitly determined` data-sharing attributes. (This is the next biggest part) - Handle `Array Sections` and `Array or Structure Element`. - Take association into consideration for example Pointer association, `ASSOCIATE` construct, and etc. - Handle all the name resolution for directives/clauses that have `parser::Name`. * N.B. Extend `AddSourceRange` to apply to current and parent scopes - motivated by a few cases that need to call `AddSourceRange` for current & parent scopes; the extension should be safe - global scope is not included Original-commit: flang-compiler/f18@0c3c39d30e3f166a6a1303337c5fd7eead720fd0 Reviewed-on: https://github.com/flang-compiler/f18/pull/940
2020-01-29 04:51:35 +08:00
scope->sourceRange_.ExtendToCover(source);
}
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) {
os << Scope::EnumToString(scope.kind()) << " scope: ";
if (auto *symbol{scope.symbol()}) {
os << *symbol << ' ';
}
if (scope.derivedTypeSpec_) {
os << "instantiation of " << *scope.derivedTypeSpec_ << ' ';
}
os << scope.children_.size() << " children\n";
[flang] Name resolution for derived types. This consists of: - a new kind of symbols to represent them with DerivedTypeDetails - creating symbols for derived types when they are declared - creating a new kind of scope for the type to hold component symbols - resolving entity declarations of objects of derived type - resolving references to objects of derived type and to components - handling derived types with same name as generic Type parameters are not yet implemented. Refactor DeclTypeSpec to be a value class wrapping an IntrinsicTypeSpec or a DerivedTypeSpec (or neither in the TypeStar and ClassStar cases). Store DerivedTypeSpec objects in a new structure the current scope MakeDerivedTypeSpec so that DeclTypeSpec can just contain a pointer to them, as it currently does for intrinsic types. In GenericDetails, add derivedType field to handle case where generic and derived type have the same name. The generic is in the scope and the derived type is referenced from the generic, similar to the case where a generic and specific have the same name. When one of these names is mis-recognized, we sometimes have to fix up the 'occurrences' lists of the symbols. Assign implicit types as soon as an entity is encountered that requires one. Otherwise implicit derived types won't work. When we see 'x%y' we have to know the type of x in order to resolve y. Add an Implicit flag to mark symbols that were implicitly typed For symbols that introduce a new scope, include a pointer back to that scope. Add CurrNonTypeScope() for the times when we want the current scope but ignoring derived type scopes. For example, that happens when looking for types or parameters, or creating implicit symbols. Original-commit: flang-compiler/f18@9bd16da020b64b78ed3928e0244765cd2e2d8068 Reviewed-on: https://github.com/flang-compiler/f18/pull/109
2018-06-22 23:21:19 +08:00
for (const auto &pair : scope.symbols_) {
const Symbol &symbol{*pair.second};
os << " " << symbol << '\n';
[flang] Partial implementation of Symbols and Scopes. A Symbol consists of a common part (in class Symbol) containing name, owner, attributes. Information for a specific kind of symbol is in a variant containing one of the *Details classes. So the kind of symbol is determined by the type of details class stored in the details_ variant. For scopes there is a single Scope class with an enum indicating the kind. So far there isn't a need for extra kind-specific details as with Symbols but that could change. Symbols defined in a Scope are stored there in a simple map. resolve-names.cc is a partial implementation of a parse-tree walker that resolves names to Symbols. Currently is only handles functions (which introduce a new Scope) and entity-decls. The test-type executable was reused as a driver for this to avoid the need for a new one. Sample output is below. When each "end function" is encountered the scope is dumped, which shows the symbols defined in it. $ cat a.f90 pure integer(8) function foo(arg1, arg2) result(res) integer :: arg1 real :: arg2 contains function bar(arg1) real :: bar real :: arg1 end function end function $ Debug/tools/f18/test-type a.f90 Subprogram scope: 0 children arg1: Entity type: REAL bar: Entity type: REAL Subprogram scope: 1 children arg1: Entity type: INTEGER arg2: Entity type: REAL bar: Subprogram (arg1) foo: Subprogram (arg1, arg2) result(res) res: Entity type: INTEGER(8) Original-commit: flang-compiler/f18@1cd2fbc04da1d6bb2ef5bc1cf07c808460ea7547 Reviewed-on: https://github.com/flang-compiler/f18/pull/30 Tree-same-pre-rewrite: false
2018-03-23 08:08:20 +08:00
}
if (!scope.equivalenceSets_.empty()) {
os << " Equivalence Sets:\n";
for (const auto &set : scope.equivalenceSets_) {
os << " ";
for (const auto &object : set) {
os << ' ' << object.AsFortran();
}
os << '\n';
}
}
for (const auto &pair : scope.commonBlocks_) {
const Symbol &symbol{*pair.second};
os << " " << symbol << '\n';
}
[flang] Partial implementation of Symbols and Scopes. A Symbol consists of a common part (in class Symbol) containing name, owner, attributes. Information for a specific kind of symbol is in a variant containing one of the *Details classes. So the kind of symbol is determined by the type of details class stored in the details_ variant. For scopes there is a single Scope class with an enum indicating the kind. So far there isn't a need for extra kind-specific details as with Symbols but that could change. Symbols defined in a Scope are stored there in a simple map. resolve-names.cc is a partial implementation of a parse-tree walker that resolves names to Symbols. Currently is only handles functions (which introduce a new Scope) and entity-decls. The test-type executable was reused as a driver for this to avoid the need for a new one. Sample output is below. When each "end function" is encountered the scope is dumped, which shows the symbols defined in it. $ cat a.f90 pure integer(8) function foo(arg1, arg2) result(res) integer :: arg1 real :: arg2 contains function bar(arg1) real :: bar real :: arg1 end function end function $ Debug/tools/f18/test-type a.f90 Subprogram scope: 0 children arg1: Entity type: REAL bar: Entity type: REAL Subprogram scope: 1 children arg1: Entity type: INTEGER arg2: Entity type: REAL bar: Subprogram (arg1) foo: Subprogram (arg1, arg2) result(res) res: Entity type: INTEGER(8) Original-commit: flang-compiler/f18@1cd2fbc04da1d6bb2ef5bc1cf07c808460ea7547 Reviewed-on: https://github.com/flang-compiler/f18/pull/30 Tree-same-pre-rewrite: false
2018-03-23 08:08:20 +08:00
return os;
}
bool Scope::IsStmtFunction() const {
return symbol_ && symbol_->test(Symbol::Flag::StmtFunction);
}
template <common::TypeParamAttr... ParamAttr> struct IsTypeParamHelper {
static_assert(sizeof...(ParamAttr) == 0, "must have one or zero template");
static bool IsParam(const Symbol &symbol) {
return symbol.has<TypeParamDetails>();
}
};
template <common::TypeParamAttr ParamAttr> struct IsTypeParamHelper<ParamAttr> {
static bool IsParam(const Symbol &symbol) {
if (const auto *typeParam{symbol.detailsIf<TypeParamDetails>()}) {
return typeParam->attr() == ParamAttr;
}
[flang] Generate PDT runtime type info in the type definition scope This patches modifies PDT runtime type info generation so that it is easier to handle derived type descriptor in lowering. It changes three aspects: 1. The symbol name suffix of runtime type info for PDT instantiation is changed from a serial number unrelated to the types to an encoding of the instantiated KIND parameters. 2. New runtime type info is not created for each instantiation of PDT without KIND parameters (only length parameters). Instead, the runtime type info of the type definition is always used. It is updated to contain the component descriptions. 3. Runtime type info of PDT instantiation is now always generated in the scope where the type is defined. If several PDT type instantiation are made in different scope with the same kind parameters, they will use the same runtime type info. Rational of the change: In lowering, derived type descriptors are not mapped when instantiating derived type objects. They are mapped later when symbol knowledge is not available anymore. This mapping is based on the FIR representation of derived types. For PDT, the FIR type information does not allow deducing the instantiation scope, it only allows retrieving the type name, the type _definition_ scope, and the kind parameter values. Therefore, in order to be able to retrieve the derived type descriptor from a FIR type, the derived type descriptor must be generated in the definition scope and must reflect the kind parameters. This justifies the need for changes 1. and 3. above (suffix and scope change). Changes 2. comes from the fact that all runtime type info of type without kind parameters can be generated from the type definition, and that because of the suffix change, the symbol name for type definition and type instantiation are the same. Although this change is first motivated by how lowering handles derived types, I believe it is also an improvement from a functional point of view since this change will allow reducing the number of generated runtime type info for PDTs, since redundant information (different instantiations with same kind parameters) will only be generated once. Differential Revision: https://reviews.llvm.org/D120801
2022-03-03 17:14:08 +08:00
return false;
}
};
template <common::TypeParamAttr... ParamAttr>
static bool IsParameterizedDerivedTypeHelper(const Scope &scope) {
if (scope.IsDerivedType()) {
if (const Scope * parent{scope.GetDerivedTypeParent()}) {
if (IsParameterizedDerivedTypeHelper<ParamAttr...>(*parent)) {
return true;
}
[flang] Generate PDT runtime type info in the type definition scope This patches modifies PDT runtime type info generation so that it is easier to handle derived type descriptor in lowering. It changes three aspects: 1. The symbol name suffix of runtime type info for PDT instantiation is changed from a serial number unrelated to the types to an encoding of the instantiated KIND parameters. 2. New runtime type info is not created for each instantiation of PDT without KIND parameters (only length parameters). Instead, the runtime type info of the type definition is always used. It is updated to contain the component descriptions. 3. Runtime type info of PDT instantiation is now always generated in the scope where the type is defined. If several PDT type instantiation are made in different scope with the same kind parameters, they will use the same runtime type info. Rational of the change: In lowering, derived type descriptors are not mapped when instantiating derived type objects. They are mapped later when symbol knowledge is not available anymore. This mapping is based on the FIR representation of derived types. For PDT, the FIR type information does not allow deducing the instantiation scope, it only allows retrieving the type name, the type _definition_ scope, and the kind parameter values. Therefore, in order to be able to retrieve the derived type descriptor from a FIR type, the derived type descriptor must be generated in the definition scope and must reflect the kind parameters. This justifies the need for changes 1. and 3. above (suffix and scope change). Changes 2. comes from the fact that all runtime type info of type without kind parameters can be generated from the type definition, and that because of the suffix change, the symbol name for type definition and type instantiation are the same. Although this change is first motivated by how lowering handles derived types, I believe it is also an improvement from a functional point of view since this change will allow reducing the number of generated runtime type info for PDTs, since redundant information (different instantiations with same kind parameters) will only be generated once. Differential Revision: https://reviews.llvm.org/D120801
2022-03-03 17:14:08 +08:00
}
for (const auto &nameAndSymbolPair : scope) {
if (IsTypeParamHelper<ParamAttr...>::IsParam(*nameAndSymbolPair.second)) {
[flang] Generate PDT runtime type info in the type definition scope This patches modifies PDT runtime type info generation so that it is easier to handle derived type descriptor in lowering. It changes three aspects: 1. The symbol name suffix of runtime type info for PDT instantiation is changed from a serial number unrelated to the types to an encoding of the instantiated KIND parameters. 2. New runtime type info is not created for each instantiation of PDT without KIND parameters (only length parameters). Instead, the runtime type info of the type definition is always used. It is updated to contain the component descriptions. 3. Runtime type info of PDT instantiation is now always generated in the scope where the type is defined. If several PDT type instantiation are made in different scope with the same kind parameters, they will use the same runtime type info. Rational of the change: In lowering, derived type descriptors are not mapped when instantiating derived type objects. They are mapped later when symbol knowledge is not available anymore. This mapping is based on the FIR representation of derived types. For PDT, the FIR type information does not allow deducing the instantiation scope, it only allows retrieving the type name, the type _definition_ scope, and the kind parameter values. Therefore, in order to be able to retrieve the derived type descriptor from a FIR type, the derived type descriptor must be generated in the definition scope and must reflect the kind parameters. This justifies the need for changes 1. and 3. above (suffix and scope change). Changes 2. comes from the fact that all runtime type info of type without kind parameters can be generated from the type definition, and that because of the suffix change, the symbol name for type definition and type instantiation are the same. Although this change is first motivated by how lowering handles derived types, I believe it is also an improvement from a functional point of view since this change will allow reducing the number of generated runtime type info for PDTs, since redundant information (different instantiations with same kind parameters) will only be generated once. Differential Revision: https://reviews.llvm.org/D120801
2022-03-03 17:14:08 +08:00
return true;
}
}
}
return false;
}
bool Scope::IsParameterizedDerivedType() const {
return IsParameterizedDerivedTypeHelper<>(*this);
}
bool Scope::IsDerivedTypeWithLengthParameter() const {
return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Len>(*this);
}
bool Scope::IsDerivedTypeWithKindParameter() const {
return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Kind>(*this);
}
const DeclTypeSpec *Scope::FindInstantiatedDerivedType(
const DerivedTypeSpec &spec, DeclTypeSpec::Category category) const {
DeclTypeSpec type{category, spec};
if (const auto *result{FindType(type)}) {
return result;
} else if (IsGlobal()) {
return nullptr;
} else {
return parent().FindInstantiatedDerivedType(spec, category);
}
}
const Scope *Scope::GetDerivedTypeParent() const {
if (const Symbol * symbol{GetSymbol()}) {
if (const DerivedTypeSpec * parent{symbol->GetParentTypeSpec(this)}) {
return parent->scope();
}
}
return nullptr;
}
const Scope &Scope::GetDerivedTypeBase() const {
const Scope *child{this};
for (const Scope *parent{GetDerivedTypeParent()}; parent != nullptr;
parent = child->GetDerivedTypeParent()) {
child = parent;
}
return *child;
}
void Scope::InstantiateDerivedTypes() {
for (DeclTypeSpec &type : declTypeSpecs_) {
if (type.category() == DeclTypeSpec::TypeDerived ||
type.category() == DeclTypeSpec::ClassDerived) {
type.derivedTypeSpec().Instantiate(*this);
}
}
}
} // namespace Fortran::semantics