[flang] Very early semantic analysis.

Original-commit: flang-compiler/f18@862eca9c5b
Reviewed-on: https://github.com/flang-compiler/f18/pull/24
Tree-same-pre-rewrite: false
This commit is contained in:
Stephane Chauveau 2018-03-02 16:16:36 +01:00 committed by GitHub
parent cb463ab27b
commit 20ac31fa9f
14 changed files with 1843 additions and 245 deletions

View File

@ -13,8 +13,8 @@ namespace semantics {
// //
// Also, identifiers are immutable and are never destroyed. // Also, identifiers are immutable and are never destroyed.
// //
// The comparison of two 'Identifier*' returns true iff their // The comparison of two 'Identifier*' is expected to return
// name are identical. // true iff their name are identical.
// //
class Identifier class Identifier
{ {
@ -25,7 +25,7 @@ private:
private: private:
std::string name_ ; std::string name_ ;
public: public:
const std::string & name() { return name_ ; } std::string name() const { return name_ ; }
static const Identifier *get(std::string n) ; static const Identifier *get(std::string n) ;
}; };

View File

@ -0,0 +1,16 @@
SEMA_DEFINE_SCOPE(SK_SYSTEM ,"System")
SEMA_DEFINE_SCOPE(SK_GLOBAL ,"Global")
SEMA_DEFINE_SCOPE(SK_PROGRAM ,"Program")
SEMA_DEFINE_SCOPE(SK_MODULE ,"Module")
SEMA_DEFINE_SCOPE(SK_SUBMODULE ,"Submodule")
SEMA_DEFINE_SCOPE(SK_FUNCTION ,"Function")
SEMA_DEFINE_SCOPE(SK_SUBROUTINE ,"Subroutine")
SEMA_DEFINE_SCOPE(SK_BLOCKDATA ,"Blockdata")
SEMA_DEFINE_SCOPE(SK_USE_MODULE ,"Used Module")
SEMA_DEFINE_SCOPE(SK_BLOCK ,"Block")
SEMA_DEFINE_SCOPE(SK_DERIVED ,"Derived Type")
SEMA_DEFINE_SCOPE(SK_INTERFACE ,"Interface")
#undef SEMA_DEFINE_SCOPE

View File

@ -4,232 +4,15 @@
#include "flang/Sema/Identifier.h" #include "flang/Sema/Identifier.h"
#include "flang/Sema/Type.h" #include "flang/Sema/Type.h"
#include "flang/Sema/Attr.h" #include "flang/Sema/Attr.h"
#include "flang/Sema/Symbol.h"
#include <vector> #include <vector>
#include <cassert>
namespace Fortran { namespace Fortran::semantics {
namespace semantics {
class Scope ; 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
//
//
// ======================================================= // =======================================================
// //
@ -238,7 +21,7 @@ private:
// For now, there is only one class. Not sure if we need more. // For now, there is only one class. Not sure if we need more.
// Of course, there are multiple kinds of scopes in Fortran and // Of course, there are multiple kinds of scopes in Fortran and
// they all behave slightly differently. However, a single Scope class // they all behave slightly differently. However, a single Scope class
// that adjust its behavior according to its ScopeKind is probably // that adjust its behavior according to its Kind is probably
// easier to implement than a dozen of classes. // easier to implement than a dozen of classes.
// //
// Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they // Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they
@ -248,7 +31,9 @@ class Scope
{ {
public: public:
enum ScopeKind { enum Kind {
SK_SYSTEM, // the unique scope associated to the system (and providing intrinsic procedures)
SK_GLOBAL, // the unique global scope containing all units
SK_PROGRAM, // a scope associated to a PROGRAM SK_PROGRAM, // a scope associated to a PROGRAM
SK_MODULE, // a scope associated to a MODULE SK_MODULE, // a scope associated to a MODULE
SK_SUBMODULE, // a scope associated to a SUBMODULE SK_SUBMODULE, // a scope associated to a SUBMODULE
@ -263,9 +48,32 @@ public:
// ... and probably more to come // ... and probably more to come
} ; } ;
public:
Scope(Kind k, Scope *p, Symbol *s) ;
~Scope() ;
private: private:
ScopeKind kind_ ; Kind kind_ ;
//
// For most ranks, id contains a unique identifier that is increased
// each time a new Scope is created.
//
// The System and Global scope respectively have 0 and 1
//
// Scopes that are created within the System scope (i.e. intrinsics)
// are assigned a negative id.
//
// Scopes that are created within the Global scope (i.e. modules or
// user code) are assigned an id larger than 1
//
// The unique System and Global scopes do not use that field to store
// their respectived id (which are hardcoded). Instead they use it
// to count the number of scopes already created.
//
int id_or_counter_ ;
// For scopes associated to a name (so most of them except BLOCK // For scopes associated to a name (so most of them except BLOCK
// and unnamed INTERFACE) provide the associated symbol in the // and unnamed INTERFACE) provide the associated symbol in the
@ -368,15 +176,77 @@ private:
public: public:
// The system scope is the scope that provides intrinsic subprograms Kind getKind() const { return kind_; }
Scope * getSystemScope() ; const Scope * getParentScope() const { return parent_scope_; }
Scope * getParentScope() { return parent_scope_; }
Symbol *lookup(const Identifier *name) ; //
// The system scope is the scope that provides all intrinsics definitions
//
const Scope * getSystemScope() const;
//
// The Global scope is the scope that is immediately below the System scope.
//
// It contains the symbols for the Program unit (PROGRAM, MODULE, FUNCTION,
// SUBROUTINE and BLOCKDATA)
//
// However, that scope is not standard in the sense that it requires a
// dedicated lookup to find its symbols.
// This is because the program units do not see each others by default
//
// This is also the scope that owns all USE_MODULE.
//
//
Scope * getGlobalScope() ;
const Scope * getGlobalScope() const;
// Look for a symbol locally and in the host scope (if any)
Symbol *Lookup(const Identifier *name);
const Symbol *Lookup(const Identifier *name) const;
// Look for a symbol locally (do not explore the host scope).
//
Symbol *LookupLocal(const Identifier *name);
const Symbol *LookupLocal(const Identifier *name) const;
//
// Lookup for a Program Unit by name
//
Symbol *LookupProgramUnit(const Identifier *name) ;
const Symbol *LookupProgramUnit(const Identifier *name) const;
//
// Lookup for a known module.
//
// The result is either null or a scope of kind SK_USE_MODULE.
//
Symbol *LookupModule(const Identifier *name);
const Symbol *LookupModule(const Identifier *name) const;
//
//
//
int getId() const ;
//
// Add a symbol to the scope.
//
void add(Symbol *s) ;
public:
std::string toString(void) ;
public:
// a temporary method to fail
void fail(const std::string &msg) const ;
}; };
} // of namespace semantics } // of namespace Fortran::semantics
} // of namespace Fortran
#endif #endif

View File

@ -0,0 +1,21 @@
SEMA_DEFINE_SYMBOL(BlockDataSymbol)
SEMA_DEFINE_SYMBOL(CommonSymbol)
SEMA_DEFINE_SYMBOL(ConstructSymbol)
SEMA_DEFINE_SYMBOL(DerivedTypeSymbol)
SEMA_DEFINE_SYMBOL(DummyArgumentSymbol)
SEMA_DEFINE_SYMBOL(ExternalSymbol)
SEMA_DEFINE_SYMBOL(FunctionSymbol)
SEMA_DEFINE_SYMBOL(ImportedSymbol)
SEMA_DEFINE_SYMBOL(InterfaceSymbol)
SEMA_DEFINE_SYMBOL(MemberSymbol)
SEMA_DEFINE_SYMBOL(ModuleSymbol)
SEMA_DEFINE_SYMBOL(NamelistSymbol)
SEMA_DEFINE_SYMBOL(ParameterSymbol)
SEMA_DEFINE_SYMBOL(ProgramSymbol)
SEMA_DEFINE_SYMBOL(SubSymbol)
SEMA_DEFINE_SYMBOL(SubroutineSymbol)
SEMA_DEFINE_SYMBOL(TemporarySymbol)
SEMA_DEFINE_SYMBOL(UsedSymbol)
SEMA_DEFINE_SYMBOL(VariableSymbol)
#undef SEMA_DEFINE_SYMBOL

View File

@ -0,0 +1,291 @@
#ifndef FLANG_SEMA_SYMBOL_H
#define FLANG_SEMA_SYMBOL_H
#include "flang/Sema/Identifier.h"
#include "flang/Sema/Type.h"
#include "flang/Sema/Attr.h"
#include <vector>
namespace Fortran::semantics {
class Scope ;
// Forward declaration of all Symbol classes
#define SEMA_DEFINE_SYMBOL(Classname) class Classname;
#include "flang/Sema/Symbol.def"
#undef SEMA_DEFINE_SYMBOL
// Describe a symbol.
class Symbol {
public:
enum ClassId {
#define SEMA_DEFINE_SYMBOL(Classname) Classname##Id,
#include "flang/Sema/Symbol.def"
#undef SEMA_DEFINE_SYMBOL
// ... 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, const Identifier *name) ;
public:
// For now, provide some toXXXX() member to perform the dynamic cast.
//
//
#define SEMA_DEFINE_SYMBOL(Classname) \
Classname *to##Classname(void) { \
return (cid_==Classname##Id) ? (Classname*)this : nullptr ; \
} \
const Classname *to##Classname(void) const { \
return (cid_==Classname##Id) ? (const Classname*)this : nullptr ; \
}
#include "flang/Sema/Symbol.def"
#undef SEMA_DEFINE_SYMBOL
public:
Scope *owner() { return owner_; }
const Identifier * name() { return name_ ; }
bool Match(const Identifier *ident) { return ident == name_ ; }
std::string toString() const { return 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, const 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 the name of a Module
//
class ModuleSymbol : public Symbol {
public:
ModuleSymbol(Scope * owner, const Identifier *name) : Symbol(ProgramSymbolId,owner,name) {}
private:
};
//
// A symbol representing the name of a Program
//
class ProgramSymbol : public Symbol {
public:
ProgramSymbol(Scope * owner, const Identifier *name) : Symbol(ProgramSymbolId,owner,name) {}
private:
};
//
// A symbol representing the name of a Program
//
class BlockDataSymbol : public Symbol {
public:
BlockDataSymbol(Scope * owner, const Identifier *name) : Symbol(BlockDataSymbolId,owner,name) {}
private:
};
// A symbol representing a parameter whose value can be queried.
//
//
class ParameterSymbol : public Symbol {
public:
ParameterSymbol(Scope * owner, const Identifier *name) : Symbol(ParameterSymbolId,owner,name) {}
};
// A symbol representing an EXTERNAL function.
class ExternalSymbol : public Symbol {
public:
ExternalSymbol(Scope * owner, const 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, const Identifier *name) : Symbol(VariableSymbolId,owner,name) {}
};
// A symbol representing a dummy argument.
class DummyArgumentSymbol : public Symbol {
public:
DummyArgumentSymbol(Scope * owner, const 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, const Identifier *name) : Symbol(CommonSymbolId,owner,name) {}
private:
// The content of the common section.
std::vector<const 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 SubroutineSymbol : public Symbol {
public:
SubroutineSymbol(Scope * owner, const Identifier *name) : Symbol(SubroutineSymbolId,owner,name) {}
private:
Scope * inner_scope_ ;
std::vector<DummyArgumentSymbol *> args ;
};
class FunctionSymbol : public Symbol {
public:
FunctionSymbol(Scope * owner, const Identifier *name) : Symbol(FunctionSymbolId,owner,name) {}
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, const 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, const 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, const 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, const 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, const Identifier *name) : Symbol(DerivedTypeSymbolId,owner,name) {}
private:
Scope * inner_scope_ ;
};
//
// A symbol representing a member in a Derived type
//
class MemberSymbol : public Symbol {
public:
MemberSymbol(Scope * owner, const Identifier *name) : Symbol(MemberSymbolId,owner,name) {}
private:
};
//
// A symbol representing a namelist-group
//
class NamelistSymbol : public Symbol {
public:
NamelistSymbol(Scope * owner, const Identifier *name) : Symbol(NamelistSymbolId,owner,name) {}
private:
std::vector<Symbol*> content_ ;
};
//
// A symbol representing the name of a construct
//
class ConstructSymbol : public Symbol {
public:
ConstructSymbol(Scope * owner, const Identifier *name) : Symbol(ConstructSymbolId,owner,name) {}
private:
};
} // of namespace
// NOTE: Support for EQUIVALENCE ...
#endif

View File

@ -2,6 +2,7 @@
add_library( FlangSemantics add_library( FlangSemantics
Identifier.cc Identifier.cc
Scope.cc Scope.cc
Symbol.cc
Type.cc # ../semantics/type.cc Type.cc # ../semantics/type.cc
Attr.cc # ../semantics/attr.cc Attr.cc # ../semantics/attr.cc
) )

View File

@ -3,11 +3,289 @@
#include "flang/Sema/Scope.h" #include "flang/Sema/Scope.h"
#include "flang/Sema/Identifier.h" #include "flang/Sema/Identifier.h"
using namespace Fortran; #include <cassert>
using namespace semantics;
Symbol *
Scope::lookup(const Identifier *name) namespace Fortran::semantics {
Scope::Scope(Kind k, Scope *p, Symbol *s) :
kind_(k),
id_or_counter_(0),
self_(s),
parent_scope_(p),
host_scope_(p)
{ {
switch(k) {
case SK_SYSTEM:
assert( parent_scope_ == NULL) ;
id_or_counter_ = -1 ;
break ;
case SK_GLOBAL:
assert( parent_scope_->kind_ == SK_SYSTEM) ;
id_or_counter_ = 2 ;
break ;
case SK_PROGRAM:
assert( parent_scope_->kind_ == SK_GLOBAL) ;
if( self_ ) {
assert( self_->owner() == parent_scope_) ;
}
break;
case SK_MODULE:
assert( parent_scope_->kind_ == SK_GLOBAL) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_) ;
break;
case SK_SUBMODULE:
assert( parent_scope_->kind_ == SK_GLOBAL) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_) ;
break;
case SK_FUNCTION:
assert( parent_scope_->kind_ == SK_GLOBAL ||
parent_scope_->kind_ == SK_PROGRAM ||
parent_scope_->kind_ == SK_MODULE ||
parent_scope_->kind_ == SK_FUNCTION ||
parent_scope_->kind_ == SK_SUBROUTINE ||
parent_scope_->kind_ == SK_INTERFACE ||
parent_scope_->kind_ == SK_USE_MODULE
) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_) ;
break;
case SK_SUBROUTINE:
assert( parent_scope_->kind_ == SK_GLOBAL ||
parent_scope_->kind_ == SK_PROGRAM ||
parent_scope_->kind_ == SK_MODULE ||
parent_scope_->kind_ == SK_FUNCTION ||
parent_scope_->kind_ == SK_SUBROUTINE ||
parent_scope_->kind_ == SK_INTERFACE ||
parent_scope_->kind_ == SK_USE_MODULE
) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_) ;
break;
case SK_BLOCKDATA:
assert( parent_scope_->kind_ == SK_GLOBAL) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_) ;
break;
case SK_USE_MODULE:
assert( parent_scope_->kind_ == SK_GLOBAL) ;
assert( self_ ) ;
assert( self_->owner() == parent_scope_ ) ;
break;
case SK_BLOCK:
assert( parent_scope_->kind_ == SK_PROGRAM ||
parent_scope_->kind_ == SK_BLOCK ||
parent_scope_->kind_ == SK_FUNCTION ||
parent_scope_->kind_ == SK_SUBROUTINE
) ;
if ( self_ ) {
assert( self_->owner() == parent_scope_ ) ;
assert( self_->toConstructSymbol() ) ;
}
break;
case SK_DERIVED:
assert( parent_scope_->kind_ == SK_PROGRAM ||
parent_scope_->kind_ == SK_MODULE ||
parent_scope_->kind_ == SK_FUNCTION ||
parent_scope_->kind_ == SK_SUBROUTINE ||
parent_scope_->kind_ == SK_INTERFACE ||
parent_scope_->kind_ == SK_USE_MODULE
) ;
break;
case SK_INTERFACE:
assert( parent_scope_->kind_ == SK_PROGRAM ||
parent_scope_->kind_ == SK_MODULE ||
parent_scope_->kind_ == SK_FUNCTION ||
parent_scope_->kind_ == SK_SUBROUTINE ||
parent_scope_->kind_ == SK_USE_MODULE
) ;
// Within an interface, the symbols must be explicitly imported
// from the parent scope so there is no default host scope.
host_scope_ = NULL ;
break;
default:
fail("unknown scope kind") ;
break;
} // of switch
// Figure out the ID
if (id_or_counter_ == 0) {
if ( Scope *global_scope = getGlobalScope() ) {
// Within the global scope, the IDs are growing
id_or_counter_ = global_scope->id_or_counter_++ ;
} else {
// Within the system scope, the IDs are shrinking (in negative)
Scope *system_scope = const_cast<Scope *>( getSystemScope() ) ;
id_or_counter_ = system_scope->id_or_counter_-- ;
}
}
} }
Scope::~Scope() {
}
void
Scope::fail(const std::string &msg) const
{
std::cerr << "FATAL Scope: " << msg << "\n" ;
exit(1) ;
}
const Scope *
Scope::getSystemScope() const
{
for ( const Scope * scope = this ; scope ; scope = scope->parent_scope_) {
if ( scope->kind_ == SK_SYSTEM )
return scope;
}
fail("System scope not found") ;
return nullptr;
}
Scope *
Scope::getGlobalScope()
{
for ( auto scope = this ; scope ; scope = scope->parent_scope_) {
if ( scope->kind_ == SK_GLOBAL )
return scope;
}
return nullptr;
}
const Scope *
Scope::getGlobalScope() const
{
return const_cast<Scope*>(this)->getGlobalScope() ;
}
Symbol *
Scope::Lookup(const Identifier *name)
{
if ( kind_ == SK_GLOBAL )
return parent_scope_->Lookup(name) ;
// TODO
return nullptr ;
}
Symbol *
Scope::LookupLocal(const Identifier *name)
{
if ( kind_ == SK_GLOBAL )
return nullptr ;
auto & entries{this->entries_} ;
for (auto it = entries.rbegin() ; it != entries.rend() ; ++it) {
Symbol *s = *it ;
if ( s->Match(name) )
return s ;
}
return nullptr ;
}
Symbol *
Scope::LookupProgramUnit(const Identifier *name)
{
Scope * scope = this->getGlobalScope() ;
auto & entries{scope->entries_} ;
for (auto it = entries.rbegin() ; it != entries.rend() ; ++it) {
Symbol *s = *it ;
std::cerr << "found " << s << " " << s->name() << "\n" ;
if ( s->toModuleSymbol() ||
s->toProgramSymbol() ||
s->toSubroutineSymbol() ||
s->toFunctionSymbol() ||
s->toBlockDataSymbol()
)
{
if ( s->Match(name) )
return s ;
}
}
return nullptr ;
}
const Symbol *
Scope::LookupProgramUnit(const Identifier *name) const
{
return const_cast<Scope*>(this)->LookupProgramUnit(name) ;
}
Symbol *
Scope::LookupModule(const Identifier *name)
{
return nullptr ;
}
const Symbol *
Scope::LookupModule(const Identifier *name) const
{
return const_cast<Scope*>(this)->LookupModule(name) ;
}
int Scope::getId(void) const
{
if (this->kind_ == SK_SYSTEM)
return 0;
else if (this->kind_ == SK_GLOBAL)
return 1;
else
return id_or_counter_;
}
void Scope::add(Symbol *s)
{
assert(s) ;
assert(s->owner()==this) ;
entries_.push_back(s);
}
std::string Scope::toString(void)
{
const char * info;
switch(kind_) {
#define SEMA_DEFINE_SCOPE(KIND,INFO) case KIND: info=INFO ; break;
#include "flang/Sema/Scope.def"
#undef SEMA_DEFINE_SCOPE
default:
info = "???????" ;
}
if ( self_ ) {
return std::string("#") + std::to_string(getId()) + " " + info + " (" + self_->toString() + ")" ;
} else {
return std::string("#") + std::to_string(getId()) + " " + info + " (***UNNAMED***)" ;
}
}
} // of namespace

