diff --git a/include/soll/ADT/STLExtras.h b/include/soll/ADT/STLExtras.h index 3329a33a..123c0064 100644 --- a/include/soll/ADT/STLExtras.h +++ b/include/soll/ADT/STLExtras.h @@ -10,10 +10,10 @@ namespace soll { template -std::vector> make_unique_vector(Args &&...args) { +std::vector> make_unique_vector(Args &&... As) { std::vector> result; - result.reserve(sizeof...(args)); - (result.emplace_back(std::forward(args)), ...); + result.reserve(sizeof...(As)); + (result.emplace_back(std::forward(As)), ...); return result; } @@ -29,8 +29,9 @@ template struct cond_const { typedef Type type; }; /// Concatenate the contents of a container onto a vector template std::vector &operator+=(std::vector &A, U &B) { - for (auto const &I : B) + for (auto const &I : B) { A.push_back(T(I)); + } return A; } /// Concatenate the contents of a container onto a vector, move variant. diff --git a/include/soll/AST/ASTBase.h b/include/soll/AST/ASTBase.h index c2980baf..33546aee 100644 --- a/include/soll/AST/ASTBase.h +++ b/include/soll/AST/ASTBase.h @@ -7,6 +7,7 @@ class ASTNode { public: enum class ASTNodeType { DECL, STMT }; ASTNode() = default; + virtual ~ASTNode() = default; virtual ASTNodeType getASTType() = 0; }; diff --git a/include/soll/AST/Decl.h b/include/soll/AST/Decl.h index 0be0075e..9501ac62 100644 --- a/include/soll/AST/Decl.h +++ b/include/soll/AST/Decl.h @@ -27,6 +27,7 @@ class Decl : public ASTNode { std::string Name; Visibility Vis; std::string UniqueName; + const ASTNode *Parent; protected: friend class ASTReader; @@ -35,10 +36,11 @@ class Decl : public ASTNode { Decl(SourceRange L, llvm::StringRef Name = llvm::StringRef::withNullAsEmpty(nullptr), Visibility Vis = Visibility::Default) - : Location(L), Name(Name.str()), Vis(Vis), UniqueName(Name.str()) {} + : Location(L), Name(Name.str()), Vis(Vis), UniqueName(Name.str()), + Parent(nullptr) {} Decl(SourceRange L, std::string Name, Visibility Vis = Visibility::Default) - : Location(L), Name(Name), Vis(Vis), UniqueName(Name) {} + : Location(L), Name(Name), Vis(Vis), UniqueName(Name), Parent(nullptr) {} public: virtual void accept(DeclVisitor &visitor) = 0; @@ -48,6 +50,15 @@ class Decl : public ASTNode { llvm::StringRef getUniqueName() const { return UniqueName; } void setUniqueName(llvm::StringRef NewName) { UniqueName = NewName.str(); } Visibility getVisibility() const { return Vis; } + + void setScope(const ASTNode *D) { Parent = D; } + /// @returns the scope this declaration resides in. Can be nullptr if it is + /// the global scope. Available only after name and type resolution step. + const ASTNode *scope() const { return Parent; } + + bool isStructMember() const; + + bool isVisibleAsUnqualifiedName() const; }; /** @@ -414,6 +425,18 @@ class StructDecl : public Decl { Token getToken() const { return Tok; } TypePtr getType() const { return Ty; } TypePtr getConstructorType() const { return ConstructorTy; } + std::vector getMembers() { + std::vector Res; + for (auto &M : Members) + Res.push_back(M.get()); + return Res; + } + std::vector getMembers() const { + std::vector Res; + for (auto &M : Members) + Res.push_back(M.get()); + return Res; + } }; class ModifierInvocation { diff --git a/include/soll/Basic/DiagnosticSemaKinds.inc b/include/soll/Basic/DiagnosticSemaKinds.inc index 81740a68..e2df65ec 100644 --- a/include/soll/Basic/DiagnosticSemaKinds.inc +++ b/include/soll/Basic/DiagnosticSemaKinds.inc @@ -33,3 +33,6 @@ DIAG(err_visibility_not_match, CLASS_ERROR, (unsigned)diag::Severity::Error, "vi DIAG(err_private_function_can_not_be_virtual, CLASS_ERROR, (unsigned)diag::Severity::Error, "private funtcion %0 can not be virtual", 0, false, 1) DIAG(err_mutability_overriding_not_allow, CLASS_ERROR, (unsigned)diag::Severity::Error, "can not change function %0 mutability", 0, false, 1) DIAG(err_func_to_var_overrided_unsoupport, CLASS_ERROR, (unsigned)diag::Severity::Error, "function to state variables override is unsupport", 0, false, 1) +DIAG(err_use_illegalnames, CLASS_WARNING, (unsigned)diag::Severity::Warning, "Use of illegal identifier name: '%0'", 0, false, 0) +DIAG(err_already_declared, CLASS_WARNING, (unsigned)diag::Severity::Warning, "identifier '%0' already declared", 0, false, 0) +DIAG(err_declaration_not_found, CLASS_ERROR, (unsigned)diag::Severity::Error, "identifier '%0' not found", 0, false, 0) diff --git a/include/soll/Sema/DeclarationContainer.h b/include/soll/Sema/DeclarationContainer.h new file mode 100644 index 00000000..d79fde6b --- /dev/null +++ b/include/soll/Sema/DeclarationContainer.h @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + +#include "soll/AST/AST.h" +#include "soll/AST/Decl.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/Sema.h" + +#include +#include +#include +#include + +namespace soll { + +class DeclarationContainer { +public: + DeclarationContainer() = default; + explicit DeclarationContainer(const ASTNode *EnclosingNode, + DeclarationContainer *EnclosingContainer) + : EnclosingNode(EnclosingNode), EnclosingContainer(EnclosingContainer) { + if (EnclosingContainer) + EnclosingContainer->InnerContainers.emplace_back(this); + } + + ASTNode const *enclosingNode() const { return EnclosingNode; } + + void activateVariable(llvm::StringRef Name) { + assert(InvisibleDeclarations.count(Name) && + InvisibleDeclarations.at(Name).size() == 1); + assert(Declarations.count(Name) == 0 || Declarations.at(Name).empty()); + + Declarations[Name].emplace_back(InvisibleDeclarations.at(Name).front()); + InvisibleDeclarations.erase(Name); + } + + bool isInvisible(llvm::StringRef Name) const { + return InvisibleDeclarations.count(Name); + } + + bool registerDeclaration(const Decl *D, bool Invisible, bool Update) { + return registerDeclaration(D, llvm::StringRef(), nullptr, Invisible, + Update); + } + + bool registerDeclaration(const Decl *D, llvm::StringRef Name, + const SourceRange *Loc, bool Invisible, + bool Update) { + if (Name.empty()) { + Name = D->getName(); + } + + if (Name.empty()) + return true; + + if (Update) { + assert(!dynamic_cast(D) && + "Attempt to update function definition."); + Declarations.erase(Name); + InvisibleDeclarations.erase(Name); + return true; + } else { + if (conflictingDeclaration(D, Name)) + return false; + if (EnclosingContainer && D->isVisibleAsUnqualifiedName()) { + // TODO: isVisibleAsUnqualifiedName for struct not merged yet. + // It use old path to handle + } + } + + std::vector &Decls = + Invisible ? InvisibleDeclarations[Name] : Declarations[Name]; + if (find(Decls.begin(), Decls.end(), D) == Decls.end()) + Decls.emplace_back(D); + return true; + } + + std::vector + resolveName(llvm::StringRef Name, bool Recursive = false, + bool AlsoInvisible = false, + bool OnlyVisibleAsUnqualifiedNames = false) const { + std::vector Res; + + if (Declarations.count(Name)) { + for (auto E : Declarations.at(Name)) { + if (OnlyVisibleAsUnqualifiedNames) { + if (!E->isVisibleAsUnqualifiedName()) + continue; + } + Res.emplace_back(E); + } + } + + if (AlsoInvisible && InvisibleDeclarations.count(Name)) { + for (auto E : InvisibleDeclarations.at(Name)) { + if (OnlyVisibleAsUnqualifiedNames) { + if (!E->isVisibleAsUnqualifiedName()) + continue; + } + Res.emplace_back(E); + } + } + + if (Res.empty() && Recursive && EnclosingContainer) + Res = EnclosingContainer->resolveName(Name, true, AlsoInvisible, + OnlyVisibleAsUnqualifiedNames); + + return Res; + } + + const Decl *conflictingDeclaration(const Decl *D, + llvm::StringRef Name) const { + if (Name.empty()) + Name = D->getName(); + assert(!Name.empty()); + + std::vector Decls; + if (Declarations.count(Name)) + Decls += Declarations.at(Name); + + if (InvisibleDeclarations.count(Name)) + Decls += InvisibleDeclarations.at(Name); + + if (dynamic_cast(D) || + dynamic_cast(D) || + dynamic_cast(D)) { + for (const Decl *RegDecl : Decls) { + if (dynamic_cast(D) && + !dynamic_cast(RegDecl)) + return RegDecl; + if (dynamic_cast(D) && + !dynamic_cast(RegDecl)) + return RegDecl; + if (dynamic_cast(D) && + !dynamic_cast(RegDecl)) + return RegDecl; + } + } else if (Decls.size() == 1 && Decls.front() == D) { + return nullptr; + } else if (!Decls.empty()) { + return Decls.front(); + } + + return nullptr; + } + +private: + const ASTNode *EnclosingNode = nullptr; + const DeclarationContainer *EnclosingContainer = nullptr; + std::vector InnerContainers; + std::map> Declarations; + std::map> InvisibleDeclarations; +}; + +} // namespace soll \ No newline at end of file diff --git a/include/soll/Sema/GlobalContent.h b/include/soll/Sema/GlobalContent.h new file mode 100644 index 00000000..ec52decf --- /dev/null +++ b/include/soll/Sema/GlobalContent.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + +#include "soll/AST/AST.h" +#include "soll/AST/Decl.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/Sema.h" + +#include +#include +#include +#include + +namespace soll { +/// Magic variables get negative ids for easy differentiation +namespace { +int magicVariableToID(std::string const &_name) { + if (_name == "abi") + return -1; + else if (_name == "addmod") + return -2; + else if (_name == "assert") + return -3; + else if (_name == "block") + return -4; + else if (_name == "blockhash") + return -5; + else if (_name == "ecrecover") + return -6; + else if (_name == "gasleft") + return -7; + else if (_name == "keccak256") + return -8; + else if (_name == "msg") + return -15; + else if (_name == "mulmod") + return -16; + else if (_name == "now") + return -17; + else if (_name == "require") + return -18; + else if (_name == "revert") + return -19; + else if (_name == "ripemd160") + return -20; + else if (_name == "selfdestruct") + return -21; + else if (_name == "sha256") + return -22; + else if (_name == "sha3") + return -23; + else if (_name == "suicide") + return -24; + else if (_name == "super") + return -25; + else if (_name == "tx") + return -26; + else if (_name == "type") + return -27; + else if (_name == "this") + return -28; + else + __builtin_unreachable(); +} +} // namespace + +class GlobalContext { +public: + void setCurrentContract(const ContractDecl *Cont) { CurrentContract = Cont; } + void resetCurrentContract() { CurrentContract = nullptr; } + const MagicVariableDecl *currentThis() { + if (!ThisCache[CurrentContract]) { + TypePtr Type = CurrentContract->getType(); + ThisCache[CurrentContract] = std::make_shared( + magicVariableToID("this"), "this", Type); + } + return ThisCache[CurrentContract].get(); + } + + const MagicVariableDecl *currentSuper() { + assert(false && "unimp currentSuper"); + return nullptr; + } + + GlobalContext() = default; + GlobalContext(const GlobalContext &) = delete; + ~GlobalContext() = default; + +private: + const ContractDecl *CurrentContract = nullptr; + std::map> ThisCache; + std::map> SuperCache; +}; +} // namespace soll \ No newline at end of file diff --git a/include/soll/Sema/NameAndTypeResolver.h b/include/soll/Sema/NameAndTypeResolver.h new file mode 100644 index 00000000..b50feff3 --- /dev/null +++ b/include/soll/Sema/NameAndTypeResolver.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + +#include "soll/AST/AST.h" +#include "soll/AST/Decl.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/DeclarationContainer.h" +#include "soll/Sema/Sema.h" + +#include +#include + +namespace soll { +class NameAndTypeResolver { +public: + void setScope(const ASTNode *Node) { CurrentScope = Scopes[Node].get(); } + bool resolveNamesAndTypesInternal(ASTNode *Node, bool ResolveCode); + bool updateDeclaration(const Decl *D) { + Scopes[nullptr]->registerDeclaration(D, false, true); + return true; + } + + void activateVariable(llvm::StringRef Name) { + if (CurrentScope == nullptr) { + return; + } + if (CurrentScope->isInvisible(Name)) + CurrentScope->activateVariable(Name); + } + + std::vector resolveName(llvm::StringRef Name, + const ASTNode *Scope) const; + std::vector + nameFromCurrentScope(llvm::StringRef Name, + bool IncludeInvisibles = false) const; + NameAndTypeResolver(Sema &Action, GlobalContext &GC); + void Register(SourceUnit &SU); + bool Resolve(SourceUnit &SU); + +private: + Sema &Action; + GlobalContext &GC; + std::map> Scopes; + DeclarationContainer *CurrentScope = nullptr; +}; +} // namespace soll \ No newline at end of file diff --git a/include/soll/Sema/ReferenceResolver.h b/include/soll/Sema/ReferenceResolver.h new file mode 100644 index 00000000..7889f921 --- /dev/null +++ b/include/soll/Sema/ReferenceResolver.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + +#include "soll/AST/AST.h" +#include "soll/AST/Decl.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/DeclarationContainer.h" +#include "soll/Sema/NameAndTypeResolver.h" +#include "soll/Sema/Sema.h" + +namespace soll { +class ReferencesResolver : public DeclVisitor, public StmtVisitor { +public: + ReferencesResolver(Sema &Action, NameAndTypeResolver &NTR, + bool ResolveInsideCode); + bool resolve(ASTNode *Node) { + Status = true; + if (auto D = dynamic_cast(Node)) + D->accept(*this); + else if (auto S = dynamic_cast(Node)) + S->accept(*this); + return Status; + } + void visit(Block &B) override; + void visit(ForStmt &F) override; + void visit(DeclStmt &D) override; + void visit(Identifier &I) override; + void visit(MemberExpr &M) override; + + void visit(FunctionDecl &FD) override; + +private: + Sema &Action; + NameAndTypeResolver &Resolver; + bool ResolveInsideCode; + bool Status; +}; + +} // namespace soll \ No newline at end of file diff --git a/include/soll/Sema/Sema.h b/include/soll/Sema/Sema.h index c3d7a44b..6c0bdae8 100644 --- a/include/soll/Sema/Sema.h +++ b/include/soll/Sema/Sema.h @@ -3,6 +3,7 @@ #include "soll/AST/ASTContext.h" #include "soll/AST/ExprAsm.h" +#include "soll/Sema/GlobalContent.h" #include "soll/Sema/Scope.h" #include #include @@ -17,6 +18,7 @@ class Lexer; class Sema; class SourceManager; class Token; +class NameAndTypeResolver; class Sema { Sema(const Sema &) = delete; @@ -51,6 +53,8 @@ class Sema { DiagnosticsEngine &Diags; SourceManager &SourceMgr; const llvm::StringMap *LibrariesAddressMap; + std::shared_ptr NTR; + GlobalContext GC; Sema(Lexer &lexer, ASTContext &ctxt, ASTConsumer &consumer); @@ -135,6 +139,9 @@ class Sema { bool AppendChild); void resolveType(SourceUnit &SU); + void resolveScope(SourceUnit &SU); + void registerDeclarations(SourceUnit &SU); + void resolveNameAndType(SourceUnit &SU); void resolveInherit(SourceUnit &SU); void resolveUniqueName(SourceUnit &SU); void resolveIdentifierDecl(SourceUnit &SU); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9fbe2a48..0444c111 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5,6 +5,25 @@ namespace soll { +bool Decl::isStructMember() const { + if (auto D = dynamic_cast(scope())) { + return dynamic_cast(D); + } + return false; +} + +bool Decl::isVisibleAsUnqualifiedName() const { + if (!scope()) + return true; + if (isStructMember()) + return false; + if (auto D = dynamic_cast(scope())) + if (auto F = dynamic_cast(D)) + if (!F->getBody()) + return false; // parameter of a function without body + return true; +} + /// /// Source Unit /// diff --git a/lib/AST/DeclVisitor.cpp b/lib/AST/DeclVisitor.cpp index 9aa708f9..eefcda27 100644 --- a/lib/AST/DeclVisitor.cpp +++ b/lib/AST/DeclVisitor.cpp @@ -65,8 +65,9 @@ template void DeclVisitorBase::visit(VarDeclType &) { // leaf, do nothing } -template void DeclVisitorBase::visit(StructDeclType &) { - // leaf, do nothing +template void DeclVisitorBase::visit(StructDeclType &SD) { + for (auto E : SD.getMembers()) + E->accept(*this); } template diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 0b00e0e3..b07921c9 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -54,7 +54,7 @@ void CallExpr::resolveNamedCall() { std::unordered_map ParamNamesIndex; size_t ParamSize = 0; for (const auto &ParamName : *FnTy->getParamNames()) { - ParamNamesIndex[ParamName] = ParamSize++; + ParamNamesIndex[ParamName.str()] = ParamSize++; } Args.resize(ParamSize); for (size_t I = 0; I < ParamSize; ++I) { @@ -72,7 +72,7 @@ std::vector CallExpr::getArguments() { std::unordered_map ParamNamesIndex; size_t ParamSize = 0; for (const auto &ParamName : *FnTy->getParamNames()) { - ParamNamesIndex[ParamName] = ParamSize++; + ParamNamesIndex[ParamName.str()] = ParamSize++; } Args.resize(ParamSize); for (size_t I = 0; I < ParamSize; ++I) { @@ -92,7 +92,7 @@ std::vector CallExpr::getArguments() const { std::unordered_map ParamNamesIndex; size_t ParamSize = 0; for (const auto &ParamName : *FnTy->getParamNames()) { - ParamNamesIndex[ParamName] = ParamSize++; + ParamNamesIndex[ParamName.str()] = ParamSize++; } Args.resize(ParamSize); for (size_t I = 0; I < ParamSize; ++I) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index ed051141..cdc56cd2 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -383,8 +383,11 @@ std::unique_ptr Parser::parse() { std::move(Nodes)); } Actions.setLibrariesAddressMap(&LibrariesAddressMap); + Actions.resolveScope(*SU); + Actions.registerDeclarations(*SU); Actions.resolveInherit(*SU); Actions.resolveIdentifierDecl(*SU); + Actions.resolveNameAndType(*SU); Actions.resolveType(*SU); Actions.resolveUniqueName(*SU); return SU; diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 1458709a..d11f7227 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -3,7 +3,10 @@ add_llvm_library(sollSema Scope.cpp Sema.cpp SemaExprAsm.cpp + ReferenceResolver.cpp SemaResolveType.cpp + SemaResolveScope.cpp + SemaNameAndTypeResolver.cpp SemaResolveInherit.cpp SemaResolveUniqueName.cpp SemaResolveImmutable.cpp diff --git a/lib/Sema/ReferenceResolver.cpp b/lib/Sema/ReferenceResolver.cpp new file mode 100644 index 00000000..28dfa8a7 --- /dev/null +++ b/lib/Sema/ReferenceResolver.cpp @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "soll/Sema/ReferenceResolver.h" + +namespace soll { +ReferencesResolver::ReferencesResolver(Sema &Action, NameAndTypeResolver &NTR, + bool ResolveInsideCode = false) + : Action(Action), Resolver(NTR), ResolveInsideCode(ResolveInsideCode) {} + +void ReferencesResolver::visit(Block &B) { + if (!ResolveInsideCode) + return; + Resolver.setScope(&B); + StmtVisitor::visit(B); + Resolver.setScope(&B); +} + +void ReferencesResolver::visit(ForStmt &F) { + if (!ResolveInsideCode) + return; + Resolver.setScope(&F); + StmtVisitor::visit(F); + Resolver.setScope(&F); +} + +void ReferencesResolver::visit(DeclStmt &D) { + StmtVisitor::visit(D); + + if (!ResolveInsideCode) + return; + + for (auto Decl : D.getVarDecls()) + Resolver.activateVariable(Decl->getName()); +} + +void ReferencesResolver::visit(MemberExpr &M) { + M.getBase()->accept(*this); + // XXX : We need a new pass to check if the member is availible. + // Before we finish that, we should ignore right side. +} + +void ReferencesResolver::visit(Identifier &I) { + auto Decls = Resolver.nameFromCurrentScope(I.getName()); + if (Decls.empty()) { + // Bypass to next pass + // TODO: Action.Diag(I.getLocation().getBegin(), + // diag::err_declaration_not_found) << I.getName(); + } else if (Decls.size() == 1) { + I.setCorrespondDecl(const_cast(Decls.front())); + } else { + // Can not be resolved yet. + } + return; +} + +void ReferencesResolver::visit(FunctionDecl &FD) { + DeclVisitor::visit(FD); + + if (FD.getBody()) + FD.getBody()->accept(*this); +} +} // namespace soll diff --git a/lib/Sema/SemaNameAndTypeResolver.cpp b/lib/Sema/SemaNameAndTypeResolver.cpp new file mode 100644 index 00000000..202a3d6b --- /dev/null +++ b/lib/Sema/SemaNameAndTypeResolver.cpp @@ -0,0 +1,285 @@ +#include "soll/AST/AST.h" +#include "soll/AST/Decl.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/DeclarationContainer.h" +#include "soll/Sema/GlobalContent.h" +#include "soll/Sema/NameAndTypeResolver.h" +#include "soll/Sema/ReferenceResolver.h" +#include "soll/Sema/Sema.h" + +#include +#include +#include +#include +#include + +namespace soll { + +class DeclarationRegistrationHelper : public DeclVisitor, public StmtVisitor { +public: + DeclarationRegistrationHelper( + std::map> &Scopes, + Decl *ASTRoot, Sema &Action, GlobalContext &GC, + const ASTNode *CurrentScope = nullptr) + : Scopes(Scopes), Action(Action), GC(GC), CurrentScope(nullptr), + CurrentFunc(nullptr), CurrentCont(nullptr) { + ASTRoot->accept(*this); + } + + void enterNewSubScope(ASTNode *SubScope) { + if (Scopes.count(SubScope)) { + auto D = dynamic_cast(SubScope); + assert(D && dynamic_cast(D) && "Unexpected scope type."); + } else { + bool NewlyAdded = + Scopes + .emplace(SubScope, std::make_shared( + CurrentScope, Scopes[CurrentScope].get())) + .second; + assert(NewlyAdded && "Unable to add new scope."); + } + CurrentScope = SubScope; + } + + void closeCurrentScope() { + assert(CurrentScope && Scopes.count(CurrentScope) && + "Closed non-existing scope."); + CurrentScope = Scopes[CurrentScope]->enclosingNode(); + } + + void registerDeclaration(Decl *D) { + assert(D); + assert(CurrentScope && Scopes.count(CurrentScope)); + + bool Inactive = false; + + if (auto ST = dynamic_cast(CurrentScope)) { + if (dynamic_cast(ST) || dynamic_cast(ST)) + Inactive = true; + } + + registerDeclaration(Scopes[CurrentScope].get(), D, llvm::StringRef(), + Inactive); + assert(D->scope() == CurrentScope); + } + + bool registerDeclaration(DeclarationContainer *DeclCont, const Decl *D, + llvm::StringRef Name_, bool Inactive) { + llvm::StringRef Name = Name_.empty() ? D->getName() : Name_; + + // We use "invisible" for both inactive variables in blocks and for members + // invisible in contracts. They cannot both be true at the same time. + // assert(!(Inactive && D->getVisibility() != Decl::Visibility::External)); + + static std::set IllegalNames{"_", "super", "this"}; + + if (IllegalNames.count(Name.str())) { + Action.Diag(D->getLocation().getBegin(), diag::err_use_illegalnames) + << Name; + } + + if (!DeclCont->registerDeclaration(D, Name, &D->getLocation(), Inactive, + false)) { + // Bypass to next pass + // TODO: Action.Diag(D->getLocation().getBegin(), + // diag::err_already_declared) << Name; + return false; + } + + return true; + } + + void visit(SourceUnit &SU) override { + if (!Scopes[&SU]) + // By importing, it is possible that the container already exists. + Scopes[&SU] = std::make_shared( + CurrentScope, Scopes[CurrentScope].get()); + DeclVisitor::visit(SU); + } + + // void visit(ImportDirective &ID) override; + + void visit(ContractDecl &CD) override { + GC.setCurrentContract(&CD); + + Scopes[nullptr]->registerDeclaration(GC.currentThis(), false, true); + // Scopes[nullptr]->registerDeclaration(GC.currentSuper(), false, true); + CurrentCont = &CD; + + enterNewSubScope(&CD); + DeclVisitor::visit(CD); + closeCurrentScope(); + + Scopes[nullptr]->registerDeclaration(GC.currentThis(), true, true); + // Scopes[nullptr]->registerDeclaration(GC.currentSuper(), true, true); + CurrentCont = nullptr; + + GC.resetCurrentContract(); + } + + void visit(VarDecl &VD) override { + registerDeclaration(&VD); + DeclVisitor::visit(VD); + } + + void visit(FunctionDecl &FD) override { + registerDeclaration(&FD); + enterNewSubScope(&FD); + CurrentFunc = &FD; + + DeclVisitor::visit(FD); + if (FD.getBody()) { + FD.getBody()->accept(*this); + } + + CurrentFunc = nullptr; + closeCurrentScope(); + } + + void visit(EventDeclType &ED) override { + registerDeclaration(&ED); + enterNewSubScope(&ED); + + DeclVisitor::visit(ED); + + closeCurrentScope(); + } + + void visit(StructDecl &SD) override { registerDeclaration(&SD); } + + // StmtVisitor Part + + void visit(Block &B) override { + enterNewSubScope(&B); + StmtVisitor::visit(B); + closeCurrentScope(); + } + + void visit(ForStmt &B) override { + enterNewSubScope(&B); + StmtVisitor::visit(B); + closeCurrentScope(); + } + + void visit(DeclStmt &DS) override { + StmtVisitor::visit(DS); + for (auto Decl : DS.getVarDecls()) { + registerDeclaration(Decl); + } + } + +private: + std::map> &Scopes; + Sema &Action; + GlobalContext &GC; + const ASTNode *CurrentScope; + FunctionDecl *CurrentFunc; + ContractDecl *CurrentCont; +}; + +NameAndTypeResolver::NameAndTypeResolver(Sema &Action, GlobalContext &GC) + : Action(Action), GC(GC) { + Scopes[nullptr] = std::make_shared(); + // TODO + // inject GC.decl to Scopes +} + +void NameAndTypeResolver::Register(SourceUnit &SU) { + DeclarationRegistrationHelper DRH(Scopes, &SU, Action, GC); +} + +bool NameAndTypeResolver::Resolve(SourceUnit &SU) { + for (auto Node : SU.getNodes()) { + setScope(&SU); + if (!resolveNamesAndTypesInternal(Node, true)) + return false; + } + + return true; +} + +bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode *Node, + bool ResolveCode) { + if (auto Cont = dynamic_cast(Node)) { + bool Succ = true; + setScope(Cont->scope()); + + assert(!!CurrentScope); + assert(ResolveCode); + + GC.setCurrentContract(Cont); + if (Cont->getKind() != ContractDecl::ContractKind::Library) { + // TODO : Super + // updateDeclaration(GC.currentSuper()); + } + updateDeclaration(GC.currentThis()); + + for (auto Base : Cont->getInheritNodes()) + if (!resolveNamesAndTypesInternal(Base, true)) + Succ = false; + + setScope(Cont); + + // TODO : Handle InheritNodes Scope + + // these can contain code, only resolve parameters for now + for (auto SubNode : Cont->getSubNodes()) { + setScope(Cont); + if (!resolveNamesAndTypesInternal(SubNode, false)) + Succ = false; + } + + if (!Succ) + return false; + setScope(Cont); + + // now resolve references inside the code + for (auto SubNode : Cont->getSubNodes()) { + setScope(Cont); + if (!resolveNamesAndTypesInternal(SubNode, true)) + Succ = false; + } + + // make "this" and "super" invisible. + Scopes[nullptr]->registerDeclaration(GC.currentThis(), true, true); + // Scopes[nullptr]->registerDeclaration(GC.currentSuper(), true, true); + GC.resetCurrentContract(); + + return Succ; + + } else { + if (Scopes.count(Node)) + setScope(Node); + return ReferencesResolver(Action, *this, ResolveCode).resolve(Node); + } +} + +std::vector +NameAndTypeResolver::resolveName(llvm::StringRef Name, + const ASTNode *Scope) const { + auto It = Scopes.find(Scope); + if (It == std::end(Scopes)) + return {}; + return It->second->resolveName(Name, false); +} + +std::vector +NameAndTypeResolver::nameFromCurrentScope(llvm::StringRef Name, + bool IncludeInvisibles) const { + if (CurrentScope) + return CurrentScope->resolveName(Name, true, IncludeInvisibles); + return {}; +} + +void Sema::registerDeclarations(SourceUnit &SU) { + if (NTR == nullptr) + NTR = std::make_shared(*this, GC); + NTR->Register(SU); +} + +void Sema::resolveNameAndType(SourceUnit &SU) { + if (NTR == nullptr) + NTR = std::make_shared(*this, GC); + NTR->Resolve(SU); +} +} // namespace soll diff --git a/lib/Sema/SemaResolveScope.cpp b/lib/Sema/SemaResolveScope.cpp new file mode 100644 index 00000000..0e0dad23 --- /dev/null +++ b/lib/Sema/SemaResolveScope.cpp @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "soll/AST/AST.h" +#include "soll/Basic/DiagnosticSema.h" +#include "soll/Sema/Sema.h" + +#include + +namespace soll { +namespace { +class ScopeResolver : public DeclVisitor, public StmtVisitor { + struct ScopeOpenHelper { + explicit ScopeOpenHelper(ASTNode &Node) { Scopes.emplace_back(&Node); } + ~ScopeOpenHelper() { Scopes.pop_back(); } + inline static std::vector Scopes; + }; + + const ASTNode *currentScope() { + return ScopeOpenHelper::Scopes.empty() ? nullptr + : ScopeOpenHelper::Scopes.back(); + } + + void SetScope(ASTNode *A) { + if (auto D = dynamic_cast(A)) { + D->setScope(currentScope()); + } + } + + ContractDecl *Cont; + +public: + void Resolve(SourceUnit &SU) { + ScopeOpenHelper::Scopes.clear(); + Cont = nullptr; + SU.accept(*this); + } + + void visit(ContractDecl &Node) override { + assert(Cont == nullptr); + Cont = &Node; + ScopeOpenHelper S(Node); + DeclVisitor::visit(Node); + + assert(Cont == &Node); + Cont = nullptr; + } + + void visit(FunctionDecl &Node) override { + SetScope(&Node); + ScopeOpenHelper S(Node); + DeclVisitor::visit(Node); + + if (Node.getBody()) + Node.getBody()->accept(*this); + } + + void visit(EventDeclType &Node) override { + SetScope(&Node); + ScopeOpenHelper S(Node); + DeclVisitor::visit(Node); + } + + void visit(VarDecl &Node) override { + SetScope(&Node); + DeclVisitor::visit(Node); + } + + void visit(StructDecl &Node) override { + SetScope(&Node); + ScopeOpenHelper S(Node); + DeclVisitor::visit(Node); + } + + void visit(Block &Node) override { + SetScope(&Node); + ScopeOpenHelper S(Node); + StmtVisitor::visit(Node); + } + + void visit(ForStmt &Node) override { + SetScope(&Node); + ScopeOpenHelper S(Node); + StmtVisitor::visit(Node); + } + + void visit(DeclStmt &ST) override { + for (auto D : ST.getVarDecls()) + D->setScope(currentScope()); + } +}; +} // namespace + +void Sema::resolveScope(SourceUnit &SU) { + ScopeResolver SR; + SR.Resolve(SU); +} +} // namespace soll \ No newline at end of file