forked from OSchip/llvm-project
[flang] commit before rebase
Original-commit: flang-compiler/f18@8366a87e47 Reviewed-on: https://github.com/flang-compiler/f18/pull/24 Tree-same-pre-rewrite: false
This commit is contained in:
parent
92cd5b1beb
commit
fc2ec6a68b
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "../../../lib/semantics/attr.h"
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef FLANG_SEMA_IDENTIFIER_H
|
||||
#define FLANG_SEMA_IDENTIFIER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
|
||||
|
||||
// A class describing an identifier.
|
||||
//
|
||||
// For each name, there is one and only one identifier.
|
||||
//
|
||||
// Also, identifiers are immutable and are never destroyed.
|
||||
//
|
||||
// The comparison of two 'Identifier*' returns true iff their
|
||||
// name are identical.
|
||||
//
|
||||
class Identifier
|
||||
{
|
||||
private:
|
||||
Identifier(Identifier &&) = delete ;
|
||||
~Identifier() = delete ;
|
||||
Identifier(std::string n) : name_(n) { }
|
||||
private:
|
||||
std::string name_ ;
|
||||
public:
|
||||
const std::string & name() { return name_ ; }
|
||||
static const Identifier *get(std::string n) ;
|
||||
};
|
||||
|
||||
} // of namespace flang::Sema
|
||||
} // of namespace flang
|
||||
|
||||
#endif
|
|
@ -0,0 +1,382 @@
|
|||
#ifndef FLANG_SEMA_SCOPE_H
|
||||
#define FLANG_SEMA_SCOPE_H
|
||||
|
||||
#include "flang/Sema/Identifier.h"
|
||||
#include "flang/Sema/Type.h"
|
||||
#include "flang/Sema/Attr.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
|
||||
class Scope ;
|
||||
|
||||
//
|
||||
|
||||
// Describe a symbol.
|
||||
//
|
||||
//
|
||||
//
|
||||
class Symbol {
|
||||
public:
|
||||
enum ClassId {
|
||||
TemporarySymbolId,
|
||||
ParameterSymbolId,
|
||||
SubprogramSymbolId,
|
||||
SubSymbolId,
|
||||
InterfaceSymbolId,
|
||||
ImportedSymbolId,
|
||||
UsedSymbolId,
|
||||
MemberSymbolId,
|
||||
DummyArgumentSymbolId,
|
||||
ExternalSymbolId,
|
||||
NamelistSymbolId,
|
||||
CommonSymbolId,
|
||||
VariableSymbolId,
|
||||
DerivedTypeSymbolId,
|
||||
// ... and probably more to come
|
||||
last_ClassId
|
||||
} ;
|
||||
private:
|
||||
ClassId cid_;
|
||||
Scope * owner_ ; // The scope that owns this symbol
|
||||
const Identifier *name_; // The local name of that symbol
|
||||
private:
|
||||
TypeSpec * type_ = nullptr; // The type associated to that symbol
|
||||
public:
|
||||
Symbol(ClassId cid, Scope * owner, Identifier *name) :
|
||||
cid_(cid), owner_(owner), name_(name) {}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// A local symbol is a temporary placeholder for symbols whose nature cannot be
|
||||
// infered when the it is first encounterd. It shall eventually be replaced by
|
||||
// an actual symbol.
|
||||
//
|
||||
// For instance:
|
||||
//
|
||||
// INTEGER :: x,y,z ! x, y, and z start as TemporarySymbol
|
||||
// VOLATILE :: x ! x is resolved as a VariableSymbol
|
||||
// PARAMETER(y=42) ! y is resolved as a ParameterSymbol
|
||||
// print *, z(10) ! z is resolved as an ExternalSymbol
|
||||
//
|
||||
class TemporarySymbol : public Symbol {
|
||||
public:
|
||||
TemporarySymbol(Scope * owner, Identifier *name) : Symbol(TemporarySymbolId,owner,name) {}
|
||||
private:
|
||||
// If not NULL, then this is the actual symbol to be used instead of this symbol.
|
||||
Symbol *actual_ ;
|
||||
private:
|
||||
};
|
||||
|
||||
// A symbol representing a parameter whose value can be queried.
|
||||
//
|
||||
//
|
||||
class ParameterSymbol : public Symbol {
|
||||
public:
|
||||
ParameterSymbol(Scope * owner, Identifier *name) : Symbol(ParameterSymbolId,owner,name) {}
|
||||
};
|
||||
|
||||
// A symbol representing an EXTERNAL function.
|
||||
class ExternalSymbol : public Symbol {
|
||||
public:
|
||||
ExternalSymbol(Scope * owner, Identifier *name) : Symbol(ExternalSymbolId,owner,name) {}
|
||||
};
|
||||
|
||||
//
|
||||
// A symbol representing a variable.
|
||||
//
|
||||
// The variable may be local or part of a common.
|
||||
//
|
||||
// Question: Do we want to represent pointers using VariableSymbol or a dedicated class?
|
||||
//
|
||||
class VariableSymbol : public Symbol {
|
||||
public:
|
||||
VariableSymbol(Scope * owner, Identifier *name) : Symbol(VariableSymbolId,owner,name) {}
|
||||
};
|
||||
|
||||
// A symbol representing a dummy argument.
|
||||
class DummyArgumentSymbol : public Symbol {
|
||||
public:
|
||||
DummyArgumentSymbol(Scope * owner, Identifier *name) : Symbol(DummyArgumentSymbolId,owner,name) {}
|
||||
};
|
||||
|
||||
// A symbol representing a COMMON block.
|
||||
//
|
||||
// TODO: Each member of common shall appear as a VariableSymbol probably with a
|
||||
// boolean flag to indicate that it belongs to a COMMON. The actual
|
||||
// CommonSymbol is then easy to figure out by the local scope.
|
||||
//
|
||||
//
|
||||
class CommonSymbol : public Symbol {
|
||||
public:
|
||||
CommonSymbol(Scope * owner, Identifier *name) : Symbol(CommonSymbolId,owner,name) {}
|
||||
private:
|
||||
// The content of the common section.
|
||||
std::vector<Identifier *> content_ ;
|
||||
};
|
||||
|
||||
//
|
||||
// A symbol describing a subprogram declared by a FUNCTION or SUBROUTINE construct
|
||||
// (either as a main unit, a internal subprogram, or an interface).
|
||||
//
|
||||
// The SubprogramSymbol only occurs in the local scope providing the FUNCTION
|
||||
// or SUBROUTINE construct.
|
||||
//
|
||||
class SubprogramSymbol : public Symbol {
|
||||
public:
|
||||
private:
|
||||
Scope * inner_scope_ ;
|
||||
VariableSymbol *result ; // Shortcut to the variable that holds the result
|
||||
std::vector<DummyArgumentSymbol *> args ;
|
||||
};
|
||||
|
||||
// Symbol describing an interface.
|
||||
// A null name is allowed to represent an unnamed interface.
|
||||
class InterfaceSymbol : public Symbol {
|
||||
public:
|
||||
InterfaceSymbol(Scope * owner, Identifier *name) : Symbol(InterfaceSymbolId,owner,name) {}
|
||||
private:
|
||||
Scope * inner_scope_ ;
|
||||
};
|
||||
|
||||
// A symbol imported from the parent or host scope of the local scope either
|
||||
// automatically or via an 'import' statement.
|
||||
class ImportedSymbol : public Symbol {
|
||||
public:
|
||||
ImportedSymbol(Scope *owner, Identifier *name) : Symbol(ImportedSymbolId,owner,name) {}
|
||||
private:
|
||||
// Provide the target symbol in the parent or host scope.
|
||||
Symbol *target_ ;
|
||||
};
|
||||
|
||||
// A symbol imported from a module within a submodule.
|
||||
//
|
||||
// FIXME:
|
||||
// I am not sure how submodules scopes shall be implemented.
|
||||
// On one side, a submodule inherit all the symbols from its parent
|
||||
// module or submodule so that part looks a lot like regular host
|
||||
// association.
|
||||
//
|
||||
// However, a submodule may also provide the implementation of
|
||||
// a subroutine or function already declared in the parent module.
|
||||
// A dedicated mecanism is probably needed here. A boolean flag
|
||||
// added to SubprogramSymbol to tag all 'module function' and
|
||||
// 'module subroutine' could be enough. TO BE INVESTIGATED
|
||||
//
|
||||
//
|
||||
class SubSymbol : public Symbol {
|
||||
public:
|
||||
SubSymbol(Scope * owner, Identifier *name) : Symbol(SubSymbolId,owner,name) {}
|
||||
private:
|
||||
// Provide the target symbol in the parent or host scope.
|
||||
Symbol *target_ ;
|
||||
};
|
||||
|
||||
|
||||
// A symbol provided by one or more USE statements.
|
||||
//
|
||||
// Reminder: a UsedSymbol may be ambiguous even when it has
|
||||
// only one target. This is because ambiguities are always
|
||||
// resolved when the symbol is actually used so a module
|
||||
// may export ambiguous symbols.
|
||||
//
|
||||
//
|
||||
class UsedSymbol : public Symbol {
|
||||
public:
|
||||
UsedSymbol(Scope * owner, Identifier *name) : Symbol(UsedSymbolId,owner,name) {}
|
||||
private:
|
||||
// Provide the list of target symbols in the
|
||||
std::vector<Symbol*> targets_ ;
|
||||
bool ambiguous_;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// A symbol representing a derived type.
|
||||
//
|
||||
class DerivedTypeSymbol : public Symbol{
|
||||
public:
|
||||
DerivedTypeSymbol(Scope *owner, Identifier *name) : Symbol(DerivedTypeSymbolId,owner,name) {}
|
||||
private:
|
||||
Scope * inner_scope_ ;
|
||||
};
|
||||
|
||||
//
|
||||
// A symbol representing a member in a Derived type
|
||||
//
|
||||
// Those symbols require a dedicated type because they are in a dedicated namespace.
|
||||
//
|
||||
class MemberSymbol : public Symbol {
|
||||
public:
|
||||
MemberSymbol(Scope * owner, Identifier *name) : Symbol(MemberSymbolId,owner,name) {}
|
||||
private:
|
||||
};
|
||||
|
||||
//
|
||||
// A symbol representing a namelist-group
|
||||
|
||||
//
|
||||
class NamelistSymbol : public Symbol {
|
||||
public:
|
||||
NamelistSymbol(Scope * owner, Identifier *name) : Symbol(NamelistSymbolId,owner,name) {}
|
||||
private:
|
||||
std::vector<Symbol*> content_ ;
|
||||
};
|
||||
|
||||
// NOTE: Support for EQUIVALENCE is not
|
||||
//
|
||||
//
|
||||
|
||||
// =======================================================
|
||||
|
||||
//
|
||||
// Describe a scope (or a symbol table)
|
||||
//
|
||||
// For now, there is only one class. Not sure if we need more.
|
||||
// Of course, there are multiple kinds of scopes in Fortran and
|
||||
// they all behave slightly differently. However, a single Scope class
|
||||
// that adjust its behavior according to its ScopeKind is probably
|
||||
// easier to implement than a dozen of classes.
|
||||
//
|
||||
// Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they
|
||||
// are referenced (in order to avoid an explosion of the number of symbols)
|
||||
//
|
||||
class Scope
|
||||
{
|
||||
public:
|
||||
|
||||
enum ScopeKind {
|
||||
SK_PROGRAM, // a scope associated to a PROGRAM
|
||||
SK_MODULE, // a scope associated to a MODULE
|
||||
SK_SUBMODULE, // a scope associated to a SUBMODULE
|
||||
SK_FUNCTION, // a scope associated to a FUNCTION
|
||||
SK_SUBROUTINE, // a scope associated to a SUBROUTNE
|
||||
SK_BLOCKDATA, // a scope associated to a BLOCKDATA
|
||||
SK_USE_MODULE, // a scope describing all the public symbols of a module.
|
||||
SK_BLOCK, // a scope associated to a BLOCK construct
|
||||
SK_DERIVED, // a scope associated to a derived TYPE construct
|
||||
SK_INTERFACE // a scope associated to an interface
|
||||
|
||||
// ... and probably more to come
|
||||
} ;
|
||||
|
||||
private:
|
||||
|
||||
ScopeKind kind_ ;
|
||||
|
||||
// For scopes associated to a name (so most of them except BLOCK
|
||||
// and unnamed INTERFACE) provide the associated symbol in the
|
||||
// parent scope.
|
||||
Symbol * self_ ;
|
||||
|
||||
// The UsedModule describes how a module shall be used.
|
||||
//
|
||||
// For instance, consider the statement
|
||||
//
|
||||
// use foobar , A => B, C => D
|
||||
//
|
||||
// The 'exclude' vector will contain the identifiers for "B" and "D"
|
||||
//
|
||||
// Remark: A and C are descibed as UsedSymbol entries in the local
|
||||
// scope.
|
||||
//
|
||||
//
|
||||
// Reminder: Module that are imported with a 'only' qualifier are
|
||||
// entirely handled via UsedSymbol entries and so, do not have
|
||||
// a UsedModule descriptor
|
||||
//
|
||||
struct UsedModule {
|
||||
// A reference to the
|
||||
//
|
||||
Scope * module ;
|
||||
// All identifiers to exclude when doing a lookup
|
||||
// in that module
|
||||
std::vector<Identifier *> exclude ;
|
||||
};
|
||||
|
||||
//
|
||||
// The parent scope is the scope that lexically owns this local scope.
|
||||
// Symbols are normally not searched is the parent scope except in the case of
|
||||
// symbols declared by an 'import, only'.
|
||||
//
|
||||
|
||||
Scope * parent_scope_ ;
|
||||
|
||||
// The host scope is the scope that is searched last. 9
|
||||
//
|
||||
//
|
||||
//
|
||||
Scope * host_scope_ ; // The host scope
|
||||
|
||||
std::list<Scope *> use_ ; // Scopes for the modules that are entirely used
|
||||
std::list<Scope *> use_only_ ; // Scopes for the modules that are partially used
|
||||
|
||||
// All visible entries in the scope.
|
||||
//
|
||||
// The entries are sorted by lexical order of declaration (except for
|
||||
// subprogram that are added at the end of the declaration part) which
|
||||
// means that lookups must be performed in reverse order.
|
||||
//
|
||||
// The reason to do that instead of using a map or any other data structure
|
||||
// optimized for fast lookup is to allow inner scopes (e.g. BLOCK) to see only a
|
||||
// subset of their parent scope.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// PROGRAM test
|
||||
// implicit integer (a-z)
|
||||
// integer :: i
|
||||
// do i = 1,2
|
||||
// if (i==1) bar=0
|
||||
// if (i==2) then
|
||||
// BLOCK
|
||||
// bar=42 ! this is the 'bar' declared above
|
||||
// foo=42 ! this 'foo' is private to the BLOCK
|
||||
// END BLOCK
|
||||
// endif
|
||||
// if (i==1) foo=0
|
||||
// print *,'foo=',foo, 'bar=',bar
|
||||
// enddo
|
||||
// END PROGRAM test
|
||||
//
|
||||
// The expected output is
|
||||
// foo=0 bar=0
|
||||
// foo=0 bar=42
|
||||
//
|
||||
// In that case, the idea is that the block shall not have access
|
||||
// to the (implicit) declarations of its parent scope after 'bar'
|
||||
// This is easy to do with a vector by keeping the number of entries
|
||||
// in the parent scope at the time the block construct was created.
|
||||
//
|
||||
// Remark: That mecanism may not be required if all symbols can
|
||||
// be resolved in a single pass.
|
||||
//
|
||||
// Another potential reason to keep the declaration order is when
|
||||
// a symbol hides or extends another symbol. A typical example would be
|
||||
// named interfaces that can be redeclared multiple times.
|
||||
//
|
||||
|
||||
std::vector<Symbol*> entries_ ;
|
||||
|
||||
//
|
||||
// A vector containing all Symbols owned by that scope
|
||||
//
|
||||
std::vector<Symbol*> owned_ ;
|
||||
|
||||
public:
|
||||
|
||||
// The system scope is the scope that provides intrinsic subprograms
|
||||
|
||||
Scope * getSystemScope() ;
|
||||
|
||||
Symbol *lookup(const Identifier *name) ;
|
||||
|
||||
};
|
||||
|
||||
} // of namespace semantics
|
||||
} // of namespace Fortran
|
||||
|
||||
#endif
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "../../../lib/semantics/type.h"
|
|
@ -1,3 +1,3 @@
|
|||
|
||||
add_subdirectory(parser)
|
||||
add_subdirectory(semantics)
|
||||
add_subdirectory(Sema)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "../semantics/attr.cc"
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
add_library( FlangSemantics
|
||||
Identifier.cc
|
||||
Scope.cc
|
||||
Type.cc # ../semantics/type.cc
|
||||
Attr.cc # ../semantics/attr.cc
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
#include "flang/Sema/Identifier.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
using Fortran::semantics::Identifier ;
|
||||
|
||||
static std::map<std::string, Identifier*> all ;
|
||||
|
||||
const Identifier *
|
||||
Identifier::get(std::string n)
|
||||
{
|
||||
Identifier * &ref = all[n] ;
|
||||
if (!ref) {
|
||||
ref = new Identifier(n) ;
|
||||
}
|
||||
return ref ;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
|
||||
#include "flang/Sema/Scope.h"
|
||||
#include "flang/Sema/Identifier.h"
|
||||
|
||||
using namespace Fortran;
|
||||
using namespace semantics;
|
||||
|
||||
Symbol *
|
||||
Scope::lookup(const Identifier *name)
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "../semantics/type.cc"
|
|
@ -35,11 +35,27 @@
|
|||
// although a C++ compiler wouldn't default them anyway due to the presence
|
||||
// of move constructors and move assignments.
|
||||
|
||||
|
||||
CLASS_TRAIT(EmptyTrait);
|
||||
CLASS_TRAIT(WrapperTrait);
|
||||
CLASS_TRAIT(UnionTrait);
|
||||
CLASS_TRAIT(TupleTrait);
|
||||
|
||||
//
|
||||
// An empty class to attach semantic information to each class in
|
||||
// the parse-tree. In practice, each parser-tree 'classname' shall
|
||||
// implement a member:
|
||||
//
|
||||
// Semantic<classname> * s = nullptr;
|
||||
//
|
||||
// The actual implementation of each Sema<classname> will be provided
|
||||
// later thus allowing the parser to be build without an dependency
|
||||
// with the Sema library
|
||||
//
|
||||
template <typename T> struct Semantic {
|
||||
Semantic(T*) {}
|
||||
};
|
||||
|
||||
// Most non-template classes in this file use these default definitions
|
||||
// for their move constructor and move assignment operator=, and disable
|
||||
// their copy constructor and copy assignment operator=.
|
||||
|
@ -47,7 +63,8 @@ CLASS_TRAIT(TupleTrait);
|
|||
classname(classname &&) = default; \
|
||||
classname &operator=(classname &&) = default; \
|
||||
classname(const classname &) = delete; \
|
||||
classname &operator=(const classname &) = delete
|
||||
classname &operator=(const classname &) = delete; \
|
||||
Semantic<classname> * s = nullptr \
|
||||
|
||||
// Almost all classes in this file have no default constructor.
|
||||
#define BOILERPLATE(classname) \
|
||||
|
@ -64,6 +81,7 @@ CLASS_TRAIT(TupleTrait);
|
|||
classname &operator=(const classname &) { return *this; }; \
|
||||
classname &operator=(classname &&) { return *this; }; \
|
||||
using EmptyTrait = std::true_type; \
|
||||
Semantic<classname> * s = nullptr ; \
|
||||
}
|
||||
|
||||
// Many classes below simply wrap a std::variant<> discriminated union,
|
||||
|
@ -300,6 +318,14 @@ template<typename A> struct Statement {
|
|||
A statement;
|
||||
};
|
||||
|
||||
// A wrapper for xzy-stmt productions that are statements, so that
|
||||
// source provenances and labels have a uniform representation.
|
||||
template<typename A> struct Statement : public StatementBase {
|
||||
Statement(Provenance &&at, std::optional<long> &&lab, bool &&accept, A &&s)
|
||||
: StatementBase(std::move(at),std::move(lab),std::move(accept)) , statement(std::move(s)) {}
|
||||
A statement;
|
||||
};
|
||||
|
||||
// Error recovery marker
|
||||
EMPTY_CLASS(ErrorRecovery);
|
||||
|
||||
|
|
|
@ -18,3 +18,16 @@ target_link_libraries( test-type
|
|||
FortranParser
|
||||
FlangSemantics
|
||||
)
|
||||
|
||||
######## test-sema ##########
|
||||
|
||||
add_executable( test-sema
|
||||
test-sema.cc
|
||||
sema-impl.cc
|
||||
)
|
||||
|
||||
target_link_libraries( test-sema
|
||||
FlangParser
|
||||
FlangSemantics
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue