Skip to content

Commit

Permalink
Add basic type inference infrastructure for experimental Solidity
Browse files Browse the repository at this point in the history
Co-authored-by: Kamil Śliwak <[email protected]>
Co-authored-by: Matheus Aguiar <[email protected]>
Co-authored-by: Nikola Matic <[email protected]>
  • Loading branch information
4 people authored and r0qs committed Dec 18, 2023
1 parent 740c861 commit 8d311b9
Show file tree
Hide file tree
Showing 93 changed files with 6,536 additions and 133 deletions.
1 change: 1 addition & 0 deletions .circleci/osx_install_dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ then
brew install wget
brew install coreutils
brew install diffutils
brew install grep
./scripts/install_obsolete_jsoncpp_1_7_4.sh

# z3
Expand Down
2 changes: 1 addition & 1 deletion liblangutil/Scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ void Scanner::scanToken()
case '.':
// . Number
advance();
if (isDecimalDigit(m_char))
if (m_kind != ScannerKind::ExperimentalSolidity && isDecimalDigit(m_char))
token = scanNumber('.');
else
token = Token::Period;
Expand Down
29 changes: 23 additions & 6 deletions liblangutil/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,15 @@ namespace solidity::langutil
T(Leave, "leave", 0) \
\
T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \
/* Experimental Solidity specific keywords. */ \
K(Class, "class", 0) \
K(Instantiation, "instantiation", 0) \
K(Integer, "Integer", 0) \
K(Itself, "itself", 0) \
K(StaticAssert, "static_assert", 0) \
K(Builtin, "__builtin", 0) \
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
\
/* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \
\
Expand All @@ -292,7 +300,7 @@ namespace TokenTraits
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }

// Predicates
constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; }
constexpr bool isElementaryTypeName(Token _token) { return Token::Int <= _token && _token < Token::TypesEnd; }
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
Expand Down Expand Up @@ -325,6 +333,16 @@ namespace TokenTraits
tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex;
}

constexpr bool isBuiltinTypeClassName(Token _token)
{
return
_token == Token::Integer ||
(isBinaryOp(_token) && _token != Token::Comma) ||
isCompareOp(_token) ||
isUnaryOp(_token) ||
(isAssignmentOp(_token) && _token != Token::Assign);
}

constexpr bool isExperimentalSolidityKeyword(Token token)
{
return
Expand All @@ -345,17 +363,16 @@ namespace TokenTraits
token == Token::While ||
token == Token::For ||
token == Token::Continue ||
token == Token::Break;
// TODO: see isExperimentalSolidityKeyword below
// || (token > Token::NonExperimentalEnd && token < Token::ExperimentalEnd);
token == Token::Break ||
(token > Token::NonExperimentalEnd && token< Token::ExperimentalEnd);
}

constexpr bool isExperimentalSolidityOnlyKeyword(Token)
constexpr bool isExperimentalSolidityOnlyKeyword(Token _token)
{
// TODO: use token > Token::NonExperimentalEnd && token < Token::ExperimentalEnd
// as soon as other experimental tokens are added. For now the comparison generates
// a warning from clang because it is always false.
return false;
return _token > Token::NonExperimentalEnd && _token < Token::ExperimentalEnd;
}

bool isYulKeyword(std::string const& _literal);
Expand Down
28 changes: 28 additions & 0 deletions libsolidity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,34 @@ set(sources
parsing/Parser.cpp
parsing/Parser.h
parsing/Token.h
experimental/analysis/Analysis.cpp
experimental/analysis/Analysis.h
experimental/analysis/DebugWarner.cpp
experimental/analysis/DebugWarner.h
experimental/analysis/FunctionDependencyAnalysis.cpp
experimental/analysis/FunctionDependencyAnalysis.h
experimental/analysis/TypeClassRegistration.cpp
experimental/analysis/TypeClassRegistration.h
experimental/analysis/TypeInference.cpp
experimental/analysis/TypeInference.h
experimental/analysis/TypeRegistration.cpp
experimental/analysis/TypeRegistration.h
experimental/analysis/SyntaxRestrictor.cpp
experimental/analysis/SyntaxRestrictor.h
experimental/ast/FunctionCallGraph.h
experimental/ast/Type.cpp
experimental/ast/Type.h
experimental/ast/TypeSystem.cpp
experimental/ast/TypeSystem.h
experimental/ast/TypeSystemHelper.cpp
experimental/ast/TypeSystemHelper.h
experimental/codegen/Common.h
experimental/codegen/Common.cpp
experimental/codegen/IRGenerationContext.h
experimental/codegen/IRGenerator.cpp
experimental/codegen/IRGenerator.h
experimental/codegen/IRGeneratorForStatements.cpp
experimental/codegen/IRGeneratorForStatements.h
)

add_library(solidity ${sources})
Expand Down
6 changes: 4 additions & 2 deletions libsolidity/analysis/NameAndTypeResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ namespace solidity::frontend
NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
ErrorReporter& _errorReporter
ErrorReporter& _errorReporter,
bool _experimentalSolidity
):
m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter),
m_globalContext(_globalContext)
m_globalContext(_globalContext),
m_experimentalSolidity(_experimentalSolidity)
{
m_scopes[nullptr] = std::make_shared<DeclarationContainer>();
for (Declaration const* declaration: _globalContext.declarations())
Expand Down
5 changes: 4 additions & 1 deletion libsolidity/analysis/NameAndTypeResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class NameAndTypeResolver
NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
langutil::ErrorReporter& _errorReporter
langutil::ErrorReporter& _errorReporter,
bool _experimentalSolidity
);
/// Registers all declarations found in the AST node, usually a source unit.
/// @returns false in case of error.
Expand Down Expand Up @@ -107,6 +108,7 @@ class NameAndTypeResolver
/// Sets the current scope.
void setScope(ASTNode const* _node);

bool experimentalSolidity() const { return m_experimentalSolidity; }
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
Expand All @@ -132,6 +134,7 @@ class NameAndTypeResolver
DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext;
bool m_experimentalSolidity = false;
};

/**
Expand Down
93 changes: 85 additions & 8 deletions libsolidity/analysis/ReferencesResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
if (_varDecl.documentation())
resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());

if (m_resolver.experimentalSolidity())
{
solAssert(!_varDecl.hasTypeName());
if (_varDecl.typeExpression())
{
ScopedSaveAndRestore typeContext{m_typeContext, true};
_varDecl.typeExpression()->accept(*this);
}
if (_varDecl.overrides())
_varDecl.overrides()->accept(*this);
if (_varDecl.value())
_varDecl.value()->accept(*this);
return false;
}

return true;
}

Expand All @@ -120,6 +135,8 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
if (declarations.empty())
{
if (m_resolver.experimentalSolidity() && m_typeContext)
return false;
std::string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
std::string errorMessage = "Undeclared identifier.";
if (!suggestions.empty())
Expand All @@ -140,7 +157,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier)

bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
{
m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
m_functionDefinitions.push_back(&_functionDefinition);

if (_functionDefinition.documentation())
resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation());
Expand All @@ -150,13 +167,13 @@ bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)

void ReferencesResolver::endVisit(FunctionDefinition const&)
{
solAssert(!m_returnParameters.empty(), "");
m_returnParameters.pop_back();
solAssert(!m_functionDefinitions.empty(), "");
m_functionDefinitions.pop_back();
}

bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)
{
m_returnParameters.push_back(nullptr);
m_functionDefinitions.push_back(nullptr);

if (_modifierDefinition.documentation())
resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation());
Expand All @@ -166,8 +183,8 @@ bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)

void ReferencesResolver::endVisit(ModifierDefinition const&)
{
solAssert(!m_returnParameters.empty(), "");
m_returnParameters.pop_back();
solAssert(!m_functionDefinitions.empty(), "");
m_functionDefinitions.pop_back();
}

void ReferencesResolver::endVisit(IdentifierPath const& _path)
Expand Down Expand Up @@ -227,11 +244,30 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)

bool ReferencesResolver::visit(Return const& _return)
{
solAssert(!m_returnParameters.empty(), "");
_return.annotation().functionReturnParameters = m_returnParameters.back();
solAssert(!m_functionDefinitions.empty(), "");
_return.annotation().function = m_functionDefinitions.back();
_return.annotation().functionReturnParameters = m_functionDefinitions.back() ? m_functionDefinitions.back()->returnParameterList().get() : nullptr;
return true;
}

bool ReferencesResolver::visit(BinaryOperation const& _binaryOperation)
{
if (m_resolver.experimentalSolidity())
{
_binaryOperation.leftExpression().accept(*this);
if (_binaryOperation.getOperator() == Token::Colon)
{
ScopedSaveAndRestore typeContext(m_typeContext, !m_typeContext);
_binaryOperation.rightExpression().accept(*this);
}
else
_binaryOperation.rightExpression().accept(*this);
return false;
}
else
return ASTConstVisitor::visit(_binaryOperation);
}

void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
{
solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
Expand All @@ -252,6 +288,47 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{
solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");

if (m_resolver.experimentalSolidity())
{
std::vector<std::string> splitName;
boost::split(splitName, _identifier.name.str(), boost::is_any_of("."));
solAssert(!splitName.empty());
if (splitName.size() > 2)
{
m_errorReporter.declarationError(
4955_error,
nativeLocationOf(_identifier),
"Unsupported identifier in inline assembly."
);
return;
}
std::string name = splitName.front();
auto declarations = m_resolver.nameFromCurrentScope(name);
switch (declarations.size())
{
case 0:
if (splitName.size() > 1)
m_errorReporter.declarationError(
7531_error,
nativeLocationOf(_identifier),
"Unsupported identifier in inline assembly."
);
break;
case 1:
m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
m_yulAnnotation->externalReferences[&_identifier].suffix = splitName.size() > 1 ? splitName.back() : "";
break;
default:
m_errorReporter.declarationError(
5387_error,
nativeLocationOf(_identifier),
"Multiple matching identifiers. Resolving overloaded identifiers is not supported."
);
break;
}
return;
}

static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"};
std::string suffix;
for (std::string const& s: suffixes)
Expand Down
6 changes: 4 additions & 2 deletions libsolidity/analysis/ReferencesResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker
bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(Return const& _return) override;
bool visit(UsingForDirective const& _usingFor) override;
bool visit(BinaryOperation const& _binaryOperation) override;

void operator()(yul::FunctionDefinition const& _function) override;
void operator()(yul::Identifier const& _identifier) override;
Expand All @@ -98,12 +99,13 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion;
/// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;
/// Stack of function definitions.
std::vector<FunctionDefinition const*> m_functionDefinitions;
bool const m_resolveInsideCode;

InlineAssemblyAnnotation* m_yulAnnotation = nullptr;
bool m_yulInsideFunction = false;
bool m_typeContext = false;
};

}
14 changes: 13 additions & 1 deletion libsolidity/analysis/SyntaxChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,9 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)

bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");
if (m_sourceUnit && m_sourceUnit->experimentalSolidity())
// Handled in experimental::SyntaxRestrictor instead.
return true;

if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified())
{
Expand Down Expand Up @@ -498,3 +500,13 @@ bool SyntaxChecker::visit(StructDefinition const& _struct)

return true;
}

bool SyntaxChecker::visitNode(ASTNode const& _node)
{
if (_node.experimentalSolidityOnly())
{
solAssert(m_sourceUnit);
solAssert(m_sourceUnit->experimentalSolidity());
}
return ASTConstVisitor::visitNode(_node);
}
2 changes: 2 additions & 0 deletions libsolidity/analysis/SyntaxChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class SyntaxChecker: private ASTConstVisitor
bool visit(StructDefinition const& _struct) override;
bool visit(Literal const& _literal) override;

bool visitNode(ASTNode const&) override;

langutil::ErrorReporter& m_errorReporter;

bool m_useYulOptimizer = false;
Expand Down
12 changes: 12 additions & 0 deletions libsolidity/ast/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,3 +1057,15 @@ TryCatchClause const* TryStatement::errorClause() const {
TryCatchClause const* TryStatement::fallbackClause() const {
return findClause(m_clauses);
}

/// Experimental Solidity nodes
/// @{
TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const
{
return initAnnotation<TypeClassDefinitionAnnotation>();
}
TypeDeclarationAnnotation& TypeDefinition::annotation() const
{
return initAnnotation<TypeDeclarationAnnotation>();
}
/// @}
Loading

0 comments on commit 8d311b9

Please sign in to comment.