21
flang/lib/Sema/Symbol.cc Normal file
View File

@ -0,0 +1,21 @@
#include "flang/Sema/Symbol.h"
#include "flang/Sema/Scope.h"
#include "flang/Sema/Identifier.h"
#include <cassert>
namespace Fortran::semantics {
Symbol::Symbol(ClassId cid, Scope * owner, const Identifier *name) :
cid_(cid),
owner_(owner),
name_(name)
{
owner->add(this);
}
} // of namespace

View File

@ -52,9 +52,14 @@ CLASS_TRAIT(TupleTrait);
// later thus allowing the parser to be build without an dependency // later thus allowing the parser to be build without an dependency
// with the Sema library // with the Sema library
// //
namespace Fortran {
namespace semantics {
template <typename T> struct Semantic { template <typename T> struct Semantic {
Semantic(T*) {} Semantic(T*) {}
}; };
}
}
// Most non-template classes in this file use these default definitions // Most non-template classes in this file use these default definitions
// for their move constructor and move assignment operator=, and disable // for their move constructor and move assignment operator=, and disable
@ -64,7 +69,7 @@ template <typename T> struct Semantic {
classname &operator=(classname &&) = default; \ classname &operator=(classname &&) = default; \
classname(const classname &) = delete; \ classname(const classname &) = delete; \
classname &operator=(const classname &) = delete; \ classname &operator=(const classname &) = delete; \
Semantic<classname> * s = nullptr \ Fortran::semantics::Semantic<classname> * s = nullptr \
// Almost all classes in this file have no default constructor. // Almost all classes in this file have no default constructor.
#define BOILERPLATE(classname) \ #define BOILERPLATE(classname) \
@ -81,7 +86,7 @@ template <typename T> struct Semantic {
classname &operator=(const classname &) { return *this; }; \ classname &operator=(const classname &) { return *this; }; \
classname &operator=(classname &&) { return *this; }; \ classname &operator=(classname &&) { return *this; }; \
using EmptyTrait = std::true_type; \ using EmptyTrait = std::true_type; \
Semantic<classname> * s = nullptr ; \ Fortran::semantics::Semantic<classname> * s = nullptr ; \
} }
// Many classes below simply wrap a std::variant<> discriminated union, // Many classes below simply wrap a std::variant<> discriminated union,
@ -2748,6 +2753,7 @@ struct MainProgram {
ExecutionPart, std::optional<InternalSubprogramPart>, ExecutionPart, std::optional<InternalSubprogramPart>,
Statement<EndProgramStmt>> Statement<EndProgramStmt>>
t; t;
enum { PROG, SPEC, EXEC, INTERNAL, END } ;
}; };
// R1405 module-stmt -> MODULE module-name // R1405 module-stmt -> MODULE module-name
@ -3072,6 +3078,7 @@ struct FunctionSubprogram {
std::tuple<Statement<FunctionStmt>, SpecificationPart, ExecutionPart, std::tuple<Statement<FunctionStmt>, SpecificationPart, ExecutionPart,
std::optional<InternalSubprogramPart>, Statement<EndFunctionStmt>> std::optional<InternalSubprogramPart>, Statement<EndFunctionStmt>>
t; t;
enum { FUNC, SPEC, EXEC, INTERNAL, END } ;
}; };
// R1534 subroutine-subprogram -> // R1534 subroutine-subprogram ->

