summaryrefslogtreecommitdiff
path: root/flang/include/flang/Semantics/scope.h
blob: 0c5beb6492c6d87724f291a4324350bce50f9f0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
//===-- include/flang/Semantics/scope.h -------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_SEMANTICS_SCOPE_H_
#define FORTRAN_SEMANTICS_SCOPE_H_

#include "attr.h"
#include "symbol.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/idioms.h"
#include "flang/Common/reference.h"
#include "flang/Parser/message.h"
#include "flang/Parser/provenance.h"
#include <list>
#include <map>
#include <optional>
#include <set>
#include <string>

namespace llvm {
class raw_ostream;
}

namespace Fortran::semantics {

using namespace parser::literals;

using common::ConstantSubscript;

class SemanticsContext;

// An equivalence object is represented by a symbol for the variable name,
// the indices for an array element, and the lower bound for a substring.
struct EquivalenceObject {
  EquivalenceObject(Symbol &symbol, std::vector<ConstantSubscript> subscripts,
      std::optional<ConstantSubscript> substringStart, parser::CharBlock source)
      : symbol{symbol}, subscripts{subscripts},
        substringStart{substringStart}, source{source} {}
  explicit EquivalenceObject(Symbol &symbol)
      : symbol{symbol}, source{symbol.name()} {}

  bool operator==(const EquivalenceObject &) const;
  bool operator<(const EquivalenceObject &) const;
  std::string AsFortran() const;

  Symbol &symbol;
  std::vector<ConstantSubscript> subscripts; // for array elem
  std::optional<ConstantSubscript> substringStart;
  parser::CharBlock source;
};
using EquivalenceSet = std::vector<EquivalenceObject>;

class Scope {
  using mapType = std::map<SourceName, MutableSymbolRef>;

public:
  ENUM_CLASS(Kind, Global, IntrinsicModules, Module, MainProgram, Subprogram,
      BlockData, DerivedType, BlockConstruct, Forall, OtherConstruct,
      ImpliedDos)
  using ImportKind = common::ImportKind;

  // Create the Global scope -- the root of the scope tree
  explicit Scope(SemanticsContext &context)
      : Scope{*this, Kind::Global, nullptr, context} {}
  Scope(Scope &parent, Kind kind, Symbol *symbol, SemanticsContext &context)
      : parent_{parent}, kind_{kind}, symbol_{symbol}, context_{context} {
    if (symbol) {
      symbol->set_scope(this);
    }
  }
  Scope(const Scope &) = delete;

  bool operator==(const Scope &that) const { return this == &that; }
  bool operator!=(const Scope &that) const { return this != &that; }

  Scope &parent() {
    CHECK(&parent_ != this);
    return parent_;
  }
  const Scope &parent() const {
    CHECK(&parent_ != this);
    return parent_;
  }
  Kind kind() const { return kind_; }
  bool IsGlobal() const { return kind_ == Kind::Global; }
  bool IsIntrinsicModules() const { return kind_ == Kind::IntrinsicModules; }
  bool IsTopLevel() const {
    return kind_ == Kind::Global || kind_ == Kind::IntrinsicModules;
  }
  bool IsModule() const {
    return kind_ == Kind::Module &&
        !symbol_->get<ModuleDetails>().isSubmodule();
  }
  bool IsSubmodule() const {
    return kind_ == Kind::Module && symbol_->get<ModuleDetails>().isSubmodule();
  }
  bool IsDerivedType() const { return kind_ == Kind::DerivedType; }
  bool IsStmtFunction() const;
  bool IsParameterizedDerivedType() const;
  bool IsParameterizedDerivedTypeInstantiation() const {
    return kind_ == Kind::DerivedType && !symbol_;
  }
  /// Does this derived type have at least one kind parameter ?
  bool IsDerivedTypeWithKindParameter() const;
  /// Does this derived type have at least one length parameter ?
  bool IsDerivedTypeWithLengthParameter() const;
  Symbol *symbol() { return symbol_; }
  const Symbol *symbol() const { return symbol_; }
  SemanticsContext &context() const { return context_; }

