diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 8e795b1dc833..61ac4a106610 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -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 diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 3db3f29d5d23..806efa311668 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -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; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index d79c7f193e64..98de0b228d04 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -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) \ \ @@ -292,7 +300,7 @@ namespace TokenTraits constexpr size_t count() { return static_cast(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 || @@ -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 @@ -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); diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 0a8c90f36bbb..3db857241f28 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -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}) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index d6ff63dbf34f..94d0568118b9 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -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(); for (Declaration const* declaration: _globalContext.declarations()) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 6ba591e8ad16..a6cf5ed76096 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -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. @@ -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); @@ -132,6 +134,7 @@ class NameAndTypeResolver DeclarationContainer* m_currentScope = nullptr; langutil::ErrorReporter& m_errorReporter; GlobalContext& m_globalContext; + bool m_experimentalSolidity = false; }; /** diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index e680021331a2..fd7def71459a 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -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; } @@ -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()) @@ -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()); @@ -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()); @@ -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) @@ -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), ""); @@ -252,6 +288,47 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); + if (m_resolver.experimentalSolidity()) + { + std::vector 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 suffixes{"slot", "offset", "length", "address", "selector"}; std::string suffix; for (std::string const& s: suffixes) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 512a681b456b..13752e9c9345 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -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; @@ -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 m_returnParameters; + /// Stack of function definitions. + std::vector m_functionDefinitions; bool const m_resolveInsideCode; InlineAssemblyAnnotation* m_yulAnnotation = nullptr; bool m_yulInsideFunction = false; + bool m_typeContext = false; }; } diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b2dd73cc244c..ac23ca36a8f4 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -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()) { @@ -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); +} diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index f221df09fcac..69bda229f701 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -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; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index c0bf9009186d..bae16967595b 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -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(); +} +TypeDeclarationAnnotation& TypeDefinition::annotation() const +{ + return initAnnotation(); +} +/// @} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index ca23b0d61975..b9cc2118ef54 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -125,6 +125,8 @@ class ASTNode bool operator!=(ASTNode const& _other) const { return !operator==(_other); } ///@} + virtual bool experimentalSolidityOnly() const { return false; } + protected: size_t const m_id = 0; @@ -960,7 +962,8 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ASTPointer const& _parameters, std::vector> _modifiers, ASTPointer const& _returnParameters, - ASTPointer const& _body + ASTPointer const& _body, + ASTPointer const& _experimentalReturnExpression = {} ): CallableDeclaration(_id, _location, _name, _nameLocation, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), StructurallyDocumented(_documentation), @@ -969,10 +972,12 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), - m_body(_body) + m_body(_body), + m_experimentalReturnExpression(_experimentalReturnExpression) { solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); solAssert(isOrdinary() == !name().empty(), ""); + // TODO: assert _returnParameters implies non-experimental _experimentalReturnExpression implies experimental } void accept(ASTVisitor& _visitor) override; @@ -1030,12 +1035,15 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ContractDefinition const* _searchStart = nullptr ) const override; + Expression const* experimentalReturnExpression() const { return m_experimentalReturnExpression.get(); } + private: StateMutability m_stateMutability; bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; + ASTPointer m_experimentalReturnExpression; }; /** @@ -1070,7 +1078,8 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented bool _isIndexed = false, Mutability _mutability = Mutability::Mutable, ASTPointer _overrides = nullptr, - Location _referenceLocation = Location::Unspecified + Location _referenceLocation = Location::Unspecified, + ASTPointer _typeExpression = {} ): Declaration(_id, _location, _name, std::move(_nameLocation), _visibility), StructurallyDocumented(std::move(_documentation)), @@ -1079,15 +1088,18 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented m_isIndexed(_isIndexed), m_mutability(_mutability), m_overrides(std::move(_overrides)), - m_location(_referenceLocation) + m_location(_referenceLocation), + m_typeExpression(std::move(_typeExpression)) { - solAssert(m_typeName, ""); + // TODO: consider still asserting unless we are in experimental solidity. + // solAssert(m_typeName, ""); solAssert(!m_typeExpression, ""); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; + bool hasTypeName() const { return m_typeName != nullptr; } TypeName const& typeName() const { return *m_typeName; } ASTPointer const& value() const { return m_value; } @@ -1142,6 +1154,7 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented /// @returns null when it is not accessible as a function. FunctionTypePointer functionType(bool /*_internal*/) const override; + ASTPointer const& typeExpression() const { return m_typeExpression; } VariableDeclarationAnnotation& annotation() const override; protected: @@ -1157,6 +1170,7 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented Mutability m_mutability = Mutability::Mutable; ASTPointer m_overrides; ///< Contains the override specifier node Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. + ASTPointer m_typeExpression; }; /** @@ -2138,7 +2152,8 @@ class BinaryOperation: public Expression ): Expression(_id, _location), m_left(std::move(_left)), m_operator(_operator), m_right(std::move(_right)) { - solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); + // TODO: assert against colon for non-experimental solidity + solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator) || _operator == Token::Colon || _operator == Token::RightArrow, ""); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; @@ -2449,4 +2464,167 @@ class Literal: public PrimaryExpression /// @} +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener +{ +public: + TypeClassDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _typeVariable, + ASTPointer const& _name, + SourceLocation _nameLocation, + ASTPointer const& _documentation, + std::vector> _subNodes + ): + Declaration(_id, _location, _name, std::move(_nameLocation)), + StructurallyDocumented(_documentation), + m_typeVariable(std::move(_typeVariable)), + m_subNodes(std::move(_subNodes)) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + VariableDeclaration const& typeVariable() const { return *m_typeVariable; } + std::vector> const& subNodes() const { return m_subNodes; } + + TypeClassDefinitionAnnotation& annotation() const override; + + Type const* type() const override { solAssert(false, "Requested type of experimental solidity node."); } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_typeVariable; + std::vector> m_subNodes; +}; + +class TypeClassInstantiation: public ASTNode, public ScopeOpener +{ +public: + TypeClassInstantiation( + int64_t _id, + SourceLocation const& _location, + ASTPointer _typeConstructor, + ASTPointer _argumentSorts, + ASTPointer _class, + std::vector> _subNodes + ): + ASTNode(_id, _location), + m_typeConstructor(std::move(_typeConstructor)), + m_argumentSorts(std::move(_argumentSorts)), + m_class(std::move(_class)), + m_subNodes(std::move(_subNodes)) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + TypeName const& typeConstructor() const { return *m_typeConstructor; } + ParameterList const* argumentSorts() const { return m_argumentSorts.get(); } + TypeClassName const& typeClass() const { return *m_class; } + std::vector> const& subNodes() const { return m_subNodes; } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_typeConstructor; + ASTPointer m_argumentSorts; + ASTPointer m_class; + std::vector> m_subNodes; +}; + +class TypeDefinition: public Declaration, public ScopeOpener +{ +public: + TypeDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _name, + SourceLocation _nameLocation, + ASTPointer _arguments, + ASTPointer _typeExpression + ): + Declaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default), + m_arguments(std::move(_arguments)), + m_typeExpression(std::move(_typeExpression)) + { + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Type const* type() const override { return nullptr; } + + TypeDeclarationAnnotation& annotation() const override; + + ParameterList const* arguments() const { return m_arguments.get(); } + Expression const* typeExpression() const { return m_typeExpression.get(); } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_arguments; + ASTPointer m_typeExpression; +}; + +class TypeClassName: public ASTNode +{ +public: + TypeClassName( + int64_t _id, + SourceLocation const& _location, + std::variant> _name + ): + ASTNode(_id, _location), + m_name(std::move(_name)) + { + if (Token const* token = std::get_if(&_name)) + solAssert(TokenTraits::isBuiltinTypeClassName(*token)); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + bool experimentalSolidityOnly() const override { return true; } + + std::variant> name() const { return m_name; } + +private: + std::variant> m_name; +}; + +class Builtin: public Expression +{ +public: + Builtin( + int64_t _id, + SourceLocation _location, + ASTPointer _nameParameter, + SourceLocation _nameParameterLocation + ): + Expression(_id, std::move(_location)), + m_nameParameter(std::move(_nameParameter)), + m_nameParameterLocation(std::move(_nameParameterLocation)) + { + solAssert(m_nameParameter); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + bool experimentalSolidityOnly() const override { return true; } + + ASTString const& nameParameter() const { return *m_nameParameter; } + SourceLocation const& nameParameterLocation() const { return m_nameParameterLocation; } + +private: + ASTPointer m_nameParameter; + SourceLocation m_nameParameterLocation; +}; + +/// @} + } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9a19907117eb..e68614f464c0 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -245,6 +245,8 @@ struct ReturnAnnotation: StatementAnnotation { /// Reference to the return parameters of the function. ParameterList const* functionReturnParameters = nullptr; + /// Reference to the function containing the return statement. + FunctionDefinition const* function = nullptr; }; struct TypeNameAnnotation: ASTAnnotation @@ -342,4 +344,12 @@ struct FunctionCallAnnotation: ExpressionAnnotation bool tryCall = false; }; +/// Experimental Solidity annotations. +/// Used to integrate with name and type resolution. +/// @{ +struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation +{ +}; +/// @} + } diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 79e6a4ecf217..f7c3d60237d7 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -99,6 +99,15 @@ class ElementaryTypeNameExpression; class Literal; class StructuredDocumentation; +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition; +class TypeClassInstantiation; +class TypeClassName; +class TypeDefinition; +class Builtin; +/// @} + class VariableScope; template diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index 98a368d1958f..c54a2d013ad0 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -1038,6 +1038,15 @@ void ASTJsonExporter::endVisit(EventDefinition const&) m_inEvent = false; } +bool ASTJsonExporter::visitNode(ASTNode const& _node) +{ + solAssert(false, _node.experimentalSolidityOnly() ? + "Attempt to export an AST of experimental solidity." : + "Attempt to export an AST that contains unexpected nodes." + ); + return false; +} + std::string ASTJsonExporter::location(VariableDeclaration::Location _location) { switch (_location) diff --git a/libsolidity/ast/ASTJsonExporter.h b/libsolidity/ast/ASTJsonExporter.h index 71d15ce0a5f2..ff3b94fa28de 100644 --- a/libsolidity/ast/ASTJsonExporter.h +++ b/libsolidity/ast/ASTJsonExporter.h @@ -130,6 +130,7 @@ class ASTJsonExporter: public ASTConstVisitor void endVisit(EventDefinition const&) override; + bool visitNode(ASTNode const& _node) override; private: void setJsonNode( ASTNode const& _node, diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index ef03e6339322..f9196d6b95c8 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -109,6 +109,14 @@ class ASTVisitor virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(Literal& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); } + virtual bool visit(TypeClassInstantiation& _node) { return visitNode(_node); } + virtual bool visit(TypeDefinition& _node) { return visitNode(_node); } + virtual bool visit(TypeClassName& _node) { return visitNode(_node); } + virtual bool visit(Builtin& _node) { return visitNode(_node); } + /// @} virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -165,6 +173,14 @@ class ASTVisitor virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); } + virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassName& _node) { endVisitNode(_node); } + virtual void endVisit(Builtin& _node) { endVisitNode(_node); } + /// @} protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -243,6 +259,14 @@ class ASTConstVisitor virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } virtual bool visit(Literal const& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); } + virtual bool visit(TypeClassInstantiation const& _node) { return visitNode(_node); } + virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); } + virtual bool visit(TypeClassName const& _node) { return visitNode(_node); } + virtual bool visit(Builtin const& _node) { return visitNode(_node); } + /// @} virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -299,6 +323,14 @@ class ASTConstVisitor virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassName const& _node) { endVisitNode(_node); } + virtual void endVisit(Builtin const& _node) { endVisitNode(_node); } + /// @} protected: /// Generic function called by default for each node, to be overridden by derived classes diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 78dc0e5dddda..be9de64db7a2 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -265,6 +265,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) m_parameters->accept(_visitor); if (m_returnParameters) m_returnParameters->accept(_visitor); + if (m_experimentalReturnExpression) + m_experimentalReturnExpression->accept(_visitor); listAccept(m_functionModifiers, _visitor); if (m_body) m_body->accept(_visitor); @@ -283,6 +285,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const m_parameters->accept(_visitor); if (m_returnParameters) m_returnParameters->accept(_visitor); + if (m_experimentalReturnExpression) + m_experimentalReturnExpression->accept(_visitor); listAccept(m_functionModifiers, _visitor); if (m_body) m_body->accept(_visitor); @@ -296,6 +300,8 @@ void VariableDeclaration::accept(ASTVisitor& _visitor) { if (m_typeName) m_typeName->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -310,6 +316,8 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const { if (m_typeName) m_typeName->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -1024,4 +1032,114 @@ void Literal::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +/// Experimental Solidity nodes +/// @{ +void TypeClassDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_typeVariable->accept(_visitor); + if (m_documentation) + m_documentation->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_typeVariable->accept(_visitor); + if (m_documentation) + m_documentation->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassInstantiation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_typeConstructor->accept(_visitor); + if (m_argumentSorts) + m_argumentSorts->accept(_visitor); + m_class->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_typeConstructor->accept(_visitor); + if (m_argumentSorts) + m_argumentSorts->accept(_visitor); + m_class->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_arguments) + m_arguments->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TypeDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_arguments) + m_arguments->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); + } + _visitor.endVisit(*this); +} + + +void TypeClassName::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (auto* path = std::get_if>(&m_name)) + (*path)->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassName::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (auto* path = std::get_if>(&m_name)) + (*path)->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Builtin::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Builtin::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} +/// @} + } diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 672aa4f87958..441700675f5f 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -16,19 +16,189 @@ */ // SPDX-License-Identifier: GPL-3.0 #include - -#include +#include +#include +#include +#include +#include +#include using namespace solidity::langutil; using namespace solidity::frontend::experimental; -bool Analysis::check(std::vector> const&) +// TODO: creating all of them for all nodes up front may be wasteful, we should improve the mechanism. +struct Analysis::AnnotationContainer +{ + TypeClassRegistration::Annotation typeClassRegistrationAnnotation; + TypeRegistration::Annotation typeRegistrationAnnotation; + TypeInference::Annotation typeInferenceAnnotation; +}; + +struct Analysis::GlobalAnnotationContainer +{ + FunctionDependencyAnalysis::GlobalAnnotation functionDependencyGraphAnnotation; + TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation; + TypeRegistration::GlobalAnnotation typeRegistrationAnnotation; + TypeInference::GlobalAnnotation typeInferenceAnnotation; +}; + +template<> +TypeClassRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + +template<> +FunctionDependencyAnalysis::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().functionDependencyGraphAnnotation; +} + +template<> +FunctionDependencyAnalysis::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().functionDependencyGraphAnnotation; +} + +template<> +TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeRegistrationAnnotation; +} + +template<> +TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeRegistrationAnnotation; +} + +template<> +TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeRegistrationAnnotation; +} + +template<> +TypeRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const { - m_errorReporter.error( - 6547_error, - Error::Type::UnimplementedFeatureError, - SourceLocation{}, - "Experimental Analysis is not implemented yet." - ); - return false; + return analysis.annotationContainer(_node).typeRegistrationAnnotation; +} + +template<> +TypeInference::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeInferenceAnnotation; +} + +template<> +TypeInference::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeInferenceAnnotation; +} + +template<> +TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeInferenceAnnotation; +} + +template<> +TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeInferenceAnnotation; +} + +Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node) +{ + solAssert(_node.id() > 0); + size_t id = static_cast(_node.id()); + solAssert(id <= m_maxAstId); + return m_annotations[id]; +} + +Analysis::AnnotationContainer const& Analysis::annotationContainer(ASTNode const& _node) const +{ + solAssert(_node.id() > 0); + size_t id = static_cast(_node.id()); + solAssert(id <= m_maxAstId); + return m_annotations[id]; +} + +Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): + m_errorReporter(_errorReporter), + m_maxAstId(_maxAstId), + m_annotations(std::make_unique(static_cast(_maxAstId + 1))), + m_globalAnnotation(std::make_unique()) +{ +} + +Analysis::~Analysis() +{} + +template +std::tuple...> makeIndexTuple(std::index_sequence) { + return std::make_tuple( std::integral_constant{}...); +} + +bool Analysis::check(std::vector> const& _sourceUnits) +{ + using AnalysisSteps = std::tuple< + SyntaxRestrictor, + TypeClassRegistration, + TypeRegistration, + // TODO move after step introduced in https://github.com/ethereum/solidity/pull/14578, but before TypeInference + FunctionDependencyAnalysis, + TypeInference, + DebugWarner + >; + + return std::apply([&](auto... _indexTuple) { + return ([&](auto&& _step) { + for (auto source: _sourceUnits) + if (!_step.analyze(*source)) + return false; + return true; + }(std::tuple_element_t{*this}) && ...); + }, makeIndexTuple(std::make_index_sequence>{})); + +/* + { + SyntaxRestrictor syntaxRestrictor{*this}; + for (auto source: _sourceUnits) + if (!syntaxRestrictor.analyze(*source)) + return false; + } + + { + TypeRegistration typeRegistration{*this}; + for (auto source: _sourceUnits) + if (!typeRegistration.analyze(*source)) + return false; + } + { + TypeInference typeInference{*this}; + for (auto source: _sourceUnits) + if (!typeInference.analyze(*source)) + return false; + } + return true; + */ } diff --git a/libsolidity/experimental/analysis/Analysis.h b/libsolidity/experimental/analysis/Analysis.h index 729ff2b93d33..dde3a24efb91 100644 --- a/libsolidity/experimental/analysis/Analysis.h +++ b/libsolidity/experimental/analysis/Analysis.h @@ -17,12 +17,16 @@ // SPDX-License-Identifier: GPL-3.0 #pragma once -#include +#include + +#include #include +#include namespace solidity::frontend { class SourceUnit; +class ASTNode; } namespace solidity::langutil @@ -33,17 +37,79 @@ class ErrorReporter; namespace solidity::frontend::experimental { +class TypeSystem; + +class Analysis; + +namespace detail +{ +template +struct AnnotationFetcher +{ + Analysis& analysis; + typename Step::Annotation& get(ASTNode const& _node); + typename Step::GlobalAnnotation& get(); +}; +template +struct ConstAnnotationFetcher +{ + Analysis const& analysis; + typename Step::Annotation const& get(ASTNode const& _node) const; + typename Step::GlobalAnnotation const& get() const; +}; +} + class Analysis { -public: - Analysis(langutil::ErrorReporter& _errorReporter): - m_errorReporter(_errorReporter) - {} + struct AnnotationContainer; + struct GlobalAnnotationContainer; +public: + Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId); + Analysis(Analysis const&) = delete; + ~Analysis(); + Analysis const& operator=(Analysis const&) = delete; bool check(std::vector> const& _sourceUnits); + langutil::ErrorReporter& errorReporter() { return m_errorReporter; } + uint64_t maxAstId() const { return m_maxAstId; } + TypeSystem& typeSystem() { return m_typeSystem; } + TypeSystem const& typeSystem() const { return m_typeSystem; } + + template + typename Step::Annotation& annotation(ASTNode const& _node) + { + return detail::AnnotationFetcher{*this}.get(_node); + } + + template + typename Step::Annotation const& annotation(ASTNode const& _node) const + { + return detail::ConstAnnotationFetcher{*this}.get(_node); + } + + template + typename Step::GlobalAnnotation& annotation() + { + return detail::AnnotationFetcher{*this}.get(); + } + + template + typename Step::GlobalAnnotation const& annotation() const + { + return detail::ConstAnnotationFetcher{*this}.get(); + } + + AnnotationContainer& annotationContainer(ASTNode const& _node); + AnnotationContainer const& annotationContainer(ASTNode const& _node) const; + GlobalAnnotationContainer& annotationContainer() { return *m_globalAnnotation; } + GlobalAnnotationContainer const& annotationContainer() const { return *m_globalAnnotation; } private: langutil::ErrorReporter& m_errorReporter; + TypeSystem m_typeSystem; + uint64_t m_maxAstId = 0; + std::unique_ptr m_annotations; + std::unique_ptr m_globalAnnotation; }; } diff --git a/libsolidity/experimental/analysis/DebugWarner.cpp b/libsolidity/experimental/analysis/DebugWarner.cpp new file mode 100644 index 000000000000..2b5c23b9bed0 --- /dev/null +++ b/libsolidity/experimental/analysis/DebugWarner.cpp @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include + +#include + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +DebugWarner::DebugWarner(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter()) +{} + +bool DebugWarner::analyze(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return !Error::containsErrors(m_errorReporter.errors()); +} + +bool DebugWarner::visitNode(ASTNode const& _node) +{ + std::optional const& inferredType = m_analysis.annotation(_node).type; + if (inferredType.has_value()) + m_errorReporter.info( + 4164_error, + _node.location(), + "Inferred type: " + TypeEnvironmentHelpers{m_analysis.typeSystem().env()}.typeToString(*inferredType) + ); + return true; +} diff --git a/libsolidity/experimental/analysis/DebugWarner.h b/libsolidity/experimental/analysis/DebugWarner.h new file mode 100644 index 000000000000..a7b5ac6d017b --- /dev/null +++ b/libsolidity/experimental/analysis/DebugWarner.h @@ -0,0 +1,42 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +#include + +namespace solidity::frontend::experimental +{ +class Analysis; + +class DebugWarner: public ASTConstVisitor +{ +public: + DebugWarner(Analysis& _analysis); + + bool analyze(ASTNode const& _astRoot); + +private: + bool visitNode(ASTNode const& _node) override; + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp new file mode 100644 index 000000000000..332f7620c9b6 --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +using namespace solidity::frontend::experimental; +using namespace solidity::util; + +FunctionDependencyAnalysis::FunctionDependencyAnalysis(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()) +{ +} + +bool FunctionDependencyAnalysis::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool FunctionDependencyAnalysis::visit(FunctionDefinition const& _functionDefinition) +{ + solAssert(!m_currentFunction); + m_currentFunction = &_functionDefinition; + // Insert a function definition pointer that maps to an empty set; the pointed to set will later be + // populated in ``endVisit(Identifier const& _identifier)`` if ``m_currentFunction`` references another. + auto [_, inserted] = annotation().functionCallGraph.edges.try_emplace( + m_currentFunction, std::set>{} + ); + solAssert(inserted); + return true; +} + +void FunctionDependencyAnalysis::endVisit(FunctionDefinition const&) +{ + m_currentFunction = nullptr; +} + +void FunctionDependencyAnalysis::endVisit(Identifier const& _identifier) +{ + auto const* callee = dynamic_cast(_identifier.annotation().referencedDeclaration); + // Check that the identifier is within a function body and is a function, and add it to the graph + // as an ``m_currentFunction`` -> ``callee`` edge. + if (m_currentFunction && callee) + addEdge(m_currentFunction, callee); +} + +void FunctionDependencyAnalysis::addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee) +{ + annotation().functionCallGraph.edges[_caller].insert(_callee); +} + +FunctionDependencyAnalysis::GlobalAnnotation& FunctionDependencyAnalysis::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h new file mode 100644 index 000000000000..acd3f1b213fa --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h @@ -0,0 +1,57 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class FunctionDependencyAnalysis: private ASTConstVisitor +{ +public: + FunctionDependencyAnalysis(Analysis& _analysis); + bool analyze(SourceUnit const& _sourceUnit); + + struct Annotation {}; + struct GlobalAnnotation + { + FunctionDependencyGraph functionCallGraph; + }; + +private: + bool visit(FunctionDefinition const& _functionDefinition) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(Identifier const& _identifier) override; + void addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee); + GlobalAnnotation& annotation(); + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + FunctionDefinition const* m_currentFunction = nullptr; +}; + +} diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp new file mode 100644 index 000000000000..e93fddcf50ef --- /dev/null +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp @@ -0,0 +1,113 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +SyntaxRestrictor::SyntaxRestrictor(Analysis& _analysis): m_errorReporter(_analysis.errorReporter()) +{} + +bool SyntaxRestrictor::analyze(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return !Error::containsErrors(m_errorReporter.errors()); +} + +bool SyntaxRestrictor::visitNode(ASTNode const& _node) +{ + if (!_node.experimentalSolidityOnly()) + m_errorReporter.syntaxError(9282_error, _node.location(), "Unsupported AST node."); + return false; +} + +bool SyntaxRestrictor::visit(ContractDefinition const& _contractDefinition) +{ + if (_contractDefinition.contractKind() != ContractKind::Contract) + m_errorReporter.syntaxError(9159_error, _contractDefinition.location(), "Only contracts are supported."); + if (!_contractDefinition.baseContracts().empty()) + m_errorReporter.syntaxError(5731_error, _contractDefinition.location(), "Inheritance unsupported."); + return true; +} + +bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) +{ + if (!_functionDefinition.isImplemented()) + m_errorReporter.syntaxError(1741_error, _functionDefinition.location(), "Functions must be implemented."); + if (!_functionDefinition.modifiers().empty()) + m_errorReporter.syntaxError(9988_error, _functionDefinition.location(), "Function may not have modifiers."); + if (_functionDefinition.overrides()) + m_errorReporter.syntaxError(5044_error, _functionDefinition.location(), "Function may not have override specifiers."); + solAssert(!_functionDefinition.returnParameterList()); + if (_functionDefinition.isFree()) + { + if (_functionDefinition.stateMutability() != StateMutability::NonPayable) + m_errorReporter.syntaxError(5714_error, _functionDefinition.location(), "Free functions may not have a mutability."); + } + else + { + if (_functionDefinition.isFallback()) + { + if (_functionDefinition.visibility() != Visibility::External) + m_errorReporter.syntaxError(7341_error, _functionDefinition.location(), "Fallback function must be external."); + } + else + m_errorReporter.syntaxError(4496_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); + } + + return true; +} + +bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + if (_variableDeclarationStatement.declarations().size() == 1) + { + if (!_variableDeclarationStatement.declarations().front()) + m_errorReporter.syntaxError(9658_error, _variableDeclarationStatement.initialValue()->location(), "Variable declaration has to declare a single variable."); + } + else + m_errorReporter.syntaxError(3520_error, _variableDeclarationStatement.initialValue()->location(), "Variable declarations can only declare a single variable."); + return true; +} + +bool SyntaxRestrictor::visit(VariableDeclaration const& _variableDeclaration) +{ + if (_variableDeclaration.value()) + m_errorReporter.syntaxError(1801_error, _variableDeclaration.value()->location(), "Variable declarations with initial value not supported."); + if (_variableDeclaration.isStateVariable()) + m_errorReporter.syntaxError(6388_error, _variableDeclaration.location(), "State variables are not supported."); + if (!_variableDeclaration.isLocalVariable()) + m_errorReporter.syntaxError(8953_error, _variableDeclaration.location(), "Only local variables are supported."); + if (_variableDeclaration.mutability() != VariableDeclaration::Mutability::Mutable) + m_errorReporter.syntaxError(2934_error, _variableDeclaration.location(), "Only mutable variables are supported."); + if (_variableDeclaration.isIndexed()) + m_errorReporter.syntaxError(9603_error, _variableDeclaration.location(), "Indexed variables are not supported."); + if (!_variableDeclaration.noVisibilitySpecified()) + m_errorReporter.syntaxError(8809_error, _variableDeclaration.location(), "Variables with visibility not supported."); + if (_variableDeclaration.overrides()) + m_errorReporter.syntaxError(6175_error, _variableDeclaration.location(), "Variables with override specifier not supported."); + if (_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Unspecified) + m_errorReporter.syntaxError(5360_error, _variableDeclaration.location(), "Variables with reference location not supported."); + return true; +} diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.h b/libsolidity/experimental/analysis/SyntaxRestrictor.h new file mode 100644 index 000000000000..86d2d1b496e3 --- /dev/null +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.h @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +#include +#include + +namespace solidity::frontend::experimental +{ +class Analysis; + +class SyntaxRestrictor: public ASTConstVisitor +{ +public: + SyntaxRestrictor(Analysis& _analysis); + + bool analyze(ASTNode const& _astRoot); + +private: + /// Default visit will reject all AST nodes that are not explicitly allowed. + bool visitNode(ASTNode const& _node) override; + + bool visit(SourceUnit const&) override { return true; } + bool visit(PragmaDirective const&) override { return true; } + bool visit(ImportDirective const&) override { return true; } + bool visit(ContractDefinition const& _contractDefinition) override; + bool visit(FunctionDefinition const& _functionDefinition) override; + bool visit(ExpressionStatement const&) override { return true; } + bool visit(FunctionCall const&) override { return true; } + bool visit(Assignment const&) override { return true; } + bool visit(Block const&) override { return true; } + bool visit(InlineAssembly const&) override { return true; } + bool visit(Identifier const&) override { return true; } + bool visit(IdentifierPath const&) override { return true; } + bool visit(IfStatement const&) override { return true; } + bool visit(VariableDeclarationStatement const&) override; + bool visit(VariableDeclaration const&) override; + bool visit(ElementaryTypeName const&) override { return true; } + bool visit(ParameterList const&) override { return true; } + bool visit(Return const&) override { return true; } + bool visit(MemberAccess const&) override { return true; } + bool visit(BinaryOperation const&) override { return true; } + bool visit(ElementaryTypeNameExpression const&) override { return true; } + bool visit(TupleExpression const&) override { return true; } + bool visit(Literal const&) override { return true; } + + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.cpp b/libsolidity/experimental/analysis/TypeClassRegistration.cpp new file mode 100644 index 000000000000..fe70a44d1e27 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.cpp @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include + +#include + +#include +#include + +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeClassRegistration::TypeClassRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ +} + +bool TypeClassRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeClassRegistration::visit(TypeClassDefinition const& _typeClassDefinition) +{ + std::variant typeClassOrError = m_typeSystem.declareTypeClass( + _typeClassDefinition.name(), + &_typeClassDefinition + ); + + m_analysis.annotation(_typeClassDefinition).typeClass = std::visit( + util::GenericVisitor{ + [](TypeClass _class) -> TypeClass { return _class; }, + [&](std::string _error) -> TypeClass { + m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); + util::unreachable(); + } + }, + typeClassOrError + ); + + return true; +} diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.h b/libsolidity/experimental/analysis/TypeClassRegistration.h new file mode 100644 index 000000000000..c0c1c3549bba --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend::experimental +{ + +class Analysis; +class TypeSystem; + +class TypeClassRegistration: public ASTConstVisitor +{ +public: + struct Annotation + { + // Type classes. + std::optional typeClass; + }; + struct GlobalAnnotation + { + }; + + TypeClassRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + +private: + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp new file mode 100644 index 000000000000..a13d6ebc9403 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -0,0 +1,1211 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeInference::TypeInference(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()), + m_env(&m_typeSystem.env()), + m_voidType(m_typeSystem.type(PrimitiveType::Void, {})), + m_wordType(m_typeSystem.type(PrimitiveType::Word, {})), + m_integerType(m_typeSystem.type(PrimitiveType::Integer, {})), + m_unitType(m_typeSystem.type(PrimitiveType::Unit, {})), + m_boolType(m_typeSystem.type(PrimitiveType::Bool, {})) +{ + TypeSystemHelpers helper{m_typeSystem}; + + auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class) -> TypeClass { + auto result = m_typeSystem.declareTypeClass(_name, nullptr); + if (auto error = std::get_if(&result)) + solAssert(!error, *error); + TypeClass declaredClass = std::get(result); + // TODO: validation? + solAssert(annotation().builtinClassesByName.emplace(_name, _class).second); + return annotation().builtinClasses.emplace(_class, declaredClass).first->second; + }; + + auto registeredTypeClass = [&](BuiltinClass _builtinClass) -> TypeClass { + return annotation().builtinClasses.at(_builtinClass); + }; + + auto defineConversion = [&](BuiltinClass _builtinClass, PrimitiveType _fromType, std::string _functionName) { + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + m_typeSystem.type(_fromType, {}), + m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable + ), + }}; + }; + + auto defineBinaryMonoidalOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + typeVar + ) + }}; + }; + + auto defineBinaryCompareOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + m_typeSystem.type(PrimitiveType::Bool, {}) + ) + }}; + }; + + declareBuiltinClass("integer", BuiltinClass::Integer); + declareBuiltinClass("*", BuiltinClass::Mul); + declareBuiltinClass("+", BuiltinClass::Add); + declareBuiltinClass("==", BuiltinClass::Equal); + declareBuiltinClass("<", BuiltinClass::Less); + declareBuiltinClass("<=", BuiltinClass::LessOrEqual); + declareBuiltinClass(">", BuiltinClass::Greater); + declareBuiltinClass(">=", BuiltinClass::GreaterOrEqual); + + defineConversion(BuiltinClass::Integer, PrimitiveType::Integer, "fromInteger"); + + defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); + defineBinaryMonoidalOperator(BuiltinClass::Add, Token::Add, "add"); + + defineBinaryCompareOperator(BuiltinClass::Equal, Token::Equal, "eq"); + defineBinaryCompareOperator(BuiltinClass::Less, Token::LessThan, "lt"); + defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq"); + defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt"); + defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq"); +} + +bool TypeInference::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeInference::visit(FunctionDefinition const& _functionDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + auto& functionAnnotation = annotation(_functionDefinition); + if (functionAnnotation.type) + return false; + + ScopedSaveAndRestore signatureRestore(m_currentFunctionType, std::nullopt); + + Type argumentsType = m_typeSystem.freshTypeVariable({}); + Type returnType = m_typeSystem.freshTypeVariable({}); + Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(argumentsType, returnType); + + m_currentFunctionType = functionType; + functionAnnotation.type = functionType; + + + _functionDefinition.parameterList().accept(*this); + unify(argumentsType, type(_functionDefinition.parameterList()), _functionDefinition.parameterList().location()); + if (_functionDefinition.experimentalReturnExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _functionDefinition.experimentalReturnExpression()->accept(*this); + unify( + returnType, + type(*_functionDefinition.experimentalReturnExpression()), + _functionDefinition.experimentalReturnExpression()->location() + ); + } + else + unify(returnType, m_unitType, _functionDefinition.location()); + + if (_functionDefinition.isImplemented()) + _functionDefinition.body().accept(*this); + + return false; +} + +void TypeInference::endVisit(Return const& _return) +{ + solAssert(m_currentFunctionType); + Type functionReturnType = std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); + if (_return.expression()) + unify(functionReturnType, type(*_return.expression()), _return.location()); + else + unify(functionReturnType, m_unitType, _return.location()); +} + +void TypeInference::endVisit(ParameterList const& _parameterList) +{ + auto& listAnnotation = annotation(_parameterList); + solAssert(!listAnnotation.type); + listAnnotation.type = TypeSystemHelpers{m_typeSystem}.tupleType( + _parameterList.parameters() | ranges::views::transform([&](auto _arg) { return type(*_arg); }) | ranges::to> + ); +} + +bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + auto& typeClassDefinitionAnnotation = annotation(_typeClassDefinition); + if (typeClassDefinitionAnnotation.type) + return false; + + typeClassDefinitionAnnotation.type = type(&_typeClassDefinition, {}); + + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeClassDefinition.typeVariable().accept(*this); + } + + std::map functionTypes; + + solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); + Type typeVar = m_typeSystem.typeClassVariable(typeClass); + auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; + + for (auto subNode: _typeClassDefinition.subNodes()) + { + subNode->accept(*this); + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + auto functionType = type(*functionDefinition); + if (!functionTypes.emplace(functionDefinition->name(), functionType).second) + m_errorReporter.fatalTypeError(3195_error, functionDefinition->location(), "Function in type class declared multiple times."); + auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); + if (typeVars.size() != 1) + m_errorReporter.fatalTypeError(8379_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); + unify(typeVars.front(), typeVar, functionDefinition->location()); + typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; + } + + annotation().typeClassFunctions[typeClass] = std::move(functionTypes); + + for (auto [functionName, functionType]: functionTypes) + { + TypeEnvironmentHelpers helper{*m_env}; + auto typeVars = helper.typeVars(functionType); + if (typeVars.empty()) + m_errorReporter.typeError(1723_error, _typeClassDefinition.location(), "Function " + functionName + " does not depend on class variable."); + if (typeVars.size() > 2) + m_errorReporter.typeError(6387_error, _typeClassDefinition.location(), "Function " + functionName + " depends on multiple type variables."); + if (!m_env->typeEquals(typeVars.front(), typeVar)) + m_errorReporter.typeError(1807_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); + } + + unify(type(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{typeClass}}), _typeClassDefinition.location()); + for (auto instantiation: m_analysis.annotation(_typeClassDefinition).instantiations | ranges::views::values) + // TODO: recursion-safety? Order of instantiation? + instantiation->accept(*this); + + return false; +} + +bool TypeInference::visit(InlineAssembly const& _inlineAssembly) +{ + // External references have already been resolved in a prior stage and stored in the annotation. + // We run the resolve step again regardless. + yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( + yul::Identifier const& _identifier, + yul::IdentifierContext _context, + bool + ) -> bool + { + if (_context == yul::IdentifierContext::NonExternal) + { + // TODO: do we need this? + // Hack until we can disallow any shadowing: If we found an internal reference, + // clear the external references, so that codegen does not use it. + _inlineAssembly.annotation().externalReferences.erase(& _identifier); + return false; + } + InlineAssemblyAnnotation::ExternalIdentifierInfo* identifierInfo = util::valueOrNullptr(_inlineAssembly.annotation().externalReferences, &_identifier); + if (!identifierInfo) + return false; + Declaration const* declaration = identifierInfo->declaration; + solAssert(!!declaration, ""); + solAssert(identifierInfo->suffix == "", ""); + + unify(type(*declaration), m_wordType, originLocationOf(_identifier)); + identifierInfo->valueSize = 1; + return true; + }; + solAssert(!_inlineAssembly.annotation().analysisInfo, ""); + _inlineAssembly.annotation().analysisInfo = std::make_shared(); + yul::AsmAnalyzer analyzer( + *_inlineAssembly.annotation().analysisInfo, + m_errorReporter, + _inlineAssembly.dialect(), + identifierAccess + ); + if (!analyzer.analyze(_inlineAssembly.operations())) + solAssert(m_errorReporter.hasErrors()); + return false; +} + +bool TypeInference::visit(BinaryOperation const& _binaryOperation) +{ + auto& operationAnnotation = annotation(_binaryOperation); + solAssert(!operationAnnotation.type); + TypeSystemHelpers helper{m_typeSystem}; + switch (m_expressionContext) + { + case ExpressionContext::Term: + if (auto* operatorInfo = util::valueOrNullptr(annotation().operators, _binaryOperation.getOperator())) + { + auto [typeClass, functionName] = *operatorInfo; + // TODO: error robustness? + Type functionType = m_env->fresh(annotation().typeClassFunctions.at(typeClass).at(functionName)); + + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + + Type argTuple = helper.tupleType({type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())}); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.functionType(argTuple, resultType); + unify(functionType, genericFunctionType, _binaryOperation.location()); + + operationAnnotation.type = resultType; + } + else if (_binaryOperation.getOperator() == Token::Colon) + { + _binaryOperation.leftExpression().accept(*this); + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _binaryOperation.rightExpression().accept(*this); + } + Type leftType = type(_binaryOperation.leftExpression()); + unify(leftType, type(_binaryOperation.rightExpression()), _binaryOperation.location()); + operationAnnotation.type = leftType; + } + else + { + m_errorReporter.typeError(4504_error, _binaryOperation.location(), "Binary operation in term context not yet supported."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + return false; + case ExpressionContext::Type: + if (_binaryOperation.getOperator() == Token::Colon) + { + _binaryOperation.leftExpression().accept(*this); + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort}; + _binaryOperation.rightExpression().accept(*this); + } + Type leftType = type(_binaryOperation.leftExpression()); + unify(leftType, type(_binaryOperation.rightExpression()), _binaryOperation.location()); + operationAnnotation.type = leftType; + } + else if (_binaryOperation.getOperator() == Token::RightArrow) + { + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + operationAnnotation.type = helper.functionType(type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())); + } + else if (_binaryOperation.getOperator() == Token::BitOr) + { + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + operationAnnotation.type = helper.sumType({type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())}); + } + else + { + m_errorReporter.typeError(1439_error, _binaryOperation.location(), "Invalid binary operations in type context."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(1017_error, _binaryOperation.location(), "Invalid binary operation in sort context."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + } + return false; +} + +void TypeInference::endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + if (_variableDeclarationStatement.declarations().size () != 1) + { + m_errorReporter.typeError(2655_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported."); + return; + } + Type variableType = type(*_variableDeclarationStatement.declarations().front()); + if (_variableDeclarationStatement.initialValue()) + unify(variableType, type(*_variableDeclarationStatement.initialValue()), _variableDeclarationStatement.location()); +} + +bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) +{ + solAssert(!_variableDeclaration.value()); + auto& variableAnnotation = annotation(_variableDeclaration); + solAssert(!variableAnnotation.type); + + switch (m_expressionContext) + { + case ExpressionContext::Term: + if (_variableDeclaration.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _variableDeclaration.typeExpression()->accept(*this); + variableAnnotation.type = type(*_variableDeclaration.typeExpression()); + return false; + } + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + case ExpressionContext::Type: + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + if (_variableDeclaration.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort}; + _variableDeclaration.typeExpression()->accept(*this); + unify(*variableAnnotation.type, type(*_variableDeclaration.typeExpression()), _variableDeclaration.typeExpression()->location()); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(2399_error, _variableDeclaration.location(), "Variable declaration in sort context."); + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + } + util::unreachable(); +} + +void TypeInference::endVisit(IfStatement const& _ifStatement) +{ + auto& ifAnnotation = annotation(_ifStatement); + solAssert(!ifAnnotation.type); + + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(2015_error, _ifStatement.location(), "If statement outside term context."); + ifAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + unify(type(_ifStatement.condition()), m_boolType, _ifStatement.condition().location()); + + ifAnnotation.type = m_unitType; +} + +void TypeInference::endVisit(Assignment const& _assignment) +{ + auto& assignmentAnnotation = annotation(_assignment); + solAssert(!assignmentAnnotation.type); + + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(4337_error, _assignment.location(), "Assignment outside term context."); + assignmentAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + Type leftType = type(_assignment.leftHandSide()); + unify(leftType, type(_assignment.rightHandSide()), _assignment.location()); + assignmentAnnotation.type = leftType; +} + +experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration) +{ + switch (m_expressionContext) + { + case ExpressionContext::Term: + { + if ( + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) + ) + { + SecondarySourceLocation ssl; + ssl.append("Referenced node.", _declaration.location()); + m_errorReporter.fatalTypeError(3101_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + } + + auto& declarationAnnotation = annotation(_declaration); + if (!declarationAnnotation.type) + _declaration.accept(*this); + + solAssert(declarationAnnotation.type); + + if (dynamic_cast(&_declaration)) + return *declarationAnnotation.type; + else if (dynamic_cast(&_declaration)) + return polymorphicInstance(*declarationAnnotation.type); + else if (dynamic_cast(&_declaration)) + { + solAssert(TypeEnvironmentHelpers{*m_env}.typeVars(*declarationAnnotation.type).empty()); + return *declarationAnnotation.type; + } + else if (dynamic_cast(&_declaration)) + { + // TODO: can we avoid this? + Type type = *declarationAnnotation.type; + if (TypeSystemHelpers{m_typeSystem}.isTypeFunctionType(type)) + type = std::get<1>(TypeSystemHelpers{m_typeSystem}.destTypeFunctionType(type)); + return polymorphicInstance(type); + } + else + solAssert(false); + break; + } + case ExpressionContext::Type: + { + if ( + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) + ) + { + SecondarySourceLocation ssl; + ssl.append("Referenced node.", _declaration.location()); + m_errorReporter.fatalTypeError(2217_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + } + + // TODO: Assert that this is a type class variable declaration? + auto& declarationAnnotation = annotation(_declaration); + if (!declarationAnnotation.type) + _declaration.accept(*this); + + solAssert(declarationAnnotation.type); + + if (dynamic_cast(&_declaration)) + return *declarationAnnotation.type; + else if (dynamic_cast(&_declaration)) + return polymorphicInstance(*declarationAnnotation.type); + else + solAssert(false); + break; + } + case ExpressionContext::Sort: + { + if (auto const* typeClassDefinition = dynamic_cast(&_declaration)) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; + typeClassDefinition->accept(*this); + + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(*typeClassDefinition).typeClass.value(); + return m_typeSystem.freshTypeVariable(Sort{{typeClass}}); + } + else + { + m_errorReporter.typeError(2599_error, _location, "Expected type class."); + return m_typeSystem.freshTypeVariable({}); + } + break; + } + } + util::unreachable(); +} + +bool TypeInference::visit(Identifier const& _identifier) +{ + auto& identifierAnnotation = annotation(_identifier); + solAssert(!identifierAnnotation.type); + + if (auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration) + { + identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifier.location(), *referencedDeclaration); + return false; + } + + switch (m_expressionContext) + { + case ExpressionContext::Term: + // TODO: error handling + solAssert(false); + break; + case ExpressionContext::Type: + // TODO: register free type variable name! + identifierAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + case ExpressionContext::Sort: + // TODO: error handling + solAssert(false); + break; + } + + return false; +} + +void TypeInference::endVisit(TupleExpression const& _tupleExpression) +{ + auto& expressionAnnotation = annotation(_tupleExpression); + solAssert(!expressionAnnotation.type); + + TypeSystemHelpers helper{m_typeSystem}; + auto componentTypes = _tupleExpression.components() | ranges::views::transform([&](auto _expr) -> Type { + auto& componentAnnotation = annotation(*_expr); + solAssert(componentAnnotation.type); + return *componentAnnotation.type; + }) | ranges::to>; + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: + expressionAnnotation.type = helper.tupleType(componentTypes); + break; + case ExpressionContext::Sort: + { + Type type = m_typeSystem.freshTypeVariable({}); + for (auto componentType: componentTypes) + unify(type, componentType, _tupleExpression.location()); + expressionAnnotation.type = type; + break; + } + } +} + +bool TypeInference::visit(IdentifierPath const& _identifierPath) +{ + auto& identifierAnnotation = annotation(_identifierPath); + solAssert(!identifierAnnotation.type); + + if (auto const* referencedDeclaration = _identifierPath.annotation().referencedDeclaration) + { + identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifierPath.location(), *referencedDeclaration); + return false; + } + + // TODO: error handling + solAssert(false); +} + +bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) +{ + ScopedSaveAndRestore activeInstantiations{m_activeInstantiations, m_activeInstantiations + std::set{&_typeClassInstantiation}}; + // Note: recursion is resolved due to special handling during unification. + auto& instantiationAnnotation = annotation(_typeClassInstantiation); + if (instantiationAnnotation.type) + return false; + instantiationAnnotation.type = m_voidType; + std::optional typeClass = std::visit(util::GenericVisitor{ + [&](ASTPointer _typeClassName) -> std::optional { + if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) + { + // visiting the type class will re-visit this instantiation + typeClassDefinition->accept(*this); + // TODO: more error handling? Should be covered by the visit above. + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + return m_analysis.annotation(*typeClassDefinition).typeClass.value(); + } + else + { + m_errorReporter.typeError(9817_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + return std::nullopt; + } + }, + [&](Token _token) -> std::optional { + if (auto builtinClass = builtinClassFromToken(_token)) + if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) + return *typeClass; + m_errorReporter.typeError(2658_error, _typeClassInstantiation.location(), "Invalid type class name."); + return std::nullopt; + } + }, _typeClassInstantiation.typeClass().name()); + if (!typeClass) + return false; + + // TODO: _typeClassInstantiation.typeConstructor().accept(*this); ? + auto typeConstructor = m_analysis.annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; + if (!typeConstructor) + { + m_errorReporter.typeError(2138_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); + return false; + } + + std::vector arguments; + Arity arity{ + {}, + *typeClass + }; + + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + if (_typeClassInstantiation.argumentSorts()) + { + _typeClassInstantiation.argumentSorts()->accept(*this); + auto& argumentSortAnnotation = annotation(*_typeClassInstantiation.argumentSorts()); + solAssert(argumentSortAnnotation.type); + arguments = TypeSystemHelpers{m_typeSystem}.destTupleType(*argumentSortAnnotation.type); + arity.argumentSorts = arguments | ranges::views::transform([&](Type _type) { + return m_env->sort(_type); + }) | ranges::to>; + } + } + + Type instanceType{TypeConstant{*typeConstructor, arguments}}; + + std::map functionTypes; + + for (auto subNode: _typeClassInstantiation.subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + subNode->accept(*this); + if (!functionTypes.emplace(functionDefinition->name(), type(*functionDefinition)).second) + m_errorReporter.typeError(3654_error, subNode->location(), "Duplicate definition of function " + functionDefinition->name() + " during type class instantiation."); + } + + if (auto error = m_typeSystem.instantiateClass(instanceType, arity)) + m_errorReporter.typeError(5094_error, _typeClassInstantiation.location(), *error); + + auto const& classFunctions = annotation().typeClassFunctions.at(*typeClass); + + TypeEnvironment newEnv = m_env->clone(); + if (!newEnv.unify(m_typeSystem.typeClassVariable(*typeClass), instanceType).empty()) + { + m_errorReporter.typeError(4686_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); + return false; + } + + for (auto [name, classFunctionType]: classFunctions) + { + if (!functionTypes.count(name)) + { + m_errorReporter.typeError(6948_error, _typeClassInstantiation.location(), "Missing function: " + name); + continue; + } + Type instanceFunctionType = functionTypes.at(name); + functionTypes.erase(name); + + if (!newEnv.typeEquals(instanceFunctionType, classFunctionType)) + m_errorReporter.typeError(7428_error, _typeClassInstantiation.location(), "Type mismatch for function " + name + " " + TypeEnvironmentHelpers{newEnv}.typeToString(instanceFunctionType) + " != " + TypeEnvironmentHelpers{newEnv}.typeToString(classFunctionType)); + } + + if (!functionTypes.empty()) + m_errorReporter.typeError(4873_error, _typeClassInstantiation.location(), "Additional functions in class instantiation."); + + return false; +} + +bool TypeInference::visit(MemberAccess const& _memberAccess) +{ + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(5195_error, _memberAccess.location(), "Member access outside term context."); + annotation(_memberAccess).type = m_typeSystem.freshTypeVariable({}); + return false; + } + return true; +} + +experimental::Type TypeInference::memberType(Type _type, std::string _memberName, langutil::SourceLocation _location) +{ + Type resolvedType = m_env->resolve(_type); + TypeSystemHelpers helper{m_typeSystem}; + if (helper.isTypeConstant(resolvedType)) + { + auto constructor = std::get<0>(helper.destTypeConstant(resolvedType)); + if (auto* typeMember = util::valueOrNullptr(annotation().members.at(constructor), _memberName)) + return polymorphicInstance(typeMember->type); + else + { + m_errorReporter.typeError(5755_error, _location, fmt::format("Member {} not found in type {}.", _memberName, TypeEnvironmentHelpers{*m_env}.typeToString(_type))); + return m_typeSystem.freshTypeVariable({}); + } + } + else + { + m_errorReporter.typeError(5104_error, _location, "Unsupported member access expression."); + return m_typeSystem.freshTypeVariable({}); + } +} + +void TypeInference::endVisit(MemberAccess const& _memberAccess) +{ + auto& memberAccessAnnotation = annotation(_memberAccess); + solAssert(!memberAccessAnnotation.type); + Type expressionType = type(_memberAccess.expression()); + memberAccessAnnotation.type = memberType(expressionType, _memberAccess.memberName(), _memberAccess.location()); +} + +bool TypeInference::visit(TypeDefinition const& _typeDefinition) +{ + bool isBuiltIn = dynamic_cast(_typeDefinition.typeExpression()); + + TypeSystemHelpers helper{m_typeSystem}; + auto& typeDefinitionAnnotation = annotation(_typeDefinition); + if (typeDefinitionAnnotation.type) + return false; + + if (_typeDefinition.arguments()) + _typeDefinition.arguments()->accept(*this); + + std::vector arguments; + if (_typeDefinition.arguments()) + for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) + arguments.emplace_back(m_typeSystem.freshTypeVariable({})); + + Type definedType = type(&_typeDefinition, arguments); + if (arguments.empty()) + typeDefinitionAnnotation.type = definedType; + else + typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), definedType); + + std::optional underlyingType; + + if (isBuiltIn) + // TODO: This special case should eventually become user-definable. + underlyingType = m_wordType; + else if (_typeDefinition.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeDefinition.typeExpression()->accept(*this); + + underlyingType = annotation(*_typeDefinition.typeExpression()).type; + } + + TypeConstructor constructor = typeConstructor(&_typeDefinition); + auto [members, newlyInserted] = annotation().members.emplace(constructor, std::map{}); + solAssert(newlyInserted, fmt::format("Members of type '{}' are already defined.", m_typeSystem.constructorInfo(constructor).name)); + if (underlyingType) + { + members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)}); + members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)}); + } + + if (helper.isPrimitiveType(definedType, PrimitiveType::Pair)) + { + solAssert(isBuiltIn); + solAssert(arguments.size() == 2); + members->second.emplace("first", TypeMember{helper.functionType(definedType, arguments[0])}); + members->second.emplace("second", TypeMember{helper.functionType(definedType, arguments[1])}); + } + + return false; +} + +bool TypeInference::visit(FunctionCall const&) { return true; } +void TypeInference::endVisit(FunctionCall const& _functionCall) +{ + auto& functionCallAnnotation = annotation(_functionCall); + solAssert(!functionCallAnnotation.type); + + Type functionType = type(_functionCall.expression()); + + TypeSystemHelpers helper{m_typeSystem}; + std::vector argTypes; + for (auto arg: _functionCall.arguments()) + { + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: + argTypes.emplace_back(type(*arg)); + break; + case ExpressionContext::Sort: + m_errorReporter.typeError(9173_error, _functionCall.location(), "Function call in sort context."); + functionCallAnnotation.type = m_typeSystem.freshTypeVariable({}); + break; + } + } + + switch (m_expressionContext) + { + case ExpressionContext::Term: + { + Type argTuple = helper.tupleType(argTypes); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.functionType(argTuple, resultType); + unify(functionType, genericFunctionType, _functionCall.location()); + functionCallAnnotation.type = resultType; + break; + } + case ExpressionContext::Type: + { + Type argTuple = helper.tupleType(argTypes); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.typeFunctionType(argTuple, resultType); + unify(functionType, genericFunctionType, _functionCall.location()); + functionCallAnnotation.type = resultType; + break; + } + case ExpressionContext::Sort: + solAssert(false); + } +} + +// TODO: clean up rational parsing +namespace +{ + +std::optional parseRational(std::string const& _value) +{ + rational value; + try + { + auto radixPoint = find(_value.begin(), _value.end(), '.'); + + if (radixPoint != _value.end()) + { + if ( + !all_of(radixPoint + 1, _value.end(), util::isDigit) || + !all_of(_value.begin(), radixPoint, util::isDigit) + ) + return std::nullopt; + + // Only decimal notation allowed here, leading zeros would switch to octal. + auto fractionalBegin = find_if_not( + radixPoint + 1, + _value.end(), + [](char const& a) { return a == '0'; } + ); + + rational numerator; + rational denominator(1); + + denominator = bigint(std::string(fractionalBegin, _value.end())); + denominator /= boost::multiprecision::pow( + bigint(10), + static_cast(distance(radixPoint + 1, _value.end())) + ); + numerator = bigint(std::string(_value.begin(), radixPoint)); + value = numerator + denominator; + } + else + value = bigint(_value); + return value; + } + catch (...) + { + return std::nullopt; + } +} + +/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) +{ + double const log2Of10AwayFromZero = 3.3219280948873624; + return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); +} + +std::optional rationalValue(Literal const& _literal) +{ + rational value; + try + { + ASTString valueString = _literal.valueWithoutUnderscores(); + + auto expPoint = find(valueString.begin(), valueString.end(), 'e'); + if (expPoint == valueString.end()) + expPoint = find(valueString.begin(), valueString.end(), 'E'); + + if (boost::starts_with(valueString, "0x")) + { + // process as hex + value = bigint(valueString); + } + else if (expPoint != valueString.end()) + { + // Parse mantissa and exponent. Checks numeric limit. + std::optional mantissa = parseRational(std::string(valueString.begin(), expPoint)); + + if (!mantissa) + return std::nullopt; + value = *mantissa; + + // 0E... is always zero. + if (value == 0) + return std::nullopt; + + bigint exp = bigint(std::string(expPoint + 1, valueString.end())); + + if (exp > std::numeric_limits::max() || exp < std::numeric_limits::min()) + return std::nullopt; + + uint32_t expAbs = bigint(abs(exp)).convert_to(); + + if (exp < 0) + { + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return std::nullopt; + value /= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return std::nullopt; + value *= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + } + else + { + // parse as rational number + std::optional tmp = parseRational(valueString); + if (!tmp) + return std::nullopt; + value = *tmp; + } + } + catch (...) + { + return std::nullopt; + } + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Gwei: + value *= bigint("1000000000"); + break; + case Literal::SubDenomination::Ether: + value *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + value *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + value *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + value *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + value *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + value *= bigint("31536000"); + break; + } + + return value; +} +} + +bool TypeInference::visit(Literal const& _literal) +{ + auto& literalAnnotation = annotation(_literal); + if (_literal.token() != Token::Number) + { + m_errorReporter.typeError(4316_error, _literal.location(), "Only number literals are supported."); + return false; + } + std::optional value = rationalValue(_literal); + if (!value) + { + m_errorReporter.typeError(6739_error, _literal.location(), "Invalid number literals."); + return false; + } + if (value->denominator() != 1) + { + m_errorReporter.typeError(2345_error, _literal.location(), "Only integers are supported."); + return false; + } + literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{annotation().builtinClasses.at(BuiltinClass::Integer)}}); + return false; +} + + +namespace +{ +// TODO: put at a nice place to deduplicate. +TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(Analysis const& _analysis, TypeClass _class) +{ + auto const* typeClassDeclaration = _analysis.typeSystem().typeClassDeclaration(_class); + if (typeClassDeclaration) + return _analysis.annotation(*typeClassDeclaration).instantiations; + // TODO: better mechanism than fetching by name. + auto const& annotation = _analysis.annotation(); + auto const& inferenceAnnotation = _analysis.annotation(); + return annotation.builtinClassInstantiations.at( + inferenceAnnotation.builtinClassesByName.at( + _analysis.typeSystem().typeClassName(_class) + ) + ); +} +} + +experimental::Type TypeInference::polymorphicInstance(Type const& _scheme) +{ + return m_env->fresh(_scheme); +} + +void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) +{ + TypeSystemHelpers helper{m_typeSystem}; + auto unificationFailures = m_env->unify(_a, _b); + + if (!m_activeInstantiations.empty()) + { + // TODO: This entire logic is superfluous - I thought mutually recursive dependencies between + // class instantiations are a problem, but in fact they're not, they just resolve to mutually recursive + // functions that are fine. So instead, all instantiations can be registered with the type system directly + // when visiting the type class (assuming that they all work out) - and then all instantiations can be checked + // individually, which should still catch all actual issues (while allowing recursions). + // Original comment: Attempt to resolve interdependencies between type class instantiations. + std::vector missingInstantiations; + bool recursion = false; + bool onlyMissingInstantiations = [&]() { + for (auto failure: unificationFailures) + { + if (auto* sortMismatch = std::get_if(&failure)) + if (helper.isTypeConstant(sortMismatch->type)) + { + TypeConstructor constructor = std::get<0>(helper.destTypeConstant(sortMismatch->type)); + for (auto typeClass: sortMismatch->sort.classes) + { + if (auto const* instantiation = util::valueOrDefault(typeClassInstantiations(m_analysis, typeClass), constructor, nullptr)) + { + if (m_activeInstantiations.count(instantiation)) + { + langutil::SecondarySourceLocation ssl; + for (auto activeInstantiation: m_activeInstantiations) + ssl.append("Involved instantiation", activeInstantiation->location()); + m_errorReporter.typeError( + 3573_error, + _location, + ssl, + "Recursion during type class instantiation." + ); + recursion = true; + return false; + } + missingInstantiations.emplace_back(instantiation); + } + else + return false; + } + continue; + } + return false; + } + return true; + }(); + + if (recursion) + return; + + if (onlyMissingInstantiations) + { + for (auto instantiation: missingInstantiations) + instantiation->accept(*this); + unificationFailures = m_env->unify(_a, _b); + } + } + + for (auto failure: unificationFailures) + { + TypeEnvironmentHelpers envHelper{*m_env}; + std::visit(util::GenericVisitor{ + [&](TypeEnvironment::TypeMismatch _typeMismatch) { + m_errorReporter.typeError( + 8456_error, + _location, + fmt::format( + "Cannot unify {} and {}.", + envHelper.typeToString(_typeMismatch.a), + envHelper.typeToString(_typeMismatch.b) + ) + ); + }, + [&](TypeEnvironment::SortMismatch _sortMismatch) { + m_errorReporter.typeError(3111_error, _location, fmt::format( + "{} does not have sort {}", + envHelper.typeToString(_sortMismatch.type), + TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort) + )); + }, + [&](TypeEnvironment::RecursiveUnification _recursiveUnification) { + m_errorReporter.typeError( + 6460_error, + _location, + fmt::format( + "Recursive unification: {} occurs in {}.", + envHelper.typeToString(_recursiveUnification.var), + envHelper.typeToString(_recursiveUnification.type) + ) + ); + } + }, failure); + } +} + +experimental::Type TypeInference::type(ASTNode const& _node) const +{ + auto result = annotation(_node).type; + solAssert(result); + return *result; +} +TypeConstructor TypeInference::typeConstructor(Declaration const* _type) const +{ + if (auto const& constructor = m_analysis.annotation(*_type).typeConstructor) + return *constructor; + m_errorReporter.fatalTypeError(5904_error, _type->location(), "Unregistered type."); + util::unreachable(); +} +experimental::Type TypeInference::type(Declaration const* _type, std::vector _arguments) const +{ + return m_typeSystem.type(typeConstructor(_type), std::move(_arguments)); +} + +bool TypeInference::visitNode(ASTNode const& _node) +{ + m_errorReporter.fatalTypeError(5348_error, _node.location(), "Unsupported AST node during type inference."); + return false; +} + +TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node) +{ + return m_analysis.annotation(_node); +} + +TypeInference::Annotation const& TypeInference::annotation(ASTNode const& _node) const +{ + return m_analysis.annotation(_node); +} + +TypeInference::GlobalAnnotation& TypeInference::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h new file mode 100644 index 000000000000..df9d0610a9a5 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -0,0 +1,124 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class TypeInference: public ASTConstVisitor +{ +public: + TypeInference(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + + struct Annotation + { + /// Expressions, variable declarations, function declarations. + std::optional type; + }; + struct TypeMember + { + Type type; + }; + struct GlobalAnnotation + { + std::map builtinClasses; + std::map builtinClassesByName; + std::map> typeClassFunctions; + std::map> operators; + std::map> members; + }; + bool visit(Block const&) override { return true; } + bool visit(VariableDeclarationStatement const&) override { return true; } + void endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + bool visit(VariableDeclaration const& _variableDeclaration) override; + + bool visit(FunctionDefinition const& _functionDefinition) override; + bool visit(ParameterList const&) override { return true; } + void endVisit(ParameterList const& _parameterList) override; + bool visit(SourceUnit const&) override { return true; } + bool visit(ContractDefinition const&) override { return true; } + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(ImportDirective const&) override { return true; } + bool visit(PragmaDirective const&) override { return false; } + + bool visit(IfStatement const&) override { return true; } + void endVisit(IfStatement const& _ifStatement) override; + bool visit(ExpressionStatement const&) override { return true; } + bool visit(Assignment const&) override { return true; } + void endVisit(Assignment const& _assignment) override; + bool visit(Identifier const&) override; + bool visit(IdentifierPath const&) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(FunctionCall const& _functionCall) override; + bool visit(Return const&) override { return true; } + void endVisit(Return const& _return) override; + + bool visit(MemberAccess const& _memberAccess) override; + void endVisit(MemberAccess const& _memberAccess) override; + + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; + bool visit(TupleExpression const&) override { return true; } + void endVisit(TupleExpression const& _tupleExpression) override; + bool visit(TypeDefinition const& _typeDefinition) override; + + bool visitNode(ASTNode const& _node) override; + + bool visit(BinaryOperation const& _operation) override; + + bool visit(Literal const& _literal) override; +private: + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; + TypeEnvironment* m_env = nullptr; + Type m_voidType; + Type m_wordType; + Type m_integerType; + Type m_unitType; + Type m_boolType; + std::optional m_currentFunctionType; + + Type type(ASTNode const& _node) const; + + Annotation& annotation(ASTNode const& _node); + Annotation const& annotation(ASTNode const& _node) const; + GlobalAnnotation& annotation(); + + void unify(Type _a, Type _b, langutil::SourceLocation _location = {}); + /// Creates a polymorphic instance of a global type scheme + Type polymorphicInstance(Type const& _scheme); + Type memberType(Type _type, std::string _memberName, langutil::SourceLocation _location = {}); + enum class ExpressionContext { Term, Type, Sort }; + Type handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration); + TypeConstructor typeConstructor(Declaration const* _type) const; + Type type(Declaration const* _type, std::vector _arguments) const; + ExpressionContext m_expressionContext = ExpressionContext::Term; + std::set> m_activeInstantiations; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeRegistration.cpp b/libsolidity/experimental/analysis/TypeRegistration.cpp new file mode 100644 index 000000000000..43fd8d1175b9 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -0,0 +1,211 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include +#include +#include +#include + +#include +#include +#include + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeRegistration::TypeRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ +} + +bool TypeRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition) +{ + if (annotation(_typeClassDefinition).typeConstructor) + return false; + annotation(_typeClassDefinition).typeConstructor = m_typeSystem.declareTypeConstructor( + _typeClassDefinition.name(), + "t_" + *_typeClassDefinition.annotation().canonicalName + "_" + util::toString(_typeClassDefinition.id()), + 0, + &_typeClassDefinition + ); + return true; +} + +bool TypeRegistration::visit(Builtin const& _builtin) +{ + if (annotation(_builtin).typeConstructor) + return false; + + auto primitiveType = [&](std::string _name) -> std::optional { + if (_name == "void") return PrimitiveType::Void; + if (_name == "fun") return PrimitiveType::Function; + if (_name == "unit") return PrimitiveType::Unit; + if (_name == "pair") return PrimitiveType::Pair; + if (_name == "word") return PrimitiveType::Word; + if (_name == "integer") return PrimitiveType::Integer; + if (_name == "bool") return PrimitiveType::Bool; + return std::nullopt; + }(_builtin.nameParameter()); + + if (!primitiveType.has_value()) + m_errorReporter.fatalTypeError( + 7758_error, + _builtin.location(), + "Expected the name of a built-in primitive type." + ); + + annotation(_builtin).typeConstructor = m_typeSystem.constructor(primitiveType.value()); + return true; +} + +void TypeRegistration::endVisit(ElementaryTypeNameExpression const & _typeNameExpression) +{ + if (annotation(_typeNameExpression).typeConstructor) + return; + + // TODO: this is not visited in the ElementaryTypeNameExpression visit - is that intentional? + _typeNameExpression.type().accept(*this); + if (auto constructor = annotation(_typeNameExpression.type()).typeConstructor) + annotation(_typeNameExpression).typeConstructor = constructor; + else + solAssert(m_errorReporter.hasErrors()); +} + +bool TypeRegistration::visit(UserDefinedTypeName const& _userDefinedTypeName) +{ + if (annotation(_userDefinedTypeName).typeConstructor) + return false; + auto const* declaration = _userDefinedTypeName.pathNode().annotation().referencedDeclaration; + if (!declaration) + { + // TODO: fatal/non-fatal + m_errorReporter.fatalTypeError(5096_error, _userDefinedTypeName.pathNode().location(), "Expected declaration."); + return false; + } + declaration->accept(*this); + if (!(annotation(_userDefinedTypeName).typeConstructor = annotation(*declaration).typeConstructor)) + { + // TODO: fatal/non-fatal + m_errorReporter.fatalTypeError(9831_error, _userDefinedTypeName.pathNode().location(), "Expected type declaration."); + return false; + } + return true; +} + +bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation) +{ + if (annotation(_typeClassInstantiation).typeConstructor) + return false; + _typeClassInstantiation.typeConstructor().accept(*this); + auto typeConstructor = annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; + if (!typeConstructor) + { + m_errorReporter.typeError(5577_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name."); + return false; + } + auto* instantiations = std::visit(util::GenericVisitor{ + [&](ASTPointer _path) -> TypeClassInstantiations* + { + if (TypeClassDefinition const* classDefinition = dynamic_cast(_path->annotation().referencedDeclaration)) + return &annotation(*classDefinition).instantiations; + m_errorReporter.typeError(3570_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + return nullptr; + }, + [&](Token _token) -> TypeClassInstantiations* + { + if (auto typeClass = builtinClassFromToken(_token)) + return &annotation().builtinClassInstantiations[*typeClass]; + m_errorReporter.typeError(5262_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + return nullptr; + } + }, _typeClassInstantiation.typeClass().name()); + + if (!instantiations) + return false; + + if ( + auto [instantiation, newlyInserted] = instantiations->emplace(*typeConstructor, &_typeClassInstantiation); + !newlyInserted + ) + { + SecondarySourceLocation ssl; + ssl.append("Previous instantiation.", instantiation->second->location()); + m_errorReporter.typeError(6620_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); + } + + return true; +} + +bool TypeRegistration::visit(TypeDefinition const& _typeDefinition) +{ + return !annotation(_typeDefinition).typeConstructor.has_value(); +} + +void TypeRegistration::endVisit(TypeDefinition const& _typeDefinition) +{ + if (annotation(_typeDefinition).typeConstructor.has_value()) + return; + + if (auto const* builtin = dynamic_cast(_typeDefinition.typeExpression())) + { + auto [previousDefinitionIt, inserted] = annotation().builtinTypeDefinitions.try_emplace( + builtin->nameParameter(), + &_typeDefinition + ); + + if (inserted) + annotation(_typeDefinition).typeConstructor = annotation(*builtin).typeConstructor; + else + { + auto const& [builtinName, previousDefinition] = *previousDefinitionIt; + m_errorReporter.typeError( + 9609_error, + _typeDefinition.location(), + SecondarySourceLocation{}.append("Previous definition:", previousDefinition->location()), + "Duplicate builtin type definition." + ); + } + } + else + annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor( + _typeDefinition.name(), + "t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()), + _typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0, + &_typeDefinition + ); +} + +TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node) +{ + return m_analysis.annotation(_node); +} + +TypeRegistration::GlobalAnnotation& TypeRegistration::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/TypeRegistration.h b/libsolidity/experimental/analysis/TypeRegistration.h new file mode 100644 index 000000000000..9bc49a29cfe4 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.h @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class TypeRegistration: public ASTConstVisitor +{ +public: + using TypeClassInstantiations = std::map; + struct Annotation + { + // For type class definitions. + TypeClassInstantiations instantiations; + // For builtins, type definitions, type class definitions, type names and type name expressions. + std::optional typeConstructor; + }; + struct GlobalAnnotation + { + std::map primitiveClassInstantiations; + std::map builtinClassInstantiations; + std::map builtinTypeDefinitions; + }; + TypeRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); +private: + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; + bool visit(TypeDefinition const& _typeDefinition) override; + void endVisit(TypeDefinition const& _typeDefinition) override; + bool visit(UserDefinedTypeName const& _typeName) override; + void endVisit(ElementaryTypeNameExpression const& _typeName) override; + bool visit(Builtin const& _builtin) override; + Annotation& annotation(ASTNode const& _node); + GlobalAnnotation& annotation(); + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; + std::set m_visitedClasses; +}; + +} diff --git a/libsolidity/experimental/ast/FunctionCallGraph.h b/libsolidity/experimental/ast/FunctionCallGraph.h new file mode 100644 index 000000000000..0a1db845514b --- /dev/null +++ b/libsolidity/experimental/ast/FunctionCallGraph.h @@ -0,0 +1,40 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Data structure representing a function dependency graph. + +#pragma once + +#include + +#include +#include +#include + +namespace solidity::frontend::experimental +{ + +struct FunctionDependencyGraph +{ + /// Graph edges. Edges are directed and lead from the caller to the callee. + /// The map contains a key for every function, even if does not actually perform + /// any calls. + std::map>, ASTCompareByID> edges; +}; + +} diff --git a/libsolidity/experimental/ast/Type.cpp b/libsolidity/experimental/ast/Type.cpp new file mode 100644 index 000000000000..ec822dc64c54 --- /dev/null +++ b/libsolidity/experimental/ast/Type.cpp @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include +#include + +#include + +using namespace solidity; +using namespace solidity::frontend::experimental; + +bool Sort::operator==(Sort const& _rhs) const +{ + if (classes.size() != _rhs.classes.size()) + return false; + for (auto [lhs, rhs]: ranges::zip_view(classes, _rhs.classes)) + if (lhs != rhs) + return false; + return true; +} + +bool Sort::operator<=(Sort const& _rhs) const +{ + for (auto c: classes) + if (!_rhs.classes.count(c)) + return false; + return true; +} + +Sort Sort::operator+(Sort const& _rhs) const +{ + Sort result { classes }; + result.classes += _rhs.classes; + return result; +} + + +Sort Sort::operator-(Sort const& _rhs) const +{ + Sort result { classes }; + result.classes -= _rhs.classes; + return result; +} diff --git a/libsolidity/experimental/ast/Type.h b/libsolidity/experimental/ast/Type.h new file mode 100644 index 000000000000..dde41b1df875 --- /dev/null +++ b/libsolidity/experimental/ast/Type.h @@ -0,0 +1,154 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include +#include +#include + +namespace solidity::frontend::experimental +{ + +class TypeSystem; + +struct TypeConstant; +struct TypeVariable; + +using Type = std::variant; + +enum class PrimitiveType +{ + Void, + Function, + TypeFunction, + Itself, + Unit, + Pair, + Sum, + Word, + Bool, + Integer +}; + +enum class PrimitiveClass +{ + Type +}; + +// TODO: move elsewhere? +enum class BuiltinClass +{ + Integer, + Mul, + Add, + Equal, + Less, + LessOrEqual, + Greater, + GreaterOrEqual +}; + +struct TypeConstructor +{ +public: + TypeConstructor(TypeConstructor const& _typeConstructor): m_index(_typeConstructor.m_index) {} + TypeConstructor& operator=(TypeConstructor const& _typeConstructor) + { + m_index = _typeConstructor.m_index; + return *this; + } + bool operator<(TypeConstructor const& _rhs) const + { + return m_index < _rhs.m_index; + } + bool operator==(TypeConstructor const& _rhs) const + { + return m_index == _rhs.m_index; + } + bool operator!=(TypeConstructor const& _rhs) const + { + return m_index != _rhs.m_index; + } +private: + friend class TypeSystem; + TypeConstructor(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; +}; + +struct TypeConstant +{ + TypeConstructor constructor; + std::vector arguments; +}; + +struct TypeClass +{ +public: + TypeClass(TypeClass const& _typeClass): m_index(_typeClass.m_index) {} + TypeClass& operator=(TypeClass const& _typeConstructor) + { + m_index = _typeConstructor.m_index; + return *this; + } + bool operator<(TypeClass const& _rhs) const + { + return m_index < _rhs.m_index; + } + bool operator==(TypeClass const& _rhs) const + { + return m_index == _rhs.m_index; + } + bool operator!=(TypeClass const& _rhs) const + { + return m_index != _rhs.m_index; + } +private: + friend class TypeSystem; + TypeClass(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; +}; + +struct Sort +{ + std::set classes; + bool operator==(Sort const& _rhs) const; + bool operator!=(Sort const& _rhs) const { return !operator==(_rhs); } + bool operator<=(Sort const& _rhs) const; + Sort operator+(Sort const& _rhs) const; + Sort operator-(Sort const& _rhs) const; +}; + +struct Arity +{ + std::vector argumentSorts; + TypeClass typeClass; +}; + +struct TypeVariable +{ + std::size_t index() const { return m_index; } + Sort const& sort() const { return m_sort; } +private: + friend class TypeSystem; + std::size_t m_index = 0; + Sort m_sort; + TypeVariable(std::size_t _index, Sort _sort): m_index(_index), m_sort(std::move(_sort)) {} +}; + +} diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp new file mode 100644 index 000000000000..680edce845f6 --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -0,0 +1,347 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +std::vector TypeEnvironment::unify(Type _a, Type _b) +{ + std::vector failures; + auto unificationFailure = [&]() { + failures.emplace_back(UnificationFailure{TypeMismatch{_a, _b}}); + }; + _a = resolve(_a); + _b = resolve(_b); + std::visit(util::GenericVisitor{ + [&](TypeVariable _left, TypeVariable _right) { + if (_left.index() == _right.index()) + solAssert(_left.sort() == _right.sort()); + else + { + if (_left.sort() <= _right.sort()) + failures += instantiate(_left, _right); + else if (_right.sort() <= _left.sort()) + failures += instantiate(_right, _left); + else + { + Type newVar = m_typeSystem.freshVariable(_left.sort() + _right.sort()); + failures += instantiate(_left, newVar); + failures += instantiate(_right, newVar); + } + } + }, + [&](TypeVariable _var, auto) { + failures += instantiate(_var, _b); + }, + [&](auto, TypeVariable _var) { + failures += instantiate(_var, _a); + }, + [&](TypeConstant _left, TypeConstant _right) { + if (_left.constructor != _right.constructor) + return unificationFailure(); + if (_left.arguments.size() != _right.arguments.size()) + return unificationFailure(); + for (auto&& [left, right]: ranges::zip_view(_left.arguments, _right.arguments)) + failures += unify(left, right); + }, + [&](auto, auto) { + unificationFailure(); + } + }, _a, _b); + return failures; +} + +bool TypeEnvironment::typeEquals(Type _lhs, Type _rhs) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeVariable _left, TypeVariable _right) { + if (_left.index() == _right.index()) + { + solAssert(_left.sort() == _right.sort()); + return true; + } + return false; + }, + [&](TypeConstant _left, TypeConstant _right) { + if (_left.constructor != _right.constructor) + return false; + if (_left.arguments.size() != _right.arguments.size()) + return false; + for (auto&& [left, right]: ranges::zip_view(_left.arguments, _right.arguments)) + if (!typeEquals(left, right)) + return false; + return true; + }, + [&](auto, auto) { + return false; + } + }, resolve(_lhs), resolve(_rhs)); +} + +TypeEnvironment TypeEnvironment::clone() const +{ + TypeEnvironment result{m_typeSystem}; + result.m_typeVariables = m_typeVariables; + return result; +} + +TypeSystem::TypeSystem() +{ + auto declarePrimitiveClass = [&](std::string _name) { + return std::visit(util::GenericVisitor{ + [](std::string _error) -> TypeClass { + solAssert(false, _error); + }, + [](TypeClass _class) -> TypeClass { return _class; } + }, declareTypeClass(_name, nullptr, true /* _primitive */)); + }; + + m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type")); + + for (auto [type, name, arity]: std::initializer_list>{ + {PrimitiveType::TypeFunction, "tfun", 2}, + {PrimitiveType::Function, "fun", 2}, + {PrimitiveType::Itself, "itself", 1}, + {PrimitiveType::Void, "void", 0}, + {PrimitiveType::Unit, "unit", 0}, + {PrimitiveType::Pair, "pair", 2}, + {PrimitiveType::Sum, "sum", 2}, + {PrimitiveType::Word, "word", 0}, + {PrimitiveType::Integer, "integer", 0}, + {PrimitiveType::Bool, "bool", 0}, + }) + m_primitiveTypeConstructors.emplace(type, declareTypeConstructor(name, name, arity, nullptr)); + + TypeClass classType = primitiveClass(PrimitiveClass::Type); + //TypeClass classKind = primitiveClass(PrimitiveClass::Kind); + Sort typeSort{{classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::TypeFunction).m_index).arities = {Arity{std::vector{{typeSort},{typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{std::vector{{typeSort, typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Itself).m_index).arities = {Arity{std::vector{{typeSort, typeSort}}, classType}}; +} + +experimental::Type TypeSystem::freshVariable(Sort _sort) +{ + size_t index = m_numTypeVariables++; + return TypeVariable(index, std::move(_sort)); +} + +experimental::Type TypeSystem::freshTypeVariable(Sort _sort) +{ + _sort.classes.emplace(primitiveClass(PrimitiveClass::Type)); + return freshVariable(_sort); +} + +std::vector TypeEnvironment::instantiate(TypeVariable _variable, Type _type) +{ + for (auto const& maybeTypeVar: TypeEnvironmentHelpers{*this}.typeVars(_type)) + if (auto const* typeVar = std::get_if(&maybeTypeVar)) + if (typeVar->index() == _variable.index()) + return {UnificationFailure{RecursiveUnification{_variable, _type}}}; + Sort typeSort = sort(_type); + if (!(_variable.sort() <= typeSort)) + { + return {UnificationFailure{SortMismatch{_type, _variable.sort() - typeSort}}}; + } + solAssert(m_typeVariables.emplace(_variable.index(), _type).second); + return {}; +} + +experimental::Type TypeEnvironment::resolve(Type _type) const +{ + Type result = _type; + while (auto const* var = std::get_if(&result)) + if (Type const* resolvedType = util::valueOrNullptr(m_typeVariables, var->index())) + result = *resolvedType; + else + break; + return result; +} + +experimental::Type TypeEnvironment::resolveRecursive(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _typeConstant) -> Type { + return TypeConstant{ + _typeConstant.constructor, + _typeConstant.arguments | ranges::views::transform([&](Type const& _argType) { + return resolveRecursive(_argType); + }) | ranges::to> + }; + }, + [](TypeVariable const& _typeVar) -> Type { + return _typeVar; + }, + [](std::monostate _nothing) -> Type { + return _nothing; + } + }, resolve(_type)); +} + +Sort TypeEnvironment::sort(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _expression) -> Sort + { + auto const& constructorInfo = m_typeSystem.constructorInfo(_expression.constructor); + auto argumentSorts = _expression.arguments | ranges::views::transform([&](Type _argumentType) { + return sort(resolve(_argumentType)); + }) | ranges::to>; + Sort sort; + for (auto const& arity: constructorInfo.arities) + { + solAssert(arity.argumentSorts.size() == argumentSorts.size()); + bool hasArity = true; + for (auto&& [argumentSort, arityArgumentSort]: ranges::zip_view(argumentSorts, arity.argumentSorts)) + { + if (!(arityArgumentSort <= argumentSort)) + { + hasArity = false; + break; + } + } + + if (hasArity) + sort.classes.insert(arity.typeClass); + } + return sort; + }, + [](TypeVariable const& _variable) -> Sort { return _variable.sort(); }, + [](std::monostate) -> Sort { solAssert(false); } + }, _type); +} + +TypeConstructor TypeSystem::declareTypeConstructor(std::string _name, std::string _canonicalName, size_t _arguments, Declaration const* _declaration) +{ + solAssert(m_canonicalTypeNames.insert(_canonicalName).second, "Duplicate canonical type name."); + Sort baseSort{{primitiveClass(PrimitiveClass::Type)}}; + size_t index = m_typeConstructors.size(); + m_typeConstructors.emplace_back(TypeConstructorInfo{ + _name, + _canonicalName, + {Arity{std::vector{_arguments, baseSort}, primitiveClass(PrimitiveClass::Type)}}, + _declaration + }); + TypeConstructor constructor{index}; + if (_arguments) + { + std::vector argumentSorts; + std::generate_n(std::back_inserter(argumentSorts), _arguments, [&](){ return Sort{{primitiveClass(PrimitiveClass::Type)}}; }); + std::vector argumentTypes; + std::generate_n(std::back_inserter(argumentTypes), _arguments, [&](){ return freshVariable({}); }); + auto error = instantiateClass(type(constructor, argumentTypes), Arity{argumentSorts, primitiveClass(PrimitiveClass::Type)}); + solAssert(!error, *error); + } + else + { + auto error = instantiateClass(type(constructor, {}), Arity{{}, primitiveClass(PrimitiveClass::Type)}); + solAssert(!error, *error); + } + return constructor; +} + +std::variant TypeSystem::declareTypeClass(std::string _name, Declaration const* _declaration, bool _primitive) +{ + TypeClass typeClass{m_typeClasses.size()}; + + Type typeVariable = (_primitive ? freshVariable({{typeClass}}) : freshTypeVariable({{typeClass}})); + solAssert(std::holds_alternative(typeVariable)); + + m_typeClasses.emplace_back(TypeClassInfo{ + typeVariable, + _name, + _declaration + }); + return typeClass; +} + +experimental::Type TypeSystem::type(TypeConstructor _constructor, std::vector _arguments) const +{ + // TODO: proper error handling + auto const& info = m_typeConstructors.at(_constructor.m_index); + solAssert( + info.arguments() == _arguments.size(), + fmt::format("Type constructor '{}' accepts {} type arguments (got {}).", constructorInfo(_constructor).name, info.arguments(), _arguments.size()) + ); + return TypeConstant{_constructor, _arguments}; +} + +experimental::Type TypeEnvironment::fresh(Type _type) +{ + std::unordered_map mapping; + auto freshImpl = [&](Type _type, auto _recurse) -> Type { + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) -> Type { + return TypeConstant{ + _type.constructor, + _type.arguments | ranges::views::transform([&](Type _argType) { + return _recurse(_argType, _recurse); + }) | ranges::to> + }; + }, + [&](TypeVariable const& _var) -> Type { + if (auto* mapped = util::valueOrNullptr(mapping, _var.index())) + { + auto* typeVariable = std::get_if(mapped); + solAssert(typeVariable); + // TODO: can there be a mismatch? + solAssert(typeVariable->sort() == _var.sort()); + return *mapped; + } + return mapping[_var.index()] = m_typeSystem.freshTypeVariable(_var.sort()); + }, + [](std::monostate) -> Type { solAssert(false); } + }, resolve(_type)); + }; + return freshImpl(_type, freshImpl); +} + +std::optional TypeSystem::instantiateClass(Type _instanceVariable, Arity _arity) +{ + if (!TypeSystemHelpers{*this}.isTypeConstant(_instanceVariable)) + return "Invalid instance variable."; + auto [typeConstructor, typeArguments] = TypeSystemHelpers{*this}.destTypeConstant(_instanceVariable); + auto& typeConstructorInfo = m_typeConstructors.at(typeConstructor.m_index); + if (_arity.argumentSorts.size() != typeConstructorInfo.arguments()) + return "Invalid arity."; + if (typeArguments.size() != typeConstructorInfo.arguments()) + return "Invalid arity."; + + typeConstructorInfo.arities.emplace_back(_arity); + + return std::nullopt; +} diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h new file mode 100644 index 000000000000..cac0feb81fe2 --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystem.h @@ -0,0 +1,184 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace solidity::frontend +{ +class Declaration; +} + +namespace solidity::frontend::experimental +{ + +class TypeEnvironment +{ +public: + struct TypeMismatch + { + Type a; + Type b; + }; + + struct SortMismatch { + Type type; + Sort sort; + }; + + struct RecursiveUnification + { + Type var; + Type type; + }; + + using UnificationFailure = std::variant< + TypeMismatch, + SortMismatch, + RecursiveUnification + >; + + TypeEnvironment(TypeSystem& _typeSystem): m_typeSystem(_typeSystem) {} + TypeEnvironment(TypeEnvironment const&) = delete; + TypeEnvironment& operator=(TypeEnvironment const&) = delete; + TypeEnvironment clone() const; + + Type resolve(Type _type) const; + Type resolveRecursive(Type _type) const; + Type fresh(Type _type); + [[nodiscard]] std::vector unify(Type _a, Type _b); + Sort sort(Type _type) const; + bool typeEquals(Type _lhs, Type _rhs) const; + + TypeSystem& typeSystem() { return m_typeSystem; } + TypeSystem const& typeSystem() const { return m_typeSystem; } + +private: + TypeEnvironment(TypeEnvironment&& _env): + m_typeSystem(_env.m_typeSystem), + m_typeVariables(std::move(_env.m_typeVariables)) + {} + + [[nodiscard]] std::vector instantiate(TypeVariable _variable, Type _type); + + TypeSystem& m_typeSystem; + std::map m_typeVariables; +}; + +class TypeSystem +{ +public: + struct TypeConstructorInfo + { + std::string name; + std::string canonicalName; + std::vector arities; + Declaration const* typeDeclaration = nullptr; + size_t arguments() const + { + solAssert(!arities.empty()); + return arities.front().argumentSorts.size(); + } + }; + struct TypeClassInfo + { + Type typeVariable; + std::string name; + Declaration const* classDeclaration = nullptr; + }; + TypeSystem(); + TypeSystem(TypeSystem const&) = delete; + TypeSystem const& operator=(TypeSystem const&) = delete; + Type type(PrimitiveType _typeConstructor, std::vector _arguments) const + { + return type(m_primitiveTypeConstructors.at(_typeConstructor), std::move(_arguments)); + } + Type type(TypeConstructor _typeConstructor, std::vector _arguments) const; + std::string typeName(TypeConstructor _typeConstructor) const + { + // TODO: proper error handling + return m_typeConstructors.at(_typeConstructor.m_index).name; + } + std::string canonicalName(TypeConstructor _typeConstructor) const + { + // TODO: proper error handling + return m_typeConstructors.at(_typeConstructor.m_index).canonicalName; + } + TypeConstructor declareTypeConstructor(std::string _name, std::string _canonicalName, size_t _arguments, Declaration const* _declaration); + TypeConstructor constructor(PrimitiveType _type) const + { + return m_primitiveTypeConstructors.at(_type); + } + TypeClass primitiveClass(PrimitiveClass _class) const + { + return m_primitiveTypeClasses.at(_class); + } + size_t constructorArguments(TypeConstructor _typeConstructor) const + { + // TODO: error handling + return m_typeConstructors.at(_typeConstructor.m_index).arguments(); + } + TypeConstructorInfo const& constructorInfo(TypeConstructor _typeConstructor) const + { + // TODO: error handling + return m_typeConstructors.at(_typeConstructor.m_index); + } + TypeConstructorInfo const& constructorInfo(PrimitiveType _typeConstructor) const + { + return constructorInfo(constructor(_typeConstructor)); + } + + std::variant declareTypeClass(std::string _name, Declaration const* _declaration, bool _primitive = false); + [[nodiscard]] std::optional instantiateClass(Type _instanceVariable, Arity _arity); + + Type freshTypeVariable(Sort _sort); + + TypeEnvironment const& env() const { return m_globalTypeEnvironment; } + TypeEnvironment& env() { return m_globalTypeEnvironment; } + + Type freshVariable(Sort _sort); + std::string typeClassName(TypeClass _class) const { return m_typeClasses.at(_class.m_index).name; } + Declaration const* typeClassDeclaration(TypeClass _class) const { return m_typeClasses.at(_class.m_index).classDeclaration; } + Type typeClassVariable(TypeClass _class) const + { + return m_typeClasses.at(_class.m_index).typeVariable; + } + + TypeClassInfo const& typeClassInfo(TypeClass _class) const + { + return m_typeClasses.at(_class.m_index); + } + +private: + friend class TypeEnvironment; + size_t m_numTypeVariables = 0; + std::map m_primitiveTypeConstructors; + std::map m_primitiveTypeClasses; + std::set m_canonicalTypeNames; + std::vector m_typeConstructors; + std::vector m_typeClasses; + TypeEnvironment m_globalTypeEnvironment{*this}; +}; + +} diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp new file mode 100644 index 000000000000..bc7dd4fcc35a --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -0,0 +1,404 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +/*std::optional experimental::typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName) +{ + if (auto const* elementaryTypeName = dynamic_cast(&_typeName)) + { + if (auto constructor = typeConstructorFromToken(_analysis, elementaryTypeName->typeName().token())) + return *constructor; + } + else if (auto const* userDefinedType = dynamic_cast(&_typeName)) + { + if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration) + return _analysis.annotation(*referencedDeclaration).typeConstructor; + } + return nullopt; +}*/ +/* +std::optional experimental::typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token) +{ + TypeSystem const& typeSystem = _analysis.typeSystem(); + switch (_token) + { + case Token::Void: + return typeSystem.builtinConstructor(BuiltinType::Void); + case Token::Fun: + return typeSystem.builtinConstructor(BuiltinType::Function); + case Token::Unit: + return typeSystem.builtinConstructor(BuiltinType::Unit); + case Token::Pair: + return typeSystem.builtinConstructor(BuiltinType::Pair); + case Token::Word: + return typeSystem.builtinConstructor(BuiltinType::Word); + case Token::IntegerType: + return typeSystem.builtinConstructor(BuiltinType::Integer); + case Token::Bool: + return typeSystem.builtinConstructor(BuiltinType::Bool); + default: + return nullopt; + } +}*/ + +std::optional experimental::builtinClassFromToken(langutil::Token _token) +{ + switch (_token) + { + case Token::Integer: + return BuiltinClass::Integer; + case Token::Mul: + return BuiltinClass::Mul; + case Token::Add: + return BuiltinClass::Add; + case Token::Equal: + return BuiltinClass::Equal; + case Token::LessThan: + return BuiltinClass::Less; + case Token::LessThanOrEqual: + return BuiltinClass::LessOrEqual; + case Token::GreaterThan: + return BuiltinClass::Greater; + case Token::GreaterThanOrEqual: + return BuiltinClass::GreaterOrEqual; + default: + return std::nullopt; + } +} +/* +std::optional experimental::typeClassFromTypeClassName(TypeClassName const& _typeClass) +{ + return std::visit(util::GenericVisitor{ + [&](ASTPointer _path) -> optional { + auto classDefinition = dynamic_cast(_path->annotation().referencedDeclaration); + if (!classDefinition) + return nullopt; + return TypeClass{classDefinition}; + }, + [&](Token _token) -> optional { + return typeClassFromToken(_token); + } + }, _typeClass.name()); +} +*/ +experimental::Type TypeSystemHelpers::tupleType(std::vector _elements) const +{ + if (_elements.empty()) + return typeSystem.type(PrimitiveType::Unit, {}); + if (_elements.size() == 1) + return _elements.front(); + Type result = _elements.back(); + for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1)) + result = typeSystem.type(PrimitiveType::Pair, {type, result}); + return result; +} + +std::vector TypeSystemHelpers::destTupleType(Type _tupleType) const +{ + if (!isTypeConstant(_tupleType)) + return {_tupleType}; + TypeConstructor pairConstructor = typeSystem.constructor(PrimitiveType::Pair); + auto [constructor, arguments] = destTypeConstant(_tupleType); + if (constructor == typeSystem.constructor(PrimitiveType::Unit)) + return {}; + if (constructor != pairConstructor) + return {_tupleType}; + solAssert(arguments.size() == 2); + + std::vector result; + result.emplace_back(arguments.front()); + Type tail = arguments.back(); + while (true) + { + if (!isTypeConstant(tail)) + break; + auto [tailConstructor, tailArguments] = destTypeConstant(tail); + if (tailConstructor != pairConstructor) + break; + solAssert(tailArguments.size() == 2); + result.emplace_back(tailArguments.front()); + tail = tailArguments.back(); + } + result.emplace_back(tail); + return result; +} + +experimental::Type TypeSystemHelpers::sumType(std::vector _elements) const +{ + if (_elements.empty()) + return typeSystem.type(PrimitiveType::Void, {}); + if (_elements.size() == 1) + return _elements.front(); + Type result = _elements.back(); + for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1)) + result = typeSystem.type(PrimitiveType::Sum, {type, result}); + return result; +} + +std::vector TypeSystemHelpers::destSumType(Type _tupleType) const +{ + if (!isTypeConstant(_tupleType)) + return {_tupleType}; + TypeConstructor sumConstructor = typeSystem.constructor(PrimitiveType::Sum); + auto [constructor, arguments] = destTypeConstant(_tupleType); + if (constructor == typeSystem.constructor(PrimitiveType::Void)) + return {}; + if (constructor != sumConstructor) + return {_tupleType}; + solAssert(arguments.size() == 2); + + std::vector result; + result.emplace_back(arguments.front()); + Type tail = arguments.back(); + while (true) + { + if (!isTypeConstant(tail)) + break; + auto [tailConstructor, tailArguments] = destTypeConstant(tail); + if (tailConstructor != sumConstructor) + break; + solAssert(tailArguments.size() == 2); + result.emplace_back(tailArguments.front()); + tail = tailArguments.back(); + } + result.emplace_back(tail); + return result; +} + +std::tuple> TypeSystemHelpers::destTypeConstant(Type _type) const +{ + using ResultType = std::tuple>; + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) -> ResultType { + return std::make_tuple(_type.constructor, _type.arguments); + }, + [](auto const&) -> ResultType { + solAssert(false); + } + }, _type); +} + +bool TypeSystemHelpers::isTypeConstant(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const&) -> bool { + return true; + }, + [](auto const&) -> bool { + return false; + } + }, _type); +} + +bool TypeSystemHelpers::isPrimitiveType(Type _type, PrimitiveType _primitiveType) const +{ + if (!isTypeConstant(_type)) + return false; + auto constructor = std::get<0>(destTypeConstant(_type)); + return constructor == typeSystem.constructor(_primitiveType); +} + +experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::Function, {_argType, _resultType}); +} + +std::tuple TypeSystemHelpers::destFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::Function)); + solAssert(arguments.size() == 2); + return std::make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isFunctionType(Type _type) const +{ + return isPrimitiveType(_type, PrimitiveType::Function); +} + +experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::TypeFunction, {_argType, _resultType}); +} + +std::tuple TypeSystemHelpers::destTypeFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::TypeFunction)); + solAssert(arguments.size() == 2); + return std::make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isTypeFunctionType(Type _type) const +{ + return isPrimitiveType(_type, PrimitiveType::TypeFunction); +} + +std::vector TypeEnvironmentHelpers::typeVars(Type _type) const +{ + std::set indices; + std::vector typeVars; + auto typeVarsImpl = [&](Type _type, auto _recurse) -> void { + std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) { + for (auto arg: _type.arguments) + _recurse(arg, _recurse); + }, + [&](TypeVariable const& _var) { + if (indices.emplace(_var.index()).second) + typeVars.emplace_back(_var); + }, + [](std::monostate) { solAssert(false); } + }, env.resolve(_type)); + }; + typeVarsImpl(_type, typeVarsImpl); + return typeVars; + +} + + +std::string TypeSystemHelpers::sortToString(Sort _sort) const +{ + switch (_sort.classes.size()) + { + case 0: + return "()"; + case 1: + return typeSystem.typeClassName(*_sort.classes.begin()); + default: + { + std::stringstream stream; + stream << "("; + for (auto typeClass: _sort.classes | ranges::views::drop_last(1)) + stream << typeSystem.typeClassName(typeClass) << ", "; + stream << typeSystem.typeClassName(*_sort.classes.rbegin()) << ")"; + return stream.str(); + } + } +} + +std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const +{ + return visit(util::GenericVisitor{ + [&](TypeConstant _type) -> std::string { + std::stringstream stream; + stream << env.typeSystem().constructorInfo(_type.constructor).canonicalName; + if (!_type.arguments.empty()) + { + stream << "$"; + for (auto type: _type.arguments | ranges::views::drop_last(1)) + stream << canonicalTypeName(type) << "$"; + stream << canonicalTypeName(_type.arguments.back()); + stream << "$"; + } + return stream.str(); + }, + [](TypeVariable) -> std::string { + solAssert(false); + }, + [](std::monostate) -> std::string { + solAssert(false); + }, + }, env.resolve(_type)); +} + +std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const +{ + std::map)>> formatters{ + {env.typeSystem().constructor(PrimitiveType::Function), [&](auto const& _args) { + solAssert(_args.size() == 2); + return fmt::format("{} -> {}", typeToString(_args.front()), typeToString(_args.back())); + }}, + {env.typeSystem().constructor(PrimitiveType::Unit), [&](auto const& _args) { + solAssert(_args.size() == 0); + return "()"; + }}, + {env.typeSystem().constructor(PrimitiveType::Pair), [&](auto const& _arguments) { + auto tupleTypes = TypeSystemHelpers{env.typeSystem()}.destTupleType(_arguments.back()); + std::string result = "("; + result += typeToString(_arguments.front()); + for (auto type: tupleTypes) + result += ", " + typeToString(type); + result += ")"; + return result; + }}, + }; + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) { + if (auto* formatter = util::valueOrNullptr(formatters, _type.constructor)) + return (*formatter)(_type.arguments); + std::stringstream stream; + stream << env.typeSystem().constructorInfo(_type.constructor).name; + if (!_type.arguments.empty()) + { + stream << "("; + for (auto type: _type.arguments | ranges::views::drop_last(1)) + stream << typeToString(type) << ", "; + stream << typeToString(_type.arguments.back()); + stream << ")"; + } + return stream.str(); + }, + [&](TypeVariable const& _type) { + std::stringstream stream; + std::string varName; + size_t index = _type.index(); + varName += static_cast('a' + (index%26)); + while (index /= 26) + varName += static_cast('a' + (index%26)); + reverse(varName.begin(), varName.end()); + stream << '\'' << varName; + switch (_type.sort().classes.size()) + { + case 0: + break; + case 1: + stream << ":" << env.typeSystem().typeClassName(*_type.sort().classes.begin()); + break; + default: + stream << ":("; + for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1)) + stream << env.typeSystem().typeClassName(typeClass) << ", "; + stream << env.typeSystem().typeClassName(*_type.sort().classes.rbegin()); + stream << ")"; + break; + } + return stream.str(); + }, + [](std::monostate) -> std::string { solAssert(false); } + }, env.resolve(_type)); +} diff --git a/libsolidity/experimental/ast/TypeSystemHelper.h b/libsolidity/experimental/ast/TypeSystemHelper.h new file mode 100644 index 000000000000..fd55c1bf842e --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.h @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include +#include + +namespace solidity::frontend::experimental +{ +class Analysis; +enum class BuiltinClass; +//std::optional typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName); +//std::optional typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token); +//std::optional typeClassFromTypeClassName(TypeClassName const& _typeClass); +std::optional builtinClassFromToken(langutil::Token _token); + +struct TypeSystemHelpers +{ + TypeSystem const& typeSystem; + std::tuple> destTypeConstant(Type _type) const; + bool isTypeConstant(Type _type) const; + bool isPrimitiveType(Type _type, PrimitiveType _primitiveType) const; + Type tupleType(std::vector _elements) const; + std::vector destTupleType(Type _tupleType) const; + Type sumType(std::vector _elements) const; + std::vector destSumType(Type _tupleType) const; + Type functionType(Type _argType, Type _resultType) const; + std::tuple destFunctionType(Type _functionType) const; + bool isFunctionType(Type _type) const; + Type typeFunctionType(Type _argType, Type _resultType) const; + std::tuple destTypeFunctionType(Type _functionType) const; + bool isTypeFunctionType(Type _type) const; + std::string sortToString(Sort _sort) const; +}; + +struct TypeEnvironmentHelpers +{ + TypeEnvironment const& env; + std::string typeToString(Type const& _type) const; + std::string canonicalTypeName(Type _type) const; + std::vector typeVars(Type _type) const; +}; + +} diff --git a/libsolidity/experimental/codegen/Common.cpp b/libsolidity/experimental/codegen/Common.cpp new file mode 100644 index 000000000000..de50fd47c682 --- /dev/null +++ b/libsolidity/experimental/codegen/Common.cpp @@ -0,0 +1,73 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include + +#include + +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::util; +using namespace solidity::yul; + +namespace solidity::frontend::experimental +{ + +std::string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type) +{ + if (_function.isConstructor()) + return constructor(*_function.annotation().contract); + + return "fun_" + _function.name() + "_" + std::to_string(_function.id()) + "$" + TypeEnvironmentHelpers{_env}.canonicalTypeName(_type) + "$"; +} + +std::string IRNames::function(VariableDeclaration const& _varDecl) +{ + return "getter_fun_" + _varDecl.name() + "_" + std::to_string(_varDecl.id()); +} + +std::string IRNames::creationObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()); +} + +std::string IRNames::deployedObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; +} + +std::string IRNames::constructor(ContractDefinition const& _contract) +{ + return "constructor_" + _contract.name() + "_" + std::to_string(_contract.id()); +} + +std::string IRNames::localVariable(VariableDeclaration const& _declaration) +{ + return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id()); +} + +std::string IRNames::localVariable(Expression const& _expression) +{ + return "expr_" + std::to_string(_expression.id()); +} + +} diff --git a/libsolidity/experimental/codegen/Common.h b/libsolidity/experimental/codegen/Common.h new file mode 100644 index 000000000000..25f05efcbf09 --- /dev/null +++ b/libsolidity/experimental/codegen/Common.h @@ -0,0 +1,41 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include + +namespace solidity::frontend::experimental +{ + +struct IRNames +{ + static std::string function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type); + static std::string function(VariableDeclaration const& _varDecl); + static std::string creationObject(ContractDefinition const& _contract); + static std::string deployedObject(ContractDefinition const& _contract); + static std::string constructor(ContractDefinition const& _contract); + static std::string localVariable(VariableDeclaration const& _declaration); + static std::string localVariable(Expression const& _expression); +}; + +} diff --git a/libsolidity/experimental/codegen/IRGenerationContext.h b/libsolidity/experimental/codegen/IRGenerationContext.h new file mode 100644 index 000000000000..b0958cf6780a --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerationContext.h @@ -0,0 +1,55 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include + +#include +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +struct IRGenerationContext +{ + Analysis const& analysis; + TypeEnvironment const* env = nullptr; + void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition, Type _type) + { + QueuedFunction queue{_functionDefinition, env->resolve(_type)}; + for (auto type: generatedFunctions[_functionDefinition]) + if (env->typeEquals(type, _type)) + return; + functionQueue.emplace_back(queue); + } + struct QueuedFunction + { + FunctionDefinition const* function = nullptr; + Type type = std::monostate{}; + }; + std::list functionQueue; + std::map> generatedFunctions; +}; + +} diff --git a/libsolidity/experimental/codegen/IRGenerator.cpp b/libsolidity/experimental/codegen/IRGenerator.cpp new file mode 100644 index 000000000000..257bd2be16ba --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerator.cpp @@ -0,0 +1,160 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; +using namespace solidity::util; + +IRGenerator::IRGenerator( + EVMVersion _evmVersion, + std::optional _eofVersion, + frontend::RevertStrings, std::map, + DebugInfoSelection const&, + CharStreamProvider const*, + Analysis const& _analysis +) +: +m_evmVersion(_evmVersion), +m_eofVersion(_eofVersion), +// m_debugInfoSelection(_debugInfoSelection), +// m_soliditySourceProvider(_soliditySourceProvider), +m_env(_analysis.typeSystem().env().clone()), +m_context{_analysis, &m_env, {}, {}} +{ +} + +std::string IRGenerator::run( + ContractDefinition const& _contract, + bytes const& /*_cborMetadata*/, + std::map const& /*_otherYulSources*/ +) +{ + + Whiskers t(R"( + object "" { + code { + codecopy(0, dataoffset(""), datasize("")) + return(0, datasize("")) + } + object "" { + code { + + } + } + } + )"); + t("CreationObject", IRNames::creationObject(_contract)); + t("DeployedObject", IRNames::deployedObject(_contract)); + t("code", generate(_contract)); + + return t.render(); +} + +std::string IRGenerator::generate(ContractDefinition const& _contract) +{ + std::stringstream code; + code << "{\n"; + if (_contract.fallbackFunction()) + { + auto type = m_context.analysis.annotation(*_contract.fallbackFunction()).type; + solAssert(type); + type = m_context.env->resolve(*type); + code << IRNames::function(*m_context.env, *_contract.fallbackFunction(), *type) << "()\n"; + m_context.enqueueFunctionDefinition(_contract.fallbackFunction(), *type); + } + code << "revert(0,0)\n"; + code << "}\n"; + + while (!m_context.functionQueue.empty()) + { + auto queueEntry = m_context.functionQueue.front(); + m_context.functionQueue.pop_front(); + auto& generatedTypes = m_context.generatedFunctions.insert(std::make_pair(queueEntry.function, std::vector{})).first->second; + if (!util::contains_if(generatedTypes, [&](auto const& _generatedType) { return m_context.env->typeEquals(_generatedType, queueEntry.type); })) + { + generatedTypes.emplace_back(queueEntry.type); + code << generate(*queueEntry.function, queueEntry.type); + } + } + + return code.str(); +} + +std::string IRGenerator::generate(FunctionDefinition const& _function, Type _type) +{ + TypeEnvironment newEnv = m_context.env->clone(); + ScopedSaveAndRestore envRestore{m_context.env, &newEnv}; + auto type = m_context.analysis.annotation(_function).type; + solAssert(type); + for (auto err: newEnv.unify(*type, _type)) + { + TypeEnvironmentHelpers helper{newEnv}; + solAssert(false, helper.typeToString(*type) + " <-> " + helper.typeToString(_type)); + } + std::stringstream code; + code << "function " << IRNames::function(newEnv, _function, _type) << "("; + if (_function.parameters().size() > 1) + for (auto const& arg: _function.parameters() | ranges::views::drop_last(1)) + code << IRNames::localVariable(*arg) << ", "; + if (!_function.parameters().empty()) + code << IRNames::localVariable(*_function.parameters().back()); + code << ")"; + if (_function.experimentalReturnExpression()) + { + auto returnType = m_context.analysis.annotation(*_function.experimentalReturnExpression()).type; + solAssert(returnType); + if (!m_env.typeEquals(*returnType, m_context.analysis.typeSystem().type(PrimitiveType::Unit, {}))) + { + // TODO: destructure tuples. + code << " -> " << IRNames::localVariable(*_function.experimentalReturnExpression()) << " "; + } + } + code << "{\n"; + for (auto _statement: _function.body().statements()) + { + IRGeneratorForStatements statementGenerator{m_context}; + code << statementGenerator.generate(*_statement); + } + code << "}\n"; + return code.str(); +} diff --git a/libsolidity/experimental/codegen/IRGenerator.h b/libsolidity/experimental/codegen/IRGenerator.h new file mode 100644 index 000000000000..cce8c730572b --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerator.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class IRGenerator +{ +public: + IRGenerator( + langutil::EVMVersion _evmVersion, + std::optional _eofVersion, + RevertStrings /*_revertStrings*/, + std::map /*_sourceIndices*/, + langutil::DebugInfoSelection const& /*_debugInfoSelection*/, + langutil::CharStreamProvider const* /*_soliditySourceProvider*/, + Analysis const& _analysis + ); + + std::string run( + ContractDefinition const& _contract, + bytes const& _cborMetadata, + std::map const& _otherYulSources + ); + + std::string generate(ContractDefinition const& _contract); + std::string generate(FunctionDefinition const& _function, Type _type); +private: + langutil::EVMVersion const m_evmVersion; + std::optional const m_eofVersion; + OptimiserSettings const m_optimiserSettings; +// langutil::DebugInfoSelection m_debugInfoSelection = {}; +// langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; + TypeEnvironment m_env; + IRGenerationContext m_context; +}; + +} diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp new file mode 100644 index 000000000000..cb4705f0d189 --- /dev/null +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -0,0 +1,393 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace std::string_literals; + +std::string IRGeneratorForStatements::generate(ASTNode const& _node) +{ + _node.accept(*this); + return m_code.str(); +} + + +namespace +{ + +struct CopyTranslate: public yul::ASTCopier +{ + CopyTranslate( + IRGenerationContext const& _context, + yul::Dialect const& _dialect, + std::map _references + ): m_context(_context), m_dialect(_dialect), m_references(std::move(_references)) {} + + using ASTCopier::operator(); + + yul::Expression operator()(yul::Identifier const& _identifier) override + { + // The operator() function is only called in lvalue context. In rvalue context, + // only translate(yul::Identifier) is called. + if (m_references.count(&_identifier)) + return translateReference(_identifier); + else + return ASTCopier::operator()(_identifier); + } + + yul::YulString translateIdentifier(yul::YulString _name) override + { + if (m_dialect.builtin(_name)) + return _name; + else + return yul::YulString{"usr$" + _name.str()}; + } + + yul::Identifier translate(yul::Identifier const& _identifier) override + { + if (!m_references.count(&_identifier)) + return ASTCopier::translate(_identifier); + + yul::Expression translated = translateReference(_identifier); + solAssert(std::holds_alternative(translated)); + return std::get(std::move(translated)); + } + +private: + + /// Translates a reference to a local variable, potentially including + /// a suffix. Might return a literal, which causes this to be invalid in + /// lvalue-context. + yul::Expression translateReference(yul::Identifier const& _identifier) + { + auto const& reference = m_references.at(&_identifier); + auto const varDecl = dynamic_cast(reference.declaration); + solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration."); + auto type = m_context.analysis.annotation(*varDecl).type; + solAssert(type); + solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {}))); + std::string value = IRNames::localVariable(*varDecl); + return yul::Identifier{_identifier.debugData, yul::YulString{value}}; + } + + IRGenerationContext const& m_context; + yul::Dialect const& m_dialect; + std::map m_references; +}; + +} + +bool IRGeneratorForStatements::visit(TupleExpression const& _tupleExpression) +{ + std::vector components; + for (auto const& component: _tupleExpression.components()) + { + solUnimplementedAssert(component); + component->accept(*this); + components.emplace_back(IRNames::localVariable(*component)); + } + + solUnimplementedAssert(false, "No support for tuples."); + + return false; +} + +bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly) +{ + CopyTranslate bodyCopier{m_context, _assembly.dialect(), _assembly.annotation().externalReferences}; + yul::Statement modified = bodyCopier(_assembly.operations()); + solAssert(std::holds_alternative(modified)); + m_code << yul::AsmPrinter()(std::get(modified)) << "\n"; + return false; +} + +bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + if (_variableDeclarationStatement.initialValue()) + _variableDeclarationStatement.initialValue()->accept(*this); + solAssert(_variableDeclarationStatement.declarations().size() == 1, "multi variable declarations not supported"); + VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get(); + solAssert(variableDeclaration); + // TODO: check the type of the variable; register local variable; initialize + m_code << "let " << IRNames::localVariable(*variableDeclaration); + if (_variableDeclarationStatement.initialValue()) + m_code << " := " << IRNames::localVariable(*_variableDeclarationStatement.initialValue()); + m_code << "\n"; + return false; +} + +bool IRGeneratorForStatements::visit(ExpressionStatement const&) +{ + return true; +} + +bool IRGeneratorForStatements::visit(Identifier const& _identifier) +{ + if (auto const* var = dynamic_cast(_identifier.annotation().referencedDeclaration)) + { + m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*var) << "\n"; + } + else if (auto const* function = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, function).second); + else if (auto const* typeClass = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, typeClass).second); + else if (auto const* typeDefinition = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, typeDefinition).second); + else + solAssert(false, "Unsupported Identifier"); + return false; +} + +void IRGeneratorForStatements::endVisit(Return const& _return) +{ + if (Expression const* value = _return.expression()) + { + solAssert(_return.annotation().function, "Invalid return."); + solAssert(_return.annotation().function->experimentalReturnExpression(), "Invalid return."); + m_code << IRNames::localVariable(*_return.annotation().function->experimentalReturnExpression()) << " := " << IRNames::localVariable(*value) << "\n"; + } + + m_code << "leave\n"; +} + +experimental::Type IRGeneratorForStatements::type(ASTNode const& _node) const +{ + auto type = m_context.analysis.annotation(_node).type; + solAssert(type); + return *type; +} + +void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + Type leftType = type(_binaryOperation.leftExpression()); + Type rightType = type(_binaryOperation.rightExpression()); + Type resultType = type(_binaryOperation); + Type functionType = helper.functionType(helper.tupleType({leftType, rightType}), resultType); + auto [typeClass, memberName] = m_context.analysis.annotation().operators.at(_binaryOperation.getOperator()); + auto const& functionDefinition = resolveTypeClassFunction(typeClass, memberName, functionType); + // TODO: deduplicate with FunctionCall + // TODO: get around resolveRecursive by passing the environment further down? + functionType = m_context.env->resolveRecursive(functionType); + m_context.enqueueFunctionDefinition(&functionDefinition, functionType); + // TODO: account for return stack size + m_code << "let " << IRNames::localVariable(_binaryOperation) << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "(" + << IRNames::localVariable(_binaryOperation.leftExpression()) << ", " << IRNames::localVariable(_binaryOperation.rightExpression()) << ")\n"; +} + +namespace +{ +TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGenerationContext const& _context, TypeClass _class) +{ + auto const* typeClassDeclaration = _context.analysis.typeSystem().typeClassDeclaration(_class); + if (typeClassDeclaration) + return _context.analysis.annotation(*typeClassDeclaration).instantiations; + // TODO: better mechanism than fetching by name. + auto& instantiations = _context.analysis.annotation().builtinClassInstantiations; + auto& builtinClassesByName = _context.analysis.annotation().builtinClassesByName; + return instantiations.at(builtinClassesByName.at(_context.analysis.typeSystem().typeClassName(_class))); +} +} + +FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + + TypeEnvironment env = m_context.env->clone(); + Type genericFunctionType = env.fresh(m_context.analysis.annotation().typeClassFunctions.at(_class).at(_name)); + auto typeVars = TypeEnvironmentHelpers{env}.typeVars(genericFunctionType); + solAssert(typeVars.size() == 1); + solAssert(env.unify(genericFunctionType, _type).empty()); + auto typeClassInstantiation = std::get<0>(helper.destTypeConstant(env.resolve(typeVars.front()))); + + auto const& instantiations = typeClassInstantiations(m_context, _class); + TypeClassInstantiation const* instantiation = instantiations.at(typeClassInstantiation); + FunctionDefinition const* functionDefinition = nullptr; + for (auto const& node: instantiation->subNodes()) + { + auto const* def = dynamic_cast(node.get()); + solAssert(def); + if (def->name() == _name) + { + functionDefinition = def; + break; + } + } + solAssert(functionDefinition); + return *functionDefinition; +} + +void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + // TODO: avoid resolve? + auto expressionType = m_context.env->resolve(type(_memberAccess.expression())); + auto constructor = std::get<0>(helper.destTypeConstant(expressionType)); + auto memberAccessType = type(_memberAccess); + // TODO: better mechanism + if (constructor == m_context.analysis.typeSystem().constructor(PrimitiveType::Bool)) + { + if (_memberAccess.memberName() == "abs") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::ToBool).second); + else if (_memberAccess.memberName() == "rep") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::FromBool).second); + return; + } + auto const* declaration = m_context.analysis.typeSystem().constructorInfo(constructor).typeDeclaration; + solAssert(declaration); + if (auto const* typeClassDefinition = dynamic_cast(declaration)) + { + solAssert(m_context.analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass.value(); + solAssert(m_expressionDeclaration.emplace( + &_memberAccess, + &resolveTypeClassFunction(typeClass, _memberAccess.memberName(), memberAccessType) + ).second); + } + else if (dynamic_cast(declaration)) + { + if (_memberAccess.memberName() == "abs" || _memberAccess.memberName() == "rep") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::Identity).second); + else + solAssert(false); + } + else + solAssert(false); +} + +bool IRGeneratorForStatements::visit(ElementaryTypeNameExpression const&) +{ + // TODO: is this always a no-op? + return false; +} + +void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) +{ + Type functionType = type(_functionCall.expression()); + solUnimplementedAssert(m_expressionDeclaration.count(&_functionCall.expression()) != 0, "No support for calling functions pointers yet."); + auto declaration = m_expressionDeclaration.at(&_functionCall.expression()); + if (auto builtin = std::get_if(&declaration)) + { + switch (*builtin) + { + case Builtins::FromBool: + case Builtins::Identity: + solAssert(_functionCall.arguments().size() == 1); + m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::localVariable(*_functionCall.arguments().front()) << "\n"; + return; + case Builtins::ToBool: + solAssert(_functionCall.arguments().size() == 1); + m_code << "let " << IRNames::localVariable(_functionCall) << " := iszero(iszero(" << IRNames::localVariable(*_functionCall.arguments().front()) << "))\n"; + return; + } + solAssert(false); + } + FunctionDefinition const* functionDefinition = dynamic_cast(std::get(declaration)); + solAssert(functionDefinition); + // TODO: get around resolveRecursive by passing the environment further down? + functionType = m_context.env->resolveRecursive(functionType); + m_context.enqueueFunctionDefinition(functionDefinition, functionType); + // TODO: account for return stack size + solAssert(!functionDefinition->returnParameterList()); + if (functionDefinition->experimentalReturnExpression()) + m_code << "let " << IRNames::localVariable(_functionCall) << " := "; + m_code << IRNames::function(*m_context.env, *functionDefinition, functionType) << "("; + auto const& arguments = _functionCall.arguments(); + if (arguments.size() > 1) + for (auto arg: arguments | ranges::views::drop_last(1)) + m_code << IRNames::localVariable(*arg) << ", "; + if (!arguments.empty()) + m_code << IRNames::localVariable(*arguments.back()); + m_code << ")\n"; +} + +bool IRGeneratorForStatements::visit(FunctionCall const&) +{ + return true; +} + +bool IRGeneratorForStatements::visit(Block const& _block) +{ + m_code << "{\n"; + solAssert(!_block.unchecked()); + for (auto const& statement: _block.statements()) + statement->accept(*this); + m_code << "}\n"; + return false; +} + +bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) +{ + _ifStatement.condition().accept(*this); + if (_ifStatement.falseStatement()) + { + m_code << "switch " << IRNames::localVariable(_ifStatement.condition()) << " {\n"; + m_code << "case 0 {\n"; + _ifStatement.falseStatement()->accept(*this); + m_code << "}\n"; + m_code << "default {\n"; + _ifStatement.trueStatement().accept(*this); + m_code << "}\n"; + } + else + { + m_code << "if " << IRNames::localVariable(_ifStatement.condition()) << " {\n"; + _ifStatement.trueStatement().accept(*this); + m_code << "}\n"; + } + return false; +} + +bool IRGeneratorForStatements::visit(Assignment const& _assignment) +{ + _assignment.rightHandSide().accept(*this); + auto const* lhs = dynamic_cast(&_assignment.leftHandSide()); + solAssert(lhs, "Can only assign to identifiers."); + auto const* lhsVar = dynamic_cast(lhs->annotation().referencedDeclaration); + solAssert(lhsVar, "Can only assign to identifiers referring to variables."); + m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n"; + + m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n"; + return false; +} + + +bool IRGeneratorForStatements::visitNode(ASTNode const&) +{ + solAssert(false, "Unsupported AST node during statement code generation."); +} diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.h b/libsolidity/experimental/codegen/IRGeneratorForStatements.h new file mode 100644 index 000000000000..527a6abe8562 --- /dev/null +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.h @@ -0,0 +1,71 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include + +namespace solidity::frontend::experimental +{ +class Analysis; + +class IRGeneratorForStatements: public ASTConstVisitor +{ +public: + IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {} + + std::string generate(ASTNode const& _node); +private: + bool visit(ExpressionStatement const& _expressionStatement) override; + bool visit(Block const& _block) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(Assignment const& _assignment) override; + bool visit(Identifier const& _identifier) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(FunctionCall const& _functionCall) override; + bool visit(ElementaryTypeNameExpression const& _elementaryTypeNameExpression) override; + bool visit(MemberAccess const&) override { return true; } + bool visit(TupleExpression const&) override; + void endVisit(MemberAccess const& _memberAccess) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(BinaryOperation const&) override { return true; } + void endVisit(BinaryOperation const& _binaryOperation) override; + bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + bool visit(Return const&) override { return true; } + void endVisit(Return const& _return) override; + /// Default visit will reject all AST nodes that are not explicitly supported. + bool visitNode(ASTNode const& _node) override; + IRGenerationContext& m_context; + std::stringstream m_code; + enum class Builtins + { + Identity, + FromBool, + ToBool + }; + std::map> m_expressionDeclaration; + Type type(ASTNode const& _node) const; + + FunctionDefinition const& resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type); +}; + +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 058151928b2a..b4e3cf7f389a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -57,6 +57,8 @@ #include #include +#include +#include #include #include @@ -312,6 +314,7 @@ void CompilerStack::reset(bool _keepSettings) { m_stackState = Empty; m_sources.clear(); + m_maxAstId.reset(); m_smtlib2Responses.clear(); m_unhandledSMTLib2Queries.clear(); if (!_keepSettings) @@ -412,6 +415,10 @@ bool CompilerStack::parse() m_stackState = (m_stopAfter <= Parsed ? Parsed : ParsedAndImported); storeContractDefinitions(); + + solAssert(!m_maxAstId.has_value()); + m_maxAstId = parser.maxID(); + return true; } @@ -464,7 +471,7 @@ bool CompilerStack::analyze() m_globalContext = std::make_shared(); // We need to keep the same resolver during the whole process. - NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter); + NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter, experimentalSolidity); for (Source const* source: m_sourceOrder) if (source->ast && !resolver.registerDeclarations(*source->ast)) return false; @@ -478,10 +485,12 @@ bool CompilerStack::analyze() resolver.warnHomonymDeclarations(); - DocStringTagParser docStringTagParser(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) - noErrors = false; + { + DocStringTagParser docStringTagParser(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) + noErrors = false; + } // Requires DocStringTagParser for (Source const* source: m_sourceOrder) @@ -658,7 +667,8 @@ bool CompilerStack::analyzeLegacy(bool _noErrorsSoFar) bool CompilerStack::analyzeExperimental() { solAssert(!m_experimentalAnalysis); - m_experimentalAnalysis = std::make_unique(m_errorReporter); + solAssert(m_maxAstId && *m_maxAstId >= 0); + m_experimentalAnalysis = std::make_unique(m_errorReporter, static_cast(*m_maxAstId)); std::vector> sourceAsts; for (Source const* source: m_sourceOrder) if (source->ast) @@ -925,8 +935,6 @@ std::string const& CompilerStack::yulIR(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIR; } @@ -945,8 +953,6 @@ std::string const& CompilerStack::yulIROptimized(std::string const& _contractNam if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIROptimized; } @@ -965,8 +971,6 @@ evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractNa if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).object; } @@ -975,8 +979,6 @@ evmasm::LinkerObject const& CompilerStack::runtimeObject(std::string const& _con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).runtimeObject; } @@ -986,8 +988,6 @@ std::string CompilerStack::assemblyString(std::string const& _contractName, Stri if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); @@ -1496,9 +1496,6 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) { solAssert(m_stackState >= AnalysisSuccessful, ""); - if (m_experimentalAnalysis) - solThrow(CompilerError, "IR codegen after experimental analysis is unsupported."); - Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); if (!compiledContract.yulIR.empty()) return; @@ -1522,20 +1519,40 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) for (auto const& pair: m_contracts) otherYulSources.emplace(pair.second.contract, pair.second.yulIR); - IRGenerator generator( - m_evmVersion, - m_eofVersion, - m_revertStrings, - sourceIndices(), - m_debugInfoSelection, - this, - m_optimiserSettings - ); - compiledContract.yulIR = generator.run( - _contract, - createCBORMetadata(compiledContract, /* _forIR */ true), - otherYulSources - ); + if (m_experimentalAnalysis) + { + experimental::IRGenerator generator( + m_evmVersion, + m_eofVersion, + m_revertStrings, + sourceIndices(), + m_debugInfoSelection, + this, + *m_experimentalAnalysis + ); + compiledContract.yulIR = generator.run( + _contract, + {}, // TODO: createCBORMetadata(compiledContract, /* _forIR */ true), + otherYulSources + ); + } + else + { + IRGenerator generator( + m_evmVersion, + m_eofVersion, + m_revertStrings, + sourceIndices(), + m_debugInfoSelection, + this, + m_optimiserSettings + ); + compiledContract.yulIR = generator.run( + _contract, + createCBORMetadata(compiledContract, /* _forIR */ true), + otherYulSources + ); + } yul::YulStack stack( m_evmVersion, @@ -1977,3 +1994,9 @@ bool CompilerStack::isExperimentalSolidity() const ranges::all_of(m_sourceOrder, [](auto const* _source) { return _source->ast->experimentalSolidity(); } ) ; } + +experimental::Analysis const& CompilerStack::experimentalAnalysis() const +{ + solAssert(!!m_experimentalAnalysis); + return *m_experimentalAnalysis; +} diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 0ccb33219e54..57d30e602d1d 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -236,6 +236,13 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// @returns false on error. bool compile(State _stopAfter = State::CompilationSuccessful); + /// Checks whether experimental analysis is on; used in SyntaxTests to skip compilation in case it's ``true``. + /// @returns true if experimental analysis is set + bool isExperimentalAnalysis() const + { + return !!m_experimentalAnalysis; + } + /// @returns the list of sources (paths) used virtual std::vector sourceNames() const override; @@ -350,6 +357,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac bool isExperimentalSolidity() const; + experimental::Analysis const& experimentalAnalysis() const; + static MetadataFormat defaultMetadataFormat() { return VersionIsRelease ? MetadataFormat::WithReleaseVersionTag : MetadataFormat::WithPrereleaseVersionTag; @@ -509,6 +518,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac std::map m_libraries; ImportRemapper m_importRemapper; std::map m_sources; + std::optional m_maxAstId; std::vector m_unhandledSMTLib2Queries; std::map m_smtlib2Responses; std::shared_ptr m_globalContext; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 76b5d23d31ac..42be1ab9eecf 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -100,6 +100,9 @@ ASTPointer Parser::parse(CharStream& _charStream) while (m_scanner->currentToken() == Token::Pragma) nodes.push_back(parsePragmaDirective(false)); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + m_scanner->setScannerMode(ScannerKind::ExperimentalSolidity); + while (m_scanner->currentToken() != Token::EOS) { switch (m_scanner->currentToken()) @@ -123,7 +126,10 @@ ASTPointer Parser::parse(CharStream& _charStream) nodes.push_back(parseEnumDefinition()); break; case Token::Type: - nodes.push_back(parseUserDefinedValueTypeDefinition()); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + nodes.push_back(parseTypeDefinition()); + else + nodes.push_back(parseUserDefinedValueTypeDefinition()); break; case Token::Using: nodes.push_back(parseUsingDirective()); @@ -134,6 +140,14 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Event: nodes.push_back(parseEventDefinition()); break; + case Token::Class: + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + nodes.push_back(parseTypeClassDefinition()); + break; + case Token::Instantiation: + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + nodes.push_back(parseTypeClassInstantiation()); + break; default: if ( // Workaround because `error` is not a keyword. @@ -591,18 +605,29 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else break; } - if (m_scanner->currentToken() == Token::Returns) + if (m_experimentalSolidityEnabledInCurrentSourceUnit) { - bool const permitEmptyParameterList = false; - advance(); - result.returnParameters = parseParameterList(options, permitEmptyParameterList); + if (m_scanner->currentToken() == Token::RightArrow) + { + advance(); + result.experimentalReturnExpression = parseBinaryExpression(); + } } else - result.returnParameters = createEmptyParameterList(); + { + if (m_scanner->currentToken() == Token::Returns) + { + bool const permitEmptyParameterList = m_experimentalSolidityEnabledInCurrentSourceUnit; + advance(); + result.returnParameters = parseParameterList(options, permitEmptyParameterList); + } + else + result.returnParameters = createEmptyParameterList(); + } return result; } -ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -650,9 +675,16 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) FunctionHeaderParserResult header = parseFunctionHeader(false); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + solAssert(!header.returnParameters); + else + solAssert(!header.experimentalReturnExpression); + ASTPointer block; nodeFactory.markEndPosition(); - if (m_scanner->currentToken() == Token::Semicolon) + if (!_allowBody) + expectToken(Token::Semicolon); + else if (m_scanner->currentToken() == Token::Semicolon) advance(); else { @@ -672,7 +704,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) header.parameters, header.modifiers, header.returnParameters, - block + block, + header.experimentalReturnExpression ); } @@ -1190,10 +1223,12 @@ ASTPointer Parser::parseTypeName() ASTPointer Parser::parseFunctionType() { + solAssert(!m_experimentalSolidityEnabledInCurrentSourceUnit); RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Function); FunctionHeaderParserResult header = parseFunctionHeader(true); + solAssert(!header.experimentalReturnExpression); return nodeFactory.createNode( header.parameters, header.returnParameters, @@ -1249,16 +1284,29 @@ ASTPointer Parser::parseParameterList( std::vector> parameters; VarDeclParserOptions options(_options); options.allowEmptyName = true; + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Identifier) + { + // Parses unary parameter lists without parentheses. TODO: is this a good idea in all cases? Including arguments? + parameters = {parsePostfixVariableDeclaration()}; + nodeFactory.setEndPositionFromNode(parameters.front()); + return nodeFactory.createNode(parameters); + } expectToken(Token::LParen); + auto parseSingleVariableDeclaration = [&]() { + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + return parsePostfixVariableDeclaration(); + else + return parseVariableDeclaration(options); + }; if (!_allowEmpty || m_scanner->currentToken() != Token::RParen) { - parameters.push_back(parseVariableDeclaration(options)); + parameters.push_back(parseSingleVariableDeclaration()); while (m_scanner->currentToken() != Token::RParen) { if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) fatalParserError(7591_error, "Unexpected trailing comma in parameter list."); expectToken(Token::Comma); - parameters.push_back(parseVariableDeclaration(options)); + parameters.push_back(parseSingleVariableDeclaration()); } } nodeFactory.markEndPosition(); @@ -1607,12 +1655,221 @@ ASTPointer Parser::parseRevertStatement(ASTPointer c return nodeFactory.createNode(_docString, errorCall); } +ASTPointer Parser::parsePostfixVariableDeclarationStatement( + ASTPointer const& _docString +) +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::Let); + + std::vector> variables; + variables.emplace_back(parsePostfixVariableDeclaration()); + nodeFactory.setEndPositionFromNode(variables.back()); + + ASTPointer value; + if (m_scanner->currentToken() == Token::Assign) + { + advance(); + value = parseExpression(); + nodeFactory.setEndPositionFromNode(value); + } + return nodeFactory.createNode(_docString, variables, value); +} + +ASTPointer Parser::parsePostfixVariableDeclaration() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + ASTPointer const documentation = parseStructuredDocumentation(); + + nodeFactory.markEndPosition(); + auto [identifier, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer type; + if (m_scanner->currentToken() == Token::Colon) + { + advance(); + type = parseBinaryExpression(); + nodeFactory.setEndPositionFromNode(type); + } + + return nodeFactory.createNode( + nullptr, + identifier, + nameLocation, + nullptr, + Visibility::Default, + documentation, + false, + VariableDeclaration::Mutability::Mutable, + nullptr, + VariableDeclaration::Location::Unspecified, + type + ); +} + +ASTPointer Parser::parseTypeClassDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + std::vector> subNodes; + + ASTPointer const documentation = parseStructuredDocumentation(); + + expectToken(Token::Class); + // TODO: parseTypeVariable()? parseTypeVariableDeclaration()? + ASTPointer typeVariable; + { + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + auto [identifier, nameLocation] = expectIdentifierWithLocation(); + typeVariable = nodeFactory.createNode( + nullptr, + identifier, + nameLocation, + nullptr, + Visibility::Default, + nullptr + ); + } + expectToken(Token::Colon); + auto [name, nameLocation] = expectIdentifierWithLocation(); + expectToken(Token::LBrace); + while (true) + { + Token currentTokenValue = m_scanner->currentToken(); + if (currentTokenValue == Token::RBrace) + break; + expectToken(Token::Function, false); + subNodes.push_back(parseFunctionDefinition(false, false)); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBrace); + + return nodeFactory.createNode( + typeVariable, + name, + nameLocation, + documentation, + subNodes + ); +} + +ASTPointer Parser::parseTypeClassName() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + std::variant> name; + if (TokenTraits::isBuiltinTypeClassName(m_scanner->currentToken())) + { + nodeFactory.markEndPosition(); + name = m_scanner->currentToken(); + advance(); + } + else + { + auto identifierPath = parseIdentifierPath(); + name = identifierPath; + nodeFactory.setEndPositionFromNode(identifierPath); + } + return nodeFactory.createNode(name); +} + +ASTPointer Parser::parseTypeClassInstantiation() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + std::vector> subNodes; + + expectToken(Token::Instantiation); + // TODO: parseTypeConstructor() + ASTPointer typeConstructor = parseTypeName(); + ASTPointer argumentSorts; + if (m_scanner->currentToken() == Token::LParen) + { + argumentSorts = parseParameterList(); + } + expectToken(Token::Colon); + ASTPointer typeClassName = parseTypeClassName(); + expectToken(Token::LBrace); + while (true) + { + Token currentTokenValue = m_scanner->currentToken(); + if (currentTokenValue == Token::RBrace) + break; + expectToken(Token::Function, false); + // TODO: require body already during parsing? + subNodes.push_back(parseFunctionDefinition(false, true)); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBrace); + + return nodeFactory.createNode( + typeConstructor, + argumentSorts, + typeClassName, + subNodes + ); +} + +ASTPointer Parser::parseTypeDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Type); + auto&& [name, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer arguments; + if (m_scanner->currentToken() == Token::LParen) + arguments = parseParameterList(); + + ASTPointer expression; + if (m_scanner->currentToken() == Token::Assign) + { + expectToken(Token::Assign); + + if (m_scanner->currentToken() != Token::Builtin) + expression = parseExpression(); + else + { + expectToken(Token::Builtin); + expectToken(Token::LParen); + + expression = nodeFactory.createNode( + std::make_shared(m_scanner->currentLiteral()), + m_scanner->currentLocation() + ); + + expectToken(Token::StringLiteral); + expectToken(Token::RParen); + } + } + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode( + std::move(name), + std::move(nameLocation), + std::move(arguments), + std::move(expression) + ); +} + ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); LookAheadInfo statementType; IndexAccessedPath iap; + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Let) + return parsePostfixVariableDeclarationStatement(_docString); + if (m_scanner->currentToken() == Token::LParen) { ASTNodeFactory nodeFactory(*this); @@ -1715,7 +1972,10 @@ std::pair Parser::tryParseInde { case LookAheadInfo::VariableDeclaration: case LookAheadInfo::Expression: - return std::make_pair(statementType, IndexAccessedPath()); + return std::make_pair( + m_experimentalSolidityEnabledInCurrentSourceUnit ? LookAheadInfo::Expression : statementType, + IndexAccessedPath() + ); default: break; } @@ -1726,6 +1986,9 @@ std::pair Parser::tryParseInde // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + return std::make_pair(LookAheadInfo::Expression, std::move(iap)); + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return std::make_pair(LookAheadInfo::VariableDeclaration, std::move(iap)); else @@ -1808,9 +2071,9 @@ ASTPointer Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer expression = parseUnaryExpression(_partiallyParsedExpression); ASTNodeFactory nodeFactory(*this, expression); - int precedence = TokenTraits::precedence(m_scanner->currentToken()); + int precedence = tokenPrecedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) - while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) + while (tokenPrecedence(m_scanner->currentToken()) == precedence) { Token op = m_scanner->currentToken(); advance(); @@ -1827,6 +2090,23 @@ ASTPointer Parser::parseBinaryExpression( return expression; } +int Parser::tokenPrecedence(Token _token) const +{ + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + { + switch (_token) + { + case Token::Colon: + return 1000; + case Token::RightArrow: + return 999; + default: + break; + } + } + return TokenTraits::precedence(m_scanner->currentToken()); +} + ASTPointer Parser::parseUnaryExpression( ASTPointer const& _partiallyParsedExpression ) @@ -2285,7 +2565,14 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath() while (m_scanner->currentToken() == Token::Period) { advance(); - iap.path.push_back(parseIdentifierOrAddress()); + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Number) + { + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + iap.path.push_back(nodeFactory.createNode(getLiteralAndAdvance())); + } + else + iap.path.push_back(parseIdentifierOrAddress()); } } else diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5f2a1213d5ae..d4c48c7a638a 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -48,6 +48,8 @@ class Parser: public langutil::ParserBase ASTPointer parse(langutil::CharStream& _charStream); + /// Returns the maximal AST node ID assigned so far + int64_t maxID() const { return m_currentNodeID; } private: class ASTNodeFactory; @@ -74,6 +76,7 @@ class Parser: public langutil::ParserBase Visibility visibility = Visibility::Default; StateMutability stateMutability = StateMutability::NonPayable; std::vector> modifiers; + ASTPointer experimentalReturnExpression; }; /// Struct to share parsed function call arguments. @@ -99,7 +102,7 @@ class Parser: public langutil::ParserBase ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(bool _freeFunction = false); + ASTPointer parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseUserDefinedValueTypeDefinition(); @@ -167,6 +170,18 @@ class Parser: public langutil::ParserBase std::pair, langutil::SourceLocation> expectIdentifierWithLocation(); ///@} + ///@{ + ///@name Specialized parsing functions for the AST nodes of experimental solidity. + ASTPointer parsePostfixVariableDeclarationStatement( + ASTPointer const& _docString + ); + ASTPointer parsePostfixVariableDeclaration(); + ASTPointer parseTypeClassDefinition(); + ASTPointer parseTypeClassInstantiation(); + ASTPointer parseTypeDefinition(); + ASTPointer parseTypeClassName(); + ///@} + ///@{ ///@name Helper functions @@ -221,6 +236,8 @@ class Parser: public langutil::ParserBase bool isQuotedPath() const; bool isStdlibPath() const; + int tokenPrecedence(Token _token) const; + ASTPointer getStdlibImportPathAndAdvance(); /// Creates an empty ParameterList at the current location (used if parameters can be omitted). diff --git a/libstdlib/src/stub.sol b/libstdlib/src/stub.sol index 46ae21dcf5a2..bd34e0475acd 100644 --- a/libstdlib/src/stub.sol +++ b/libstdlib/src/stub.sol @@ -3,7 +3,6 @@ pragma solidity >=0.0; pragma experimental solidity; -function identity(uint256 x) pure returns (uint256) +function identity() { - return x; } diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 5c3f04d8e0ff..c7f504087c96 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -326,7 +326,14 @@ esac # boost_filesystem_bug specifically tests a local fix for a boost::filesystem # bug. Since the test involves a malformed path, there is no point in running # tests on it. See https://github.com/boostorg/filesystem/issues/176 -IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*") +# In addition, exclude all experimental Solidity tests (new type inference system) +EXCLUDE_FILES=( + boost_filesystem_bug.sol + pragma_experimental_solidity.sol +) +EXCLUDE_FILES_JOINED=$(printf "! -name %s " "${EXCLUDE_FILES[@]}") +# shellcheck disable=SC2086 +IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and $EXCLUDE_FILES_JOINED -not -path "*/experimental/*") NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)" echo "Looking at ${NSOURCES} .sol files..." diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 00230c4a4af0..7bcd8cca36c9 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -29,6 +29,7 @@ NAMESPACE_STD_FREE_FILES=( libsolidity/ast/* libsolidity/codegen/ir/* libsolidity/codegen/* + libsolidity/experimental/* libsolidity/formal/* libsolidity/interface/* libsolidity/lsp/* diff --git a/scripts/common.sh b/scripts/common.sh index 39c1226d1145..d149253bf71f 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -313,3 +313,14 @@ function command_available fail "'${program}' not found or not executed successfully with parameter(s) '${parameters[*]}'. aborting." fi } + +function gnu_grep +{ + if [[ "$OSTYPE" == "darwin"* ]]; then + command_available ggrep --version + ggrep "$@" + else + command_available grep --version + grep "$@" + fi +} diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 8fe2c4c1bee3..4aff6b85e80d 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -232,16 +232,109 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): return False old_source_only_ids = { - "1218", "1584", "1823", - "1988", "2066", "2833", "3356", - "3893", "3996", "4010", "4458", "4802", - "4902", "5272", "5622", "5798", "5840", "7128", "7400", - "7589", "7593", "7649", "7710", - "8065", "8084", "8140", "8158", - "8312", "8592", "9134", "9609", + "1218", + "1584", + "1823", + "1988", + "2066", + "2833", + "3356", + "3893", + "3996", + "4010", + "4458", + "4802", + "4902", + "5272", + "5622", + "5798", + "5840", + "7128", + "7400", + "7589", + "7593", + "7649", + "7710", + "8065", + "8084", + "8140", + "8158", + "8312", + "8592", + "9134", + "9609", } - new_source_only_ids = source_only_ids - old_source_only_ids + # TODO Cover these with tests and remove from this list as the development of experimental + # TODO Solidity progresses. The aim should be to completely get rid of `experimental_source_only_ids`. + experimental_source_only_ids = { + "1017", + "1439", + "1723", + "1741", + "1801", + "1807", + "2015", + "2138", + "2345", + "2399", + "2599", + "2655", + "2658", + "2934", + "3101", + "3111", + "3195", + "3520", + "3570", + "3573", + "3654", + "4316", + "4337", + "4496", + "4504", + "4686", + "4767", + "4873", + "4955", + "5044", + "5094", + "5096", + "5104", + "5195", + "5262", + "5348", + "5360", + "5387", + "5577", + "5714", + "5731", + "5755", + "5904", + "6175", + "6387", + "6388", + "6460", + "6620", + "6739", + "6948", + "7341", + "7428", + "7531", + "8379", + "8809", + "8953", + "9159", + "9173", + "9282", + "9603", + "9658", + "9817", + "9831", + "9988", + } + + new_source_only_ids = source_only_ids - old_source_only_ids - experimental_source_only_ids if len(new_source_only_ids) != 0: print("The following new error code(s), not covered by tests, found:") print_ids(new_source_only_ids) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f32a14d9183e..d5435237ec7b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,6 +73,8 @@ set(libsolidity_sources libsolidity/ASTJSONTest.h libsolidity/ErrorCheck.cpp libsolidity/ErrorCheck.h + libsolidity/FunctionDependencyGraphTest.cpp + libsolidity/FunctionDependencyGraphTest.h libsolidity/GasCosts.cpp libsolidity/GasMeter.cpp libsolidity/GasTest.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 5021d801b3ce..2519fa86f488 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,26 +61,27 @@ struct Testsuite /// Array of testsuits that can be run interactively as well as automatically Testsuite const g_interactiveTestsuites[] = { /* - Title Path Subpath SMT NeedsVM Creator function */ - {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, - {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, - {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, - {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, - {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, - {"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create}, - {"Control Flow Side Effects","libyul", "controlFlowSideEffects",false, false, &yul::test::ControlFlowSideEffectsTest::create}, - {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, - {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, - {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, - {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, - {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, - {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, - {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, - {"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create}, - {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, - {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, - {"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, - {"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create}, + Title Path Subpath SMT NeedsVM Creator function */ + {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, + {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, + {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, + {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, + {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, + {"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create}, + {"Control Flow Side Effects", "libyul", "controlFlowSideEffects", false, false, &yul::test::ControlFlowSideEffectsTest::create}, + {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, + {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, + {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, + {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, + {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, + {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, + {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, + {"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create}, + {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, + {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, + {"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, + {"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create}, + {"Function Dependency Graph", "libsolidity", "functionDependencyGraphTests", false, false, &FunctionDependencyGraphTest::create}, }; } diff --git a/test/cmdlineTests/~soljson_via_fuzzer/test.sh b/test/cmdlineTests/~soljson_via_fuzzer/test.sh index 2d2312fd1586..9c1d338c0102 100755 --- a/test/cmdlineTests/~soljson_via_fuzzer/test.sh +++ b/test/cmdlineTests/~soljson_via_fuzzer/test.sh @@ -10,7 +10,7 @@ cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ -echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files -echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files +gnu_grep -L --include="*.sol" "^pragma experimental solidity;$" ./* | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files +gnu_grep -L --include="*.sol" "^pragma experimental solidity;$" ./* | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files rm -r "$SOLTMPDIR" diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index dcfe49ab48d2..ccad7d49d12e 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -64,7 +64,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); GlobalContext globalContext; - NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); solAssert(!Error::containsErrors(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); diff --git a/test/libsolidity/FunctionDependencyGraphTest.cpp b/test/libsolidity/FunctionDependencyGraphTest.cpp new file mode 100644 index 000000000000..914fae06f065 --- /dev/null +++ b/test/libsolidity/FunctionDependencyGraphTest.cpp @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include +#include + +#include +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::util::formatting; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::test; + +TestCase::TestResult FunctionDependencyGraphTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) +{ + compiler().reset(); + compiler().setSources(StringMap{{"", m_source}}); + compiler().setViaIR(true); + compiler().setOptimiserSettings(OptimiserSettings::none()); + if (!compiler().compile(CompilerStack::AnalysisSuccessful)) + { + _stream << formatErrors(filteredErrors(), _formatted); + return TestResult::FatalError; + } + + m_obtainedResult.clear(); + for (auto [top, subs]: compiler().experimentalAnalysis().annotation().functionCallGraph.edges) + { + std::string topName = top->name().empty() ? "fallback" : top->name(); + m_obtainedResult += "(" + topName + ") --> {"; + for (auto sub: subs) + m_obtainedResult += sub->name() + ","; + m_obtainedResult += "}\n"; + } + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libsolidity/FunctionDependencyGraphTest.h b/test/libsolidity/FunctionDependencyGraphTest.h new file mode 100644 index 000000000000..83f3eb0ab394 --- /dev/null +++ b/test/libsolidity/FunctionDependencyGraphTest.h @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace solidity::frontend::test +{ + using solidity::test::SyntaxTestError; + + class FunctionDependencyGraphTest: public AnalysisFramework, public TestCase + { + public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + + FunctionDependencyGraphTest(std::string const& _filename): TestCase(_filename) + { + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); + } + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; + }; +} diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index b05d28a8403f..a171a5be0571 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -127,7 +127,7 @@ bytes compileFirstExpression( GlobalContext globalContext; Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); - NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 7c4fa8da1e84..f17898c042a5 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -73,7 +73,7 @@ void SyntaxTest::parseAndAnalyze() try { runFramework(withPreamble(m_sources.sources), PipelineStage::Compilation); - if (!pipelineSuccessful() && stageSuccessful(PipelineStage::Analysis)) + if (!pipelineSuccessful() && stageSuccessful(PipelineStage::Analysis) && !compiler().isExperimentalAnalysis()) { ErrorList const& errors = compiler().errors(); auto codeGeneretionErrorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) { diff --git a/test/libsolidity/functionDependencyGraphTests/callgraph.sol b/test/libsolidity/functionDependencyGraphTests/callgraph.sol new file mode 100644 index 000000000000..21f0e7e5f622 --- /dev/null +++ b/test/libsolidity/functionDependencyGraphTests/callgraph.sol @@ -0,0 +1,60 @@ +// fallback---------->f----------+ +// | | +// | | +// | | +// | | +// v v +// g h +// +// +// add +// +// unreferenced + +pragma experimental solidity; + +type uint256 = __builtin("word"); + +instantiation uint256: + { + function add(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := add(a,b) + } + return uint256.abs(a); + } +} + +function unreferenced(x:uint256) -> uint256 +{ + return x; +} + +function f(x:uint256) -> uint256 +{ + return g(h(x)); +} + +function g(x:uint256) -> uint256 +{ + return x; +} + +function h(x:uint256) -> uint256 +{ + return x; +} + +contract C { + fallback() external { + let a: uint256->uint256 = f; + } +} +// ---- +// (add) --> {} +// (unreferenced) --> {} +// (f) --> {g,h,} +// (g) --> {} +// (h) --> {} +// (fallback) --> {f,} diff --git a/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol b/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol new file mode 100644 index 000000000000..5fe1a75fed7e --- /dev/null +++ b/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol @@ -0,0 +1,66 @@ +// a<------b +// | ^ +// | | +// | | +// | | +// | | +// +------>c------->d-------->e------->f +// ^ | +// | | +// | | +// | | +// | v +// h<-------g + +pragma experimental solidity; + +function a() +{ + c(); +} + +function b() +{ + a(); +} + +function c() +{ + b(); + d(); +} + +function d() +{ + e(); +} + +function e() +{ + f(); +} + +function f() +{ + g(); +} + +function g() +{ + h(); +} + +function h() +{ + e(); +} + +// ---- +// (a) --> {c,} +// (b) --> {a,} +// (c) --> {b,d,} +// (d) --> {e,} +// (e) --> {f,} +// (f) --> {g,} +// (g) --> {h,} +// (h) --> {e,} diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol new file mode 100644 index 000000000000..6e7a750fe980 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -0,0 +1,97 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type bool = __builtin("bool"); +type integer = __builtin("integer"); + +type uint256 = word; + +instantiation uint256: + { + function add(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := add(a,b) + } + return uint256.abs(a); + } +} + + +instantiation uint256: * { + function mul(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := mul(a,b) + } + return uint256.abs(a); + } +} +instantiation word: * { + function mul(x, y) -> word { + let z: word; + assembly { + z := mul(x,y) + } + return z; + } +} + +instantiation word: Integer { + function fromInteger(x:integer) -> word { + //x + x; + } +} + +instantiation word: == { + function eq(x, y) -> bool { + assembly { + x := eq(x, y) + } + } +} + + +function f(x:uint256->uint256,y:uint256) -> uint256 +{ + return x(y); +} + +function g(x:uint256) -> uint256 +{ + return x; +} + +contract C { + fallback() external { + let arg; + assembly { + arg := calldataload(0) + } + let x : word; + if (bool.abs(arg)) { + assembly { + x := 0x10 + } + } + let w = uint256.abs(x); +// w = f(g, w); + w = w * w + w; + let y : word; + let z : (uint256,uint256); + assembly { y := 2 } + y = uint256.rep(w) * y; + assembly { + mstore(0, y) + return(0, 32) + } + } +} +// ==== +// EVMVersion: >=constantinople +// ==== +// compileViaYul: true +// ---- +// (): 0 -> 0 +// (): 1 -> 544 diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol new file mode 100644 index 000000000000..69fa568dfd96 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -0,0 +1,67 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type bool = __builtin("bool"); + +type Cat = word; +type Dog = word; + +class Self: Animal { + function new() -> Self; + function alive(self: Self) -> bool; +} + +instantiation Cat: Animal { + function new() -> Cat { + let c; + return c; + } + + function alive(self: Cat) -> bool { + // TODO: Boolean literals or operators not implemented. + let w; + assembly { + w := 1 + } + return bool.abs(w); + } +} + +instantiation Dog: Animal { + function new() -> Dog { + let d: Dog; + return d; + } + + function alive(self: Dog) -> bool { + let b: bool; + return b; + } +} + +contract C { + fallback() external { + let boolResult1: bool; + let boolResult2: bool; + + let c: Cat = Animal.new(); + boolResult1 = Animal.alive(c); + + let d: Dog = Animal.new(); + boolResult2 = Animal.alive(d); + + let wordResult1 = bool.rep(boolResult1); + let wordResult2 = bool.rep(boolResult2); + assembly { + mstore(0, wordResult1) + mstore(32, wordResult2) + return(0, 64) + } + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// () -> 1, 0 diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol new file mode 100644 index 000000000000..fc4783ceba19 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol @@ -0,0 +1,92 @@ +pragma experimental solidity; + +type void = __builtin("void"); + +type bool = __builtin("bool"); +type word = __builtin("word"); +type integer = __builtin("integer"); +type unit = __builtin("unit"); + +type fun(T, U) = __builtin("fun"); +type pair(T, U) = __builtin("pair"); + +contract C { + fallback() external { + let v: void; + + let b: bool; + bool.abs(bool.rep(b)); + + let w: word; + let i: integer; + let u: unit; + + let f: fun(word, bool); + b = f(w); + + let p: pair(bool, word); + pair.first(p); + pair.second(p); + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// UnimplementedFeatureError: No support for calling functions pointers yet. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: void +// Info 4164: (63-93): Inferred type: bool +// Info 4164: (94-124): Inferred type: word +// Info 4164: (125-161): Inferred type: integer +// Info 4164: (162-192): Inferred type: () +// Info 4164: (194-228): Inferred type: tfun(('u:type, 'v:type), 'u:type -> 'v:type) +// Info 4164: (202-208): Inferred type: ('s:type, 't:type) +// Info 4164: (203-204): Inferred type: 's:type +// Info 4164: (206-207): Inferred type: 't:type +// Info 4164: (229-265): Inferred type: tfun(('y:type, 'z:type), ('y:type, 'z:type)) +// Info 4164: (238-244): Inferred type: ('w:type, 'x:type) +// Info 4164: (239-240): Inferred type: 'w:type +// Info 4164: (242-243): Inferred type: 'x:type +// Info 4164: (284-584): Inferred type: () -> () +// Info 4164: (292-294): Inferred type: () +// Info 4164: (318-325): Inferred type: void +// Info 4164: (321-325): Inferred type: void +// Info 4164: (340-347): Inferred type: bool +// Info 4164: (343-347): Inferred type: bool +// Info 4164: (357-378): Inferred type: bool +// Info 4164: (357-365): Inferred type: word -> bool +// Info 4164: (357-361): Inferred type: bool +// Info 4164: (366-377): Inferred type: word +// Info 4164: (366-374): Inferred type: bool -> word +// Info 4164: (366-370): Inferred type: bool +// Info 4164: (375-376): Inferred type: bool +// Info 4164: (393-400): Inferred type: word +// Info 4164: (396-400): Inferred type: word +// Info 4164: (414-424): Inferred type: integer +// Info 4164: (417-424): Inferred type: integer +// Info 4164: (438-445): Inferred type: () +// Info 4164: (441-445): Inferred type: () +// Info 4164: (460-478): Inferred type: word -> bool +// Info 4164: (463-478): Inferred type: word -> bool +// Info 4164: (463-466): Inferred type: tfun((word, bool), word -> bool) +// Info 4164: (467-471): Inferred type: word +// Info 4164: (473-477): Inferred type: bool +// Info 4164: (488-496): Inferred type: bool +// Info 4164: (488-489): Inferred type: bool +// Info 4164: (492-496): Inferred type: bool +// Info 4164: (492-493): Inferred type: word -> bool +// Info 4164: (494-495): Inferred type: word +// Info 4164: (511-530): Inferred type: (bool, word) +// Info 4164: (514-530): Inferred type: (bool, word) +// Info 4164: (514-518): Inferred type: tfun((bool, word), (bool, word)) +// Info 4164: (519-523): Inferred type: bool +// Info 4164: (525-529): Inferred type: word +// Info 4164: (540-553): Inferred type: bool +// Info 4164: (540-550): Inferred type: (bool, word) -> bool +// Info 4164: (540-544): Inferred type: ('bl:type, 'bm:type) +// Info 4164: (551-552): Inferred type: (bool, word) +// Info 4164: (563-577): Inferred type: word +// Info 4164: (563-574): Inferred type: (bool, word) -> word +// Info 4164: (563-567): Inferred type: ('bq:type, 'br:type) +// Info 4164: (575-576): Inferred type: (bool, word) diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol new file mode 100644 index 000000000000..28f6c39fd754 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol @@ -0,0 +1,17 @@ +pragma experimental solidity; + +type void1 = __builtin("void"); +type void2 = __builtin("void"); + +type word1 = __builtin("word"); +type word2 = __builtin("word"); + +type fun1(T, U) = __builtin("fun"); +type fun2(T, U) = __builtin("fun"); +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 9609: (63-94): Duplicate builtin type definition. +// TypeError 9609: (128-159): Duplicate builtin type definition. +// TypeError 9609: (197-232): Duplicate builtin type definition. diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol new file mode 100644 index 000000000000..90072f19838f --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +type someUnknownType = __builtin("someUnknownType"); +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 7758: (31-81): Expected the name of a built-in primitive type. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol similarity index 70% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol index 285c76ed3030..a84ac0e759ab 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol @@ -1,6 +1,6 @@ pragma experimental solidity; // ==== // EVMVersion: >=constantinople +// compileViaYul: true // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_wrong_evm_version.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_wrong_evm_version.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol similarity index 76% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol index f8799de42b65..5628a588f802 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol @@ -7,4 +7,5 @@ import std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol similarity index 77% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol index f5bcf8efcdf5..bdc66e4ad393 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol @@ -7,4 +7,5 @@ import std.stub as stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol similarity index 70% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol index 4b325ea4edf1..1bb340536c11 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol @@ -7,4 +7,6 @@ import { identity } from std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () +// Info 4164: (40-48): Inferred type: () -> () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol similarity index 77% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol index 865e59b554f4..64a67fe6c96d 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol @@ -7,4 +7,5 @@ import * as stub from std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_1.sol similarity index 100% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_1.sol diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_2.sol similarity index 100% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_2.sol diff --git a/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol b/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol new file mode 100644 index 000000000000..b5920aed0759 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol @@ -0,0 +1,4 @@ +function f() pure { + uint word; word; + uint static_assert; static_assert; +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol new file mode 100644 index 000000000000..fc75e85947eb --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol @@ -0,0 +1,25 @@ +pragma experimental solidity; + +import { identity as id } from std.stub; + +contract C +{ + fallback() external + { + id(); + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () +// Info 4164: (40-48): Inferred type: () -> () +// Info 4164: (90-135): Inferred type: () -> () +// Info 4164: (98-100): Inferred type: () +// Info 4164: (124-128): Inferred type: () +// Info 4164: (124-126): Inferred type: () -> () diff --git a/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol new file mode 100644 index 000000000000..ac097bf3927f --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol @@ -0,0 +1,13 @@ +pragma experimental solidity; + +function f() {} + +class Self: C +{ + function g(self: Self, x: f); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 2217: (94-95): Attempt to type identifier referring to unexpected node. diff --git a/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol b/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol new file mode 100644 index 000000000000..e7af573aeed6 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T; +type U; + +function f(x: T, y: U) {} + +function run(a: U, b: T) { + f(a, b); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (106-113): Cannot unify T and U. +// TypeError 8456: (106-113): Cannot unify U and T. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol new file mode 100644 index 000000000000..eacc895202f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -0,0 +1,53 @@ +pragma experimental solidity; + +type T; +type U(A); + +function f(x, y: X, z: U(Y)) {} + +function run(a: T, b: U(T), c: U(U(T))) { + f(a, a, b); + f(b, b, c); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-38): Inferred type: T +// Info 4164: (39-49): Inferred type: tfun('u:type, U('u:type)) +// Info 4164: (45-48): Inferred type: 't:type +// Info 4164: (46-47): Inferred type: 't:type +// Info 4164: (51-82): Inferred type: ('x:type, 'y:type, U('ba:type)) -> () +// Info 4164: (61-79): Inferred type: ('x:type, 'y:type, U('ba:type)) +// Info 4164: (62-63): Inferred type: 'x:type +// Info 4164: (65-69): Inferred type: 'y:type +// Info 4164: (68-69): Inferred type: 'y:type +// Info 4164: (71-78): Inferred type: U('ba:type) +// Info 4164: (74-78): Inferred type: U('ba:type) +// Info 4164: (74-75): Inferred type: tfun('ba:type, U('ba:type)) +// Info 4164: (76-77): Inferred type: 'ba:type +// Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () +// Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) +// Info 4164: (97-101): Inferred type: T +// Info 4164: (100-101): Inferred type: T +// Info 4164: (103-110): Inferred type: U(T) +// Info 4164: (106-110): Inferred type: U(T) +// Info 4164: (106-107): Inferred type: tfun(T, U(T)) +// Info 4164: (108-109): Inferred type: T +// Info 4164: (112-122): Inferred type: U(U(T)) +// Info 4164: (115-122): Inferred type: U(U(T)) +// Info 4164: (115-116): Inferred type: tfun(U(T), U(U(T))) +// Info 4164: (117-121): Inferred type: U(T) +// Info 4164: (117-118): Inferred type: tfun(T, U(T)) +// Info 4164: (119-120): Inferred type: T +// Info 4164: (130-140): Inferred type: () +// Info 4164: (130-131): Inferred type: (T, T, U(T)) -> () +// Info 4164: (132-133): Inferred type: T +// Info 4164: (135-136): Inferred type: T +// Info 4164: (138-139): Inferred type: U(T) +// Info 4164: (146-156): Inferred type: () +// Info 4164: (146-147): Inferred type: (U(T), U(T), U(U(T))) -> () +// Info 4164: (148-149): Inferred type: U(T) +// Info 4164: (151-152): Inferred type: U(T) +// Info 4164: (154-155): Inferred type: U(U(T)) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol new file mode 100644 index 000000000000..1a43d0807ac2 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol @@ -0,0 +1,19 @@ +pragma experimental solidity; + +type T; +type U; + +function f(x) {} + +function run(a: T, b: U) { + // NOTE: The type of f is polymorphic but the inferred type of g is not - this would be + // let-polymorphism, which we decided not to support. + let g = f; + g(a); + g(b); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (272-276): Cannot unify T and U. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol new file mode 100644 index 000000000000..74f102a88183 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T(A); +type U; +type V; + +function f(x: T(U)) {} + +function run(a: T(V)) { + f(a); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (111-115): Cannot unify U and V. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol new file mode 100644 index 000000000000..b597a5b25295 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -0,0 +1,47 @@ +pragma experimental solidity; + +type T(P, Q, R); +type U; +type V; + +class Self: C {} +class Self: D {} + +function run() { + let x: T(U, X, Z: C); + let y: T(V, Y, Z: D); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-47): Inferred type: tfun(('ba:type, 'bb:type, 'bc:type), T('ba:type, 'bb:type, 'bc:type)) +// Info 4164: (37-46): Inferred type: ('x:type, 'y:type, 'z:type) +// Info 4164: (38-39): Inferred type: 'x:type +// Info 4164: (41-42): Inferred type: 'y:type +// Info 4164: (44-45): Inferred type: 'z:type +// Info 4164: (48-55): Inferred type: U +// Info 4164: (56-63): Inferred type: V +// Info 4164: (65-81): Inferred type: C +// Info 4164: (71-75): Inferred type: 'be:(type, C) +// Info 4164: (82-98): Inferred type: D +// Info 4164: (88-92): Inferred type: 'bg:(type, D) +// Info 4164: (100-170): Inferred type: () -> () +// Info 4164: (112-114): Inferred type: () +// Info 4164: (125-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) +// Info 4164: (128-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) +// Info 4164: (128-129): Inferred type: tfun((U, 'bm:type, 'bo:(type, C)), T(U, 'bm:type, 'bo:(type, C))) +// Info 4164: (130-131): Inferred type: U +// Info 4164: (133-134): Inferred type: 'bm:type +// Info 4164: (136-140): Inferred type: 'bo:(type, C) +// Info 4164: (136-137): Inferred type: 'bo:(type, C) +// Info 4164: (139-140): Inferred type: 'bo:(type, C) +// Info 4164: (151-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) +// Info 4164: (154-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) +// Info 4164: (154-155): Inferred type: tfun((V, 'bt:type, 'bv:(type, D)), T(V, 'bt:type, 'bv:(type, D))) +// Info 4164: (156-157): Inferred type: V +// Info 4164: (159-160): Inferred type: 'bt:type +// Info 4164: (162-166): Inferred type: 'bv:(type, D) +// Info 4164: (162-163): Inferred type: 'bv:(type, D) +// Info 4164: (165-166): Inferred type: 'bv:(type, D) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol new file mode 100644 index 000000000000..89f3f5f1a8b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol @@ -0,0 +1,69 @@ +pragma experimental solidity; + +type uint; +type string; + +type T(A); +type U(B) = T(B); + +function fun() { + let w: U(uint); + let v: T(uint); + U.rep(w); + U.abs(v); + + let s: U(string); + let t: T(string); + U.rep(s); + U.abs(t); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-41): Inferred type: uint +// Info 4164: (42-54): Inferred type: string +// Info 4164: (56-66): Inferred type: tfun('v:type, T('v:type)) +// Info 4164: (62-65): Inferred type: 'u:type +// Info 4164: (63-64): Inferred type: 'u:type +// Info 4164: (67-84): Inferred type: tfun('x:type, U('x:type)) +// Info 4164: (73-76): Inferred type: 'w:type +// Info 4164: (74-75): Inferred type: 'w:type +// Info 4164: (79-83): Inferred type: T('w:type) +// Info 4164: (79-80): Inferred type: tfun('w:type, T('w:type)) +// Info 4164: (81-82): Inferred type: 'w:type +// Info 4164: (86-245): Inferred type: () -> () +// Info 4164: (98-100): Inferred type: () +// Info 4164: (111-121): Inferred type: U(uint) +// Info 4164: (114-121): Inferred type: U(uint) +// Info 4164: (114-115): Inferred type: tfun(uint, U(uint)) +// Info 4164: (116-120): Inferred type: uint +// Info 4164: (131-141): Inferred type: T(uint) +// Info 4164: (134-141): Inferred type: T(uint) +// Info 4164: (134-135): Inferred type: tfun(uint, T(uint)) +// Info 4164: (136-140): Inferred type: uint +// Info 4164: (147-155): Inferred type: T('bi:type) +// Info 4164: (147-152): Inferred type: U(uint) -> T('bi:type) +// Info 4164: (147-148): Inferred type: U('bg:type) +// Info 4164: (153-154): Inferred type: U(uint) +// Info 4164: (161-169): Inferred type: U('bm:type) +// Info 4164: (161-166): Inferred type: T(uint) -> U('bm:type) +// Info 4164: (161-162): Inferred type: U('bk:type) +// Info 4164: (167-168): Inferred type: T(uint) +// Info 4164: (180-192): Inferred type: U(string) +// Info 4164: (183-192): Inferred type: U(string) +// Info 4164: (183-184): Inferred type: tfun(string, U(string)) +// Info 4164: (185-191): Inferred type: string +// Info 4164: (202-214): Inferred type: T(string) +// Info 4164: (205-214): Inferred type: T(string) +// Info 4164: (205-206): Inferred type: tfun(string, T(string)) +// Info 4164: (207-213): Inferred type: string +// Info 4164: (220-228): Inferred type: T('bu:type) +// Info 4164: (220-225): Inferred type: U(string) -> T('bu:type) +// Info 4164: (220-221): Inferred type: U('bs:type) +// Info 4164: (226-227): Inferred type: U(string) +// Info 4164: (234-242): Inferred type: U('by:type) +// Info 4164: (234-239): Inferred type: T(string) -> U('by:type) +// Info 4164: (234-235): Inferred type: U('bw:type) +// Info 4164: (240-241): Inferred type: T(string) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol new file mode 100644 index 000000000000..25ab330f3b32 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol @@ -0,0 +1,168 @@ +pragma experimental solidity; + +type bool = __builtin("bool"); + +type T(A); +type int; +type str; + +class Self: C { + function foo(a: Self, b: Self) -> Self; +} + +class Self: P1 {} +class Self: P2 {} +class Self: P3 {} +class Self: P4 {} + +instantiation int: P1 {} +instantiation int: P2 {} +instantiation int: P3 {} + +instantiation str: P1 {} +instantiation str: P2 {} +instantiation str: P4 {} + +instantiation T(A: P1): + { + function add(x: T(A), y: T(A)) -> T(A) {} +} + +instantiation T(A: P2): == { + function eq(x: T(A), y: T(A)) -> bool {} +} + +instantiation T(A: (P1, P2)): C { + function foo(x: T(A), y: T(A)) -> T(A) {} +} + +function fun(a: T(int: P3), b: T(str: P4)) { + a + a; + b + b; + + a == a; + b == b; + + C.foo(a, a); + C.foo(b, b); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: bool +// Info 4164: (63-73): Inferred type: tfun('z:type, T('z:type)) +// Info 4164: (69-72): Inferred type: 'y:type +// Info 4164: (70-71): Inferred type: 'y:type +// Info 4164: (74-83): Inferred type: int +// Info 4164: (84-93): Inferred type: str +// Info 4164: (95-156): Inferred type: C +// Info 4164: (101-105): Inferred type: 'bd:(type, C) +// Info 4164: (115-154): Inferred type: ('bd:(type, C), 'bd:(type, C)) -> 'bd:(type, C) +// Info 4164: (127-145): Inferred type: ('bd:(type, C), 'bd:(type, C)) +// Info 4164: (128-135): Inferred type: 'bd:(type, C) +// Info 4164: (131-135): Inferred type: 'bd:(type, C) +// Info 4164: (137-144): Inferred type: 'bd:(type, C) +// Info 4164: (140-144): Inferred type: 'bd:(type, C) +// Info 4164: (149-153): Inferred type: 'bd:(type, C) +// Info 4164: (158-175): Inferred type: P1 +// Info 4164: (164-168): Inferred type: 'bg:(type, P1) +// Info 4164: (176-193): Inferred type: P2 +// Info 4164: (182-186): Inferred type: 'bj:(type, P2) +// Info 4164: (194-211): Inferred type: P3 +// Info 4164: (200-204): Inferred type: 'bw:(type, P3) +// Info 4164: (212-229): Inferred type: P4 +// Info 4164: (218-222): Inferred type: 'by:(type, P4) +// Info 4164: (231-255): Inferred type: void +// Info 4164: (256-280): Inferred type: void +// Info 4164: (281-305): Inferred type: void +// Info 4164: (307-331): Inferred type: void +// Info 4164: (332-356): Inferred type: void +// Info 4164: (357-381): Inferred type: void +// Info 4164: (383-458): Inferred type: void +// Info 4164: (398-405): Inferred type: 'ca:(type, P1) +// Info 4164: (399-404): Inferred type: 'ca:(type, P1) +// Info 4164: (402-404): Inferred type: 'ca:(type, P1) +// Info 4164: (415-456): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) -> T('ca:(type, P1)) +// Info 4164: (427-445): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) +// Info 4164: (428-435): Inferred type: T('ca:(type, P1)) +// Info 4164: (431-435): Inferred type: T('ca:(type, P1)) +// Info 4164: (431-432): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (433-434): Inferred type: 'ca:(type, P1) +// Info 4164: (437-444): Inferred type: T('ca:(type, P1)) +// Info 4164: (440-444): Inferred type: T('ca:(type, P1)) +// Info 4164: (440-441): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (442-443): Inferred type: 'ca:(type, P1) +// Info 4164: (449-453): Inferred type: T('ca:(type, P1)) +// Info 4164: (449-450): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (451-452): Inferred type: 'ca:(type, P1) +// Info 4164: (460-535): Inferred type: void +// Info 4164: (475-482): Inferred type: 'ck:(type, P2) +// Info 4164: (476-481): Inferred type: 'ck:(type, P2) +// Info 4164: (479-481): Inferred type: 'ck:(type, P2) +// Info 4164: (493-533): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) -> bool +// Info 4164: (504-522): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) +// Info 4164: (505-512): Inferred type: T('ck:(type, P2)) +// Info 4164: (508-512): Inferred type: T('ck:(type, P2)) +// Info 4164: (508-509): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) +// Info 4164: (510-511): Inferred type: 'ck:(type, P2) +// Info 4164: (514-521): Inferred type: T('ck:(type, P2)) +// Info 4164: (517-521): Inferred type: T('ck:(type, P2)) +// Info 4164: (517-518): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) +// Info 4164: (519-520): Inferred type: 'ck:(type, P2) +// Info 4164: (526-530): Inferred type: bool +// Info 4164: (537-618): Inferred type: void +// Info 4164: (552-565): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (553-564): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (556-564): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (557-559): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (561-563): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (575-616): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) -> T('bm:(type, P1, P2)) +// Info 4164: (587-605): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) +// Info 4164: (588-595): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (591-595): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (591-592): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (593-594): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (597-604): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (600-604): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (600-601): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (602-603): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (609-613): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (609-610): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (611-612): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (620-748): Inferred type: (T(int), T(str)) -> () +// Info 4164: (632-662): Inferred type: (T(int), T(str)) +// Info 4164: (633-646): Inferred type: T(int) +// Info 4164: (636-646): Inferred type: T(int) +// Info 4164: (636-637): Inferred type: tfun(int, T(int)) +// Info 4164: (638-645): Inferred type: int +// Info 4164: (638-641): Inferred type: int +// Info 4164: (643-645): Inferred type: int +// Info 4164: (648-661): Inferred type: T(str) +// Info 4164: (651-661): Inferred type: T(str) +// Info 4164: (651-652): Inferred type: tfun(str, T(str)) +// Info 4164: (653-660): Inferred type: str +// Info 4164: (653-656): Inferred type: str +// Info 4164: (658-660): Inferred type: str +// Info 4164: (669-674): Inferred type: T(int) +// Info 4164: (669-670): Inferred type: T(int) +// Info 4164: (673-674): Inferred type: T(int) +// Info 4164: (680-685): Inferred type: T(str) +// Info 4164: (680-681): Inferred type: T(str) +// Info 4164: (684-685): Inferred type: T(str) +// Info 4164: (692-698): Inferred type: bool +// Info 4164: (692-693): Inferred type: T(int) +// Info 4164: (697-698): Inferred type: T(int) +// Info 4164: (704-710): Inferred type: bool +// Info 4164: (704-705): Inferred type: T(str) +// Info 4164: (709-710): Inferred type: T(str) +// Info 4164: (717-728): Inferred type: T(int) +// Info 4164: (717-722): Inferred type: (T(int), T(int)) -> T(int) +// Info 4164: (717-718): Inferred type: C +// Info 4164: (723-724): Inferred type: T(int) +// Info 4164: (726-727): Inferred type: T(int) +// Info 4164: (734-745): Inferred type: T(str) +// Info 4164: (734-739): Inferred type: (T(str), T(str)) -> T(str) +// Info 4164: (734-735): Inferred type: C +// Info 4164: (740-741): Inferred type: T(str) +// Info 4164: (743-744): Inferred type: T(str) diff --git a/test/stopAfterParseTests.sh b/test/stopAfterParseTests.sh index 7d74704c5e38..cb15603e2399 100755 --- a/test/stopAfterParseTests.sh +++ b/test/stopAfterParseTests.sh @@ -70,5 +70,5 @@ while read -r file; do echo "$file" exit 1 fi -done < <(find "${REPO_ROOT}/test" -iname "*.sol" -and -not -name "documentation.sol" -and -not -name "boost_filesystem_bug.sol") +done < <(find "${REPO_ROOT}/test" -iname "*.sol" -and -not -name "documentation.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*" -not -path "*/functionDependencyGraphTests/*") echo diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index a86e6382df8d..532a08d9fc55 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(isoltest ../libsolidity/ABIJsonTest.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/ASTPropertyTest.cpp + ../libsolidity/FunctionDependencyGraphTest.cpp ../libsolidity/SMTCheckerTest.cpp ../libyul/Common.cpp ../libyul/ControlFlowGraphTest.cpp