View File

@ -24,7 +24,6 @@ target_link_libraries( test-type
add_executable( test-sema add_executable( test-sema
test-sema.cc test-sema.cc
sema-impl.cc sema-impl.cc
foobar.cc
) )
target_link_libraries( test-sema target_link_libraries( test-sema

View File

@ -0,0 +1,148 @@
// ============ Forward declarations ==============
template <typename T> inline const auto & GET_VALUE( const T &x) ;
template <typename T> inline auto & GET_VALUE( T &x) ;
template <typename T> inline bool HAS_VALUE( const T &x) ;
template <typename T> inline bool HAS_VALUE( T &x) ;
#ifndef IGNORE_Scalar
template <typename T> inline const auto & GET_VALUE( const psr::Scalar<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Scalar<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Scalar<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Scalar<T> &x) ;
#endif
#ifndef IGNORE_Constant
template <typename T> inline const auto & GET_VALUE( const psr::Constant<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Constant<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Constant<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Constant<T> &x) ;
#endif
#ifndef IGNORE_Integer
template <typename T> inline const auto & GET_VALUE( const psr::Integer<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Integer<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Integer<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Integer<T> &x) ;
#endif
#ifndef IGNORE_Logical
template <typename T> inline const auto & GET_VALUE( const psr::Logical<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Logical<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Logical<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Logical<T> &x) ;
#endif
#ifndef IGNORE_DefaultChar
template <typename T> inline const auto & GET_VALUE( const psr::DefaultChar<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::DefaultChar<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::DefaultChar<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::DefaultChar<T> &x) ;
#endif
#ifndef IGNORE_Indirection
template <typename T> inline const auto & GET_VALUE( const psr::Indirection<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Indirection<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Indirection<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Indirection<T> &x) ;
#endif
#ifndef IGNORE_Statement
template <typename T> inline const auto & GET_VALUE( const psr::Statement<T> &x) ;
template <typename T> inline auto & GET_VALUE( psr::Statement<T> &x) ;
template <typename T> inline bool HAS_VALUE( const psr::Statement<T> &x) ;
template <typename T> inline bool HAS_VALUE( psr::Statement<T> &x) ;
#endif
#ifndef IGNORE_optional
template <typename T> inline const auto & GET_VALUE( const std::optional<T> &x) ;
template <typename T> inline auto & GET_VALUE( std::optional<T> &x) ;
template <typename T> inline bool HAS_VALUE( const std::optional<T> &x) ;
template <typename T> inline bool HAS_VALUE( std::optional<T> &x) ;
#endif
// =========== Actual implementation of GET_VALUE() and HAS_VALUE() ==============================
template <typename T> inline const auto & GET_VALUE( const T &x) { return x ;}
template <typename T> inline auto & GET_VALUE( T &x) { return x ;}
template <typename T> inline bool HAS_VALUE( const T &x) { return true; }
template <typename T> inline bool HAS_VALUE( T &x) { return true; }
#ifndef IGNORE_Scalar
template <typename T> inline const auto & GET_VALUE( const psr::Scalar<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline auto & GET_VALUE( psr::Scalar<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( const psr::Scalar<T> &x) { return HAS_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( psr::Scalar<T> &x) { return HAS_VALUE(x.thing) ;}
#endif
#ifndef IGNORE_Constant
template <typename T> inline const auto & GET_VALUE( const psr::Constant<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline auto & GET_VALUE( psr::Constant<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( const psr::Constant<T> &x) { return HAS_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( psr::Constant<T> &x) { return HAS_VALUE(x.thing) ;}
#endif
#ifndef IGNORE_Integer
template <typename T> inline const auto & GET_VALUE( const psr::Integer<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline auto & GET_VALUE( psr::Integer<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( const psr::Integer<T> &x) { return HAS_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( psr::Integer<T> &x) { return HAS_VALUE(x.thing) ;}
#endif
#ifndef IGNORE_Logical
template <typename T> inline const auto & GET_VALUE( const psr::Logical<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline auto & GET_VALUE( psr::Logical<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( const psr::Logical<T> &x) { return HAS_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( psr::Logical<T> &x) { return HAS_VALUE(x.thing) ;}
#endif
#ifndef IGNORE_DefaultChar
template <typename T> inline const auto & GET_VALUE( const psr::DefaultChar<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline auto & GET_VALUE( psr::DefaultChar<T> &x) { return GET_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( const psr::DefaultChar<T> &x) { return HAS_VALUE(x.thing) ;}
template <typename T> inline bool HAS_VALUE( psr::DefaultChar<T> &x) { return HAS_VALUE(x.thing) ;}
#endif
#ifndef IGNORE_Indirection
template <typename T> inline const auto & GET_VALUE( const psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
template <typename T> inline auto & GET_VALUE( psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
template <typename T> inline bool HAS_VALUE( const psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
template <typename T> inline bool HAS_VALUE( psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
#endif
#ifndef IGNORE_Statement
template <typename T> inline const auto & GET_VALUE( const psr::Statement<T> &x) { return GET_VALUE(x.statement) ;}
template <typename T> inline auto & GET_VALUE( psr::Statement<T> &x) { return GET_VALUE(x.statement) ;}
template <typename T> inline bool HAS_VALUE( const psr::Statement<T> &x) { return HAS_VALUE(x.statement) ;}
template <typename T> inline bool HAS_VALUE( psr::Statement<T> &x) { return HAS_VALUE(x.statement) ;}
#endif
#ifndef IGNORE_optional
template <typename T> inline const auto & GET_VALUE( const std::optional<T> &x) { return GET_VALUE(x.value()) ; }
template <typename T> inline auto & GET_VALUE( std::optional<T> &x) { return GET_VALUE(x.value()) ; }
template <typename T> inline bool HAS_VALUE( const std::optional<T> &x) { return x.has_value() && HAS_VALUE(*x); }
template <typename T> inline bool HAS_VALUE( std::optional<T> &x) { return x.has_value() && HAS_VALUE(*x); }
#endif
template <typename T> inline auto GET_OPT_VALUE(const T &x) {
if ( HAS_VALUE(x) ) {
return & GET_VALUE(x) ;
} else {
return decltype(&GET_VALUE(x)){0} ;
}
}
template <typename T> inline auto GET_OPT_VALUE(T &x) {
if ( HAS_VALUE(x) ) {
return & GET_VALUE(x) ;
} else {
return decltype(&GET_VALUE(x)){0} ;
}
}
#undef GET_VALUE
#undef HAS_VALUE
#undef GET_OPT_VALUE

119
flang/tools/f18/GetValue.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef FLANG_SUPPORT_GET_VALUE_H
#undef IGNORE_optional
#undef IGNORE_Statement
#undef IGNORE_Scalar
#undef IGNORE_Constant
#undef IGNORE_Indirection
#undef IGNORE_Logical
#undef IGNORE_DefaultChar
// Each include of "GetValue.def" provides a set of helper functions
// whose names are specified by the macros GET_VALUE, HAS_VALUE and
// GET_OPT_VALUE.
//
// The purpose of those function is to provide easier access to the
// parse-tree by ignoring some wrapper classes/
//
//
// GET_VALUE(x) provides a reference to the value that x is holding.
//
// The following wrapper classes are ignored unless the corresponding
// IGNORES_xxx macro is defined.
//
// Scalar<T>
// Constant<T>
// Integer<T>
// Logical<T>
// DefaultChar<T>
// Indirection<T>
// Statement<T>
// std::optional<T>
//
//
// HAS_VALUE(x) return true if it is legal to call GET_VALUE(x) in case x
// contains some std::optional<T>
//
// Example:
// Constant<std::optional<Indirection<std::optional<int>>>> &x = ... ;
// if ( HasValue(x) ) {
// const int &v = getValue(x) ;
// ...
// }
//
// GET_OPT_VALUE(T &x) is equivalent to
//
// HAS_VALUE(x) ? &GET_VALUE(x) : (Type*) nullptr
//
// here Type is the type of GET_VALUE(x)
//
// Example:
//
// const Scalar<optional<Integer<Expr>>> & z = ...
// const Expr *ptr_z = GET_OPT_VALUE(z) ;
// if ( ptr_z ) {
// ... do something with *ptr_z %
// }
//
// This is the default version that handles all wrapper
#define GET_VALUE GetValue
#define HAS_VALUE HasValue
#define GET_OPT_VALUE GetOptValue
#include "GetValue.def"
// HAS_VALUE and GET_OPT_VALUE are only interesting when
// std::optional is not ignored.
// We need to give a name to the function but they are pretty much useless
#define IGNORE_optional
#define GET_VALUE GetOptionalValue
#define HAS_VALUE HasOptionalValue__
#define GET_OPT_VALUE GetOptValue__
#include "GetValue.def"
#undef IGNORE_optional
#define IGNORE_Statement
#define GET_VALUE GetStatementValue
#define HAS_VALUE HasStatementValue
#define GET_OPT_VALUE GetOptStatementValue
#include "GetValue.def"
#undef IGNORE_Statement
#define IGNORE_Scalar
#define GET_VALUE GetScalarValue
#define HAS_VALUE HasScalarValue
#define GET_OPT_VALUE GetOptScalarValue
#include "GetValue.def"
#undef IGNORE_Scalar
#define IGNORE_Constant
#define GET_VALUE GetConstantValue
#define HAS_VALUE HasConstantValue
#define GET_OPT_VALUE GetOptConstantValue
#include "GetValue.def"
#undef IGNORE_Constant
#define IGNORE_Indirection
#define GET_VALUE GetIndirectionValue
#define HAS_VALUE HasIndirectionValue
#define GET_OPT_VALUE GetOptIndirectionValue
#include "GetValue.def"
#undef IGNORE_Indirection
#define IGNORE_Logical
#define GET_VALUE GetLogicalValue
#define HAS_VALUE HasLogicalValue
#define GET_OPT_VALUE GetOptLogicalValue
#include "GetValue.def"
#undef IGNORE_Logical
#define IGNORE_DefaultChar
#define GET_VALUE GetDefaultCharValue
#define HAS_VALUE HasDefaultCharValue
#define GET_OPT_VALUE GetOptDefaultCharValue
#include "GetValue.def"
#undef IGNORE_DefaultChar
#endif

View File

@ -0,0 +1,762 @@
#include "../../lib/parser/format-specification.h"
#include "../../lib/parser/idioms.h"
#include "../../lib/parser/indirection.h"
#include "../../lib/parser/parse-tree-visitor.h"
#include "../../lib/parser/parse-tree.h"
#include "flang/Sema/Scope.h"
#include <vector>
#include <map>
#include <stack>
namespace psr = Fortran::parser ;
namespace sema = Fortran::semantics ;
#include "GetValue.h"
#define TODO do { std::cerr << "NOT YET IMPLEMENTED " << __FILE__ << ":" << __LINE__ << "\n" ; exit(1) ; } while(0)
#define CONSUME(x) do { (void)x ; } while(0)
#define TRACE_CALL() do { std::cerr << "*** call " << __PRETTY_FUNCTION__ << "\n" ; } while(0)
#define TRACE(msg) do { std::cerr << msg << "\n" ; } while(0)
#define FAIL(msg) do { std::cerr << "FATAL " << __FILE__ << ":" << __LINE__ << ":\n " << msg << "\n" ; exit(1) ; } while(0)
// Initialize the pointer used to attach semantic information to each parser-tree node
//
// Ideally, the function should be called once at the begining of the corresponding Pre()
// member in Pass1. However, in case a forward reference to the Semantic<> data would be
// required, no error will occur when setting strict=false.
//
template <typename T> Fortran::semantics::Semantic<T> & initSema(const T &node, bool strict=true) {
if (node.s) {
if (strict)
FAIL( "Duplicate call of " << __PRETTY_FUNCTION__ ) ;
else
return *(node.s);
}
auto s = new Fortran::semantics::Semantic<T>( const_cast<T*>(&node) ) ;
const_cast<T&>(node).s = s;
return *s ;
}
// Retreive the semantic information attached to a parser-tree node
template <typename T> Fortran::semantics::Semantic<T> & getSema(const T &node) {
assert(node.s) ;
return *(node.s) ;
}
#if 1
#include "flang/Sema/Scope.h"
#include <type_traits>
#include <iostream>
#include <cstdlib>
#include <iostream>
#include <list>
#include <optional>
#include <sstream>
#include <string>
#include <stddef.h>
namespace Fortran::semantics
{
using namespace Fortran::parser ;
template <typename ParserClass>
Semantic<ParserClass> &
sema(ParserClass *node)
{
if ( node->s == NULL ) {
node->s = new Semantic<ParserClass>(node) ;
}
return *(node->s);
}
template <typename P, typename T> P * vget( T & x ) {
return std::get_if<P>(x.u) ;
}
template <typename P, typename T> P * vget_i( T & x ) {
if ( auto v = std::get_if<Indirection<P>>(&x.u) ) {
return &(**v) ;
} else {
return nullptr ;
}
}
class LabelTable
{
private:
struct Entry {
// TODO: what to put here
Provenance loc;
};
std::map<int,Entry> entries_ ;
public:
void add( int label , Provenance loc )
{
if (label<1 || label>99999) return ; // Hoops!
auto &entry = entries_[label] ;
entry.loc = loc ;
}
bool find(int label, Provenance &loc)
{
auto it = entries_.find(label);
if( it != entries_.end()) {
Entry & entry{it->second};
loc = entry.loc;
return true;
}
return false;
}
void dump()
{
TRACE( "==== Label Table ====");
for ( int i=1 ; i<=99999 ;i++) {
Provenance p;
if ( find(i,p) ) {
TRACE( " #" << i << " at " << p.offset() ) ;
}
}
TRACE( "=====================");
}
}; // of class LabelTable
//
//
//
//
class LabelTableStack {
private:
std::stack<LabelTable*> stack ;
public:
LabelTable *PushLabelTable( LabelTable *table )
{
assert(table!=NULL);
stack.push(table);
return table;
}
void PopLabelTable( LabelTable *table )
{
assert( !stack.empty() ) ;
assert( stack.top() == table ) ;
stack.pop();
}
LabelTable & GetLabelTable() {
assert( !stack.empty() ) ;
return *stack.top() ;
}
bool NoLabelTable() {
return stack.empty() ;
}
}; // of class LabelTableStack
//////////////////////////////////////////////////////////////////
//
// Declare here the members of the Semantic<> information that will
// be attached to each parse-tree class. The default is an empty struct.
//
// Here are a few common fields
//
// Scope *scope_provider=0 ; // For each node providing a new scope
// int stmt_label=0 ; // For each node that consumes a label
//
//////////////////////////////////////////////////////////////////
#define DEFINE_SEMANTIC(Class) template <> struct Semantic<Class> { Semantic<Class>(Class *node) {}
#define END_SEMANTIC }
DEFINE_SEMANTIC(ProgramUnit)
END_SEMANTIC;
DEFINE_SEMANTIC(MainProgram)
Scope *scope_provider=0 ;
LabelTable *label_table=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(FunctionSubprogram)
Scope *scope_provider=0 ;
LabelTable *label_table=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(FunctionStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(EndFunctionStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(TypeDeclarationStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(AssignmentStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(PrintStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(ProgramStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(EndProgramStmt)
int stmt_label=0 ;
END_SEMANTIC;
DEFINE_SEMANTIC(ImplicitStmt)
int stmt_label=0 ;
END_SEMANTIC;
} // of namespace Fortran::semantics
//////////////////////////////////////////////////////////////////
using sema::Scope ;
using sema::LabelTable ;
using sema::LabelTableStack ;
namespace Fortran::parser {
class Pass1 : public LabelTableStack {
public:
Pass1() : current_label(-1)
{
system_scope = new Scope(Scope::SK_SYSTEM, nullptr, nullptr ) ;
unit_scope = new Scope(Scope::SK_GLOBAL, system_scope, nullptr) ;
current_scope = nullptr ;
}
public:
int current_label; // hold the value of a statement label until it get consumed (-1 means none)
Provenance current_label_loc;
Scope * system_scope ;
Scope * unit_scope ;
Scope * current_scope ;
Scope *EnterScope(Scope *s) {
assert(s) ;
assert(s->getParentScope() == current_scope ) ;
current_scope = s ;
TRACE("Entering Scope " << s->toString() );
return s;
}
void LeaveScope(Scope::Kind k) {
assert( current_scope->getKind() == k ) ;
TRACE("Leaving Scope " << current_scope->toString() );
current_scope = current_scope->getParentScope() ;
}
public:
public:
// Trace the location and label of any x with an accessible Statement<> in its type.
template <typename T> void TraceStatementInfo(const T &x) {
auto & s = GetStatementValue(x) ;
// TODO: compilation will fail is 's' is not of type Statement<...>.
// Do we have a type trait to detect Statement<>?
// if constexpr ( s is a Statement<> ) {
if ( s.label ) {
TRACE("stmt: loc=" << s.provenance.offset() << " label=" << s.label ) ;
} else {
TRACE("stmt: loc=" << s.provenance.offset() ) ;
}
// } else { TRACE("stmt: none") ; }
}
protected:
void
CheckStatementName( const sema::Identifier *expect , const sema::Identifier *found , std::string ctxt, bool required )
{
if ( expect ) {
if ( found && found != expect ) {
FAIL("Unexpected " << ctxt << " name '" << found->name() << "' (expected '" << expect->name() << "') ");
}
} else if ( found ) {
FAIL("Unexpected " << ctxt << " name '" << found->name() );
}
}
//
// Consume a label produced by a previous Statement.
// That function should be called exactly once between each pair of Statement<>.
//
// For now, the sole purpose of the 'stmt' is to provide a relevant type
// for __PRETTY_FUNCTION__ but the association <label,stmt> will eventually be stored
// somewhere.
//
// I still haven't figured out how to do that efficiently.
//
// There is obviously the problem of the type that could be solved using a huge
// std::variant but there is also the problem that node addresses are still subject
// to change where the tree requires a rewrite.
//
// Another way could be to store the label in the Semantic field of stmt.
// That is relatively easy to do but that does not really solve the
// problem of matching a label with its target.
//
template<typename T>
int ConsumeLabel(const T &stmt)
{
if ( current_label == -1 ) {
FAIL("No label to consume in " << __PRETTY_FUNCTION__ );
} else {
int label = current_label ;
current_label = -1 ;
auto &sema = getSema(stmt);
sema.stmt_label = label;
if ( label != 0 ) {
LabelTable & table = GetLabelTable() ;
Provenance old_loc ;
if ( table.find(label, old_loc) ) {
FAIL("Duplicate label " << label
<< "at @" << current_label_loc.offset()
<< "and @" << old_loc.offset() ) ;
} else {
table.add( label, current_label_loc) ;
}
}
return label ;
}
}
public:
template <typename T> bool Pre(const T &x) {
TRACE( "*** fallback " << __PRETTY_FUNCTION__ ) ;
return true ;
}
template <typename T> void Post(const T &) {
TRACE( "*** fallback " << __PRETTY_FUNCTION__ ) ;
}
// fallback for std::variant
template <typename... A> bool Pre(const std::variant<A...> &) {
//std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
return true;
}
template <typename... A> void Post(const std::variant<A...> &) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
}
// fallback for std::tuple
template <typename... A> bool Pre(const std::tuple<A...> &) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
return true;
}
template <typename... A> void Post(const std::tuple<A...> &) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
}
// fallback for std::string
bool Pre(const std::string &x) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
return true ;
}
void Post(const std::string &) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
}
// fallback for Indirection<>
template <typename T> bool Pre(const psr::Indirection<T> &x) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
return true ;
}
template <typename T> void Post(const psr::Indirection<T> &) {
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
}
// ========== Statement<> ===========
template <typename T>
bool Pre(const psr::Statement<T> &x) {
if ( current_label != -1 ) {
TRACE("*** Label " << current_label << " (" << current_label_loc.offset() << ") was not consumed in " << __PRETTY_FUNCTION__ );
}
current_label = 0 ;
current_label_loc = x.provenance ;
if ( x.label.has_value() ) {
//
// TODO: The parser stores the label in a std::uint64_t but does not report overflow
// which means that the following labels are currently accepted as valid:
// 18446744073709551617 = 2^64+1 = 1
// 18446744073709551618 = 2^64+2 = 2
// ...
//
if ( 1 <= x.label.value() && x.label.value() <= 99999 ) {
current_label = x.label.value() ;
} else {
FAIL( "##### Illegal label value " << x.label.value() << " at @" << x.provenance.offset() ) ;
}
}
return true ;
}
template <typename T>
void Post(const psr::Statement<T> &x) {
if ( current_label!=-1 ) {
TRACE("*** Label " << current_label << " (" << current_label_loc.offset() << ") was not consumed in " << __PRETTY_FUNCTION__ );
current_label=-1 ;
}
}
// ========== ProgramUnit ===========
bool Pre(const ProgramUnit &x) {
TRACE_CALL() ;
current_scope = unit_scope;
return true ;
}
void Post(const ProgramUnit &x) {
TRACE_CALL() ;
}
// ========== MainProgram ===========
bool Pre(const MainProgram &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
sema::ProgramSymbol * symbol{0};
const ProgramStmt * program_stmt = GetOptValue( std::get<x.PROG>(x.t) ) ;
const EndProgramStmt & end_stmt = GetValue( std::get<x.END>(x.t) ) ;
const sema::Identifier * program_ident{0};
if ( program_stmt ) {
const std::string & name = program_stmt->v ;
TRACE("program name = " << name ) ;
program_ident = sema::Identifier::get(name) ;
symbol = new sema::ProgramSymbol( current_scope, program_ident ) ;
TraceStatementInfo( std::get<x.PROG>(x.t) ) ;
}
// TODO: Should we create a symbol when there is no PROGRAM statement?
// Install the scope
sema.scope_provider = EnterScope( new Scope(Scope::SK_PROGRAM, current_scope, symbol) ) ;
// Install the label table
sema.label_table = PushLabelTable( new LabelTable ) ;
// Check the name consistancy
const std::string * end_name = GetOptValue(end_stmt.v) ;
const sema::Identifier * end_ident = end_name ? sema::Identifier::get(*end_name) : nullptr ;
CheckStatementName(program_ident,end_ident,"program",false) ;
// if ( program_ident ) {
// if ( end_ident && program_ident != end_ident ) {
// FAIL("Unexpected end program name '" << end_ident->name() << "' (expected '" << program_ident->name() << "') ");
// }
// } else if ( program_ident ) {
// FAIL("Unexpected end program name '" << end_ident->name() << "'");
// }
return true ;
}
void Post(const MainProgram &x) {
auto &sema = getSema(x);
GetLabelTable().dump() ;
PopLabelTable(sema.label_table) ;
LeaveScope(Scope::SK_PROGRAM) ;
TRACE_CALL() ;
}
// ========== FunctionSubprogram ===========
bool Pre(const FunctionSubprogram &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
const FunctionStmt & function_stmt = GetValue(std::get<x.FUNC>(x.t)) ;
const EndFunctionStmt & end_stmt = GetValue(std::get<x.END>(x.t)) ;
const std::string &function_name = std::get<1>(function_stmt.t) ;
const sema::Identifier *function_ident = sema::Identifier::get(function_name) ;
// TODO: lookup for name conflict
sema::Symbol *lookup ;
if ( current_scope->getKind() == Scope::SK_GLOBAL ) {
lookup = current_scope->LookupProgramUnit(function_ident) ;
if (lookup) FAIL("A unit '" << function_ident->name() << "' is already declared") ;
} else {
lookup = current_scope->LookupLocal(function_ident) ;
// TODO: There are a few cases, a function redeclaration is not necessarily a problem.
// A typical example is a PRIVATE or PUBLIC statement in a module
if (lookup) FAIL("A unit '" << function_ident->name() << "' is already declared") ;
}
auto symbol = new sema::FunctionSymbol( current_scope, function_ident ) ;
sema.scope_provider = EnterScope( new Scope(Scope::SK_FUNCTION, current_scope, symbol) ) ;
// Install the label table
sema.label_table = PushLabelTable( new LabelTable ) ;
TraceStatementInfo( std::get<x.FUNC>(x.t) ) ;
// Check the end function name
const std::string * end_name = GetOptValue(end_stmt.v) ;
const sema::Identifier * end_ident = end_name ? sema::Identifier::get(*end_name) : nullptr ;
CheckStatementName(function_ident,end_ident,"function",false) ;
return true ;
}
void Post(const FunctionSubprogram &x) {
TRACE_CALL() ;
auto &sema = getSema(x);
GetLabelTable().dump() ;
PopLabelTable(sema.label_table) ;
LeaveScope(Scope::SK_FUNCTION) ;
}
// ========== SubroutineSubprogram ===========
bool Pre(const SubroutineSubprogram &x) {
TRACE_CALL() ;
return true ;
}
void Post(const SubroutineSubprogram &x) {
TRACE_CALL() ;
}
// =========== Module ===========
bool Pre(const Module &x) {
TRACE_CALL() ;
return true ;
}
void Post(const Module &x) {
TRACE_CALL() ;
}
// =========== BlockData ===========
bool Pre(const BlockData &x) {
TRACE_CALL() ;
return true ;
}
void Post(const BlockData &x) {
TRACE_CALL() ;
}
// =========== FunctionStmt ===========
bool Pre(const FunctionStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const FunctionStmt &x) {
TRACE_CALL() ;
}
// =========== EndFunctionStmt ===========
bool Pre(const EndFunctionStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const EndFunctionStmt &x) {
TRACE_CALL() ;
}
// =========== TypeDeclarationStmt ===========
bool Pre(const TypeDeclarationStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const TypeDeclarationStmt &x) {
TRACE_CALL() ;
}
// =========== ImplicitStmt ===========
bool Pre(const ImplicitStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const ImplicitStmt &x) {
TRACE_CALL() ;
}
// =========== PrintStmt ===========
bool Pre(const PrintStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const PrintStmt &x) {
TRACE_CALL() ;
}
// =========== AssignmentStmt ===========
bool Pre(const AssignmentStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const AssignmentStmt &x) {
TRACE_CALL() ;
}
// =========== AssignmentStmt ===========
bool Pre(const ProgramStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const ProgramStmt &x) {
TRACE_CALL() ;
}
// =========== AssignmentStmt ===========
bool Pre(const EndProgramStmt &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
(void) ConsumeLabel(x) ;
return true ;
}
void Post(const EndProgramStmt &x) {
TRACE_CALL() ;
}
#if 0
// Do not remove: This is a skeleton for new node types
// =========== XXX ===========
bool Pre(const XXX &x) {
TRACE_CALL() ;
auto &sema = initSema(x);
(void) sema ;
// (void) ConsumeLabel(x) ;
return true ;
}
void Post(const XXX &x) {
TRACE_CALL() ;
}
#endif
public:
void run(const ProgramUnit &p) {
assert( NoLabelTable() ) ;
current_scope = unit_scope;
Walk(p,*this) ;
assert( current_scope == unit_scope ) ;
}
} ;
} // of namespace Fortran::parser
void DoSemanticAnalysis( const psr::Program &all)
{
psr::Pass1 pass1 ;
for (const psr::ProgramUnit &unit : all.v) {
TRACE("===========================================================================================================");
pass1.run(unit) ;
}
}
#endif

View File

@ -0,0 +1,65 @@
#include "../../lib/parser/grammar.h"
#include "../../lib/parser/idioms.h"
#include "../../lib/parser/indirection.h"
#include "../../lib/parser/message.h"
#include "../../lib/parser/parse-state.h"
#include "../../lib/parser/parse-tree.h"
#include "../../lib/parser/preprocessor.h"
#include "../../lib/parser/prescan.h"
#include "../../lib/parser/provenance.h"
#include "../../lib/parser/source.h"
#include "../../lib/parser/user-state.h"
#include "../../lib/semantics/attr.h"
#include "../../lib/semantics/type.h"
#include <cstdlib>
#include <iostream>
#include <list>
#include <optional>
#include <sstream>
#include <string>
#include <stddef.h>
using namespace Fortran;
using namespace parser;
extern void DoSemanticAnalysis(const Program &);
//static void visitProgramUnit(const ProgramUnit &unit);
int main(int argc, char *const argv[]) {
if (argc != 2) {
std::cerr << "Expected 1 source file, got " << (argc - 1) << "\n";
return EXIT_FAILURE;
}
std::string path{argv[1]};
AllSources allSources;
std::stringstream error;
const auto *sourceFile = allSources.Open(path, &error);
if (!sourceFile) {
std::cerr << error.str() << '\n';
return 1;
}
ProvenanceRange range{allSources.AddIncludedFile(
*sourceFile, ProvenanceRange{})};
Messages messages{allSources};
CookedSource cooked{&allSources};
Preprocessor preprocessor{&allSources};
bool prescanOk{Prescanner{&messages, &cooked, &preprocessor}.Prescan(range)};
messages.Emit(std::cerr);
if (!prescanOk) {
return EXIT_FAILURE;
}
cooked.Marshal();
ParseState state{cooked};
UserState ustate;
std::optional<Program> result{program.Parse(&state)};
if (!result.has_value() || state.anyErrorRecovery()) {
std::cerr << "parse FAILED\n";
state.messages()->Emit(std::cerr);
return EXIT_FAILURE;
}
DoSemanticAnalysis(*result) ;
return EXIT_SUCCESS;
}