  inline const Symbol *GetSymbol() const;
  const Scope *GetDerivedTypeParent() const;
  const Scope &GetDerivedTypeBase() const;
  inline std::optional<SourceName> GetName() const;
  // Returns true if this scope contains, or is, another scope.
  bool Contains(const Scope &) const;
  /// Make a scope nested in this one
  Scope &MakeScope(Kind kind, Symbol *symbol = nullptr);
  SemanticsContext &GetMutableSemanticsContext() const {
    return const_cast<SemanticsContext &>(context());
  }

  using size_type = mapType::size_type;
  using iterator = mapType::iterator;
  using const_iterator = mapType::const_iterator;

  iterator begin() { return symbols_.begin(); }
  iterator end() { return symbols_.end(); }
  const_iterator begin() const { return symbols_.begin(); }
  const_iterator end() const { return symbols_.end(); }
  const_iterator cbegin() const { return symbols_.cbegin(); }
  const_iterator cend() const { return symbols_.cend(); }

  // Return symbols in declaration order (the iterators above are in name order)
  SymbolVector GetSymbols() const;
  MutableSymbolVector GetSymbols();

  iterator find(const SourceName &name);
  const_iterator find(const SourceName &name) const {
    return symbols_.find(name);
  }
  size_type erase(const SourceName &);
  bool empty() const { return symbols_.empty(); }

  // Look for symbol by name in this scope and host (depending on imports).
  Symbol *FindSymbol(const SourceName &) const;

  // Look for component symbol by name in a derived type's scope and
  // parents'.
  Symbol *FindComponent(SourceName) const;

  /// Make a Symbol with unknown details.
  std::pair<iterator, bool> try_emplace(
      const SourceName &name, Attrs attrs = Attrs()) {
    return try_emplace(name, attrs, UnknownDetails());
  }
  /// Make a Symbol with provided details.
  template <typename D>
  common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
      const SourceName &name, D &&details) {
    return try_emplace(name, Attrs(), std::move(details));
  }
  /// Make a Symbol with attrs and details
  template <typename D>
  common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
      const SourceName &name, Attrs attrs, D &&details) {
    Symbol &symbol{MakeSymbol(name, attrs, std::move(details))};
    return symbols_.emplace(name, symbol);
  }
  // Make a copy of a symbol in this scope; nullptr if one is already there
  Symbol *CopySymbol(const Symbol &);

  std::list<EquivalenceSet> &equivalenceSets() { return equivalenceSets_; }
  const std::list<EquivalenceSet> &equivalenceSets() const {
    return equivalenceSets_;
  }
  void add_equivalenceSet(EquivalenceSet &&);
  // Cray pointers are saved as map of pointee name -> pointer symbol
  const mapType &crayPointers() const { return crayPointers_; }
  void add_crayPointer(const SourceName &, Symbol &);
  mapType &commonBlocks() { return commonBlocks_; }
  const mapType &commonBlocks() const { return commonBlocks_; }
  Symbol &MakeCommonBlock(const SourceName &);
  Symbol *FindCommonBlock(const SourceName &) const;

  /// Make a Symbol but don't add it to the scope.
  template <typename D>
  common::IfNoLvalue<Symbol &, D> MakeSymbol(
      const SourceName &name, Attrs attrs, D &&details) {
    return allSymbols.Make(*this, name, attrs, std::move(details));
  }

  std::list<Scope> &children() { return children_; }
  const std::list<Scope> &children() const { return children_; }

  // For Module scope, maintain a mapping of all submodule scopes with this
  // module as its ancestor module. AddSubmodule returns false if already there.
  Scope *FindSubmodule(const SourceName &) const;
  bool AddSubmodule(const SourceName &, Scope &);

  const DeclTypeSpec *FindType(const DeclTypeSpec &) const;
  const DeclTypeSpec &MakeNumericType(TypeCategory, KindExpr &&kind);
  const DeclTypeSpec &MakeLogicalType(KindExpr &&kind);
  const DeclTypeSpec &MakeCharacterType(
      ParamValue &&length, KindExpr &&kind = KindExpr{0});
  DeclTypeSpec &MakeDerivedType(DeclTypeSpec::Category, DerivedTypeSpec &&);
  const DeclTypeSpec &MakeTypeStarType();
  const DeclTypeSpec &MakeClassStarType();
  const DeclTypeSpec *GetType(const SomeExpr &);

  std::size_t size() const { return size_; }
  void set_size(std::size_t size) { size_ = size; }
  std::optional<std::size_t> alignment() const { return alignment_; }

  void SetAlignment(std::size_t n) {
    alignment_ = std::max(alignment_.value_or(0), n);
  }

  ImportKind GetImportKind() const;
  // Names appearing in IMPORT statements in this scope
  std::set<SourceName> importNames() const { return importNames_; }

  // Set the kind of imports from host into this scope.
  // Return an error message for incompatible kinds.
  std::optional<parser::MessageFixedText> SetImportKind(ImportKind);

  void add_importName(const SourceName &);

  // These members pertain to instantiations of parameterized derived types.
  const DerivedTypeSpec *derivedTypeSpec() const { return derivedTypeSpec_; }
  DerivedTypeSpec *derivedTypeSpec() { return derivedTypeSpec_; }
  void set_derivedTypeSpec(DerivedTypeSpec &spec) { derivedTypeSpec_ = &spec; }
  parser::Message::Reference instantiationContext() const {
    return instantiationContext_;
  };
  void set_instantiationContext(parser::Message::Reference &&mref) {
    instantiationContext_ = std::move(mref);
  }

  bool hasSAVE() const { return hasSAVE_; }
  void set_hasSAVE(bool yes = true) { hasSAVE_ = yes; }

  // The range of the source of this and nested scopes.
  const parser::CharBlock &sourceRange() const { return sourceRange_; }
  void AddSourceRange(const parser::CharBlock &);
  // Find the smallest scope under this one that contains source
  const Scope *FindScope(parser::CharBlock) const;
  Scope *FindScope(parser::CharBlock);

  // Attempts to find a match for a derived type instance
  const DeclTypeSpec *FindInstantiatedDerivedType(const DerivedTypeSpec &,
      DeclTypeSpec::Category = DeclTypeSpec::TypeDerived) const;

  bool IsModuleFile() const {
    return kind_ == Kind::Module && symbol_ &&
        symbol_->test(Symbol::Flag::ModFile);
  }

  void InstantiateDerivedTypes();

  const Symbol *runtimeDerivedTypeDescription() const {
    return runtimeDerivedTypeDescription_;
  }
  void set_runtimeDerivedTypeDescription(const Symbol &symbol) {
    runtimeDerivedTypeDescription_ = &symbol;
  }

