brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.2 KiB · ab75d4c Raw
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