Skip to content

Type inference experiments. #14510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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