From fa815764ddcea5b9a20ba84a8d5ec5d84a0f8501 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Jun 2023 11:18:46 +0200 Subject: [PATCH 01/17] Basic infrastructure. --- libsolidity/CMakeLists.txt | 4 + .../analysis/experimental/Analysis.cpp | 26 +++ libsolidity/analysis/experimental/Analysis.h | 43 +++++ .../codegen/experimental/IRGenerator.cpp | 160 ++++++++++++++++++ .../codegen/experimental/IRGenerator.h | 73 ++++++++ libsolidity/interface/CompilerStack.cpp | 67 +++++--- libsolidity/interface/CompilerStack.h | 7 + 7 files changed, 361 insertions(+), 19 deletions(-) create mode 100644 libsolidity/analysis/experimental/Analysis.cpp create mode 100644 libsolidity/analysis/experimental/Analysis.h create mode 100644 libsolidity/codegen/experimental/IRGenerator.cpp create mode 100644 libsolidity/codegen/experimental/IRGenerator.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 0a8c90f36..b2c847363 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -46,6 +46,8 @@ set(sources analysis/TypeChecker.h analysis/ViewPureChecker.cpp analysis/ViewPureChecker.h + analysis/experimental/Analysis.cpp + analysis/experimental/Analysis.h ast/AST.cpp ast/AST.h ast/AST_accept.h @@ -90,6 +92,8 @@ set(sources codegen/ReturnInfo.cpp codegen/YulUtilFunctions.h codegen/YulUtilFunctions.cpp + codegen/experimental/IRGenerator.cpp + codegen/experimental/IRGenerator.h codegen/ir/Common.cpp codegen/ir/Common.h codegen/ir/IRGenerator.cpp diff --git a/libsolidity/analysis/experimental/Analysis.cpp b/libsolidity/analysis/experimental/Analysis.cpp new file mode 100644 index 000000000..5138ff280 --- /dev/null +++ b/libsolidity/analysis/experimental/Analysis.cpp @@ -0,0 +1,26 @@ +/* + 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 + +using namespace solidity::langutil; +using namespace solidity::frontend::experimental; + +bool Analysis::check(ASTNode const&) +{ + return true; +} diff --git a/libsolidity/analysis/experimental/Analysis.h b/libsolidity/analysis/experimental/Analysis.h new file mode 100644 index 000000000..9433508e4 --- /dev/null +++ b/libsolidity/analysis/experimental/Analysis.h @@ -0,0 +1,43 @@ +/* + 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 + +namespace solidity::frontend +{ +class ASTNode; +} + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend::experimental +{ + +class Analysis +{ +public: + Analysis(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) + {} + bool check(ASTNode const& _ast); +private: + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/codegen/experimental/IRGenerator.cpp b/libsolidity/codegen/experimental/IRGenerator.cpp new file mode 100644 index 000000000..0db02a163 --- /dev/null +++ b/libsolidity/codegen/experimental/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 + +using namespace std; +using namespace solidity; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; +using namespace solidity::util; + +string IRGenerator::run( + ContractDefinition const& _contract, + bytes const& /*_cborMetadata*/, + map const& /*_otherYulSources*/ +) const +{ + + 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(); +} + +string IRGenerator::generate(ContractDefinition const& _contract) const +{ + std::stringstream code; + code << "{\n"; + if (_contract.fallbackFunction()) + { + code << IRNames::function(*_contract.fallbackFunction()) << "()\n"; + } + code << "revert(0,0)\n"; + code << "}\n"; + + for (FunctionDefinition const* f: _contract.definedFunctions()) + code << generate(*f); + + return code.str(); +} + +string IRGenerator::generate(FunctionDefinition const& _function) const +{ + std::stringstream code; + code << "function " << IRNames::function(_function) << "() {\n"; + for (auto _statement: _function.body().statements()) + { + if (auto assembly = dynamic_cast(_statement.get())) + code << generate(*assembly) << "\n"; + else + solUnimplemented("Unsupported statement type."); + } + code << "}\n"; + return code.str(); +} + +namespace { + +struct CopyTranslate: public yul::ASTCopier +{ + CopyTranslate( + yul::Dialect const& _dialect, + map _references + ): 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(holds_alternative(translated)); + return 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&) + { + solUnimplemented("External references in inline assembly not implemented."); + } + + yul::Dialect const& m_dialect; + map m_references; +}; + +} + +string IRGenerator::generate(InlineAssembly const& _assembly) const +{ + CopyTranslate bodyCopier{_assembly.dialect(), {}}; + yul::Statement modified = bodyCopier(_assembly.operations()); + solAssert(holds_alternative(modified)); + return yul::AsmPrinter()(std::get(modified)); +} \ No newline at end of file diff --git a/libsolidity/codegen/experimental/IRGenerator.h b/libsolidity/codegen/experimental/IRGenerator.h new file mode 100644 index 000000000..b2d1a7247 --- /dev/null +++ b/libsolidity/codegen/experimental/IRGenerator.h @@ -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 + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace solidity::frontend::experimental +{ + +class SourceUnit; + +class IRGenerator +{ +public: + IRGenerator( + langutil::EVMVersion _evmVersion, + std::optional _eofVersion, + RevertStrings /*_revertStrings*/, + std::map /*_sourceIndices*/, + langutil::DebugInfoSelection const& _debugInfoSelection, + langutil::CharStreamProvider const* _soliditySourceProvider + ): + m_evmVersion(_evmVersion), + m_eofVersion(_eofVersion), + m_debugInfoSelection(_debugInfoSelection), + m_soliditySourceProvider(_soliditySourceProvider) + {} + + std::string run( + ContractDefinition const& _contract, + bytes const& _cborMetadata, + std::map const& _otherYulSources + ) const; + + std::string generate(ContractDefinition const& _contract) const; + std::string generate(FunctionDefinition const& _function) const; + std::string generate(InlineAssembly const& _assembly) const; +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; +}; + +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 44413afdf..2d080a8ba 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -57,6 +57,7 @@ #include #include +#include #include #include @@ -310,6 +311,7 @@ void CompilerStack::reset(bool _keepSettings) { m_stackState = Empty; m_sources.clear(); + m_maxAstId.reset(); m_smtlib2Responses.clear(); m_unhandledSMTLib2Queries.clear(); if (!_keepSettings) @@ -410,6 +412,10 @@ bool CompilerStack::parse() m_stackState = (m_stopAfter <= Parsed ? Parsed : ParsedAndImported); storeContractDefinitions(); + + solAssert(!m_maxAstId.has_value()); + m_maxAstId = parser.maxID(); + return true; } @@ -461,7 +467,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; @@ -475,10 +481,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) @@ -655,7 +663,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) @@ -1492,19 +1501,39 @@ 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 - ); - 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 + ); + compiledContract.yulIR = generator.run( + _contract, + createCBORMetadata(compiledContract, /* _forIR */ true), + otherYulSources + ); + } yul::YulStack stack( m_evmVersion, diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 06c78b686..c203d5f49 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -225,6 +225,13 @@ public: /// @returns false on error. bool analyze(); + /// Perform the analysis steps of legacy language mode. + /// @returns false on error. + bool analyzeLegacy(bool _noErrorsSoFar); + /// Perform the analysis steps of experimental language mode. + /// @returns false on error. + bool analyzeExperimental(); + /// Parses and analyzes all source units that were added /// @returns false on error. bool parseAndAnalyze(State _stopAfter = State::CompilationSuccessful); From 093ec110cffd6139a2b822f2730f0d247606a606 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Jun 2023 20:42:59 +0200 Subject: [PATCH 02/17] Scanner hack. --- liblangutil/Token.h | 8 ++++++++ libsolidity/interface/CompilerStack.h | 9 +-------- libsolidity/parsing/Parser.cpp | 3 +++ libsolidity/parsing/Parser.h | 2 ++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/liblangutil/Token.h b/liblangutil/Token.h index d79c7f193..1673e407f 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -324,6 +324,14 @@ namespace TokenTraits tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; } + constexpr bool isExperimentalSolidityKeyword(Token tok) + { + return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback; + } + constexpr bool isExperimentalSolidityOnlyKeyword(Token) + { + return false; + } constexpr bool isExperimentalSolidityKeyword(Token token) { diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index c203d5f49..ae0dbb631 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -418,14 +418,6 @@ private: /// @returns true if the contract is requested to be compiled. bool isRequestedContract(ContractDefinition const& _contract) const; - /// Perform the analysis steps of legacy language mode. - /// @returns false on error. - bool analyzeLegacy(bool _noErrorsSoFar); - - /// Perform the analysis steps of experimental language mode. - /// @returns false on error. - bool analyzeExperimental(); - /// Assembles the contract. /// This function should only be internally called by compileContract and generateEVMFromIR. void assembleYul( @@ -511,6 +503,7 @@ private: 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 1f5611a2f..e7d46892a 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()) diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5f2a1213d..df282cb99 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -197,6 +197,8 @@ private: /// Returns the next AST node ID int64_t nextID() { return ++m_currentNodeID; } + /// Returns the maximal AST node ID assigned so far + int64_t maxID() const { return m_currentNodeID; } std::pair tryParseIndexAccessedPath(); /// Performs limited look-ahead to distinguish between variable declaration and expression statement. From d8a36a1d5894d9f762c6ce94306c1e98cc679d75 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Jun 2023 12:48:38 +0200 Subject: [PATCH 03/17] Type inference draft. --- liblangutil/Scanner.cpp | 2 +- liblangutil/Token.h | 67 +- libsolidity/CMakeLists.txt | 27 +- libsolidity/analysis/NameAndTypeResolver.cpp | 6 +- libsolidity/analysis/NameAndTypeResolver.h | 5 +- libsolidity/analysis/ReferencesResolver.cpp | 93 +- libsolidity/analysis/ReferencesResolver.h | 6 +- libsolidity/analysis/SyntaxChecker.cpp | 14 +- libsolidity/analysis/SyntaxChecker.h | 2 + .../analysis/experimental/Analysis.cpp | 26 - libsolidity/ast/AST.cpp | 12 + libsolidity/ast/AST.h | 159 ++- libsolidity/ast/ASTAnnotations.h | 10 + libsolidity/ast/ASTForward.h | 8 + libsolidity/ast/ASTJsonExporter.cpp | 9 + libsolidity/ast/ASTJsonExporter.h | 1 + libsolidity/ast/ASTVisitor.h | 28 + libsolidity/ast/AST_accept.h | 106 ++ .../codegen/experimental/IRGenerator.cpp | 160 --- .../experimental/analysis/Analysis.cpp | 144 +- libsolidity/experimental/analysis/Analysis.h | 71 +- .../experimental/analysis/DebugWarner.cpp | 57 + .../analysis/DebugWarner.h} | 23 +- .../analysis/SyntaxRestrictor.cpp | 113 ++ .../experimental/analysis/SyntaxRestrictor.h | 67 + .../experimental/analysis/TypeInference.cpp | 1247 +++++++++++++++++ .../experimental/analysis/TypeInference.h | 126 ++ .../analysis/TypeRegistration.cpp | 188 +++ .../experimental/analysis/TypeRegistration.h | 65 + libsolidity/experimental/ast/Type.cpp | 63 + libsolidity/experimental/ast/Type.h | 153 ++ libsolidity/experimental/ast/TypeSystem.cpp | 347 +++++ libsolidity/experimental/ast/TypeSystem.h | 154 ++ .../experimental/ast/TypeSystemHelper.cpp | 403 ++++++ .../experimental/ast/TypeSystemHelper.h | 59 + libsolidity/experimental/codegen/Common.cpp | 74 + libsolidity/experimental/codegen/Common.h | 41 + .../codegen/IRGenerationContext.h | 55 + .../experimental/codegen/IRGenerator.cpp | 161 +++ .../codegen}/IRGenerator.h | 29 +- .../codegen/IRGeneratorForStatements.cpp | 388 +++++ .../codegen/IRGeneratorForStatements.h | 71 + .../experimental/codegen/IRVariable.cpp | 135 ++ libsolidity/experimental/codegen/IRVariable.h | 85 ++ libsolidity/parsing/Parser.cpp | 299 +++- libsolidity/parsing/Parser.h | 21 +- libstdlib/src/stub.sol | 3 +- test/libsolidity/Assembly.cpp | 2 +- .../SolidityExpressionCompiler.cpp | 2 +- .../semanticTests/experimental/stub.sol | 91 ++ .../syntaxTests/experimental_keywords.sol | 4 + 51 files changed, 5169 insertions(+), 313 deletions(-) delete mode 100644 libsolidity/analysis/experimental/Analysis.cpp delete mode 100644 libsolidity/codegen/experimental/IRGenerator.cpp create mode 100644 libsolidity/experimental/analysis/DebugWarner.cpp rename libsolidity/{analysis/experimental/Analysis.h => experimental/analysis/DebugWarner.h} (74%) create mode 100644 libsolidity/experimental/analysis/SyntaxRestrictor.cpp create mode 100644 libsolidity/experimental/analysis/SyntaxRestrictor.h create mode 100644 libsolidity/experimental/analysis/TypeInference.cpp create mode 100644 libsolidity/experimental/analysis/TypeInference.h create mode 100644 libsolidity/experimental/analysis/TypeRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeRegistration.h create mode 100644 libsolidity/experimental/ast/Type.cpp create mode 100644 libsolidity/experimental/ast/Type.h create mode 100644 libsolidity/experimental/ast/TypeSystem.cpp create mode 100644 libsolidity/experimental/ast/TypeSystem.h create mode 100644 libsolidity/experimental/ast/TypeSystemHelper.cpp create mode 100644 libsolidity/experimental/ast/TypeSystemHelper.h create mode 100644 libsolidity/experimental/codegen/Common.cpp create mode 100644 libsolidity/experimental/codegen/Common.h create mode 100644 libsolidity/experimental/codegen/IRGenerationContext.h create mode 100644 libsolidity/experimental/codegen/IRGenerator.cpp rename libsolidity/{codegen/experimental => experimental/codegen}/IRGenerator.h (71%) create mode 100644 libsolidity/experimental/codegen/IRGeneratorForStatements.cpp create mode 100644 libsolidity/experimental/codegen/IRGeneratorForStatements.h create mode 100644 libsolidity/experimental/codegen/IRVariable.cpp create mode 100644 libsolidity/experimental/codegen/IRVariable.h create mode 100644 test/libsolidity/semanticTests/experimental/stub.sol create mode 100644 test/libsolidity/syntaxTests/experimental_keywords.sol diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 3db3f29d5..806efa311 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 1673e407f..abcdf6d5d 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -269,7 +269,19 @@ 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(Word, "word", 0) \ + K(Integer, "integer", 0) \ + K(Itself, "itself", 0) \ + K(Void, "void", 0) \ + K(Pair, "pair", 0) \ + K(Fun, "fun", 0) \ + K(Unit, "unit", 0) \ + K(StaticAssert, "static_assert", 0) \ T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -292,7 +304,12 @@ 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 tok) + { + return (Token::Int <= tok && tok < Token::TypesEnd) || + tok == Token::Word || tok == Token::Void || tok == Token::Integer || + tok == Token::Pair || tok == Token::Unit || tok == Token::Fun; + } 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 || @@ -324,46 +341,22 @@ namespace TokenTraits tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; } + constexpr bool isBuiltinTypeClassName(Token tok) + { + return tok == Token::Integer || (isBinaryOp(tok) && tok != Token::Comma) || + isCompareOp(tok) || isUnaryOp(tok) || (isAssignmentOp(tok) && tok != Token::Assign); + } constexpr bool isExperimentalSolidityKeyword(Token tok) { - return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback; + return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback || + tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let || + tok == Token::Return || tok == Token::Type || tok == Token::Bool || tok == Token::If || tok == Token::Else || + tok == Token::Do || tok == Token::While || tok == Token::For || tok == Token::Continue || tok == Token::Break || + (tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd); } - constexpr bool isExperimentalSolidityOnlyKeyword(Token) + constexpr bool isExperimentalSolidityOnlyKeyword(Token tok) { - return false; - } - - constexpr bool isExperimentalSolidityKeyword(Token token) - { - return - token == Token::Assembly || - token == Token::Contract || - token == Token::External || - token == Token::Fallback || - token == Token::Pragma || - token == Token::Import || - token == Token::As || - token == Token::Function || - token == Token::Let || - token == Token::Return || - token == Token::Type || - token == Token::If || - token == Token::Else || - token == Token::Do || - token == Token::While || - token == Token::For || - token == Token::Continue || - token == Token::Break; - // TODO: see isExperimentalSolidityKeyword below - // || (token > Token::NonExperimentalEnd && token < Token::ExperimentalEnd); - } - - constexpr bool isExperimentalSolidityOnlyKeyword(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 tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd; } bool isYulKeyword(std::string const& _literal); diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index b2c847363..46d43291e 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -46,8 +46,6 @@ set(sources analysis/TypeChecker.h analysis/ViewPureChecker.cpp analysis/ViewPureChecker.h - analysis/experimental/Analysis.cpp - analysis/experimental/Analysis.h ast/AST.cpp ast/AST.h ast/AST_accept.h @@ -92,8 +90,6 @@ set(sources codegen/ReturnInfo.cpp codegen/YulUtilFunctions.h codegen/YulUtilFunctions.cpp - codegen/experimental/IRGenerator.cpp - codegen/experimental/IRGenerator.h codegen/ir/Common.cpp codegen/ir/Common.h codegen/ir/IRGenerator.cpp @@ -188,6 +184,29 @@ 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/TypeInference.cpp + experimental/analysis/TypeInference.h + experimental/analysis/TypeRegistration.cpp + experimental/analysis/TypeRegistration.h + experimental/analysis/SyntaxRestrictor.cpp + experimental/analysis/SyntaxRestrictor.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 d6ff63dbf..94d056811 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 6ba591e8a..a6cf5ed76 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -59,7 +59,8 @@ public: 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 @@ public: /// 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 @@ private: 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 e68002133..3db08ed14 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( + 0000_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( + 0000_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( + 0000_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 512a681b4..13752e9c9 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -85,6 +85,7 @@ private: 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 @@ private: 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 b2dd73cc2..ac23ca36a 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 f221df09f..69bda229f 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -97,6 +97,8 @@ private: 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/analysis/experimental/Analysis.cpp b/libsolidity/analysis/experimental/Analysis.cpp deleted file mode 100644 index 5138ff280..000000000 --- a/libsolidity/analysis/experimental/Analysis.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - 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 - -using namespace solidity::langutil; -using namespace solidity::frontend::experimental; - -bool Analysis::check(ASTNode const&) -{ - return true; -} diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index c0bf90091..bae169675 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 ca23b0d61..faaae7c87 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -125,6 +125,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: /// @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 @@ private: 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 @@ public: ): 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,136 @@ private: /// @} +/// 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; +}; + +/// @} + } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 2573015c2..e2b4ab7a1 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -244,6 +244,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 @@ -341,4 +343,12 @@ struct FunctionCallAnnotation: ExpressionAnnotation bool tryCall = false; }; +/// Experimental Solidity annotations. +/// Used to intergrate with name and type resolution. +/// @{ +struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation +{ +}; +/// @} + } diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 79e6a4ecf..f13b6e55d 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -99,6 +99,14 @@ class ElementaryTypeNameExpression; class Literal; class StructuredDocumentation; +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition; +class TypeClassInstantiation; +class TypeClassName; +class TypeDefinition; +/// @} + class VariableScope; template diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index b15917809..65dd018d4 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -1032,6 +1032,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 71d15ce0a..ff3b94fa2 100644 --- a/libsolidity/ast/ASTJsonExporter.h +++ b/libsolidity/ast/ASTJsonExporter.h @@ -130,6 +130,7 @@ public: 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 ef03e6339..959cd5dce 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -109,6 +109,13 @@ public: 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 void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -165,6 +172,13 @@ public: 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); } + /// @} protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -243,6 +257,13 @@ public: 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 void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -299,6 +320,13 @@ public: 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); } + /// @} 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 78dc0e5dd..eaf179e73 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,102 @@ 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); +} +/// @} + } diff --git a/libsolidity/codegen/experimental/IRGenerator.cpp b/libsolidity/codegen/experimental/IRGenerator.cpp deleted file mode 100644 index 0db02a163..000000000 --- a/libsolidity/codegen/experimental/IRGenerator.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - 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 - -using namespace std; -using namespace solidity; -using namespace solidity::frontend::experimental; -using namespace solidity::langutil; -using namespace solidity::util; - -string IRGenerator::run( - ContractDefinition const& _contract, - bytes const& /*_cborMetadata*/, - map const& /*_otherYulSources*/ -) const -{ - - 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(); -} - -string IRGenerator::generate(ContractDefinition const& _contract) const -{ - std::stringstream code; - code << "{\n"; - if (_contract.fallbackFunction()) - { - code << IRNames::function(*_contract.fallbackFunction()) << "()\n"; - } - code << "revert(0,0)\n"; - code << "}\n"; - - for (FunctionDefinition const* f: _contract.definedFunctions()) - code << generate(*f); - - return code.str(); -} - -string IRGenerator::generate(FunctionDefinition const& _function) const -{ - std::stringstream code; - code << "function " << IRNames::function(_function) << "() {\n"; - for (auto _statement: _function.body().statements()) - { - if (auto assembly = dynamic_cast(_statement.get())) - code << generate(*assembly) << "\n"; - else - solUnimplemented("Unsupported statement type."); - } - code << "}\n"; - return code.str(); -} - -namespace { - -struct CopyTranslate: public yul::ASTCopier -{ - CopyTranslate( - yul::Dialect const& _dialect, - map _references - ): 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(holds_alternative(translated)); - return 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&) - { - solUnimplemented("External references in inline assembly not implemented."); - } - - yul::Dialect const& m_dialect; - map m_references; -}; - -} - -string IRGenerator::generate(InlineAssembly const& _assembly) const -{ - CopyTranslate bodyCopier{_assembly.dialect(), {}}; - yul::Statement modified = bodyCopier(_assembly.operations()); - solAssert(holds_alternative(modified)); - return yul::AsmPrinter()(std::get(modified)); -} \ No newline at end of file diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 672aa4f87..7695f15e8 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -16,19 +16,143 @@ */ // SPDX-License-Identifier: GPL-3.0 #include +#include +#include +#include +#include -#include - +using namespace std; 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 { - m_errorReporter.error( - 6547_error, - Error::Type::UnimplementedFeatureError, - SourceLocation{}, - "Experimental Analysis is not implemented yet." - ); - return false; + TypeRegistration::Annotation typeRegistrationAnnotation; + TypeInference::Annotation typeInferenceAnnotation; +}; + +struct Analysis::GlobalAnnotationContainer +{ + TypeRegistration::GlobalAnnotation typeRegistrationAnnotation; + TypeInference::GlobalAnnotation typeInferenceAnnotation; +}; + +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 +{ + 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(vector> const& _sourceUnits) +{ + using AnalysisSteps = std::tuple; + + 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 729ff2b93..780e25015 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,72 @@ 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 { + struct AnnotationContainer; + struct GlobalAnnotationContainer; public: - Analysis(langutil::ErrorReporter& _errorReporter): - m_errorReporter(_errorReporter) - {} - + 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 000000000..cf65d8da8 --- /dev/null +++ b/libsolidity/experimental/analysis/DebugWarner.cpp @@ -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 + +#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) +{ + auto const& typeInferenceAnnotation = m_analysis.annotation(_node); + if (typeInferenceAnnotation.type) + { + Type type = *typeInferenceAnnotation.type; + Sort sort = m_analysis.typeSystem().env().sort(type); + std::string sortString; + if (sort.classes.size() != 1 || *sort.classes.begin() != m_analysis.typeSystem().primitiveClass(PrimitiveClass::Type)) + sortString = ":" + TypeSystemHelpers{m_analysis.typeSystem()}.sortToString(m_analysis.typeSystem().env().sort(type)); + m_errorReporter.info( + 0000_error, + _node.location(), + "Inferred type: " + TypeEnvironmentHelpers{m_analysis.typeSystem().env()}.typeToString(type) + sortString + ); + } + return true; +} diff --git a/libsolidity/analysis/experimental/Analysis.h b/libsolidity/experimental/analysis/DebugWarner.h similarity index 74% rename from libsolidity/analysis/experimental/Analysis.h rename to libsolidity/experimental/analysis/DebugWarner.h index 9433508e4..a7b5ac6d0 100644 --- a/libsolidity/analysis/experimental/Analysis.h +++ b/libsolidity/experimental/analysis/DebugWarner.h @@ -17,26 +17,25 @@ // SPDX-License-Identifier: GPL-3.0 #pragma once -namespace solidity::frontend -{ -class ASTNode; -} +#include -namespace solidity::langutil -{ -class ErrorReporter; -} +#include namespace solidity::frontend::experimental { +class Analysis; -class Analysis +class DebugWarner: public ASTConstVisitor { public: - Analysis(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) - {} - bool check(ASTNode const& _ast); + 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/SyntaxRestrictor.cpp b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp new file mode 100644 index 000000000..75ed77b2d --- /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(0000_error, _node.location(), "Unsupported AST node."); + return false; +} + +bool SyntaxRestrictor::visit(ContractDefinition const& _contractDefinition) +{ + if (_contractDefinition.contractKind() != ContractKind::Contract) + m_errorReporter.syntaxError(0000_error, _contractDefinition.location(), "Only contracts are supported."); + if (!_contractDefinition.baseContracts().empty()) + m_errorReporter.syntaxError(0000_error, _contractDefinition.location(), "Inheritance unsupported."); + return true; +} + +bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) +{ + if (!_functionDefinition.isImplemented()) + m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Functions must be implemented."); + if (!_functionDefinition.modifiers().empty()) + m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have modifiers."); + if (_functionDefinition.overrides()) + m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have override specifiers."); + solAssert(!_functionDefinition.returnParameterList()); + if (_functionDefinition.isFree()) + { + if (_functionDefinition.stateMutability() != StateMutability::NonPayable) + m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Free functions may not have a mutability."); + } + else + { + if (_functionDefinition.isFallback()) + { + if (_functionDefinition.visibility() != Visibility::External) + m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Fallback function must be external."); + } + else + m_errorReporter.syntaxError(0000_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(0000_error, _variableDeclarationStatement.initialValue()->location(), "Variable declaration has to declare a single variable."); + } + else + m_errorReporter.syntaxError(0000_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(0000_error, _variableDeclaration.value()->location(), "Variable declarations with initial value not supported."); + if (_variableDeclaration.isStateVariable()) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "State variables are not supported."); + if (!_variableDeclaration.isLocalVariable()) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Only local variables are supported."); + if (_variableDeclaration.mutability() != VariableDeclaration::Mutability::Mutable) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Only mutable variables are supported."); + if (_variableDeclaration.isIndexed()) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Indexed variables are not supported."); + if (!_variableDeclaration.noVisibilitySpecified()) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with visibility not supported."); + if (_variableDeclaration.overrides()) + m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with override specifier not supported."); + if (_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Unspecified) + m_errorReporter.syntaxError(0000_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 000000000..86d2d1b49 --- /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/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp new file mode 100644 index 000000000..52a0679da --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -0,0 +1,1247 @@ +/* + 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 std; +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()) +{ + auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class, auto _memberCreator, Sort _sort = {}) -> TypeClass { + Type type = m_typeSystem.freshTypeVariable(std::move(_sort)); + auto result = m_typeSystem.declareTypeClass( + type, + _name, + nullptr + ); + if (auto error = get_if(&result)) + solAssert(!error, *error); + TypeClass resultClass = get(result); + // TODO: validation? + annotation().typeClassFunctions[resultClass] = _memberCreator(type); + solAssert(annotation().builtinClassesByName.emplace(_name, _class).second); + return annotation().builtinClasses.emplace(_class, resultClass).first->second; + }; + TypeSystemHelpers helper{m_typeSystem}; + using MemberList = std::map; + + declareBuiltinClass("integer", BuiltinClass::Integer, [&](Type _typeVar) -> MemberList { + return { + { + "fromInteger", + helper.functionType(m_typeSystem.type(PrimitiveType::Integer, {}), _typeVar) + } + }; + }); + + auto defineBinaryMonoidalOperator = [&](std::string _className, BuiltinClass _class, Token _token, std::string _functionName) { + TypeClass typeClass = declareBuiltinClass(_className, _class, [&](Type _typeVar) -> MemberList { + return { + { + _functionName, + helper.functionType(helper.tupleType({_typeVar, _typeVar}), _typeVar) + } + }; + }); + annotation().operators.emplace(_token, std::make_tuple(typeClass, _functionName)); + }; + + defineBinaryMonoidalOperator("*", BuiltinClass::Mul, Token::Mul, "mul"); + defineBinaryMonoidalOperator("+", BuiltinClass::Add, Token::Add, "add"); + + auto defineBinaryCompareOperator = [&](std::string _className, BuiltinClass _class, Token _token, std::string _functionName) { + TypeClass typeClass = declareBuiltinClass(_className, _class, [&](Type _typeVar) -> MemberList { + return { + { + _functionName, + helper.functionType(helper.tupleType({_typeVar, _typeVar}), m_typeSystem.type(PrimitiveType::Bool, {})) + } + }; + }); + annotation().operators.emplace(_token, std::make_tuple(typeClass, _functionName)); + }; + 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"); + + 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, {}); + m_env = &m_typeSystem.env(); + + { + auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Bool), map{}); + solAssert(newlyInserted); + members->second.emplace("abs", TypeMember{helper.functionType(m_wordType, m_boolType)}); + members->second.emplace("rep", TypeMember{helper.functionType(m_boolType, m_wordType)}); + } + { + Type first = m_typeSystem.freshTypeVariable({}); + Type second = m_typeSystem.freshTypeVariable({}); + Type pairType = m_typeSystem.type(PrimitiveType::Pair, {first, second}); + auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Pair), map{}); + solAssert(newlyInserted); + members->second.emplace("first", TypeMember{helper.functionType(pairType, first)}); + members->second.emplace("second", TypeMember{helper.functionType(pairType, second)}); + } +} + +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, 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, getType(_functionDefinition.parameterList()), _functionDefinition.parameterList().location()); + if (_functionDefinition.experimentalReturnExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _functionDefinition.experimentalReturnExpression()->accept(*this); + unify( + returnType, + getType(*_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 = get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); + if (_return.expression()) + unify(functionReturnType, getType(*_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 getType(*_arg); }) | ranges::to> + ); +} + +bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + auto& typeClassAnnotation = annotation(_typeClassDefinition); + if (typeClassAnnotation.type) + return false; + typeClassAnnotation.type = type(&_typeClassDefinition, {}); + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeClassDefinition.typeVariable().accept(*this); + } + + map functionTypes; + + Type typeVar = m_typeSystem.freshTypeVariable({}); + auto& typeMembers = annotation().members[typeConstructor(&_typeClassDefinition)]; + + for (auto subNode: _typeClassDefinition.subNodes()) + { + subNode->accept(*this); + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + // TODO: need polymorphicInstance? + auto functionType = polymorphicInstance(getType(*functionDefinition)); + if (!functionTypes.emplace(functionDefinition->name(), functionType).second) + m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class declared multiple times."); + auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); + if(typeVars.size() != 1) + m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); + unify(typeVars.front(), typeVar, functionDefinition->location()); + typeMembers[functionDefinition->name()] = TypeMember{functionType}; + } + + TypeClass typeClass = std::visit(util::GenericVisitor{ + [](TypeClass _class) -> TypeClass { return _class; }, + [&](std::string _error) -> TypeClass { + m_errorReporter.fatalTypeError(0000_error, _typeClassDefinition.location(), _error); + util::unreachable(); + } + }, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition)); + annotation(_typeClassDefinition).typeClass = typeClass; + 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(0000_error, _typeClassDefinition.location(), "Function " + functionName + " does not depend on class variable."); + if (typeVars.size() > 2) + m_errorReporter.typeError(0000_error, _typeClassDefinition.location(), "Function " + functionName + " depends on multiple type variables."); + if (!m_env->typeEquals(typeVars.front(), typeVar)) + m_errorReporter.typeError(0000_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); + } + + unify(getType(_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(getType(*declaration), m_wordType, originLocationOf(_identifier)); + identifierInfo->valueSize = 1; + return true; + }; + solAssert(!_inlineAssembly.annotation().analysisInfo, ""); + _inlineAssembly.annotation().analysisInfo = 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(ElementaryTypeNameExpression const& _expression) +{ + auto& expressionAnnotation = annotation(_expression); + solAssert(!expressionAnnotation.type); + + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: + if (auto constructor = m_analysis.annotation(_expression).typeConstructor) + { + vector arguments; + std::generate_n(std::back_inserter(arguments), m_typeSystem.constructorInfo(*constructor).arguments(), [&]() { + return m_typeSystem.freshTypeVariable({}); + }); + // TODO: get rid of the distinction (assign a function on unit for the empty case)? Ambiguity? + if (arguments.empty() || m_expressionContext == ExpressionContext::Term) + expressionAnnotation.type = m_typeSystem.type(*constructor, arguments); + else + { + TypeSystemHelpers helper{m_typeSystem}; + expressionAnnotation.type = + helper.typeFunctionType( + helper.tupleType(arguments), + m_typeSystem.type(*constructor, arguments) + ); + } + } + else + { + m_errorReporter.typeError(0000_error, _expression.location(), "No type constructor registered for elementary type name."); + expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + break; + case ExpressionContext::Sort: + m_errorReporter.typeError(0000_error, _expression.location(), "Elementary type name expression not supported in sort context."); + expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); + break; + } + 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({getType(_binaryOperation.leftExpression()), getType(_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 = getType(_binaryOperation.leftExpression()); + unify(leftType, getType(_binaryOperation.rightExpression()), _binaryOperation.location()); + operationAnnotation.type = leftType; + } + else + { + m_errorReporter.typeError(0000_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 = getType(_binaryOperation.leftExpression()); + unify(leftType, getType(_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(getType(_binaryOperation.leftExpression()), getType(_binaryOperation.rightExpression())); + } + else if (_binaryOperation.getOperator() == Token::BitOr) + { + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + operationAnnotation.type = helper.sumType({getType(_binaryOperation.leftExpression()), getType(_binaryOperation.rightExpression())}); + } + else + { + m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Invalid binary operations in type context."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(0000_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(0000_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported."); + return; + } + Type variableType = getType(*_variableDeclarationStatement.declarations().front()); + if (_variableDeclarationStatement.initialValue()) + unify(variableType, getType(*_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 = getType(*_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, getType(*_variableDeclaration.typeExpression()), _variableDeclaration.typeExpression()->location()); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(0000_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(0000_error, _ifStatement.location(), "If statement outside term context."); + ifAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + unify(getType(_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(0000_error, _assignment.location(), "Assignment outside term context."); + assignmentAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + Type leftType = getType(_assignment.leftHandSide()); + unify(leftType, getType(_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(0000_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)) + return polymorphicInstance(*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(0000_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* typeClass = dynamic_cast(&_declaration)) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; + typeClass->accept(*this); + if (!annotation(*typeClass).typeClass) + { + m_errorReporter.typeError(0000_error, _location, "Unregistered type class."); + return m_typeSystem.freshTypeVariable({}); + } + return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClass).typeClass}}); + } + else + { + m_errorReporter.typeError(0000_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 + 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* typeClass = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) + { + // visiting the type class will re-visit this instantiation + typeClass->accept(*this); + // TODO: more error handling? Should be covered by the visit above. + if (!annotation(*typeClass).typeClass) + m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + return annotation(*typeClass).typeClass; + } + else + { + m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + return nullopt; + } + }, + [&](Token _token) -> std::optional { + if (auto builtinClass = builtinClassFromToken(_token)) + if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) + return *typeClass; + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name."); + return 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(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); + return false; + } + + 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 type{TypeConstant{*typeConstructor, arguments}}; + + map functionTypes; + + for (auto subNode: _typeClassInstantiation.subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + subNode->accept(*this); + if (!functionTypes.emplace(functionDefinition->name(), getType(*functionDefinition)).second) + m_errorReporter.typeError(0000_error, subNode->location(), "Duplicate definition of function " + functionDefinition->name() + " during type class instantiation."); + } + + if (auto error = m_typeSystem.instantiateClass(type, arity)) + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), *error); + + auto const& classFunctions = annotation().typeClassFunctions.at(*typeClass); + + TypeEnvironment newEnv = m_env->clone(); + if (!newEnv.unify(m_typeSystem.typeClassVariable(*typeClass), type).empty()) + { + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); + return false; + } + + for (auto [name, classFunctionType]: classFunctions) + { + if (!functionTypes.count(name)) + { + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Missing function: " + name); + continue; + } + Type instanceFunctionType = functionTypes.at(name); + functionTypes.erase(name); + + if (!newEnv.typeEquals(instanceFunctionType, classFunctionType)) + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Type mismatch for function " + name + " " + TypeEnvironmentHelpers{newEnv}.typeToString(instanceFunctionType) + " != " + TypeEnvironmentHelpers{newEnv}.typeToString(classFunctionType)); + } + + if (!functionTypes.empty()) + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Additional functions in class instantiation."); + + return false; +} + +bool TypeInference::visit(MemberAccess const& _memberAccess) +{ + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(0000_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 type = m_env->resolve(_type); + TypeSystemHelpers helper{m_typeSystem}; + if (helper.isTypeConstant(type)) + { + auto constructor = std::get<0>(helper.destTypeConstant(type)); + if (auto* typeMember = util::valueOrNullptr(annotation().members.at(constructor), _memberName)) + { + return polymorphicInstance(typeMember->type); + } + else + { + m_errorReporter.typeError(0000_error, _location, fmt::format("Member {} not found in type {}.", _memberName, TypeEnvironmentHelpers{*m_env}.typeToString(_type))); + return m_typeSystem.freshTypeVariable({}); + } + } + else + { + m_errorReporter.typeError(0000_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 = getType(_memberAccess.expression()); + memberAccessAnnotation.type = memberType(expressionType, _memberAccess.memberName(), _memberAccess.location()); +} + +bool TypeInference::visit(TypeDefinition const& _typeDefinition) +{ + TypeSystemHelpers helper{m_typeSystem}; + auto& typeDefinitionAnnotation = annotation(_typeDefinition); + if (typeDefinitionAnnotation.type) + return false; + + if (_typeDefinition.arguments()) + _typeDefinition.arguments()->accept(*this); + + std::optional underlyingType; + if (_typeDefinition.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeDefinition.typeExpression()->accept(*this); + underlyingType = annotation(*_typeDefinition.typeExpression()).type; + } + + 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); + + TypeConstructor constructor = typeConstructor(&_typeDefinition); + auto [members, newlyInserted] = annotation().members.emplace(constructor, map{}); + solAssert(newlyInserted); + if (underlyingType) + { + members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)}); + members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)}); + } + return false; +} + +bool TypeInference::visit(FunctionCall const&) { return true; } +void TypeInference::endVisit(FunctionCall const& _functionCall) +{ + auto& functionCallAnnotation = annotation(_functionCall); + solAssert(!functionCallAnnotation.type); + + Type functionType = getType(_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(getType(*arg)); + break; + case ExpressionContext::Sort: + m_errorReporter.typeError(0000_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 +{ + +optional parseRational(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 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(string(fractionalBegin, _value.end())); + denominator /= boost::multiprecision::pow( + bigint(10), + static_cast(distance(radixPoint + 1, _value.end())) + ); + numerator = bigint(string(_value.begin(), radixPoint)); + value = numerator + denominator; + } + else + value = bigint(_value); + return value; + } + catch (...) + { + return 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); +} + +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. + optional mantissa = parseRational(string(valueString.begin(), expPoint)); + + if (!mantissa) + return nullopt; + value = *mantissa; + + // 0E... is always zero. + if (value == 0) + return nullopt; + + bigint exp = bigint(string(expPoint + 1, valueString.end())); + + if (exp > numeric_limits::max() || exp < numeric_limits::min()) + return nullopt; + + uint32_t expAbs = bigint(abs(exp)).convert_to(); + + if (exp < 0) + { + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return nullopt; + value /= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return nullopt; + value *= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + } + else + { + // parse as rational number + optional tmp = parseRational(valueString); + if (!tmp) + return nullopt; + value = *tmp; + } + } + catch (...) + { + return 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(0000_error, _literal.location(), "Only number literals are supported."); + return false; + } + optional value = rationalValue(_literal); + if (!value) + { + m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals."); + return false; + } + if (value->denominator() != 1) + { + m_errorReporter.typeError(0000_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& annotation = _analysis.annotation(); + auto& inferenceAnnotation = _analysis.annotation(); + return annotation.builtinClassInstantiations.at(inferenceAnnotation.builtinClassesByName.at(_analysis.typeSystem().typeClassName(_class))); +} +} + +void TypeInference::unifyGeneralized(Type _type, Type _scheme, std::vector _monomorphicTypes, langutil::SourceLocation _location) +{ + solUnimplementedAssert(_monomorphicTypes.empty(), "unsupported"); + Type fresh = m_env->fresh(_scheme); + unify(_type, fresh, _location); +} + +experimental::Type TypeInference::polymorphicInstance(Type _scheme, langutil::SourceLocation _location) +{ + Type result = m_typeSystem.freshTypeVariable({}); + unifyGeneralized(result, _scheme, {}, _location); + return result; +} + +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()) + { + // Attempt to resolve interdependencies between type class instantiations. + std::vector missingInstantiations; + bool recursion = false; + bool onlyMissingInstantiations = [&]() { + for (auto failure: unificationFailures) + { + if (auto* sortMismatch = 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( + 0000_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( + 0000_error, + _location, + fmt::format( + "Cannot unify {} and {}.", + envHelper.typeToString(_typeMismatch.a), + envHelper.typeToString(_typeMismatch.b)) + ); + }, + [&](TypeEnvironment::SortMismatch _sortMismatch) { + m_errorReporter.typeError(0000_error, _location, fmt::format( + "{} does not have sort {}", + envHelper.typeToString(_sortMismatch.type), + TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort) + )); + }, + [&](TypeEnvironment::RecursiveUnification _recursiveUnification) { + m_errorReporter.typeError( + 0000_error, + _location, + fmt::format( + "Recursive unification: {} occurs in {}.", + envHelper.typeToString(_recursiveUnification.var), + envHelper.typeToString(_recursiveUnification.type)) + ); + } + }, failure); + } +} + +experimental::Type TypeInference::getType(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(0000_error, _type->location(), "Unregistered type."); + util::unreachable(); +} +experimental::Type TypeInference::type(Declaration const* _type, vector _arguments) const +{ + return m_typeSystem.type(typeConstructor(_type), std::move(_arguments)); +} + +bool TypeInference::visitNode(ASTNode const& _node) +{ + m_errorReporter.fatalTypeError(0000_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 000000000..86686f556 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -0,0 +1,126 @@ +/* + 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; + // Type classes. + std::optional typeClass; + }; + 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(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(ElementaryTypeNameExpression const& _expression) 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 getType(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 = {}); + void unifyGeneralized(Type _type, Type _scheme, std::vector _monomorphicTypes, langutil::SourceLocation _location = {}); + Type polymorphicInstance(Type _scheme, langutil::SourceLocation _location = {}); + 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 000000000..3cc46c438 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -0,0 +1,188 @@ +/* + 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 std; +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(ElementaryTypeName const& _typeName) +{ + if (annotation(_typeName).typeConstructor) + return false; + annotation(_typeName).typeConstructor = [&]() -> optional { + switch(_typeName.typeName().token()) + { + case Token::Void: + return m_typeSystem.constructor(PrimitiveType::Void); + case Token::Fun: + return m_typeSystem.constructor(PrimitiveType::Function); + case Token::Unit: + return m_typeSystem.constructor(PrimitiveType::Unit); + case Token::Pair: + return m_typeSystem.constructor(PrimitiveType::Pair); + case Token::Word: + return m_typeSystem.constructor(PrimitiveType::Word); + case Token::Integer: + return m_typeSystem.constructor(PrimitiveType::Integer); + case Token::Bool: + return m_typeSystem.constructor(PrimitiveType::Bool); + default: + m_errorReporter.fatalTypeError(0000_error, _typeName.location(), "Expected primitive type."); + return nullopt; + } + }(); + 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(0000_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(0000_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(0000_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(0000_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(0000_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(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); + } + + return true; +} + +bool TypeRegistration::visit(TypeDefinition const& _typeDefinition) +{ + if (annotation(_typeDefinition).typeConstructor) + return false; + annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor( + _typeDefinition.name(), + "t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()), + _typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0, + &_typeDefinition + ); + return true; +} + +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 000000000..d6d534fa7 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.h @@ -0,0 +1,65 @@ +/* + 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 type definitions, type class definitions, type names and type name expressions. + std::optional typeConstructor; + }; + struct GlobalAnnotation + { + std::map primitiveClassInstantiations; + std::map builtinClassInstantiations; + }; + 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; + bool visit(UserDefinedTypeName const& _typeName) override; + void endVisit(ElementaryTypeNameExpression const& _typeName) override; + bool visit(ElementaryTypeName const& _typeName) 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/Type.cpp b/libsolidity/experimental/ast/Type.cpp new file mode 100644 index 000000000..9650a6411 --- /dev/null +++ b/libsolidity/experimental/ast/Type.cpp @@ -0,0 +1,63 @@ +/* + 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 std; +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 000000000..f98925684 --- /dev/null +++ b/libsolidity/experimental/ast/Type.h @@ -0,0 +1,153 @@ +/* + 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 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(size_t _index): m_index(_index) {} + 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(size_t _index): m_index(_index) {} + 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 +{ + size_t index() const { return m_index; } + Sort const& sort() const { return m_sort; } +private: + friend class TypeSystem; + size_t m_index = 0; + Sort m_sort; + TypeVariable(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 000000000..443946b8d --- /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 + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +vector TypeEnvironment::unify(Type _a, Type _b) +{ + 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()) + { + if (_left.sort() != _right.sort()) + unificationFailure(); + } + 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(freshVariable({}), _name, nullptr)); + }; + + m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type")); + + for (auto [type, name, arity]: std::initializer_list> { + {PrimitiveType::TypeFunction, "tfun", 2}, + {PrimitiveType::Function, "fun", 2}, + {PrimitiveType::Function, "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{vector{{typeSort},{typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector{{typeSort, typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector{{typeSort, typeSort}}, classType}}; +} + +experimental::Type TypeSystem::freshVariable(Sort _sort) +{ + uint64_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); +} + +vector TypeEnvironment::instantiate(TypeVariable _variable, Type _type) +{ + for (auto typeVar: TypeEnvironmentHelpers{*this}.typeVars(_type)) + 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& _type) -> Type { + return TypeConstant{ + _type.constructor, + _type.arguments | ranges::views::transform([&](Type _argType) { + return resolveRecursive(_argType); + }) | ranges::to> + }; + }, + [&](TypeVariable const&) -> Type { + return _type; + }, + [&](std::monostate) -> Type { + return _type; + } + }, 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(string _name, 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{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(Type _typeVariable, std::string _name, Declaration const* _declaration) +{ + TypeVariable const* typeVariable = get_if(&_typeVariable); + if (!typeVariable) + return "Invalid type variable."; + + size_t index = m_typeClasses.size(); + m_typeClasses.emplace_back(TypeClassInfo{ + _typeVariable, + _name, + _declaration + }); + TypeClass typeClass{index}; + + 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(), "Invalid arity."); + 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 = 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 nullopt; +} diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h new file mode 100644 index 000000000..17b427e85 --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystem.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 +#include +#include + +namespace solidity::frontend +{ +class Declaration; +} +namespace solidity::frontend::experimental +{ + +class TypeEnvironment +{ +public: + 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); + struct TypeMismatch { Type a; Type b; }; + struct SortMismatch { Type type; Sort sort; }; + struct RecursiveUnification { Type var; Type type; }; + using UnificationFailure = std::variant; + [[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(Type _typeVariable, std::string _name, Declaration const* _declaration); + [[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; + } +private: + friend class TypeEnvironment; + TypeClassInfo const& typeClassInfo(TypeClass _class) const + { + return m_typeClasses.at(_class.m_index); + } + 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 000000000..b8d72b85b --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -0,0 +1,403 @@ +/* + 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 std; +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::Integer: + 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 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(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; +} + +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); + + 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(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; +} + +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); + + 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; +} + +tuple> TypeSystemHelpers::destTypeConstant(Type _type) const +{ + using ResultType = 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); +} + +experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::Function, {_argType, _resultType}); +} + +tuple TypeSystemHelpers::destFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::Function)); + solAssert(arguments.size() == 2); + return make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isFunctionType(Type _type) const +{ + if (!isTypeConstant(_type)) + return false; + auto constructor = get<0>(destTypeConstant(_type)); + return constructor == typeSystem.constructor(PrimitiveType::Function); +} + +experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::TypeFunction, {_argType, _resultType}); +} + +tuple TypeSystemHelpers::destTypeFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::TypeFunction)); + solAssert(arguments.size() == 2); + return make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isTypeFunctionType(Type _type) const +{ + if (!isTypeConstant(_type)) + return false; + auto constructor = get<0>(destTypeConstant(_type)); + return constructor == typeSystem.constructor(PrimitiveType::TypeFunction); +} + +vector TypeEnvironmentHelpers::typeVars(Type _type) const +{ + set indices; + 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(); + } + } +} + +string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const +{ + return visit(util::GenericVisitor{ + [&](TypeConstant _type) -> 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) -> string { + solAssert(false); + }, + [](std::monostate) -> 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()); + 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 += 'a' + static_cast(index%26); + while (index /= 26) + varName += 'a' + static_cast(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) -> 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 000000000..5149d34bd --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.h @@ -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 +#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; + 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 000000000..b14c51c67 --- /dev/null +++ b/libsolidity/experimental/codegen/Common.cpp @@ -0,0 +1,74 @@ +/* + 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 std; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::util; +using namespace solidity::yul; + +namespace solidity::frontend::experimental +{ + +string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type) +{ + if (_function.isConstructor()) + return constructor(*_function.annotation().contract); + + return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + TypeEnvironmentHelpers{_env}.canonicalTypeName(_type) + "$"; +} + +string IRNames::function(VariableDeclaration const& _varDecl) +{ + return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); +} + +string IRNames::creationObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()); +} + +string IRNames::deployedObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; +} + +string IRNames::constructor(ContractDefinition const& _contract) +{ + return "constructor_" + _contract.name() + "_" + to_string(_contract.id()); +} + +string IRNames::localVariable(VariableDeclaration const& _declaration) +{ + return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id()); +} + +string IRNames::localVariable(Expression const& _expression) +{ + return "expr_" + to_string(_expression.id()); +} + +} diff --git a/libsolidity/experimental/codegen/Common.h b/libsolidity/experimental/codegen/Common.h new file mode 100644 index 000000000..25f05efcb --- /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 000000000..b0958cf67 --- /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 000000000..a050ed07a --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerator.cpp @@ -0,0 +1,161 @@ +/* + 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 std; +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, {}, {}} +{ +} + +string IRGenerator::run( + ContractDefinition const& _contract, + bytes const& /*_cborMetadata*/, + 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(); +} + +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, 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(); +} + +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/codegen/experimental/IRGenerator.h b/libsolidity/experimental/codegen/IRGenerator.h similarity index 71% rename from libsolidity/codegen/experimental/IRGenerator.h rename to libsolidity/experimental/codegen/IRGenerator.h index b2d1a7247..cce8c7305 100644 --- a/libsolidity/codegen/experimental/IRGenerator.h +++ b/libsolidity/experimental/codegen/IRGenerator.h @@ -18,10 +18,12 @@ #pragma once +#include #include #include #include #include +#include #include #include @@ -34,7 +36,7 @@ namespace solidity::frontend::experimental { -class SourceUnit; +class Analysis; class IRGenerator { @@ -44,30 +46,27 @@ public: std::optional _eofVersion, RevertStrings /*_revertStrings*/, std::map /*_sourceIndices*/, - langutil::DebugInfoSelection const& _debugInfoSelection, - langutil::CharStreamProvider const* _soliditySourceProvider - ): - m_evmVersion(_evmVersion), - m_eofVersion(_eofVersion), - m_debugInfoSelection(_debugInfoSelection), - m_soliditySourceProvider(_soliditySourceProvider) - {} + langutil::DebugInfoSelection const& /*_debugInfoSelection*/, + langutil::CharStreamProvider const* /*_soliditySourceProvider*/, + Analysis const& _analysis + ); std::string run( ContractDefinition const& _contract, bytes const& _cborMetadata, std::map const& _otherYulSources - ) const; + ); - std::string generate(ContractDefinition const& _contract) const; - std::string generate(FunctionDefinition const& _function) const; - std::string generate(InlineAssembly const& _assembly) const; + 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; +// 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 000000000..911dc11f9 --- /dev/null +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -0,0 +1,388 @@ +/* + 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 + +using namespace std; +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, + 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(holds_alternative(translated)); + return 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, {}))); + string value = IRNames::localVariable(*varDecl); + return yul::Identifier{_identifier.debugData, yul::YulString{value}}; + } + + IRGenerationContext const& m_context; + yul::Dialect const& m_dialect; + 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(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, 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 = 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)) + { + optional typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass; + solAssert(typeClass); + 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()); + auto declaration = m_expressionDeclaration.at(&_functionCall.expression()); + if (auto builtin = 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(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 + m_code << "let " << IRNames::localVariable(_functionCall) << " := " << 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 000000000..527a6abe8 --- /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/experimental/codegen/IRVariable.cpp b/libsolidity/experimental/codegen/IRVariable.cpp new file mode 100644 index 000000000..3f2c6f648 --- /dev/null +++ b/libsolidity/experimental/codegen/IRVariable.cpp @@ -0,0 +1,135 @@ +/* + 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 std; +using namespace solidity; +using namespace solidity::frontend::experimental; +using namespace solidity::util; + +template +Type getType(IRGenerationContext const& _context, Node const& _node) +{ + auto& annotation = _context.analysis.annotation(_node); + solAssert(annotation.type); + return _context.env->resolve(*annotation.type); +} + +namespace +{ +size_t getTypeStackSlots(IRGenerationContext const& _context, Type _type) +{ +} +} + +IRVariable::IRVariable(IRGenerationContext const& _context, std::string _baseName, Type _type): + m_baseName(std::move(_baseName)), m_type(_type) +{ +} + +IRVariable::IRVariable(IRGenerationContext const& _context, VariableDeclaration const& _declaration): + IRVariable(_context, IRNames::localVariable(_declaration), getType(_context, _declaration)) +{ +} + +IRVariable::IRVariable(IRGenerationContext const& _context, Expression const& _expression): + IRVariable(_context, IRNames::localVariable(_expression), getType(_context, _expression)) +{ +} + +IRVariable IRVariable::part(string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type}; + } + solAssert(false, "Invalid stack item name: " + _name); +} + +bool IRVariable::hasPart(std::string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return true; + } + return false; +} + +vector IRVariable::stackSlots() const +{ + vector result; + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemType) + { + solAssert(!itemName.empty(), ""); + solAssert(m_type != *itemType, ""); + result += IRVariable{suffixedName(itemName), *itemType}.stackSlots(); + } + else + { + solAssert(itemName.empty(), ""); + result.emplace_back(m_baseName); + } + return result; +} + +string IRVariable::commaSeparatedList() const +{ + return joinHumanReadable(stackSlots()); +} + +string IRVariable::commaSeparatedListPrefixed() const +{ + return joinHumanReadablePrefixed(stackSlots()); +} + +string IRVariable::name() const +{ + solAssert(m_type.sizeOnStack() == 1, ""); + auto const& [itemName, type] = m_type.stackItems().front(); + solAssert(!type, "Expected null type for name " + itemName); + return suffixedName(itemName); +} + +IRVariable IRVariable::tupleComponent(size_t _i) const +{ + solAssert( + m_type.category() == Type::Category::Tuple, + "Requested tuple component of non-tuple IR variable." + ); + return part(IRNames::tupleComponent(_i)); +} + +string IRVariable::suffixedName(string const& _suffix) const +{ + if (_suffix.empty()) + return m_baseName; + else + return m_baseName + '_' + _suffix; +} diff --git a/libsolidity/experimental/codegen/IRVariable.h b/libsolidity/experimental/codegen/IRVariable.h new file mode 100644 index 000000000..02d6f7931 --- /dev/null +++ b/libsolidity/experimental/codegen/IRVariable.h @@ -0,0 +1,85 @@ +/* + 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 +{ +class VariableDeclaration; +class Expression; +} + +namespace solidity::frontend::experimental +{ + +class IRGenerationContext; + +class IRVariable +{ +public: + /// IR variable with explicit base name @a _baseName and type @a _type. + IRVariable(IRGenerationContext const& _analysis, std::string _baseName, Type _type); + /// IR variable referring to the declaration @a _decl. + IRVariable(IRGenerationContext const& _analysis, VariableDeclaration const& _decl); + /// IR variable referring to the expression @a _expr. + IRVariable(IRGenerationContext const& _analysis, Expression const& _expression); + + /// @returns the name of the variable, if it occupies a single stack slot (otherwise throws). + std::string name() const; + + /// @returns a comma-separated list of the stack slots of the variable. + std::string commaSeparatedList() const; + + /// @returns a comma-separated list of the stack slots of the variable that is + /// prefixed with a comma, unless it is empty. + std::string commaSeparatedListPrefixed() const; + + /// @returns an IRVariable referring to the tuple component @a _i of a tuple variable. + IRVariable tupleComponent(std::size_t _i) const; + + /// @returns the type of the variable. + Type const& type() const { return m_type; } + + /// @returns an IRVariable referring to the stack component @a _slot of the variable. + /// @a _slot must be among the stack slots in ``m_type.stackItems()``. + /// The returned IRVariable is itself typed with the type of the stack slot as defined + /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. + IRVariable part(std::string const& _slot) const; + + /// @returns true if variable contains @a _name component + /// @a _name name of the component that is being checked + bool hasPart(std::string const& _name) const; + + /// @returns a vector containing the names of the stack slots of the variable. + std::vector stackSlots() const; + +private: + /// @returns a name consisting of the base name appended with an underscore and @æ _suffix, + /// unless @a _suffix is empty, in which case the base name itself is returned. + std::string suffixedName(std::string const& _suffix) const; + std::string m_baseName; + Type m_type; +}; + + +} diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e7d46892a..947329aff 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -126,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 +137,14 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Function: nodes.push_back(parseFunctionDefinition(true)); 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 +602,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 +672,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 +701,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) header.parameters, header.modifiers, header.returnParameters, - block + block, + header.experimentalReturnExpression ); } @@ -1190,10 +1220,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 +1281,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 +1652,206 @@ 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); + expression = parseExpression(); + } + 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 +1954,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 +1968,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 +2053,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 +2072,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 +2547,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 df282cb99..d4c48c7a6 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -48,6 +48,8 @@ public: 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 @@ private: Visibility visibility = Visibility::Default; StateMutability stateMutability = StateMutability::NonPayable; std::vector> modifiers; + ASTPointer experimentalReturnExpression; }; /// Struct to share parsed function call arguments. @@ -99,7 +102,7 @@ private: 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 @@ private: 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 @@ -197,8 +212,6 @@ private: /// Returns the next AST node ID int64_t nextID() { return ++m_currentNodeID; } - /// Returns the maximal AST node ID assigned so far - int64_t maxID() const { return m_currentNodeID; } std::pair tryParseIndexAccessedPath(); /// Performs limited look-ahead to distinguish between variable declaration and expression statement. @@ -223,6 +236,8 @@ private: 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 46ae21dcf..bd34e0475 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/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index dcfe49ab4..ccad7d49d 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/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index b05d28a84..a171a5be0 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/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol new file mode 100644 index 000000000..7f3061fe7 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -0,0 +1,91 @@ +pragma experimental solidity; + +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) + } + } +} +// ==== +// compileViaYul: true +// ---- +// (): 0 -> 0 +// (): 1 -> 544 diff --git a/test/libsolidity/syntaxTests/experimental_keywords.sol b/test/libsolidity/syntaxTests/experimental_keywords.sol new file mode 100644 index 000000000..b5920aed0 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental_keywords.sol @@ -0,0 +1,4 @@ +function f() pure { + uint word; word; + uint static_assert; static_assert; +} \ No newline at end of file From e4e0472407ce3037e546cf46610459dbb50dbc86 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 15:05:39 +0200 Subject: [PATCH 04/17] Eliminate using namespace std. --- .../experimental/analysis/Analysis.cpp | 3 +- .../experimental/analysis/TypeInference.cpp | 79 +++++++++---------- .../analysis/TypeRegistration.cpp | 5 +- libsolidity/experimental/ast/Type.cpp | 1 - libsolidity/experimental/ast/TypeSystem.cpp | 29 ++++--- .../experimental/ast/TypeSystemHelper.cpp | 51 ++++++------ libsolidity/experimental/codegen/Common.cpp | 23 +++--- .../experimental/codegen/IRGenerator.cpp | 11 ++- .../codegen/IRGeneratorForStatements.cpp | 25 +++--- .../experimental/codegen/IRVariable.cpp | 1 - scripts/check_style.sh | 1 + 11 files changed, 110 insertions(+), 119 deletions(-) diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 7695f15e8..7e1cf94b6 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -21,7 +21,6 @@ #include #include -using namespace std; using namespace solidity::langutil; using namespace solidity::frontend::experimental; @@ -120,7 +119,7 @@ std::tuple...> makeIndexTuple(std::index_sequ return std::make_tuple( std::integral_constant{}...); } -bool Analysis::check(vector> const& _sourceUnits) +bool Analysis::check(std::vector> const& _sourceUnits) { using AnalysisSteps = std::tuple; diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 52a0679da..56dec5dc6 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -33,7 +33,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::experimental; @@ -51,9 +50,9 @@ m_typeSystem(_analysis.typeSystem()) _name, nullptr ); - if (auto error = get_if(&result)) + if (auto error = std::get_if(&result)) solAssert(!error, *error); - TypeClass resultClass = get(result); + TypeClass resultClass = std::get(result); // TODO: validation? annotation().typeClassFunctions[resultClass] = _memberCreator(type); solAssert(annotation().builtinClassesByName.emplace(_name, _class).second); @@ -111,7 +110,7 @@ m_typeSystem(_analysis.typeSystem()) m_env = &m_typeSystem.env(); { - auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Bool), map{}); + auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Bool), std::map{}); solAssert(newlyInserted); members->second.emplace("abs", TypeMember{helper.functionType(m_wordType, m_boolType)}); members->second.emplace("rep", TypeMember{helper.functionType(m_boolType, m_wordType)}); @@ -120,7 +119,7 @@ m_typeSystem(_analysis.typeSystem()) Type first = m_typeSystem.freshTypeVariable({}); Type second = m_typeSystem.freshTypeVariable({}); Type pairType = m_typeSystem.type(PrimitiveType::Pair, {first, second}); - auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Pair), map{}); + auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Pair), std::map{}); solAssert(newlyInserted); members->second.emplace("first", TypeMember{helper.functionType(pairType, first)}); members->second.emplace("second", TypeMember{helper.functionType(pairType, second)}); @@ -140,7 +139,7 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) if (functionAnnotation.type) return false; - ScopedSaveAndRestore signatureRestore(m_currentFunctionType, nullopt); + ScopedSaveAndRestore signatureRestore(m_currentFunctionType, std::nullopt); Type argumentsType = m_typeSystem.freshTypeVariable({}); Type returnType = m_typeSystem.freshTypeVariable({}); @@ -174,7 +173,7 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) void TypeInference::endVisit(Return const& _return) { solAssert(m_currentFunctionType); - Type functionReturnType = get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); + Type functionReturnType = std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); if (_return.expression()) unify(functionReturnType, getType(*_return.expression()), _return.location()); else @@ -186,7 +185,7 @@ 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 getType(*_arg); }) | ranges::to> + _parameterList.parameters() | ranges::views::transform([&](auto _arg) { return getType(*_arg); }) | ranges::to> ); } @@ -202,7 +201,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) _typeClassDefinition.typeVariable().accept(*this); } - map functionTypes; + std::map functionTypes; Type typeVar = m_typeSystem.freshTypeVariable({}); auto& typeMembers = annotation().members[typeConstructor(&_typeClassDefinition)]; @@ -283,7 +282,7 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly) return true; }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); - _inlineAssembly.annotation().analysisInfo = make_shared(); + _inlineAssembly.annotation().analysisInfo = std::make_shared(); yul::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, @@ -306,7 +305,7 @@ bool TypeInference::visit(ElementaryTypeNameExpression const& _expression) case ExpressionContext::Type: if (auto constructor = m_analysis.annotation(_expression).typeConstructor) { - vector arguments; + std::vector arguments; std::generate_n(std::back_inserter(arguments), m_typeSystem.constructorInfo(*constructor).arguments(), [&]() { return m_typeSystem.freshTypeVariable({}); }); @@ -631,7 +630,7 @@ void TypeInference::endVisit(TupleExpression const& _tupleExpression) auto& componentAnnotation = annotation(*_expr); solAssert(componentAnnotation.type); return *componentAnnotation.type; - }) | ranges::to>; + }) | ranges::to>; switch (m_expressionContext) { case ExpressionContext::Term: @@ -666,7 +665,7 @@ bool TypeInference::visit(IdentifierPath const& _identifierPath) bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) { - ScopedSaveAndRestore activeInstantiations{m_activeInstantiations, m_activeInstantiations + set{&_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) @@ -686,7 +685,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) else { m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); - return nullopt; + return std::nullopt; } }, [&](Token _token) -> std::optional { @@ -694,7 +693,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) return *typeClass; m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name."); - return nullopt; + return std::nullopt; } }, _typeClassInstantiation.typeClass().name()); if (!typeClass) @@ -708,7 +707,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) return false; } - vector arguments; + std::vector arguments; Arity arity{ {}, *typeClass @@ -724,13 +723,13 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) arguments = TypeSystemHelpers{m_typeSystem}.destTupleType(*argumentSortAnnotation.type); arity.argumentSorts = arguments | ranges::views::transform([&](Type _type) { return m_env->sort(_type); - }) | ranges::to>; + }) | ranges::to>; } } Type type{TypeConstant{*typeConstructor, arguments}}; - map functionTypes; + std::map functionTypes; for (auto subNode: _typeClassInstantiation.subNodes()) { @@ -834,7 +833,7 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition) underlyingType = annotation(*_typeDefinition.typeExpression()).type; } - vector arguments; + std::vector arguments; if (_typeDefinition.arguments()) for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) arguments.emplace_back(m_typeSystem.freshTypeVariable({})); @@ -846,7 +845,7 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition) typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), definedType); TypeConstructor constructor = typeConstructor(&_typeDefinition); - auto [members, newlyInserted] = annotation().members.emplace(constructor, map{}); + auto [members, newlyInserted] = annotation().members.emplace(constructor, std::map{}); solAssert(newlyInserted); if (underlyingType) { @@ -910,7 +909,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) namespace { -optional parseRational(string const& _value) +std::optional parseRational(std::string const& _value) { rational value; try @@ -923,7 +922,7 @@ optional parseRational(string const& _value) !all_of(radixPoint + 1, _value.end(), util::isDigit) || !all_of(_value.begin(), radixPoint, util::isDigit) ) - return nullopt; + return std::nullopt; // Only decimal notation allowed here, leading zeros would switch to octal. auto fractionalBegin = find_if_not( @@ -935,12 +934,12 @@ optional parseRational(string const& _value) rational numerator; rational denominator(1); - denominator = bigint(string(fractionalBegin, _value.end())); + denominator = bigint(std::string(fractionalBegin, _value.end())); denominator /= boost::multiprecision::pow( bigint(10), static_cast(distance(radixPoint + 1, _value.end())) ); - numerator = bigint(string(_value.begin(), radixPoint)); + numerator = bigint(std::string(_value.begin(), radixPoint)); value = numerator + denominator; } else @@ -949,7 +948,7 @@ optional parseRational(string const& _value) } catch (...) { - return nullopt; + return std::nullopt; } } @@ -960,7 +959,7 @@ bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); } -optional rationalValue(Literal const& _literal) +std::optional rationalValue(Literal const& _literal) { rational value; try @@ -979,27 +978,27 @@ optional rationalValue(Literal const& _literal) else if (expPoint != valueString.end()) { // Parse mantissa and exponent. Checks numeric limit. - optional mantissa = parseRational(string(valueString.begin(), expPoint)); + std::optional mantissa = parseRational(std::string(valueString.begin(), expPoint)); if (!mantissa) - return nullopt; + return std::nullopt; value = *mantissa; // 0E... is always zero. if (value == 0) - return nullopt; + return std::nullopt; - bigint exp = bigint(string(expPoint + 1, valueString.end())); + bigint exp = bigint(std::string(expPoint + 1, valueString.end())); - if (exp > numeric_limits::max() || exp < numeric_limits::min()) - return nullopt; + 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 nullopt; + return std::nullopt; value /= boost::multiprecision::pow( bigint(10), expAbs @@ -1008,7 +1007,7 @@ optional rationalValue(Literal const& _literal) else if (exp > 0) { if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) - return nullopt; + return std::nullopt; value *= boost::multiprecision::pow( bigint(10), expAbs @@ -1018,15 +1017,15 @@ optional rationalValue(Literal const& _literal) else { // parse as rational number - optional tmp = parseRational(valueString); + std::optional tmp = parseRational(valueString); if (!tmp) - return nullopt; + return std::nullopt; value = *tmp; } } catch (...) { - return nullopt; + return std::nullopt; } switch (_literal.subDenomination()) { @@ -1069,7 +1068,7 @@ bool TypeInference::visit(Literal const& _literal) m_errorReporter.typeError(0000_error, _literal.location(), "Only number literals are supported."); return false; } - optional value = rationalValue(_literal); + std::optional value = rationalValue(_literal); if (!value) { m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals."); @@ -1127,7 +1126,7 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) bool onlyMissingInstantiations = [&]() { for (auto failure: unificationFailures) { - if (auto* sortMismatch = get_if(&failure)) + if (auto* sortMismatch = std::get_if(&failure)) if (helper.isTypeConstant(sortMismatch->type)) { TypeConstructor constructor = std::get<0>(helper.destTypeConstant(sortMismatch->type)); @@ -1220,7 +1219,7 @@ TypeConstructor TypeInference::typeConstructor(Declaration const* _type) const m_errorReporter.fatalTypeError(0000_error, _type->location(), "Unregistered type."); util::unreachable(); } -experimental::Type TypeInference::type(Declaration const* _type, vector _arguments) const +experimental::Type TypeInference::type(Declaration const* _type, std::vector _arguments) const { return m_typeSystem.type(typeConstructor(_type), std::move(_arguments)); } diff --git a/libsolidity/experimental/analysis/TypeRegistration.cpp b/libsolidity/experimental/analysis/TypeRegistration.cpp index 3cc46c438..424dc78ff 100644 --- a/libsolidity/experimental/analysis/TypeRegistration.cpp +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -26,7 +26,6 @@ #include #include -using namespace std; using namespace solidity::frontend; using namespace solidity::frontend::experimental; using namespace solidity::langutil; @@ -61,7 +60,7 @@ bool TypeRegistration::visit(ElementaryTypeName const& _typeName) { if (annotation(_typeName).typeConstructor) return false; - annotation(_typeName).typeConstructor = [&]() -> optional { + annotation(_typeName).typeConstructor = [&]() -> std::optional { switch(_typeName.typeName().token()) { case Token::Void: @@ -80,7 +79,7 @@ bool TypeRegistration::visit(ElementaryTypeName const& _typeName) return m_typeSystem.constructor(PrimitiveType::Bool); default: m_errorReporter.fatalTypeError(0000_error, _typeName.location(), "Expected primitive type."); - return nullopt; + return std::nullopt; } }(); return true; diff --git a/libsolidity/experimental/ast/Type.cpp b/libsolidity/experimental/ast/Type.cpp index 9650a6411..ec822dc64 100644 --- a/libsolidity/experimental/ast/Type.cpp +++ b/libsolidity/experimental/ast/Type.cpp @@ -25,7 +25,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::frontend::experimental; diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index 443946b8d..3ed04fd1f 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -32,14 +32,13 @@ #include -using namespace std; using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::experimental; -vector TypeEnvironment::unify(Type _a, Type _b) +std::vector TypeEnvironment::unify(Type _a, Type _b) { - vector failures; + std::vector failures; auto unificationFailure = [&]() { failures.emplace_back(UnificationFailure{TypeMismatch{_a, _b}}); }; @@ -151,9 +150,9 @@ TypeSystem::TypeSystem() 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{vector{{typeSort},{typeSort}}, classType}}; - m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector{{typeSort, typeSort}}, classType}}; - m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector{{typeSort, 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::Function).m_index).arities = {Arity{std::vector{{typeSort, typeSort}}, classType}}; } experimental::Type TypeSystem::freshVariable(Sort _sort) @@ -168,7 +167,7 @@ experimental::Type TypeSystem::freshTypeVariable(Sort _sort) return freshVariable(_sort); } -vector TypeEnvironment::instantiate(TypeVariable _variable, Type _type) +std::vector TypeEnvironment::instantiate(TypeVariable _variable, Type _type) { for (auto typeVar: TypeEnvironmentHelpers{*this}.typeVars(_type)) if (typeVar.index() == _variable.index()) @@ -201,7 +200,7 @@ experimental::Type TypeEnvironment::resolveRecursive(Type _type) const _type.constructor, _type.arguments | ranges::views::transform([&](Type _argType) { return resolveRecursive(_argType); - }) | ranges::to> + }) | ranges::to> }; }, [&](TypeVariable const&) -> Type { @@ -221,7 +220,7 @@ Sort TypeEnvironment::sort(Type _type) const auto const& constructorInfo = m_typeSystem.constructorInfo(_expression.constructor); auto argumentSorts = _expression.arguments | ranges::views::transform([&](Type _argumentType) { return sort(resolve(_argumentType)); - }) | ranges::to>; + }) | ranges::to>; Sort sort; for (auto const& arity: constructorInfo.arities) { @@ -246,7 +245,7 @@ Sort TypeEnvironment::sort(Type _type) const }, _type); } -TypeConstructor TypeSystem::declareTypeConstructor(string _name, string _canonicalName, size_t _arguments, Declaration const* _declaration) +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)}}; @@ -254,7 +253,7 @@ TypeConstructor TypeSystem::declareTypeConstructor(string _name, string _canonic m_typeConstructors.emplace_back(TypeConstructorInfo{ _name, _canonicalName, - {Arity{vector{_arguments, baseSort}, primitiveClass(PrimitiveClass::Type)}}, + {Arity{std::vector{_arguments, baseSort}, primitiveClass(PrimitiveClass::Type)}}, _declaration }); TypeConstructor constructor{index}; @@ -277,7 +276,7 @@ TypeConstructor TypeSystem::declareTypeConstructor(string _name, string _canonic std::variant TypeSystem::declareTypeClass(Type _typeVariable, std::string _name, Declaration const* _declaration) { - TypeVariable const* typeVariable = get_if(&_typeVariable); + TypeVariable const* typeVariable = std::get_if(&_typeVariable); if (!typeVariable) return "Invalid type variable."; @@ -310,13 +309,13 @@ experimental::Type TypeEnvironment::fresh(Type _type) _type.constructor, _type.arguments | ranges::views::transform([&](Type _argType) { return _recurse(_argType, _recurse); - }) | ranges::to> + }) | ranges::to> }; }, [&](TypeVariable const& _var) -> Type { if (auto* mapped = util::valueOrNullptr(mapping, _var.index())) { - auto* typeVariable = get_if(mapped); + auto* typeVariable = std::get_if(mapped); solAssert(typeVariable); // TODO: can there be a mismatch? solAssert(typeVariable->sort() == _var.sort()); @@ -343,5 +342,5 @@ std::optional TypeSystem::instantiateClass(Type _instanceVariable, typeConstructorInfo.arities.emplace_back(_arity); - return nullopt; + return std::nullopt; } diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index b8d72b85b..2d86ea4b5 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -32,7 +32,6 @@ #include -using namespace std; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::experimental; @@ -97,7 +96,7 @@ std::optional experimental::builtinClassFromToken(langutil::Token case Token::GreaterThanOrEqual: return BuiltinClass::GreaterOrEqual; default: - return nullopt; + return std::nullopt; } } /* @@ -116,7 +115,7 @@ std::optional experimental::typeClassFromTypeClassName(TypeClassName }, _typeClass.name()); } */ -experimental::Type TypeSystemHelpers::tupleType(vector _elements) const +experimental::Type TypeSystemHelpers::tupleType(std::vector _elements) const { if (_elements.empty()) return typeSystem.type(PrimitiveType::Unit, {}); @@ -128,7 +127,7 @@ experimental::Type TypeSystemHelpers::tupleType(vector _elements) const return result; } -vector TypeSystemHelpers::destTupleType(Type _tupleType) const +std::vector TypeSystemHelpers::destTupleType(Type _tupleType) const { if (!isTypeConstant(_tupleType)) return {_tupleType}; @@ -140,7 +139,7 @@ vector TypeSystemHelpers::destTupleType(Type _tupleType) con return {_tupleType}; solAssert(arguments.size() == 2); - vector result; + std::vector result; result.emplace_back(arguments.front()); Type tail = arguments.back(); while(true) @@ -158,7 +157,7 @@ vector TypeSystemHelpers::destTupleType(Type _tupleType) con return result; } -experimental::Type TypeSystemHelpers::sumType(vector _elements) const +experimental::Type TypeSystemHelpers::sumType(std::vector _elements) const { if (_elements.empty()) return typeSystem.type(PrimitiveType::Void, {}); @@ -170,7 +169,7 @@ experimental::Type TypeSystemHelpers::sumType(vector _elements) const return result; } -vector TypeSystemHelpers::destSumType(Type _tupleType) const +std::vector TypeSystemHelpers::destSumType(Type _tupleType) const { if (!isTypeConstant(_tupleType)) return {_tupleType}; @@ -182,7 +181,7 @@ vector TypeSystemHelpers::destSumType(Type _tupleType) const return {_tupleType}; solAssert(arguments.size() == 2); - vector result; + std::vector result; result.emplace_back(arguments.front()); Type tail = arguments.back(); while(true) @@ -200,9 +199,9 @@ vector TypeSystemHelpers::destSumType(Type _tupleType) const return result; } -tuple> TypeSystemHelpers::destTypeConstant(Type _type) const +std::tuple> TypeSystemHelpers::destTypeConstant(Type _type) const { - using ResultType = tuple>; + using ResultType = std::tuple>; return std::visit(util::GenericVisitor{ [&](TypeConstant const& _type) -> ResultType { return std::make_tuple(_type.constructor, _type.arguments); @@ -230,19 +229,19 @@ experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, return typeSystem.type(PrimitiveType::Function, {_argType, _resultType}); } -tuple TypeSystemHelpers::destFunctionType(Type _functionType) const +std::tuple TypeSystemHelpers::destFunctionType(Type _functionType) const { auto [constructor, arguments] = destTypeConstant(_functionType); solAssert(constructor == typeSystem.constructor(PrimitiveType::Function)); solAssert(arguments.size() == 2); - return make_tuple(arguments.front(), arguments.back()); + return std::make_tuple(arguments.front(), arguments.back()); } bool TypeSystemHelpers::isFunctionType(Type _type) const { if (!isTypeConstant(_type)) return false; - auto constructor = get<0>(destTypeConstant(_type)); + auto constructor = std::get<0>(destTypeConstant(_type)); return constructor == typeSystem.constructor(PrimitiveType::Function); } @@ -251,26 +250,26 @@ experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argTy return typeSystem.type(PrimitiveType::TypeFunction, {_argType, _resultType}); } -tuple TypeSystemHelpers::destTypeFunctionType(Type _functionType) const +std::tuple TypeSystemHelpers::destTypeFunctionType(Type _functionType) const { auto [constructor, arguments] = destTypeConstant(_functionType); solAssert(constructor == typeSystem.constructor(PrimitiveType::TypeFunction)); solAssert(arguments.size() == 2); - return make_tuple(arguments.front(), arguments.back()); + return std::make_tuple(arguments.front(), arguments.back()); } bool TypeSystemHelpers::isTypeFunctionType(Type _type) const { if (!isTypeConstant(_type)) return false; - auto constructor = get<0>(destTypeConstant(_type)); + auto constructor = std::get<0>(destTypeConstant(_type)); return constructor == typeSystem.constructor(PrimitiveType::TypeFunction); } -vector TypeEnvironmentHelpers::typeVars(Type _type) const +std::vector TypeEnvironmentHelpers::typeVars(Type _type) const { - set indices; - vector typeVars; + std::set indices; + std::vector typeVars; auto typeVarsImpl = [&](Type _type, auto _recurse) -> void { std::visit(util::GenericVisitor{ [&](TypeConstant const& _type) { @@ -310,10 +309,10 @@ std::string TypeSystemHelpers::sortToString(Sort _sort) const } } -string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const +std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const { return visit(util::GenericVisitor{ - [&](TypeConstant _type) -> string { + [&](TypeConstant _type) -> std::string { std::stringstream stream; stream << env.typeSystem().constructorInfo(_type.constructor).canonicalName; if (!_type.arguments.empty()) @@ -326,10 +325,10 @@ string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const } return stream.str(); }, - [](TypeVariable) -> string { + [](TypeVariable) -> std::string { solAssert(false); }, - [](std::monostate) -> string { + [](std::monostate) -> std::string { solAssert(false); }, }, env.resolve(_type)); @@ -337,7 +336,7 @@ string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const { - std::map)>> formatters{ + std::map)>> formatters{ {env.typeSystem().constructor(PrimitiveType::Function), [&](auto const& _args) { solAssert(_args.size() == 2); return fmt::format("{} -> {}", typeToString(_args.front()), typeToString(_args.back())); @@ -348,7 +347,7 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const }}, {env.typeSystem().constructor(PrimitiveType::Pair), [&](auto const& _arguments) { auto tupleTypes = TypeSystemHelpers{env.typeSystem()}.destTupleType(_arguments.back()); - string result = "("; + std::string result = "("; result += typeToString(_arguments.front()); for (auto type: tupleTypes) result += ", " + typeToString(type); @@ -398,6 +397,6 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const } return stream.str(); }, - [](std::monostate) -> string { solAssert(false); } + [](std::monostate) -> std::string { solAssert(false); } }, env.resolve(_type)); } diff --git a/libsolidity/experimental/codegen/Common.cpp b/libsolidity/experimental/codegen/Common.cpp index b14c51c67..de50fd47c 100644 --- a/libsolidity/experimental/codegen/Common.cpp +++ b/libsolidity/experimental/codegen/Common.cpp @@ -24,7 +24,6 @@ #include -using namespace std; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::util; @@ -33,42 +32,42 @@ using namespace solidity::yul; namespace solidity::frontend::experimental { -string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type) +std::string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type) { if (_function.isConstructor()) return constructor(*_function.annotation().contract); - return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + TypeEnvironmentHelpers{_env}.canonicalTypeName(_type) + "$"; + return "fun_" + _function.name() + "_" + std::to_string(_function.id()) + "$" + TypeEnvironmentHelpers{_env}.canonicalTypeName(_type) + "$"; } -string IRNames::function(VariableDeclaration const& _varDecl) +std::string IRNames::function(VariableDeclaration const& _varDecl) { - return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); + return "getter_fun_" + _varDecl.name() + "_" + std::to_string(_varDecl.id()); } -string IRNames::creationObject(ContractDefinition const& _contract) +std::string IRNames::creationObject(ContractDefinition const& _contract) { return _contract.name() + "_" + toString(_contract.id()); } -string IRNames::deployedObject(ContractDefinition const& _contract) +std::string IRNames::deployedObject(ContractDefinition const& _contract) { return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; } -string IRNames::constructor(ContractDefinition const& _contract) +std::string IRNames::constructor(ContractDefinition const& _contract) { - return "constructor_" + _contract.name() + "_" + to_string(_contract.id()); + return "constructor_" + _contract.name() + "_" + std::to_string(_contract.id()); } -string IRNames::localVariable(VariableDeclaration const& _declaration) +std::string IRNames::localVariable(VariableDeclaration const& _declaration) { return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id()); } -string IRNames::localVariable(Expression const& _expression) +std::string IRNames::localVariable(Expression const& _expression) { - return "expr_" + to_string(_expression.id()); + return "expr_" + std::to_string(_expression.id()); } } diff --git a/libsolidity/experimental/codegen/IRGenerator.cpp b/libsolidity/experimental/codegen/IRGenerator.cpp index a050ed07a..257bd2be1 100644 --- a/libsolidity/experimental/codegen/IRGenerator.cpp +++ b/libsolidity/experimental/codegen/IRGenerator.cpp @@ -40,7 +40,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::frontend::experimental; using namespace solidity::langutil; @@ -64,10 +63,10 @@ m_context{_analysis, &m_env, {}, {}} { } -string IRGenerator::run( +std::string IRGenerator::run( ContractDefinition const& _contract, bytes const& /*_cborMetadata*/, - map const& /*_otherYulSources*/ + std::map const& /*_otherYulSources*/ ) { @@ -91,7 +90,7 @@ string IRGenerator::run( return t.render(); } -string IRGenerator::generate(ContractDefinition const& _contract) +std::string IRGenerator::generate(ContractDefinition const& _contract) { std::stringstream code; code << "{\n"; @@ -110,7 +109,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) { auto queueEntry = m_context.functionQueue.front(); m_context.functionQueue.pop_front(); - auto& generatedTypes = m_context.generatedFunctions.insert(std::make_pair(queueEntry.function, vector{})).first->second; + 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); @@ -121,7 +120,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) return code.str(); } -string IRGenerator::generate(FunctionDefinition const& _function, Type _type) +std::string IRGenerator::generate(FunctionDefinition const& _function, Type _type) { TypeEnvironment newEnv = m_context.env->clone(); ScopedSaveAndRestore envRestore{m_context.env, &newEnv}; diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 911dc11f9..6add66c42 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -33,7 +33,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; @@ -54,7 +53,7 @@ struct CopyTranslate: public yul::ASTCopier CopyTranslate( IRGenerationContext const& _context, yul::Dialect const& _dialect, - map _references + std::map _references ): m_context(_context), m_dialect(_dialect), m_references(std::move(_references)) {} using ASTCopier::operator(); @@ -83,8 +82,8 @@ struct CopyTranslate: public yul::ASTCopier return ASTCopier::translate(_identifier); yul::Expression translated = translateReference(_identifier); - solAssert(holds_alternative(translated)); - return get(std::move(translated)); + solAssert(std::holds_alternative(translated)); + return std::get(std::move(translated)); } private: @@ -100,20 +99,20 @@ private: auto type = m_context.analysis.annotation(*varDecl).type; solAssert(type); solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {}))); - string value = IRNames::localVariable(*varDecl); + std::string value = IRNames::localVariable(*varDecl); return yul::Identifier{_identifier.debugData, yul::YulString{value}}; } IRGenerationContext const& m_context; yul::Dialect const& m_dialect; - map m_references; + std::map m_references; }; } bool IRGeneratorForStatements::visit(TupleExpression const& _tupleExpression) { - std::vector components; + std::vector components; for (auto const& component: _tupleExpression.components()) { solUnimplementedAssert(component); @@ -130,7 +129,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly) { CopyTranslate bodyCopier{m_context, _assembly.dialect(), _assembly.annotation().externalReferences}; yul::Statement modified = bodyCopier(_assembly.operations()); - solAssert(holds_alternative(modified)); + solAssert(std::holds_alternative(modified)); m_code << yul::AsmPrinter()(std::get(modified)) << "\n"; return false; } @@ -223,7 +222,7 @@ TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGener } } -FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, string _name, Type _type) +FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type) { TypeSystemHelpers helper{m_context.analysis.typeSystem()}; @@ -232,7 +231,7 @@ FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(Typ auto typeVars = TypeEnvironmentHelpers{env}.typeVars(genericFunctionType); solAssert(typeVars.size() == 1); solAssert(env.unify(genericFunctionType, _type).empty()); - auto typeClassInstantiation = get<0>(helper.destTypeConstant(env.resolve(typeVars.front()))); + auto typeClassInstantiation = std::get<0>(helper.destTypeConstant(env.resolve(typeVars.front()))); auto const& instantiations = typeClassInstantiations(m_context, _class); TypeClassInstantiation const* instantiation = instantiations.at(typeClassInstantiation); @@ -271,7 +270,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(declaration); if (auto const* typeClassDefinition = dynamic_cast(declaration)) { - optional typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass; + std::optional typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass; solAssert(typeClass); solAssert(m_expressionDeclaration.emplace( &_memberAccess, @@ -299,7 +298,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { Type functionType = type(_functionCall.expression()); auto declaration = m_expressionDeclaration.at(&_functionCall.expression()); - if (auto builtin = get_if(&declaration)) + if (auto builtin = std::get_if(&declaration)) { switch(*builtin) { @@ -315,7 +314,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } solAssert(false); } - FunctionDefinition const* functionDefinition = dynamic_cast(get(declaration)); + 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); diff --git a/libsolidity/experimental/codegen/IRVariable.cpp b/libsolidity/experimental/codegen/IRVariable.cpp index 3f2c6f648..7c5a50585 100644 --- a/libsolidity/experimental/codegen/IRVariable.cpp +++ b/libsolidity/experimental/codegen/IRVariable.cpp @@ -24,7 +24,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::frontend::experimental; using namespace solidity::util; diff --git a/scripts/check_style.sh b/scripts/check_style.sh index dd1f58348..a14dcc46b 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/* From 5d1d365e2f491afe76635ae9bdfc8c9b7a68969c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 17:56:26 +0200 Subject: [PATCH 05/17] style fixes --- liblangutil/Token.h | 6 +- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/ast/AST.h | 10 +- libsolidity/ast/AST_accept.h | 4 +- .../experimental/analysis/Analysis.cpp | 6 +- .../experimental/analysis/TypeInference.cpp | 148 +++++++++--------- .../analysis/TypeRegistration.cpp | 2 +- libsolidity/experimental/ast/TypeSystem.cpp | 32 ++-- .../experimental/ast/TypeSystemHelper.cpp | 34 ++-- .../codegen/IRGeneratorForStatements.cpp | 5 +- libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/parsing/Parser.cpp | 2 +- 12 files changed, 126 insertions(+), 127 deletions(-) diff --git a/liblangutil/Token.h b/liblangutil/Token.h index abcdf6d5d..741413a0b 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -307,8 +307,8 @@ namespace TokenTraits constexpr bool isElementaryTypeName(Token tok) { return (Token::Int <= tok && tok < Token::TypesEnd) || - tok == Token::Word || tok == Token::Void || tok == Token::Integer || - tok == Token::Pair || tok == Token::Unit || tok == Token::Fun; + tok == Token::Word || tok == Token::Void || tok == Token::Integer || + tok == Token::Pair || tok == Token::Unit || tok == Token::Fun; } constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } @@ -344,7 +344,7 @@ namespace TokenTraits constexpr bool isBuiltinTypeClassName(Token tok) { return tok == Token::Integer || (isBinaryOp(tok) && tok != Token::Comma) || - isCompareOp(tok) || isUnaryOp(tok) || (isAssignmentOp(tok) && tok != Token::Assign); + isCompareOp(tok) || isUnaryOp(tok) || (isAssignmentOp(tok) && tok != Token::Assign); } constexpr bool isExperimentalSolidityKeyword(Token tok) { diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 3db08ed14..da2206f45 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -304,7 +304,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) } std::string name = splitName.front(); auto declarations = m_resolver.nameFromCurrentScope(name); - switch(declarations.size()) + switch (declarations.size()) { case 0: if (splitName.size() > 1) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index faaae7c87..031068a76 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -2511,11 +2511,11 @@ public: 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)) + 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; diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index eaf179e73..d0b5ad281 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -1063,7 +1063,7 @@ void TypeClassInstantiation::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_typeConstructor->accept(_visitor); - if(m_argumentSorts) + if (m_argumentSorts) m_argumentSorts->accept(_visitor); m_class->accept(_visitor); listAccept(m_subNodes, _visitor); @@ -1076,7 +1076,7 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_typeConstructor->accept(_visitor); - if(m_argumentSorts) + if (m_argumentSorts) m_argumentSorts->accept(_visitor); m_class->accept(_visitor); listAccept(m_subNodes, _visitor); diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 7e1cf94b6..56d4d36f9 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -127,20 +127,20 @@ bool Analysis::check(std::vector> const& _sour return ([&](auto&& _step) { for (auto source: _sourceUnits) if (!_step.analyze(*source)) - return false; + 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)) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 56dec5dc6..36d105157 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -216,7 +216,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) if (!functionTypes.emplace(functionDefinition->name(), functionType).second) m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class declared multiple times."); auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); - if(typeVars.size() != 1) + if (typeVars.size() != 1) m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); unify(typeVars.front(), typeVar, functionDefinition->location()); typeMembers[functionDefinition->name()] = TypeMember{functionType}; @@ -499,7 +499,7 @@ void TypeInference::endVisit(Assignment const& _assignment) experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration) { - switch(m_expressionContext) + switch (m_expressionContext) { case ExpressionContext::Term: { @@ -601,7 +601,7 @@ bool TypeInference::visit(Identifier const& _identifier) return false; } - switch(m_expressionContext) + switch (m_expressionContext) { case ExpressionContext::Term: // TODO: error handling @@ -791,9 +791,7 @@ experimental::Type TypeInference::memberType(Type _type, std::string _memberName { auto constructor = std::get<0>(helper.destTypeConstant(type)); if (auto* typeMember = util::valueOrNullptr(annotation().members.at(constructor), _memberName)) - { return polymorphicInstance(typeMember->type); - } else { m_errorReporter.typeError(0000_error, _location, fmt::format("Member {} not found in type {}.", _memberName, TypeEnvironmentHelpers{*m_env}.typeToString(_type))); @@ -809,7 +807,7 @@ experimental::Type TypeInference::memberType(Type _type, std::string _memberName void TypeInference::endVisit(MemberAccess const& _memberAccess) { - auto &memberAccessAnnotation = annotation(_memberAccess); + auto& memberAccessAnnotation = annotation(_memberAccess); solAssert(!memberAccessAnnotation.type); Type expressionType = getType(_memberAccess.expression()); memberAccessAnnotation.type = memberType(expressionType, _memberAccess.memberName(), _memberAccess.location()); @@ -820,22 +818,22 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition) TypeSystemHelpers helper{m_typeSystem}; auto& typeDefinitionAnnotation = annotation(_typeDefinition); if (typeDefinitionAnnotation.type) - return false; + return false; if (_typeDefinition.arguments()) - _typeDefinition.arguments()->accept(*this); + _typeDefinition.arguments()->accept(*this); std::optional underlyingType; if (_typeDefinition.typeExpression()) { - ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; - _typeDefinition.typeExpression()->accept(*this); - underlyingType = annotation(*_typeDefinition.typeExpression()).type; + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeDefinition.typeExpression()->accept(*this); + underlyingType = annotation(*_typeDefinition.typeExpression()).type; } std::vector arguments; if (_typeDefinition.arguments()) - for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) + for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) arguments.emplace_back(m_typeSystem.freshTypeVariable({})); Type definedType = type(&_typeDefinition, arguments); @@ -865,43 +863,43 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) TypeSystemHelpers helper{m_typeSystem}; std::vector argTypes; - for(auto arg: _functionCall.arguments()) + for (auto arg: _functionCall.arguments()) { - switch(m_expressionContext) - { - case ExpressionContext::Term: - case ExpressionContext::Type: + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: argTypes.emplace_back(getType(*arg)); break; - case ExpressionContext::Sort: + case ExpressionContext::Sort: m_errorReporter.typeError(0000_error, _functionCall.location(), "Function call in sort context."); functionCallAnnotation.type = m_typeSystem.freshTypeVariable({}); break; - } + } } - switch(m_expressionContext) + 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; + 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; + 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); + solAssert(false); } } @@ -914,10 +912,10 @@ std::optional parseRational(std::string const& _value) rational value; try { - auto radixPoint = find(_value.begin(), _value.end(), '.'); + auto radixPoint = find(_value.begin(), _value.end(), '.'); - if (radixPoint != _value.end()) - { + if (radixPoint != _value.end()) + { if ( !all_of(radixPoint + 1, _value.end(), util::isDigit) || !all_of(_value.begin(), radixPoint, util::isDigit) @@ -941,14 +939,14 @@ std::optional parseRational(std::string const& _value) ); numerator = bigint(std::string(_value.begin(), radixPoint)); value = numerator + denominator; - } - else + } + else value = bigint(_value); - return value; + return value; } catch (...) { - return std::nullopt; + return std::nullopt; } } @@ -964,19 +962,19 @@ std::optional rationalValue(Literal const& _literal) rational value; try { - ASTString valueString = _literal.valueWithoutUnderscores(); + ASTString valueString = _literal.valueWithoutUnderscores(); - auto expPoint = find(valueString.begin(), valueString.end(), 'e'); - if (expPoint == valueString.end()) + 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")) - { + if (boost::starts_with(valueString, "0x")) + { // process as hex value = bigint(valueString); - } - else if (expPoint != valueString.end()) - { + } + else if (expPoint != valueString.end()) + { // Parse mantissa and exponent. Checks numeric limit. std::optional mantissa = parseRational(std::string(valueString.begin(), expPoint)); @@ -1013,47 +1011,47 @@ std::optional rationalValue(Literal const& _literal) expAbs ); } - } - else - { + } + else + { // parse as rational number std::optional tmp = parseRational(valueString); if (!tmp) return std::nullopt; value = *tmp; - } + } } catch (...) { - return std::nullopt; + return std::nullopt; } switch (_literal.subDenomination()) { case Literal::SubDenomination::None: case Literal::SubDenomination::Wei: case Literal::SubDenomination::Second: - break; + break; case Literal::SubDenomination::Gwei: - value *= bigint("1000000000"); - break; + value *= bigint("1000000000"); + break; case Literal::SubDenomination::Ether: - value *= bigint("1000000000000000000"); - break; + value *= bigint("1000000000000000000"); + break; case Literal::SubDenomination::Minute: - value *= bigint("60"); - break; + value *= bigint("60"); + break; case Literal::SubDenomination::Hour: - value *= bigint("3600"); - break; + value *= bigint("3600"); + break; case Literal::SubDenomination::Day: - value *= bigint("86400"); - break; + value *= bigint("86400"); + break; case Literal::SubDenomination::Week: - value *= bigint("604800"); - break; + value *= bigint("604800"); + break; case Literal::SubDenomination::Year: - value *= bigint("31536000"); - break; + value *= bigint("31536000"); + break; } return value; @@ -1065,19 +1063,19 @@ bool TypeInference::visit(Literal const& _literal) auto& literalAnnotation = annotation(_literal); if (_literal.token() != Token::Number) { - m_errorReporter.typeError(0000_error, _literal.location(), "Only number literals are supported."); - return false; + m_errorReporter.typeError(0000_error, _literal.location(), "Only number literals are supported."); + return false; } std::optional value = rationalValue(_literal); if (!value) { - m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals."); - return false; + m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals."); + return false; } if (value->denominator() != 1) { - m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported."); - return false; + m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported."); + return false; } literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{annotation().builtinClasses.at(BuiltinClass::Integer)}}); return false; @@ -1091,7 +1089,7 @@ TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(Analysi { auto const* typeClassDeclaration = _analysis.typeSystem().typeClassDeclaration(_class); if (typeClassDeclaration) - return _analysis.annotation(*typeClassDeclaration).instantiations; + return _analysis.annotation(*typeClassDeclaration).instantiations; // TODO: better mechanism than fetching by name. auto& annotation = _analysis.annotation(); auto& inferenceAnnotation = _analysis.annotation(); diff --git a/libsolidity/experimental/analysis/TypeRegistration.cpp b/libsolidity/experimental/analysis/TypeRegistration.cpp index 424dc78ff..577df25a5 100644 --- a/libsolidity/experimental/analysis/TypeRegistration.cpp +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -61,7 +61,7 @@ bool TypeRegistration::visit(ElementaryTypeName const& _typeName) if (annotation(_typeName).typeConstructor) return false; annotation(_typeName).typeConstructor = [&]() -> std::optional { - switch(_typeName.typeName().token()) + switch (_typeName.typeName().token()) { case Token::Void: return m_typeSystem.constructor(PrimitiveType::Void); diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index 3ed04fd1f..e6e65a110 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -72,12 +72,12 @@ std::vector TypeEnvironment::unify(Type _a, 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); + 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(); @@ -98,14 +98,14 @@ bool TypeEnvironment::typeEquals(Type _lhs, Type _rhs) const 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; + 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; @@ -133,7 +133,7 @@ TypeSystem::TypeSystem() m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type")); - for (auto [type, name, arity]: std::initializer_list> { + for (auto [type, name, arity]: std::initializer_list>{ {PrimitiveType::TypeFunction, "tfun", 2}, {PrimitiveType::Function, "fun", 2}, {PrimitiveType::Function, "itself", 1}, @@ -184,7 +184,7 @@ std::vector TypeEnvironment::instantiate(Ty experimental::Type TypeEnvironment::resolve(Type _type) const { Type result = _type; - while(auto const* var = std::get_if(&result)) + while (auto const* var = std::get_if(&result)) if (Type const* resolvedType = util::valueOrNullptr(m_typeVariables, var->index())) result = *resolvedType; else diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index 2d86ea4b5..e819b84ba 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -54,7 +54,7 @@ using namespace solidity::frontend::experimental; std::optional experimental::typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token) { TypeSystem const& typeSystem = _analysis.typeSystem(); - switch(_token) + switch (_token) { case Token::Void: return typeSystem.builtinConstructor(BuiltinType::Void); @@ -142,7 +142,7 @@ std::vector TypeSystemHelpers::destTupleType(Type _tupleType std::vector result; result.emplace_back(arguments.front()); Type tail = arguments.back(); - while(true) + while (true) { if (!isTypeConstant(tail)) break; @@ -184,7 +184,7 @@ std::vector TypeSystemHelpers::destSumType(Type _tupleType) std::vector result; result.emplace_back(arguments.front()); Type tail = arguments.back(); - while(true) + while (true) { if (!isTypeConstant(tail)) break; @@ -338,22 +338,22 @@ 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())); - }}, + 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 "()"; - }}, + 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; - }}, + 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) { diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 6add66c42..1da96f147 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -46,7 +46,8 @@ std::string IRGeneratorForStatements::generate(ASTNode const& _node) } -namespace { +namespace +{ struct CopyTranslate: public yul::ASTCopier { @@ -300,7 +301,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) auto declaration = m_expressionDeclaration.at(&_functionCall.expression()); if (auto builtin = std::get_if(&declaration)) { - switch(*builtin) + switch (*builtin) { case Builtins::FromBool: case Builtins::Identity: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 2d080a8ba..36a26ba40 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -486,7 +486,7 @@ bool CompilerStack::analyze() for (Source const* source: m_sourceOrder) if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) noErrors = false; - } + } // Requires DocStringTagParser for (Source const* source: m_sourceOrder) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 947329aff..700141508 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -2076,7 +2076,7 @@ int Parser::tokenPrecedence(Token _token) const { if (m_experimentalSolidityEnabledInCurrentSourceUnit) { - switch(_token) + switch (_token) { case Token::Colon: return 1000; From 4fb7e9934e6d6c1708f219a1840d1d06ab6fbea9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 18:11:11 +0200 Subject: [PATCH 06/17] Fix compiler warning. --- libsolidity/ast/ASTAnnotations.h | 2 +- libsolidity/experimental/ast/TypeSystemHelper.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index e2b4ab7a1..182aafa6b 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -344,7 +344,7 @@ struct FunctionCallAnnotation: ExpressionAnnotation }; /// Experimental Solidity annotations. -/// Used to intergrate with name and type resolution. +/// Used to integrate with name and type resolution. /// @{ struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index e819b84ba..d2280b181 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -375,9 +375,9 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const std::stringstream stream; std::string varName; size_t index = _type.index(); - varName += 'a' + static_cast(index%26); + varName += static_cast('a' + (index%26)); while (index /= 26) - varName += 'a' + static_cast(index%26); + varName += static_cast('a' + (index%26)); reverse(varName.begin(), varName.end()); stream << '\'' << varName; switch (_type.sort().classes.size()) From fadba0e4b878552155610b8ee57b6fbfea287eb4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 18:17:14 +0200 Subject: [PATCH 07/17] error ids --- libsolidity/analysis/ReferencesResolver.cpp | 6 +- .../experimental/analysis/DebugWarner.cpp | 2 +- .../analysis/SyntaxRestrictor.cpp | 38 ++++----- .../experimental/analysis/TypeInference.cpp | 84 +++++++++---------- .../analysis/TypeRegistration.cpp | 14 ++-- 5 files changed, 72 insertions(+), 72 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index da2206f45..fd7def714 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -296,7 +296,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) if (splitName.size() > 2) { m_errorReporter.declarationError( - 0000_error, + 4955_error, nativeLocationOf(_identifier), "Unsupported identifier in inline assembly." ); @@ -309,7 +309,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) case 0: if (splitName.size() > 1) m_errorReporter.declarationError( - 0000_error, + 7531_error, nativeLocationOf(_identifier), "Unsupported identifier in inline assembly." ); @@ -320,7 +320,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) break; default: m_errorReporter.declarationError( - 0000_error, + 5387_error, nativeLocationOf(_identifier), "Multiple matching identifiers. Resolving overloaded identifiers is not supported." ); diff --git a/libsolidity/experimental/analysis/DebugWarner.cpp b/libsolidity/experimental/analysis/DebugWarner.cpp index cf65d8da8..f99bd4867 100644 --- a/libsolidity/experimental/analysis/DebugWarner.cpp +++ b/libsolidity/experimental/analysis/DebugWarner.cpp @@ -48,7 +48,7 @@ bool DebugWarner::visitNode(ASTNode const& _node) if (sort.classes.size() != 1 || *sort.classes.begin() != m_analysis.typeSystem().primitiveClass(PrimitiveClass::Type)) sortString = ":" + TypeSystemHelpers{m_analysis.typeSystem()}.sortToString(m_analysis.typeSystem().env().sort(type)); m_errorReporter.info( - 0000_error, + 4164_error, _node.location(), "Inferred type: " + TypeEnvironmentHelpers{m_analysis.typeSystem().env()}.typeToString(type) + sortString ); diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp index 75ed77b2d..e93fddcf5 100644 --- a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp @@ -38,42 +38,42 @@ bool SyntaxRestrictor::analyze(ASTNode const& _astRoot) bool SyntaxRestrictor::visitNode(ASTNode const& _node) { if (!_node.experimentalSolidityOnly()) - m_errorReporter.syntaxError(0000_error, _node.location(), "Unsupported AST node."); + 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(0000_error, _contractDefinition.location(), "Only contracts are supported."); + m_errorReporter.syntaxError(9159_error, _contractDefinition.location(), "Only contracts are supported."); if (!_contractDefinition.baseContracts().empty()) - m_errorReporter.syntaxError(0000_error, _contractDefinition.location(), "Inheritance unsupported."); + m_errorReporter.syntaxError(5731_error, _contractDefinition.location(), "Inheritance unsupported."); return true; } bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) { if (!_functionDefinition.isImplemented()) - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Functions must be implemented."); + m_errorReporter.syntaxError(1741_error, _functionDefinition.location(), "Functions must be implemented."); if (!_functionDefinition.modifiers().empty()) - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have modifiers."); + m_errorReporter.syntaxError(9988_error, _functionDefinition.location(), "Function may not have modifiers."); if (_functionDefinition.overrides()) - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have override specifiers."); + 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(0000_error, _functionDefinition.location(), "Free functions may not have a mutability."); + 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(0000_error, _functionDefinition.location(), "Fallback function must be external."); + m_errorReporter.syntaxError(7341_error, _functionDefinition.location(), "Fallback function must be external."); } else - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); + m_errorReporter.syntaxError(4496_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); } return true; @@ -84,30 +84,30 @@ bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclar if (_variableDeclarationStatement.declarations().size() == 1) { if (!_variableDeclarationStatement.declarations().front()) - m_errorReporter.syntaxError(0000_error, _variableDeclarationStatement.initialValue()->location(), "Variable declaration has to declare a single variable."); + m_errorReporter.syntaxError(9658_error, _variableDeclarationStatement.initialValue()->location(), "Variable declaration has to declare a single variable."); } else - m_errorReporter.syntaxError(0000_error, _variableDeclarationStatement.initialValue()->location(), "Variable declarations can only declare a single variable."); + 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(0000_error, _variableDeclaration.value()->location(), "Variable declarations with initial value not supported."); + m_errorReporter.syntaxError(1801_error, _variableDeclaration.value()->location(), "Variable declarations with initial value not supported."); if (_variableDeclaration.isStateVariable()) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "State variables are not supported."); + m_errorReporter.syntaxError(6388_error, _variableDeclaration.location(), "State variables are not supported."); if (!_variableDeclaration.isLocalVariable()) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Only local variables are supported."); + m_errorReporter.syntaxError(8953_error, _variableDeclaration.location(), "Only local variables are supported."); if (_variableDeclaration.mutability() != VariableDeclaration::Mutability::Mutable) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Only mutable variables are supported."); + m_errorReporter.syntaxError(2934_error, _variableDeclaration.location(), "Only mutable variables are supported."); if (_variableDeclaration.isIndexed()) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Indexed variables are not supported."); + m_errorReporter.syntaxError(9603_error, _variableDeclaration.location(), "Indexed variables are not supported."); if (!_variableDeclaration.noVisibilitySpecified()) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with visibility not supported."); + m_errorReporter.syntaxError(8809_error, _variableDeclaration.location(), "Variables with visibility not supported."); if (_variableDeclaration.overrides()) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with override specifier not supported."); + m_errorReporter.syntaxError(6175_error, _variableDeclaration.location(), "Variables with override specifier not supported."); if (_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Unspecified) - m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with reference location not supported."); + m_errorReporter.syntaxError(5360_error, _variableDeclaration.location(), "Variables with reference location not supported."); return true; } diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 36d105157..74470a155 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -214,10 +214,10 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) // TODO: need polymorphicInstance? auto functionType = polymorphicInstance(getType(*functionDefinition)); if (!functionTypes.emplace(functionDefinition->name(), functionType).second) - m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class declared multiple times."); + 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(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); + 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()); typeMembers[functionDefinition->name()] = TypeMember{functionType}; } @@ -225,7 +225,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) TypeClass typeClass = std::visit(util::GenericVisitor{ [](TypeClass _class) -> TypeClass { return _class; }, [&](std::string _error) -> TypeClass { - m_errorReporter.fatalTypeError(0000_error, _typeClassDefinition.location(), _error); + m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); util::unreachable(); } }, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition)); @@ -237,11 +237,11 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) TypeEnvironmentHelpers helper{*m_env}; auto typeVars = helper.typeVars(functionType); if (typeVars.empty()) - m_errorReporter.typeError(0000_error, _typeClassDefinition.location(), "Function " + functionName + " does not depend on class variable."); + m_errorReporter.typeError(1723_error, _typeClassDefinition.location(), "Function " + functionName + " does not depend on class variable."); if (typeVars.size() > 2) - m_errorReporter.typeError(0000_error, _typeClassDefinition.location(), "Function " + functionName + " depends on multiple type variables."); + m_errorReporter.typeError(6387_error, _typeClassDefinition.location(), "Function " + functionName + " depends on multiple type variables."); if (!m_env->typeEquals(typeVars.front(), typeVar)) - m_errorReporter.typeError(0000_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); + m_errorReporter.typeError(1807_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); } unify(getType(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{typeClass}}), _typeClassDefinition.location()); @@ -324,12 +324,12 @@ bool TypeInference::visit(ElementaryTypeNameExpression const& _expression) } else { - m_errorReporter.typeError(0000_error, _expression.location(), "No type constructor registered for elementary type name."); + m_errorReporter.typeError(4107_error, _expression.location(), "No type constructor registered for elementary type name."); expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); } break; case ExpressionContext::Sort: - m_errorReporter.typeError(0000_error, _expression.location(), "Elementary type name expression not supported in sort context."); + m_errorReporter.typeError(2024_error, _expression.location(), "Elementary type name expression not supported in sort context."); expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); break; } @@ -373,7 +373,7 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation) } else { - m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operation in term context not yet supported."); + m_errorReporter.typeError(4504_error, _binaryOperation.location(), "Binary operation in term context not yet supported."); operationAnnotation.type = m_typeSystem.freshTypeVariable({}); } return false; @@ -403,12 +403,12 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation) } else { - m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Invalid binary operations in type context."); + 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(0000_error, _binaryOperation.location(), "Invalid binary operation in sort context."); + m_errorReporter.typeError(1017_error, _binaryOperation.location(), "Invalid binary operation in sort context."); operationAnnotation.type = m_typeSystem.freshTypeVariable({}); return false; } @@ -420,7 +420,7 @@ void TypeInference::endVisit(VariableDeclarationStatement const& _variableDeclar solAssert(m_expressionContext == ExpressionContext::Term); if (_variableDeclarationStatement.declarations().size () != 1) { - m_errorReporter.typeError(0000_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported."); + m_errorReporter.typeError(2655_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported."); return; } Type variableType = getType(*_variableDeclarationStatement.declarations().front()); @@ -456,7 +456,7 @@ bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) } return false; case ExpressionContext::Sort: - m_errorReporter.typeError(0000_error, _variableDeclaration.location(), "Variable declaration in sort context."); + m_errorReporter.typeError(2399_error, _variableDeclaration.location(), "Variable declaration in sort context."); variableAnnotation.type = m_typeSystem.freshTypeVariable({}); return false; } @@ -470,7 +470,7 @@ void TypeInference::endVisit(IfStatement const& _ifStatement) if (m_expressionContext != ExpressionContext::Term) { - m_errorReporter.typeError(0000_error, _ifStatement.location(), "If statement outside term context."); + m_errorReporter.typeError(2015_error, _ifStatement.location(), "If statement outside term context."); ifAnnotation.type = m_typeSystem.freshTypeVariable({}); return; } @@ -487,7 +487,7 @@ void TypeInference::endVisit(Assignment const& _assignment) if (m_expressionContext != ExpressionContext::Term) { - m_errorReporter.typeError(0000_error, _assignment.location(), "Assignment outside term context."); + m_errorReporter.typeError(4337_error, _assignment.location(), "Assignment outside term context."); assignmentAnnotation.type = m_typeSystem.freshTypeVariable({}); return; } @@ -512,7 +512,7 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut { SecondarySourceLocation ssl; ssl.append("Referenced node.", _declaration.location()); - m_errorReporter.fatalTypeError(0000_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + m_errorReporter.fatalTypeError(3101_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); } auto& declarationAnnotation = annotation(_declaration); @@ -548,7 +548,7 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut { SecondarySourceLocation ssl; ssl.append("Referenced node.", _declaration.location()); - m_errorReporter.fatalTypeError(0000_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + 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? @@ -574,14 +574,14 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut typeClass->accept(*this); if (!annotation(*typeClass).typeClass) { - m_errorReporter.typeError(0000_error, _location, "Unregistered type class."); + m_errorReporter.typeError(2736_error, _location, "Unregistered type class."); return m_typeSystem.freshTypeVariable({}); } return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClass).typeClass}}); } else { - m_errorReporter.typeError(0000_error, _location, "Expected type class."); + m_errorReporter.typeError(2599_error, _location, "Expected type class."); return m_typeSystem.freshTypeVariable({}); } break; @@ -679,12 +679,12 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) typeClass->accept(*this); // TODO: more error handling? Should be covered by the visit above. if (!annotation(*typeClass).typeClass) - m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + m_errorReporter.typeError(8503_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); return annotation(*typeClass).typeClass; } else { - m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + m_errorReporter.typeError(9817_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); return std::nullopt; } }, @@ -692,7 +692,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) if (auto builtinClass = builtinClassFromToken(_token)) if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) return *typeClass; - m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name."); + m_errorReporter.typeError(2658_error, _typeClassInstantiation.location(), "Invalid type class name."); return std::nullopt; } }, _typeClassInstantiation.typeClass().name()); @@ -703,7 +703,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) auto typeConstructor = m_analysis.annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; if (!typeConstructor) { - m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); + m_errorReporter.typeError(2138_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); return false; } @@ -737,18 +737,18 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) solAssert(functionDefinition); subNode->accept(*this); if (!functionTypes.emplace(functionDefinition->name(), getType(*functionDefinition)).second) - m_errorReporter.typeError(0000_error, subNode->location(), "Duplicate definition of function " + functionDefinition->name() + " during type class instantiation."); + m_errorReporter.typeError(3654_error, subNode->location(), "Duplicate definition of function " + functionDefinition->name() + " during type class instantiation."); } if (auto error = m_typeSystem.instantiateClass(type, arity)) - m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), *error); + 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), type).empty()) { - m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); + m_errorReporter.typeError(4686_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); return false; } @@ -756,18 +756,18 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) { if (!functionTypes.count(name)) { - m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Missing function: " + 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(0000_error, _typeClassInstantiation.location(), "Type mismatch for function " + name + " " + TypeEnvironmentHelpers{newEnv}.typeToString(instanceFunctionType) + " != " + TypeEnvironmentHelpers{newEnv}.typeToString(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(0000_error, _typeClassInstantiation.location(), "Additional functions in class instantiation."); + m_errorReporter.typeError(4873_error, _typeClassInstantiation.location(), "Additional functions in class instantiation."); return false; } @@ -776,7 +776,7 @@ bool TypeInference::visit(MemberAccess const& _memberAccess) { if (m_expressionContext != ExpressionContext::Term) { - m_errorReporter.typeError(0000_error, _memberAccess.location(), "Member access outside term context."); + m_errorReporter.typeError(5195_error, _memberAccess.location(), "Member access outside term context."); annotation(_memberAccess).type = m_typeSystem.freshTypeVariable({}); return false; } @@ -794,13 +794,13 @@ experimental::Type TypeInference::memberType(Type _type, std::string _memberName return polymorphicInstance(typeMember->type); else { - m_errorReporter.typeError(0000_error, _location, fmt::format("Member {} not found in type {}.", _memberName, TypeEnvironmentHelpers{*m_env}.typeToString(_type))); + 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(0000_error, _location, "Unsupported member access expression."); + m_errorReporter.typeError(5104_error, _location, "Unsupported member access expression."); return m_typeSystem.freshTypeVariable({}); } } @@ -872,7 +872,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) argTypes.emplace_back(getType(*arg)); break; case ExpressionContext::Sort: - m_errorReporter.typeError(0000_error, _functionCall.location(), "Function call in sort context."); + m_errorReporter.typeError(9173_error, _functionCall.location(), "Function call in sort context."); functionCallAnnotation.type = m_typeSystem.freshTypeVariable({}); break; } @@ -1063,18 +1063,18 @@ bool TypeInference::visit(Literal const& _literal) auto& literalAnnotation = annotation(_literal); if (_literal.token() != Token::Number) { - m_errorReporter.typeError(0000_error, _literal.location(), "Only number literals are supported."); + m_errorReporter.typeError(4316_error, _literal.location(), "Only number literals are supported."); return false; } std::optional value = rationalValue(_literal); if (!value) { - m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals."); + m_errorReporter.typeError(6739_error, _literal.location(), "Invalid number literals."); return false; } if (value->denominator() != 1) { - m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported."); + m_errorReporter.typeError(2345_error, _literal.location(), "Only integers are supported."); return false; } literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{annotation().builtinClasses.at(BuiltinClass::Integer)}}); @@ -1138,7 +1138,7 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) for (auto activeInstantiation: m_activeInstantiations) ssl.append("Involved instantiation", activeInstantiation->location()); m_errorReporter.typeError( - 0000_error, + 3573_error, _location, ssl, "Recursion during type class instantiation." @@ -1175,7 +1175,7 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) std::visit(util::GenericVisitor{ [&](TypeEnvironment::TypeMismatch _typeMismatch) { m_errorReporter.typeError( - 0000_error, + 8456_error, _location, fmt::format( "Cannot unify {} and {}.", @@ -1184,7 +1184,7 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) ); }, [&](TypeEnvironment::SortMismatch _sortMismatch) { - m_errorReporter.typeError(0000_error, _location, fmt::format( + m_errorReporter.typeError(3111_error, _location, fmt::format( "{} does not have sort {}", envHelper.typeToString(_sortMismatch.type), TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort) @@ -1192,7 +1192,7 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) }, [&](TypeEnvironment::RecursiveUnification _recursiveUnification) { m_errorReporter.typeError( - 0000_error, + 6460_error, _location, fmt::format( "Recursive unification: {} occurs in {}.", @@ -1214,7 +1214,7 @@ TypeConstructor TypeInference::typeConstructor(Declaration const* _type) const { if (auto const& constructor = m_analysis.annotation(*_type).typeConstructor) return *constructor; - m_errorReporter.fatalTypeError(0000_error, _type->location(), "Unregistered type."); + m_errorReporter.fatalTypeError(5904_error, _type->location(), "Unregistered type."); util::unreachable(); } experimental::Type TypeInference::type(Declaration const* _type, std::vector _arguments) const @@ -1224,7 +1224,7 @@ experimental::Type TypeInference::type(Declaration const* _type, std::vectoraccept(*this); if (!(annotation(_userDefinedTypeName).typeConstructor = annotation(*declaration).typeConstructor)) { // TODO: fatal/non-fatal - m_errorReporter.fatalTypeError(0000_error, _userDefinedTypeName.pathNode().location(), "Expected type declaration."); + m_errorReporter.fatalTypeError(9831_error, _userDefinedTypeName.pathNode().location(), "Expected type declaration."); return false; } return true; @@ -127,7 +127,7 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati auto typeConstructor = annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; if (!typeConstructor) { - m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name."); + m_errorReporter.typeError(5577_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name."); return false; } auto* instantiations = std::visit(util::GenericVisitor{ @@ -135,14 +135,14 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati { if (TypeClassDefinition const* classDefinition = dynamic_cast(_path->annotation().referencedDeclaration)) return &annotation(*classDefinition).instantiations; - m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + 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(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + m_errorReporter.typeError(5262_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); return nullptr; } }, _typeClassInstantiation.typeClass().name()); @@ -157,7 +157,7 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati { SecondarySourceLocation ssl; ssl.append("Previous instantiation.", instantiation->second->location()); - m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); + m_errorReporter.typeError(6620_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); } return true; From 5f894722df0904ce421cb2ce3dedbe991a18cc5a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 18:23:16 +0200 Subject: [PATCH 08/17] Add comment. --- libsolidity/experimental/analysis/TypeInference.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 74470a155..6b85b8f7e 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -1118,7 +1118,12 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) if (!m_activeInstantiations.empty()) { - // Attempt to resolve interdependencies between type class instantiations. + // 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 = [&]() { From 59ef5919fbbf7b37397bba30b92aa1d483e3b683 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 18:33:34 +0200 Subject: [PATCH 09/17] Platform fixes. --- libsolidity/experimental/ast/Type.h | 15 ++++++++------- libsolidity/experimental/ast/TypeSystem.cpp | 6 ++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libsolidity/experimental/ast/Type.h b/libsolidity/experimental/ast/Type.h index f98925684..dde41b1df 100644 --- a/libsolidity/experimental/ast/Type.h +++ b/libsolidity/experimental/ast/Type.h @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #pragma once +#include #include #include #include @@ -86,8 +87,8 @@ public: } private: friend class TypeSystem; - TypeConstructor(size_t _index): m_index(_index) {} - size_t m_index = 0; + TypeConstructor(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; }; struct TypeConstant @@ -119,8 +120,8 @@ public: } private: friend class TypeSystem; - TypeClass(size_t _index): m_index(_index) {} - size_t m_index = 0; + TypeClass(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; }; struct Sort @@ -141,13 +142,13 @@ struct Arity struct TypeVariable { - size_t index() const { return m_index; } + std::size_t index() const { return m_index; } Sort const& sort() const { return m_sort; } private: friend class TypeSystem; - size_t m_index = 0; + std::size_t m_index = 0; Sort m_sort; - TypeVariable(size_t _index, Sort _sort): m_index(_index), m_sort(std::move(_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 index e6e65a110..5347b3d15 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -32,6 +32,8 @@ #include +#include + using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::experimental; @@ -133,7 +135,7 @@ TypeSystem::TypeSystem() m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type")); - for (auto [type, name, arity]: std::initializer_list>{ + for (auto [type, name, arity]: std::initializer_list>{ {PrimitiveType::TypeFunction, "tfun", 2}, {PrimitiveType::Function, "fun", 2}, {PrimitiveType::Function, "itself", 1}, @@ -157,7 +159,7 @@ TypeSystem::TypeSystem() experimental::Type TypeSystem::freshVariable(Sort _sort) { - uint64_t index = m_numTypeVariables++; + size_t index = m_numTypeVariables++; return TypeVariable(index, std::move(_sort)); } From 7c2f5ee17198a3c943aceca4618f9f11dd61bdd1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 22 Aug 2023 21:05:36 +0200 Subject: [PATCH 10/17] Remove bogus unused IRVariable implementation (to be redone). --- .../experimental/codegen/IRVariable.cpp | 134 ------------------ libsolidity/experimental/codegen/IRVariable.h | 85 ----------- 2 files changed, 219 deletions(-) delete mode 100644 libsolidity/experimental/codegen/IRVariable.cpp delete mode 100644 libsolidity/experimental/codegen/IRVariable.h diff --git a/libsolidity/experimental/codegen/IRVariable.cpp b/libsolidity/experimental/codegen/IRVariable.cpp deleted file mode 100644 index 7c5a50585..000000000 --- a/libsolidity/experimental/codegen/IRVariable.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - 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::frontend::experimental; -using namespace solidity::util; - -template -Type getType(IRGenerationContext const& _context, Node const& _node) -{ - auto& annotation = _context.analysis.annotation(_node); - solAssert(annotation.type); - return _context.env->resolve(*annotation.type); -} - -namespace -{ -size_t getTypeStackSlots(IRGenerationContext const& _context, Type _type) -{ -} -} - -IRVariable::IRVariable(IRGenerationContext const& _context, std::string _baseName, Type _type): - m_baseName(std::move(_baseName)), m_type(_type) -{ -} - -IRVariable::IRVariable(IRGenerationContext const& _context, VariableDeclaration const& _declaration): - IRVariable(_context, IRNames::localVariable(_declaration), getType(_context, _declaration)) -{ -} - -IRVariable::IRVariable(IRGenerationContext const& _context, Expression const& _expression): - IRVariable(_context, IRNames::localVariable(_expression), getType(_context, _expression)) -{ -} - -IRVariable IRVariable::part(string const& _name) const -{ - for (auto const& [itemName, itemType]: m_type.stackItems()) - if (itemName == _name) - { - solAssert(itemName.empty() || itemType, ""); - return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type}; - } - solAssert(false, "Invalid stack item name: " + _name); -} - -bool IRVariable::hasPart(std::string const& _name) const -{ - for (auto const& [itemName, itemType]: m_type.stackItems()) - if (itemName == _name) - { - solAssert(itemName.empty() || itemType, ""); - return true; - } - return false; -} - -vector IRVariable::stackSlots() const -{ - vector result; - for (auto const& [itemName, itemType]: m_type.stackItems()) - if (itemType) - { - solAssert(!itemName.empty(), ""); - solAssert(m_type != *itemType, ""); - result += IRVariable{suffixedName(itemName), *itemType}.stackSlots(); - } - else - { - solAssert(itemName.empty(), ""); - result.emplace_back(m_baseName); - } - return result; -} - -string IRVariable::commaSeparatedList() const -{ - return joinHumanReadable(stackSlots()); -} - -string IRVariable::commaSeparatedListPrefixed() const -{ - return joinHumanReadablePrefixed(stackSlots()); -} - -string IRVariable::name() const -{ - solAssert(m_type.sizeOnStack() == 1, ""); - auto const& [itemName, type] = m_type.stackItems().front(); - solAssert(!type, "Expected null type for name " + itemName); - return suffixedName(itemName); -} - -IRVariable IRVariable::tupleComponent(size_t _i) const -{ - solAssert( - m_type.category() == Type::Category::Tuple, - "Requested tuple component of non-tuple IR variable." - ); - return part(IRNames::tupleComponent(_i)); -} - -string IRVariable::suffixedName(string const& _suffix) const -{ - if (_suffix.empty()) - return m_baseName; - else - return m_baseName + '_' + _suffix; -} diff --git a/libsolidity/experimental/codegen/IRVariable.h b/libsolidity/experimental/codegen/IRVariable.h deleted file mode 100644 index 02d6f7931..000000000 --- a/libsolidity/experimental/codegen/IRVariable.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - 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 -{ -class VariableDeclaration; -class Expression; -} - -namespace solidity::frontend::experimental -{ - -class IRGenerationContext; - -class IRVariable -{ -public: - /// IR variable with explicit base name @a _baseName and type @a _type. - IRVariable(IRGenerationContext const& _analysis, std::string _baseName, Type _type); - /// IR variable referring to the declaration @a _decl. - IRVariable(IRGenerationContext const& _analysis, VariableDeclaration const& _decl); - /// IR variable referring to the expression @a _expr. - IRVariable(IRGenerationContext const& _analysis, Expression const& _expression); - - /// @returns the name of the variable, if it occupies a single stack slot (otherwise throws). - std::string name() const; - - /// @returns a comma-separated list of the stack slots of the variable. - std::string commaSeparatedList() const; - - /// @returns a comma-separated list of the stack slots of the variable that is - /// prefixed with a comma, unless it is empty. - std::string commaSeparatedListPrefixed() const; - - /// @returns an IRVariable referring to the tuple component @a _i of a tuple variable. - IRVariable tupleComponent(std::size_t _i) const; - - /// @returns the type of the variable. - Type const& type() const { return m_type; } - - /// @returns an IRVariable referring to the stack component @a _slot of the variable. - /// @a _slot must be among the stack slots in ``m_type.stackItems()``. - /// The returned IRVariable is itself typed with the type of the stack slot as defined - /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. - IRVariable part(std::string const& _slot) const; - - /// @returns true if variable contains @a _name component - /// @a _name name of the component that is being checked - bool hasPart(std::string const& _name) const; - - /// @returns a vector containing the names of the stack slots of the variable. - std::vector stackSlots() const; - -private: - /// @returns a name consisting of the base name appended with an underscore and @æ _suffix, - /// unless @a _suffix is empty, in which case the base name itself is returned. - std::string suffixedName(std::string const& _suffix) const; - std::string m_baseName; - Type m_type; -}; - - -} From 319a6c4999e7a7e241aca6687dddecb7f3fdde70 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 29 Aug 2023 14:35:58 +0200 Subject: [PATCH 11/17] Move around experimental tests Exclude list for AST JSON tests Fix type inference shellcheck failure Disable shellcheck SC2086 --- .../experimental/analysis/TypeInference.h | 1 + libsolidity/interface/CompilerStack.h | 7 ++++++ scripts/ASTImportTest.sh | 9 ++++++- test/libsolidity/SyntaxTest.cpp | 2 +- .../semanticTests/experimental/stub.sol | 2 ++ .../import}/experimental_solidity.sol | 1 - ...al_solidity_multisource_not_all_enable.sol | 0 .../experimental_solidity_out_of_order_1.sol | 0 .../experimental_solidity_out_of_order_2.sol | 0 ...xperimental_solidity_wrong_evm_version.sol | 0 .../{ => import}/parsing_stdlib_import_1.sol | 3 ++- .../{ => import}/parsing_stdlib_import_2.sol | 3 ++- .../{ => import}/parsing_stdlib_import_3.sol | 4 +++- .../{ => import}/parsing_stdlib_import_4.sol | 3 ++- ...parsing_stdlib_import_without_pragma_1.sol | 0 ...parsing_stdlib_import_without_pragma_2.sol | 0 .../inference}/experimental_keywords.sol | 0 .../import_and_call_stdlib_function.sol | 24 +++++++++++++++++++ test/stopAfterParseTests.sh | 2 +- 19 files changed, 53 insertions(+), 8 deletions(-) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity.sol (70%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_multisource_not_all_enable.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_out_of_order_1.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_out_of_order_2.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_wrong_evm_version.sol (100%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_1.sol (75%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_2.sol (76%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_3.sol (69%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_4.sol (76%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_without_pragma_1.sol (100%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_without_pragma_2.sol (100%) rename test/libsolidity/syntaxTests/{ => experimental/inference}/experimental_keywords.sol (100%) create mode 100644 test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index 86686f556..e8e234862 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -64,6 +64,7 @@ public: 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; } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index ae0dbb631..074191dd7 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -240,6 +240,13 @@ public: /// @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 std::vector sourceNames() const; diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index c24526195..610d4b1a4 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -209,7 +209,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/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ad53c2b26..83756931d 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -67,7 +67,7 @@ void SyntaxTest::parseAndAnalyze() { setupCompiler(); - if (compiler().parse() && compiler().analyze()) + if (compiler().parse() && compiler().analyze() && !compiler().isExperimentalAnalysis()) try { if (!compiler().compile()) diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index 7f3061fe7..f575a1b89 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -85,6 +85,8 @@ contract C { } } // ==== +// EVMVersion: >=constantinople +// ==== // compileViaYul: true // ---- // (): 0 -> 0 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 285c76ed3..b6f871d87 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol @@ -3,4 +3,3 @@ pragma experimental solidity; // EVMVersion: >=constantinople // ---- // 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 75% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol index 7a6f79151..034cbcfb1 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol @@ -6,4 +6,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 76% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol index d6662a14a..e7d82a5bd 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol @@ -6,4 +6,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 69% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol index c5f6550aa..1a27e89e8 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol @@ -6,4 +6,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 76% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol index 5aa043746..7ccfa1950 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol @@ -6,4 +6,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_keywords.sol b/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol similarity index 100% rename from test/libsolidity/syntaxTests/experimental_keywords.sol rename to test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol 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 000000000..8f2071c78 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol @@ -0,0 +1,24 @@ +pragma experimental solidity; + +import { identity as id } from std.stub; + +contract C +{ + fallback() external + { + id(); + } +} + +// ==== +// EVMVersion: >=constantinople +// ---- +// 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/stopAfterParseTests.sh b/test/stopAfterParseTests.sh index 7d74704c5..a041adba2 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/*") echo From 761f5b342f3aa366dd5494af5f95cca8ce0d514e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 5 Sep 2023 15:56:50 +0200 Subject: [PATCH 12/17] fixup! Type inference draft. --- libsolidity/ast/AST.h | 2 ++ libsolidity/experimental/analysis/Analysis.h | 7 ++++ .../experimental/analysis/TypeInference.cpp | 7 ++-- .../analysis/TypeRegistration.cpp | 6 ++-- .../semanticTests/experimental/stub.sol | 36 +++++++++---------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 031068a76..4767f7c30 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -2500,6 +2500,7 @@ private: ASTPointer m_typeVariable; std::vector> m_subNodes; }; + class TypeClassInstantiation: public ASTNode, public ScopeOpener { public: @@ -2590,6 +2591,7 @@ public: bool experimentalSolidityOnly() const override { return true; } std::variant> name() const { return m_name; } + private: std::variant> m_name; }; diff --git a/libsolidity/experimental/analysis/Analysis.h b/libsolidity/experimental/analysis/Analysis.h index 780e25015..dde3a24ef 100644 --- a/libsolidity/experimental/analysis/Analysis.h +++ b/libsolidity/experimental/analysis/Analysis.h @@ -63,6 +63,7 @@ class Analysis { struct AnnotationContainer; struct GlobalAnnotationContainer; + public: Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId); Analysis(Analysis const&) = delete; @@ -73,30 +74,36 @@ public: 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; diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 6b85b8f7e..e7a9f0b45 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -39,9 +39,9 @@ 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_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) { auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class, auto _memberCreator, Sort _sort = {}) -> TypeClass { Type type = m_typeSystem.freshTypeVariable(std::move(_sort)); @@ -195,6 +195,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) auto& typeClassAnnotation = annotation(_typeClassDefinition); if (typeClassAnnotation.type) return false; + typeClassAnnotation.type = type(&_typeClassDefinition, {}); { ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; diff --git a/libsolidity/experimental/analysis/TypeRegistration.cpp b/libsolidity/experimental/analysis/TypeRegistration.cpp index cec0d3100..1f6ff1ba0 100644 --- a/libsolidity/experimental/analysis/TypeRegistration.cpp +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -31,9 +31,9 @@ using namespace solidity::frontend::experimental; using namespace solidity::langutil; TypeRegistration::TypeRegistration(Analysis& _analysis): -m_analysis(_analysis), -m_errorReporter(_analysis.errorReporter()), -m_typeSystem(_analysis.typeSystem()) + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) { } diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index f575a1b89..5ec9c5d1e 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -60,29 +60,29 @@ function g(x:uint256) -> uint256 } contract C { - fallback() external { - let arg; - assembly { - arg := calldataload(0) - } - let x : word; - if (bool.abs(arg)) { - assembly { - x := 0x10 - } - } + 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); + let y : word; + let z : (uint256,uint256); assembly { y := 2 } y = uint256.rep(w) * y; - assembly { - mstore(0, y) - return(0, 32) - } - } + assembly { + mstore(0, y) + return(0, 32) + } + } } // ==== // EVMVersion: >=constantinople From 0e95ca163ce928e321c95ba5065ad66faa04f64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 11 Sep 2023 13:53:23 +0200 Subject: [PATCH 13/17] fixup! Type inference draft. --- .../experimental/analysis/TypeInference.cpp | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index e7a9f0b45..9d67027b7 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -192,11 +192,12 @@ void TypeInference::endVisit(ParameterList const& _parameterList) bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) { solAssert(m_expressionContext == ExpressionContext::Term); - auto& typeClassAnnotation = annotation(_typeClassDefinition); - if (typeClassAnnotation.type) + auto& typeClassDefinitionAnnotation = annotation(_typeClassDefinition); + if (typeClassDefinitionAnnotation.type) return false; - typeClassAnnotation.type = type(&_typeClassDefinition, {}); + typeClassDefinitionAnnotation.type = type(&_typeClassDefinition, {}); + { ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; _typeClassDefinition.typeVariable().accept(*this); @@ -205,7 +206,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) std::map functionTypes; Type typeVar = m_typeSystem.freshTypeVariable({}); - auto& typeMembers = annotation().members[typeConstructor(&_typeClassDefinition)]; + auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; for (auto subNode: _typeClassDefinition.subNodes()) { @@ -220,7 +221,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) 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()); - typeMembers[functionDefinition->name()] = TypeMember{functionType}; + typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; } TypeClass typeClass = std::visit(util::GenericVisitor{ @@ -230,7 +231,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) util::unreachable(); } }, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition)); - annotation(_typeClassDefinition).typeClass = typeClass; + typeClassDefinitionAnnotation.typeClass = typeClass; annotation().typeClassFunctions[typeClass] = std::move(functionTypes); for (auto [functionName, functionType]: functionTypes) @@ -569,16 +570,16 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut } case ExpressionContext::Sort: { - if (auto const* typeClass = dynamic_cast(&_declaration)) + if (auto const* typeClassDefinition = dynamic_cast(&_declaration)) { ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; - typeClass->accept(*this); - if (!annotation(*typeClass).typeClass) + typeClassDefinition->accept(*this); + if (!annotation(*typeClassDefinition).typeClass) { m_errorReporter.typeError(2736_error, _location, "Unregistered type class."); return m_typeSystem.freshTypeVariable({}); } - return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClass).typeClass}}); + return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClassDefinition).typeClass}}); } else { @@ -674,14 +675,14 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) instantiationAnnotation.type = m_voidType; std::optional typeClass = std::visit(util::GenericVisitor{ [&](ASTPointer _typeClassName) -> std::optional { - if (auto const* typeClass = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) + if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) { // visiting the type class will re-visit this instantiation - typeClass->accept(*this); + typeClassDefinition->accept(*this); // TODO: more error handling? Should be covered by the visit above. - if (!annotation(*typeClass).typeClass) + if (!annotation(*typeClassDefinition).typeClass) m_errorReporter.typeError(8503_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); - return annotation(*typeClass).typeClass; + return annotation(*typeClassDefinition).typeClass; } else { From acad00f4615b88c8f6325987df85a76f66d52a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 13 Sep 2023 14:26:55 +0200 Subject: [PATCH 14/17] fixup! Type inference draft. Rebase fixes --- libsolidity/experimental/ast/TypeSystem.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index 5347b3d15..0400f4196 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -138,7 +138,7 @@ TypeSystem::TypeSystem() for (auto [type, name, arity]: std::initializer_list>{ {PrimitiveType::TypeFunction, "tfun", 2}, {PrimitiveType::Function, "fun", 2}, - {PrimitiveType::Function, "itself", 1}, + {PrimitiveType::Itself, "itself", 1}, {PrimitiveType::Void, "void", 0}, {PrimitiveType::Unit, "unit", 0}, {PrimitiveType::Pair, "pair", 2}, diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 36a26ba40..91150ae74 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1475,9 +1475,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; From 9f7764c21597ce663b1546ce76fdba1795f8ce41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 5 Sep 2023 19:49:59 +0200 Subject: [PATCH 15/17] Semantic test for type classes --- .../semanticTests/experimental/type_class.sol | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 test/libsolidity/semanticTests/experimental/type_class.sol diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol new file mode 100644 index 000000000..14eb59716 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -0,0 +1,65 @@ +pragma experimental solidity; + +type Cat = word; +type Dog = word; + +class A: Animal { + function new() -> A; + function alive(a: A) -> bool; +} + +instantiation Cat: Animal { + function new() -> Cat { + let c; + return c; + } + + function alive(c: 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(d: 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 From 086c912c65223f020f8acd23fc05cb94177d08a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 11 Sep 2023 14:39:35 +0200 Subject: [PATCH 16/17] TypeSystem: make typeClassInfo() public --- libsolidity/experimental/ast/TypeSystem.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h index 17b427e85..00f710a76 100644 --- a/libsolidity/experimental/ast/TypeSystem.h +++ b/libsolidity/experimental/ast/TypeSystem.h @@ -136,12 +136,14 @@ public: { return m_typeClasses.at(_class.m_index).typeVariable; } -private: - friend class TypeEnvironment; + 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; From da83b35a711f90dabc8f0a22242885613f603a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 7 Sep 2023 12:14:28 +0200 Subject: [PATCH 17/17] Separate experimental analysis pass for type class registration --- libsolidity/CMakeLists.txt | 2 + .../experimental/analysis/Analysis.cpp | 30 ++++++++- .../analysis/TypeClassRegistration.cpp | 65 +++++++++++++++++++ .../analysis/TypeClassRegistration.h | 58 +++++++++++++++++ .../experimental/analysis/TypeInference.cpp | 29 ++++----- .../experimental/analysis/TypeInference.h | 2 - .../codegen/IRGeneratorForStatements.cpp | 7 +- .../semanticTests/experimental/type_class.sol | 10 +-- 8 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 46d43291e..6ad451ed1 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -188,6 +188,8 @@ set(sources experimental/analysis/Analysis.h experimental/analysis/DebugWarner.cpp experimental/analysis/DebugWarner.h + experimental/analysis/TypeClassRegistration.cpp + experimental/analysis/TypeClassRegistration.h experimental/analysis/TypeInference.cpp experimental/analysis/TypeInference.h experimental/analysis/TypeRegistration.cpp diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 56d4d36f9..5e638f523 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -27,16 +28,43 @@ using namespace solidity::frontend::experimental; // 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 { + 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<> TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) { @@ -121,7 +149,7 @@ std::tuple...> makeIndexTuple(std::index_sequ bool Analysis::check(std::vector> const& _sourceUnits) { - using AnalysisSteps = std::tuple; + using AnalysisSteps = std::tuple; return std::apply([&](auto... _indexTuple) { return ([&](auto&& _step) { diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.cpp b/libsolidity/experimental/analysis/TypeClassRegistration.cpp new file mode 100644 index 000000000..10d3d6c3f --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.cpp @@ -0,0 +1,65 @@ +/* + 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) +{ + Type typeVar = m_typeSystem.freshTypeVariable({}); + + std::variant typeClassOrError = m_typeSystem.declareTypeClass( + typeVar, + _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 000000000..c0c1c3549 --- /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 index 9d67027b7..d0679b840 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -18,6 +18,8 @@ #include + +#include #include #include #include @@ -205,7 +207,9 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) std::map functionTypes; - Type typeVar = m_typeSystem.freshTypeVariable({}); + solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); + Type typeVar = m_typeSystem.typeClassInfo(typeClass).typeVariable; auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; for (auto subNode: _typeClassDefinition.subNodes()) @@ -224,14 +228,6 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; } - TypeClass typeClass = std::visit(util::GenericVisitor{ - [](TypeClass _class) -> TypeClass { return _class; }, - [&](std::string _error) -> TypeClass { - m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); - util::unreachable(); - } - }, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition)); - typeClassDefinitionAnnotation.typeClass = typeClass; annotation().typeClassFunctions[typeClass] = std::move(functionTypes); for (auto [functionName, functionType]: functionTypes) @@ -574,12 +570,10 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut { ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; typeClassDefinition->accept(*this); - if (!annotation(*typeClassDefinition).typeClass) - { - m_errorReporter.typeError(2736_error, _location, "Unregistered type class."); - return m_typeSystem.freshTypeVariable({}); - } - return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClassDefinition).typeClass}}); + + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(*typeClassDefinition).typeClass.value(); + return m_typeSystem.freshTypeVariable(Sort{{typeClass}}); } else { @@ -680,9 +674,8 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) // visiting the type class will re-visit this instantiation typeClassDefinition->accept(*this); // TODO: more error handling? Should be covered by the visit above. - if (!annotation(*typeClassDefinition).typeClass) - m_errorReporter.typeError(8503_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); - return annotation(*typeClassDefinition).typeClass; + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + return m_analysis.annotation(*typeClassDefinition).typeClass.value(); } else { diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index e8e234862..31de43a7c 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -38,8 +38,6 @@ public: { /// Expressions, variable declarations, function declarations. std::optional type; - // Type classes. - std::optional typeClass; }; struct TypeMember { diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 1da96f147..589fe6787 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -271,11 +272,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(declaration); if (auto const* typeClassDefinition = dynamic_cast(declaration)) { - std::optional typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass; - solAssert(typeClass); + 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) + &resolveTypeClassFunction(typeClass, _memberAccess.memberName(), memberAccessType) ).second); } else if (dynamic_cast(declaration)) diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol index 14eb59716..b987b3f06 100644 --- a/test/libsolidity/semanticTests/experimental/type_class.sol +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -3,9 +3,9 @@ pragma experimental solidity; type Cat = word; type Dog = word; -class A: Animal { - function new() -> A; - function alive(a: A) -> bool; +class Self: Animal { + function new() -> Self; + function alive(self: Self) -> bool; } instantiation Cat: Animal { @@ -14,7 +14,7 @@ instantiation Cat: Animal { return c; } - function alive(c: Cat) -> bool { + function alive(self: Cat) -> bool { // TODO: Boolean literals or operators not implemented. let w; assembly { @@ -30,7 +30,7 @@ instantiation Dog: Animal { return d; } - function alive(d: Dog) -> bool { + function alive(self: Dog) -> bool { let b: bool; return b; }