private:
  Scope &parent_; // this is enclosing scope, not extended derived type base
  const Kind kind_;
  std::size_t size_{0}; // size in bytes
  std::optional<std::size_t> alignment_; // required alignment in bytes
  parser::CharBlock sourceRange_;
  Symbol *const symbol_; // if not null, symbol_->scope() == this
  std::list<Scope> children_;
  mapType symbols_;
  mapType commonBlocks_;
  std::list<EquivalenceSet> equivalenceSets_;
  mapType crayPointers_;
  std::map<SourceName, common::Reference<Scope>> submodules_;
  std::list<DeclTypeSpec> declTypeSpecs_;
  std::optional<ImportKind> importKind_;
  std::set<SourceName> importNames_;
  DerivedTypeSpec *derivedTypeSpec_{nullptr}; // dTS->scope() == this
  parser::Message::Reference instantiationContext_;
  bool hasSAVE_{false}; // scope has a bare SAVE statement
  const Symbol *runtimeDerivedTypeDescription_{nullptr};
  SemanticsContext &context_;
  // When additional data members are added to Scope, remember to
  // copy them, if appropriate, in FindOrInstantiateDerivedType().

  // Storage for all Symbols. Every Symbol is in allSymbols and every Symbol*
  // or Symbol& points to one in there.
  static Symbols<1024> allSymbols;

  bool CanImport(const SourceName &) const;
  const DeclTypeSpec &MakeLengthlessType(DeclTypeSpec &&);

  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Scope &);
};

// Inline so that it can be called from Evaluate without a link-time dependency.

inline const Symbol *Scope::GetSymbol() const {
  return symbol_         ? symbol_
      : derivedTypeSpec_ ? &derivedTypeSpec_->typeSymbol()
                         : nullptr;
}

inline std::optional<SourceName> Scope::GetName() const {
  if (const auto *sym{GetSymbol()}) {
    return sym->name();
  } else {
    return std::nullopt;
  }
}

} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_SCOPE_H_