521 lines · cpp
1//===-- lib/Semantics/scope.cpp -------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Semantics/scope.h"10#include "flang/Parser/characters.h"11#include "flang/Semantics/semantics.h"12#include "flang/Semantics/symbol.h"13#include "flang/Semantics/type.h"14#include "llvm/Support/raw_ostream.h"15#include <algorithm>16#include <memory>17 18namespace Fortran::semantics {19 20Symbols<1024> Scope::allSymbols;21 22bool EquivalenceObject::operator==(const EquivalenceObject &that) const {23 return symbol == that.symbol && subscripts == that.subscripts &&24 substringStart == that.substringStart;25}26 27bool EquivalenceObject::operator<(const EquivalenceObject &that) const {28 return &symbol < &that.symbol ||29 (&symbol == &that.symbol &&30 (subscripts < that.subscripts ||31 (subscripts == that.subscripts &&32 substringStart < that.substringStart)));33}34 35std::string EquivalenceObject::AsFortran() const {36 std::string buf;37 llvm::raw_string_ostream ss{buf};38 ss << symbol.name().ToString();39 if (!subscripts.empty()) {40 char sep{'('};41 for (auto subscript : subscripts) {42 ss << sep << subscript;43 sep = ',';44 }45 ss << ')';46 }47 if (substringStart) {48 ss << '(' << *substringStart << ":)";49 }50 return buf;51}52 53Scope &Scope::MakeScope(Kind kind, Symbol *symbol) {54 return children_.emplace_back(*this, kind, symbol, context_);55}56 57template <typename T>58static std::vector<common::Reference<T>> GetSortedSymbols(59 const std::map<SourceName, MutableSymbolRef> &symbols) {60 std::vector<common::Reference<T>> result;61 result.reserve(symbols.size());62 for (auto &pair : symbols) {63 result.push_back(*pair.second);64 }65 std::sort(result.begin(), result.end(), SymbolSourcePositionCompare{});66 return result;67}68 69MutableSymbolVector Scope::GetSymbols() {70 return GetSortedSymbols<Symbol>(symbols_);71}72SymbolVector Scope::GetSymbols() const {73 return GetSortedSymbols<const Symbol>(symbols_);74}75 76Scope::iterator Scope::find(const SourceName &name) {77 return symbols_.find(name);78}79Scope::size_type Scope::erase(const SourceName &name) {80 auto it{symbols_.find(name)};81 if (it != end()) {82 symbols_.erase(it);83 return 1;84 } else {85 return 0;86 }87}88Symbol *Scope::FindSymbol(const SourceName &name) const {89 auto it{find(name)};90 if (it != end()) {91 return &*it->second;92 } else if (IsSubmodule()) {93 const Scope *parent{symbol_->get<ModuleDetails>().parent()};94 return parent ? parent->FindSymbol(name) : nullptr;95 } else if (CanImport(name)) {96 return parent_->FindSymbol(name);97 } else {98 return nullptr;99 }100}101 102Symbol *Scope::FindComponent(SourceName name) const {103 CHECK(IsDerivedType());104 auto found{find(name)};105 if (found != end()) {106 return &*found->second;107 } else if (const Scope * parent{GetDerivedTypeParent()}) {108 return parent->FindComponent(name);109 } else {110 return nullptr;111 }112}113 114bool Scope::Contains(const Scope &that) const {115 for (const Scope *scope{&that};; scope = &scope->parent()) {116 if (*scope == *this) {117 return true;118 }119 if (scope->IsGlobal()) {120 return false;121 }122 }123}124 125Symbol *Scope::CopySymbol(const Symbol &symbol) {126 auto pair{try_emplace(symbol.name(), symbol.attrs())};127 if (!pair.second) {128 return nullptr; // already exists129 } else {130 Symbol &result{*pair.first->second};131 result.flags() = symbol.flags();132 result.set_details(common::Clone(symbol.details()));133 return &result;134 }135}136 137void Scope::add_equivalenceSet(EquivalenceSet &&set) {138 equivalenceSets_.emplace_back(std::move(set));139}140 141void Scope::add_crayPointer(const SourceName &name, Symbol &pointer) {142 CHECK(pointer.test(Symbol::Flag::CrayPointer));143 crayPointers_.emplace(name, pointer);144}145 146Symbol &Scope::MakeCommonBlock(SourceName name, SourceName location) {147 if (auto *cb{FindCommonBlock(name)}) {148 return *cb;149 } else {150 Symbol &symbol{MakeSymbol(151 name, Attrs{}, CommonBlockDetails{name.empty() ? location : name})};152 commonBlocks_.emplace(name, symbol);153 return symbol;154 }155}156 157Symbol *Scope::FindCommonBlockInVisibleScopes(const SourceName &name) const {158 if (Symbol * cb{FindCommonBlock(name)}) {159 return cb;160 } else if (Symbol * cb{FindCommonBlockUse(name)}) {161 return &cb->GetUltimate();162 } else if (IsSubmodule()) {163 if (const Scope *parent{164 symbol_ ? symbol_->get<ModuleDetails>().parent() : nullptr}) {165 if (auto *cb{parent->FindCommonBlockInVisibleScopes(name)}) {166 return cb;167 }168 }169 } else if (!IsTopLevel() && parent_) {170 if (auto *cb{parent_->FindCommonBlockInVisibleScopes(name)}) {171 return cb;172 }173 }174 return nullptr;175}176 177Scope *Scope::FindSubmodule(const SourceName &name) const {178 auto it{submodules_.find(name)};179 if (it == submodules_.end()) {180 return nullptr;181 } else {182 return &*it->second;183 }184}185 186bool Scope::AddCommonBlockUse(187 const SourceName &name, Attrs attrs, Symbol &cbUltimate) {188 CHECK(cbUltimate.has<CommonBlockDetails>());189 // Make a symbol, but don't add it to the Scope, since it needs to190 // be added to the USE-associated COMMON blocks191 Symbol &useCB{MakeSymbol(name, attrs, UseDetails{name, cbUltimate})};192 return commonBlockUses_.emplace(name, useCB).second;193}194 195Symbol *Scope::FindCommonBlock(const SourceName &name) const {196 if (const auto it{commonBlocks_.find(name)}; it != commonBlocks_.end()) {197 return &*it->second;198 }199 return nullptr;200}201 202Symbol *Scope::FindCommonBlockUse(const SourceName &name) const {203 if (const auto it{commonBlockUses_.find(name)};204 it != commonBlockUses_.end()) {205 return &*it->second;206 }207 return nullptr;208}209 210bool Scope::AddSubmodule(const SourceName &name, Scope &submodule) {211 return submodules_.emplace(name, submodule).second;212}213 214const DeclTypeSpec *Scope::FindType(const DeclTypeSpec &type) const {215 auto it{std::find(declTypeSpecs_.begin(), declTypeSpecs_.end(), type)};216 return it != declTypeSpecs_.end() ? &*it : nullptr;217}218 219const DeclTypeSpec &Scope::MakeNumericType(220 TypeCategory category, KindExpr &&kind) {221 return MakeLengthlessType(NumericTypeSpec{category, std::move(kind)});222}223const DeclTypeSpec &Scope::MakeLogicalType(KindExpr &&kind) {224 return MakeLengthlessType(LogicalTypeSpec{std::move(kind)});225}226const DeclTypeSpec &Scope::MakeTypeStarType() {227 return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::TypeStar});228}229const DeclTypeSpec &Scope::MakeClassStarType() {230 return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::ClassStar});231}232// Types that can't have length parameters can be reused without having to233// compare length expressions. They are stored in the global scope.234const DeclTypeSpec &Scope::MakeLengthlessType(DeclTypeSpec &&type) {235 const auto *found{FindType(type)};236 return found ? *found : declTypeSpecs_.emplace_back(std::move(type));237}238 239const DeclTypeSpec &Scope::MakeCharacterType(240 ParamValue &&length, KindExpr &&kind) {241 return declTypeSpecs_.emplace_back(242 CharacterTypeSpec{std::move(length), std::move(kind)});243}244 245DeclTypeSpec &Scope::MakeDerivedType(246 DeclTypeSpec::Category category, DerivedTypeSpec &&spec) {247 return declTypeSpecs_.emplace_back(category, std::move(spec));248}249 250const DeclTypeSpec *Scope::GetType(const SomeExpr &expr) {251 if (auto dyType{expr.GetType()}) {252 if (dyType->IsAssumedType()) {253 return &MakeTypeStarType();254 } else if (dyType->IsUnlimitedPolymorphic()) {255 return &MakeClassStarType();256 } else {257 switch (dyType->category()) {258 case TypeCategory::Integer:259 case TypeCategory::Unsigned:260 case TypeCategory::Real:261 case TypeCategory::Complex:262 return &MakeNumericType(dyType->category(), KindExpr{dyType->kind()});263 case TypeCategory::Character:264 if (const ParamValue * lenParam{dyType->charLengthParamValue()}) {265 return &MakeCharacterType(266 ParamValue{*lenParam}, KindExpr{dyType->kind()});267 } else {268 auto lenExpr{dyType->GetCharLength()};269 if (!lenExpr) {270 lenExpr =271 std::get<evaluate::Expr<evaluate::SomeCharacter>>(expr.u).LEN();272 }273 if (lenExpr) {274 return &MakeCharacterType(275 ParamValue{SomeIntExpr{std::move(*lenExpr)},276 common::TypeParamAttr::Len},277 KindExpr{dyType->kind()});278 }279 }280 break;281 case TypeCategory::Logical:282 return &MakeLogicalType(KindExpr{dyType->kind()});283 case TypeCategory::Derived:284 return &MakeDerivedType(dyType->IsPolymorphic()285 ? DeclTypeSpec::ClassDerived286 : DeclTypeSpec::TypeDerived,287 DerivedTypeSpec{dyType->GetDerivedTypeSpec()});288 }289 }290 }291 return nullptr;292}293 294Scope::ImportKind Scope::GetImportKind() const {295 if (importKind_) {296 return *importKind_;297 }298 if (symbol_ && !symbol_->attrs().test(Attr::MODULE)) {299 if (auto *details{symbol_->detailsIf<SubprogramDetails>()}) {300 if (details->isInterface()) {301 return ImportKind::None; // default for non-mod-proc interface body302 }303 }304 }305 return ImportKind::Default;306}307 308std::optional<parser::MessageFixedText> Scope::SetImportKind(ImportKind kind) {309 if (!importKind_) {310 importKind_ = kind;311 return std::nullopt;312 }313 bool hasNone{kind == ImportKind::None || *importKind_ == ImportKind::None};314 bool hasAll{kind == ImportKind::All || *importKind_ == ImportKind::All};315 // Check C8100 and C898: constraints on multiple IMPORT statements316 if (hasNone || hasAll) {317 return hasNone318 ? "IMPORT,NONE must be the only IMPORT statement in a scope"_err_en_US319 : "IMPORT,ALL must be the only IMPORT statement in a scope"_err_en_US;320 } else if (kind != *importKind_ &&321 (kind != ImportKind::Only && *importKind_ != ImportKind::Only)) {322 return "Every IMPORT must have ONLY specifier if one of them does"_err_en_US;323 } else {324 return std::nullopt;325 }326}327 328void Scope::add_importName(const SourceName &name) {329 importNames_.insert(name);330}331 332// true if name can be imported or host-associated from parent scope.333bool Scope::CanImport(const SourceName &name) const {334 if (IsTopLevel() || parent_->IsTopLevel()) {335 return false;336 }337 switch (GetImportKind()) {338 SWITCH_COVERS_ALL_CASES339 case ImportKind::None:340 return false;341 case ImportKind::All:342 case ImportKind::Default:343 return true;344 case ImportKind::Only:345 return importNames_.count(name) > 0;346 }347}348 349void Scope::AddSourceRange(parser::CharBlock source) {350 if (source.empty()) {351 return;352 }353 const parser::AllCookedSources &allCookedSources{context_.allCookedSources()};354 const parser::CookedSource *cooked{allCookedSources.Find(source)};355 if (!cooked) {356 CHECK(context_.IsTempName(source.ToString()));357 return;358 }359 for (auto *scope{this}; !scope->IsTopLevel(); scope = &scope->parent()) {360 CHECK(scope->sourceRange_.empty() == (scope->cookedSource_ == nullptr));361 if (!scope->cookedSource_) {362 context_.UpdateScopeIndex(*scope, source);363 scope->cookedSource_ = cooked;364 scope->sourceRange_ = source;365 } else if (scope->cookedSource_ == cooked) {366 auto combined{scope->sourceRange()};367 combined.ExtendToCover(source);368 context_.UpdateScopeIndex(*scope, combined);369 scope->sourceRange_ = combined;370 } else {371 // There's a bug that will be hard to fix; crash informatively372 const parser::AllSources &allSources{allCookedSources.allSources()};373 const auto describe{[&](parser::CharBlock src) {374 if (auto range{allCookedSources.GetProvenanceRange(src)}) {375 std::size_t offset;376 if (const parser::SourceFile *377 file{allSources.GetSourceFile(range->start(), &offset)}) {378 return "'"s + file->path() + "' at " + std::to_string(offset) +379 " for " + std::to_string(range->size());380 } else {381 return "(GetSourceFile failed)"s;382 }383 } else {384 return "(GetProvenanceRange failed)"s;385 }386 }};387 std::string scopeDesc{describe(scope->sourceRange_)};388 std::string newDesc{describe(source)};389 common::die("AddSourceRange would have combined ranges from distinct "390 "source files \"%s\" and \"%s\"",391 scopeDesc.c_str(), newDesc.c_str());392 }393 // Note: If the "break;" here were unconditional (or, equivalently, if394 // there were no loop at all) then the source ranges of parent scopes395 // would not enclose the source ranges of their children. Timing396 // shows that it's cheap to maintain this property, with the exceptions397 // of top-level scopes and for (sub)modules and their descendant398 // submodules.399 if (scope->IsSubmodule()) {400 break; // Submodules are child scopes but not contained ranges401 }402 }403}404 405llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) {406 os << Scope::EnumToString(scope.kind()) << " scope: ";407 if (auto *symbol{scope.symbol()}) {408 os << *symbol << ' ';409 }410 if (scope.derivedTypeSpec_) {411 os << "instantiation of " << *scope.derivedTypeSpec_ << ' ';412 }413 os << scope.children_.size() << " children\n";414 for (const auto &pair : scope.symbols_) {415 const Symbol &symbol{*pair.second};416 os << " " << symbol << '\n';417 }418 if (!scope.equivalenceSets_.empty()) {419 os << " Equivalence Sets:\n";420 for (const auto &set : scope.equivalenceSets_) {421 os << " ";422 for (const auto &object : set) {423 os << ' ' << object.AsFortran();424 }425 os << '\n';426 }427 }428 for (const auto &pair : scope.commonBlocks_) {429 const Symbol &symbol{*pair.second};430 os << " " << symbol << '\n';431 }432 return os;433}434 435bool Scope::IsStmtFunction() const {436 return symbol_ && symbol_->test(Symbol::Flag::StmtFunction);437}438 439template <common::TypeParamAttr... ParamAttr> struct IsTypeParamHelper {440 static_assert(sizeof...(ParamAttr) == 0, "must have one or zero template");441 static bool IsParam(const Symbol &symbol) {442 return symbol.has<TypeParamDetails>();443 }444};445 446template <common::TypeParamAttr ParamAttr> struct IsTypeParamHelper<ParamAttr> {447 static bool IsParam(const Symbol &symbol) {448 if (const auto *typeParam{symbol.detailsIf<TypeParamDetails>()}) {449 return typeParam->attr() == ParamAttr;450 }451 return false;452 }453};454 455template <common::TypeParamAttr... ParamAttr>456static bool IsParameterizedDerivedTypeHelper(const Scope &scope) {457 if (scope.IsDerivedType()) {458 if (const Scope * parent{scope.GetDerivedTypeParent()}) {459 if (IsParameterizedDerivedTypeHelper<ParamAttr...>(*parent)) {460 return true;461 }462 }463 for (const auto &nameAndSymbolPair : scope) {464 if (IsTypeParamHelper<ParamAttr...>::IsParam(*nameAndSymbolPair.second)) {465 return true;466 }467 }468 }469 return false;470}471 472bool Scope::IsParameterizedDerivedType() const {473 return IsParameterizedDerivedTypeHelper<>(*this);474}475bool Scope::IsDerivedTypeWithLengthParameter() const {476 return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Len>(*this);477}478bool Scope::IsDerivedTypeWithKindParameter() const {479 return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Kind>(*this);480}481 482const DeclTypeSpec *Scope::FindInstantiatedDerivedType(483 const DerivedTypeSpec &spec, DeclTypeSpec::Category category) const {484 DeclTypeSpec type{category, spec};485 if (const auto *result{FindType(type)}) {486 return result;487 } else if (IsGlobal()) {488 return nullptr;489 } else {490 return parent().FindInstantiatedDerivedType(spec, category);491 }492}493 494const Scope *Scope::GetDerivedTypeParent() const {495 if (const Symbol * symbol{GetSymbol()}) {496 if (const DerivedTypeSpec * parent{symbol->GetParentTypeSpec(this)}) {497 return parent->scope();498 }499 }500 return nullptr;501}502 503const Scope &Scope::GetDerivedTypeBase() const {504 const Scope *child{this};505 for (const Scope *parent{GetDerivedTypeParent()}; parent != nullptr;506 parent = child->GetDerivedTypeParent()) {507 child = parent;508 }509 return *child;510}511 512void Scope::InstantiateDerivedTypes() {513 for (DeclTypeSpec &type : declTypeSpecs_) {514 if (type.category() == DeclTypeSpec::TypeDerived ||515 type.category() == DeclTypeSpec::ClassDerived) {516 type.derivedTypeSpec().Instantiate(*this);517 }518 }519}520} // namespace Fortran::semantics521