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-exception
6//
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 exists
129 } 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(const SourceName &name) {
147 const auto it{commonBlocks_.find(name)};
148 if (it != commonBlocks_.end()) {
149 return *it->second;
150 } else {
151 Symbol &symbol{MakeSymbol(name, Attrs{}, CommonBlockDetails{})};
152 commonBlocks_.emplace(name, symbol);
153 return symbol;
154 }
155}
156Symbol *Scope::FindCommonBlock(const SourceName &name) const {
157 const auto it{commonBlocks_.find(name)};
158 return it != commonBlocks_.end() ? &*it->second : nullptr;
159}
160
161Scope *Scope::FindSubmodule(const SourceName &name) const {
162 auto it{submodules_.find(name)};
163 if (it == submodules_.end()) {
164 return nullptr;
165 } else {
166 return &*it->second;
167 }
168}
169bool Scope::AddSubmodule(const SourceName &name, Scope &submodule) {
170 return submodules_.emplace(name, submodule).second;
171}
172
173const DeclTypeSpec *Scope::FindType(const DeclTypeSpec &type) const {
174 auto it{std::find(declTypeSpecs_.begin(), declTypeSpecs_.end(), type)};
175 return it != declTypeSpecs_.end() ? &*it : nullptr;
176}
177
178const DeclTypeSpec &Scope::MakeNumericType(
179 TypeCategory category, KindExpr &&kind) {
180 return MakeLengthlessType(NumericTypeSpec{category, std::move(kind)});
181}
182const DeclTypeSpec &Scope::MakeLogicalType(KindExpr &&kind) {
183 return MakeLengthlessType(LogicalTypeSpec{std::move(kind)});
184}
185const DeclTypeSpec &Scope::MakeTypeStarType() {
186 return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::TypeStar});
187}
188const DeclTypeSpec &Scope::MakeClassStarType() {
189 return MakeLengthlessType(DeclTypeSpec{DeclTypeSpec::ClassStar});
190}
191// Types that can't have length parameters can be reused without having to
192// compare length expressions. They are stored in the global scope.
193const DeclTypeSpec &Scope::MakeLengthlessType(DeclTypeSpec &&type) {
194 const auto *found{FindType(type)};
195 return found ? *found : declTypeSpecs_.emplace_back(std::move(type));
196}
197
198const DeclTypeSpec &Scope::MakeCharacterType(
199 ParamValue &&length, KindExpr &&kind) {
200 return declTypeSpecs_.emplace_back(
201 CharacterTypeSpec{std::move(length), std::move(kind)});
202}
203
204DeclTypeSpec &Scope::MakeDerivedType(
205 DeclTypeSpec::Category category, DerivedTypeSpec &&spec) {
206 return declTypeSpecs_.emplace_back(category, std::move(spec));
207}
208
209const DeclTypeSpec *Scope::GetType(const SomeExpr &expr) {
210 if (auto dyType{expr.GetType()}) {
211 if (dyType->IsAssumedType()) {
212 return &MakeTypeStarType();
213 } else if (dyType->IsUnlimitedPolymorphic()) {
214 return &MakeClassStarType();
215 } else {
216 switch (dyType->category()) {
217 case TypeCategory::Integer:
218 case TypeCategory::Unsigned:
219 case TypeCategory::Real:
220 case TypeCategory::Complex:
221 return &MakeNumericType(dyType->category(), KindExpr{dyType->kind()});
222 case TypeCategory::Character:
223 if (const ParamValue * lenParam{dyType->charLengthParamValue()}) {
224 return &MakeCharacterType(
225 ParamValue{*lenParam}, KindExpr{dyType->kind()});
226 } else {
227 auto lenExpr{dyType->GetCharLength()};
228 if (!lenExpr) {
229 lenExpr =
230 std::get<evaluate::Expr<evaluate::SomeCharacter>>(expr.u).LEN();
231 }
232 if (lenExpr) {
233 return &MakeCharacterType(
234 ParamValue{SomeIntExpr{std::move(*lenExpr)},
235 common::TypeParamAttr::Len},
236 KindExpr{dyType->kind()});
237 }
238 }
239 break;
240 case TypeCategory::Logical:
241 return &MakeLogicalType(KindExpr{dyType->kind()});
242 case TypeCategory::Derived:
243 return &MakeDerivedType(dyType->IsPolymorphic()
244 ? DeclTypeSpec::ClassDerived
245 : DeclTypeSpec::TypeDerived,
246 DerivedTypeSpec{dyType->GetDerivedTypeSpec()});
247 }
248 }
249 }
250 return nullptr;
251}
252
253Scope::ImportKind Scope::GetImportKind() const {
254 if (importKind_) {
255 return *importKind_;
256 }
257 if (symbol_ && !symbol_->attrs().test(Attr::MODULE)) {
258 if (auto *details{symbol_->detailsIf<SubprogramDetails>()}) {
259 if (details->isInterface()) {
260 return ImportKind::None; // default for non-mod-proc interface body
261 }
262 }
263 }
264 return ImportKind::Default;
265}
266
267std::optional<parser::MessageFixedText> Scope::SetImportKind(ImportKind kind) {
268 if (!importKind_) {
269 importKind_ = kind;
270 return std::nullopt;
271 }
272 bool hasNone{kind == ImportKind::None || *importKind_ == ImportKind::None};
273 bool hasAll{kind == ImportKind::All || *importKind_ == ImportKind::All};
274 // Check C8100 and C898: constraints on multiple IMPORT statements
275 if (hasNone || hasAll) {
276 return hasNone
277 ? "IMPORT,NONE must be the only IMPORT statement in a scope"_err_en_US
278 : "IMPORT,ALL must be the only IMPORT statement in a scope"_err_en_US;
279 } else if (kind != *importKind_ &&
280 (kind != ImportKind::Only && *importKind_ != ImportKind::Only)) {
281 return "Every IMPORT must have ONLY specifier if one of them does"_err_en_US;
282 } else {
283 return std::nullopt;
284 }
285}
286
287void Scope::add_importName(const SourceName &name) {
288 importNames_.insert(name);
289}
290
291// true if name can be imported or host-associated from parent scope.
292bool Scope::CanImport(const SourceName &name) const {
293 if (IsTopLevel() || parent_->IsTopLevel()) {
294 return false;
295 }
296 switch (GetImportKind()) {
297 SWITCH_COVERS_ALL_CASES
298 case ImportKind::None:
299 return false;
300 case ImportKind::All:
301 case ImportKind::Default:
302 return true;
303 case ImportKind::Only:
304 return importNames_.count(name) > 0;
305 }
306}
307
308void Scope::AddSourceRange(parser::CharBlock source) {
309 if (source.empty()) {
310 return;
311 }
312 const parser::AllCookedSources &allCookedSources{context_.allCookedSources()};
313 const parser::CookedSource *cooked{allCookedSources.Find(source)};
314 if (!cooked) {
315 CHECK(context_.IsTempName(source.ToString()));
316 return;
317 }
318 for (auto *scope{this}; !scope->IsTopLevel(); scope = &scope->parent()) {
319 CHECK(scope->sourceRange_.empty() == (scope->cookedSource_ == nullptr));
320 if (!scope->cookedSource_) {
321 context_.UpdateScopeIndex(*scope, source);
322 scope->cookedSource_ = cooked;
323 scope->sourceRange_ = source;
324 } else if (scope->cookedSource_ == cooked) {
325 auto combined{scope->sourceRange()};
326 combined.ExtendToCover(source);
327 context_.UpdateScopeIndex(*scope, combined);
328 scope->sourceRange_ = combined;
329 } else {
330 // There's a bug that will be hard to fix; crash informatively
331 const parser::AllSources &allSources{allCookedSources.allSources()};
332 const auto describe{[&](parser::CharBlock src) {
333 if (auto range{allCookedSources.GetProvenanceRange(src)}) {
334 std::size_t offset;
335 if (const parser::SourceFile *
336 file{allSources.GetSourceFile(range->start(), &offset)}) {
337 return "'"s + file->path() + "' at " + std::to_string(offset) +
338 " for " + std::to_string(range->size());
339 } else {
340 return "(GetSourceFile failed)"s;
341 }
342 } else {
343 return "(GetProvenanceRange failed)"s;
344 }
345 }};
346 std::string scopeDesc{describe(scope->sourceRange_)};
347 std::string newDesc{describe(source)};
348 common::die("AddSourceRange would have combined ranges from distinct "
349 "source files \"%s\" and \"%s\"",
350 scopeDesc.c_str(), newDesc.c_str());
351 }
352 // Note: If the "break;" here were unconditional (or, equivalently, if
353 // there were no loop at all) then the source ranges of parent scopes
354 // would not enclose the source ranges of their children. Timing
355 // shows that it's cheap to maintain this property, with the exceptions
356 // of top-level scopes and for (sub)modules and their descendant
357 // submodules.
358 if (scope->IsSubmodule()) {
359 break; // Submodules are child scopes but not contained ranges
360 }
361 }
362}
363
364llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) {
365 os << Scope::EnumToString(scope.kind()) << " scope: ";
366 if (auto *symbol{scope.symbol()}) {
367 os << *symbol << ' ';
368 }
369 if (scope.derivedTypeSpec_) {
370 os << "instantiation of " << *scope.derivedTypeSpec_ << ' ';
371 }
372 os << scope.children_.size() << " children\n";
373 for (const auto &pair : scope.symbols_) {
374 const Symbol &symbol{*pair.second};
375 os << " " << symbol << '\n';
376 }
377 if (!scope.equivalenceSets_.empty()) {
378 os << " Equivalence Sets:\n";
379 for (const auto &set : scope.equivalenceSets_) {
380 os << " ";
381 for (const auto &object : set) {
382 os << ' ' << object.AsFortran();
383 }
384 os << '\n';
385 }
386 }
387 for (const auto &pair : scope.commonBlocks_) {
388 const Symbol &symbol{*pair.second};
389 os << " " << symbol << '\n';
390 }
391 return os;
392}
393
394bool Scope::IsStmtFunction() const {
395 return symbol_ && symbol_->test(Symbol::Flag::StmtFunction);
396}
397
398template <common::TypeParamAttr... ParamAttr> struct IsTypeParamHelper {
399 static_assert(sizeof...(ParamAttr) == 0, "must have one or zero template");
400 static bool IsParam(const Symbol &symbol) {
401 return symbol.has<TypeParamDetails>();
402 }
403};
404
405template <common::TypeParamAttr ParamAttr> struct IsTypeParamHelper<ParamAttr> {
406 static bool IsParam(const Symbol &symbol) {
407 if (const auto *typeParam{symbol.detailsIf<TypeParamDetails>()}) {
408 return typeParam->attr() == ParamAttr;
409 }
410 return false;
411 }
412};
413
414template <common::TypeParamAttr... ParamAttr>
415static bool IsParameterizedDerivedTypeHelper(const Scope &scope) {
416 if (scope.IsDerivedType()) {
417 if (const Scope * parent{scope.GetDerivedTypeParent()}) {
418 if (IsParameterizedDerivedTypeHelper<ParamAttr...>(*parent)) {
419 return true;
420 }
421 }
422 for (const auto &nameAndSymbolPair : scope) {
423 if (IsTypeParamHelper<ParamAttr...>::IsParam(*nameAndSymbolPair.second)) {
424 return true;
425 }
426 }
427 }
428 return false;
429}
430
431bool Scope::IsParameterizedDerivedType() const {
432 return IsParameterizedDerivedTypeHelper<>(*this);
433}
434bool Scope::IsDerivedTypeWithLengthParameter() const {
435 return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Len>(*this);
436}
437bool Scope::IsDerivedTypeWithKindParameter() const {
438 return IsParameterizedDerivedTypeHelper<common::TypeParamAttr::Kind>(*this);
439}
440
441const DeclTypeSpec *Scope::FindInstantiatedDerivedType(
442 const DerivedTypeSpec &spec, DeclTypeSpec::Category category) const {
443 DeclTypeSpec type{category, spec};
444 if (const auto *result{FindType(type)}) {
445 return result;
446 } else if (IsGlobal()) {
447 return nullptr;
448 } else {
449 return parent().FindInstantiatedDerivedType(spec, category);
450 }
451}
452
453const Scope *Scope::GetDerivedTypeParent() const {
454 if (const Symbol * symbol{GetSymbol()}) {
455 if (const DerivedTypeSpec * parent{symbol->GetParentTypeSpec(this)}) {
456 return parent->scope();
457 }
458 }
459 return nullptr;
460}
461
462const Scope &Scope::GetDerivedTypeBase() const {
463 const Scope *child{this};
464 for (const Scope *parent{GetDerivedTypeParent()}; parent != nullptr;
465 parent = child->GetDerivedTypeParent()) {
466 child = parent;
467 }
468 return *child;
469}
470
471void Scope::InstantiateDerivedTypes() {
472 for (DeclTypeSpec &type : declTypeSpecs_) {
473 if (type.category() == DeclTypeSpec::TypeDerived ||
474 type.category() == DeclTypeSpec::ClassDerived) {
475 type.derivedTypeSpec().Instantiate(*this);
476 }
477 }
478}
479} // namespace Fortran::semantics
480

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of flang/lib/Semantics/scope.cpp