From 4357b0316b0385cab2633469f85c6ef171fbe780 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 19 Jun 2023 22:02:04 +0200 Subject: [PATCH] tmp --- liblangutil/Token.h | 2 +- libsolidity/CMakeLists.txt | 2 + .../analysis/experimental/Analysis.cpp | 19 +- libsolidity/analysis/experimental/Analysis.h | 11 +- .../experimental/SyntaxRestrictor.cpp | 12 +- .../analysis/experimental/SyntaxRestrictor.h | 3 + .../analysis/experimental/TypeInference.cpp | 105 ++++++++++- .../analysis/experimental/TypeInference.h | 83 ++------- libsolidity/ast/AST.h | 4 +- libsolidity/ast/experimental/TypeSystem.cpp | 64 +++++++ libsolidity/ast/experimental/TypeSystem.h | 167 ++++++++++++++++++ .../codegen/experimental/IRGenerator.cpp | 71 +------- .../codegen/experimental/IRGenerator.h | 9 +- .../experimental/IRGeneratorForStatements.cpp | 99 +++++++++++ .../experimental/IRGeneratorForStatements.h | 9 +- libsolidity/interface/CompilerStack.cpp | 9 +- libsolidity/parsing/Parser.cpp | 76 +++++++- libsolidity/parsing/Parser.h | 8 + libyul/AsmParser.cpp | 3 +- .../semanticTests/experimental/stub.sol | 17 +- 20 files changed, 604 insertions(+), 169 deletions(-) create mode 100644 libsolidity/ast/experimental/TypeSystem.cpp create mode 100644 libsolidity/ast/experimental/TypeSystem.h diff --git a/liblangutil/Token.h b/liblangutil/Token.h index a0c384a7d..475e12c7c 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -330,7 +330,7 @@ namespace TokenTraits constexpr bool isExperimentalSolidityKeyword(Token tok) { 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::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let || (tok >= Token::Word && tok < Token::ExperimentalEnd); } constexpr bool isExperimentalSolidityOnlyKeyword(Token tok) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 8bf9f9fe6..a42cc5432 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -74,6 +74,8 @@ set(sources ast/Types.h ast/TypeProvider.cpp ast/TypeProvider.h + ast/experimental/TypeSystem.cpp + ast/experimental/TypeSystem.h codegen/ABIFunctions.cpp codegen/ABIFunctions.h codegen/ArrayUtils.cpp diff --git a/libsolidity/analysis/experimental/Analysis.cpp b/libsolidity/analysis/experimental/Analysis.cpp index 919fc80df..76d6f16e9 100644 --- a/libsolidity/analysis/experimental/Analysis.cpp +++ b/libsolidity/analysis/experimental/Analysis.cpp @@ -18,14 +18,27 @@ #include #include +#include +using namespace std; using namespace solidity::langutil; using namespace solidity::frontend::experimental; -bool Analysis::check(ASTNode const& _node) +Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): + m_errorReporter(_errorReporter), + m_maxAstId(_maxAstId) +{ +} + +bool Analysis::check(vector> const& _sourceUnits) { SyntaxRestrictor syntaxRestrictor{m_errorReporter}; - if (!syntaxRestrictor.check(_node)) - return false; + for (auto source: _sourceUnits) + if (!syntaxRestrictor.check(*source)) + return false; + TypeInference typeInference{m_errorReporter}; + for (auto source: _sourceUnits) + if (!typeInference.analyze(*source)) + return false; return true; } diff --git a/libsolidity/analysis/experimental/Analysis.h b/libsolidity/analysis/experimental/Analysis.h index d94eb5c8b..56450266b 100644 --- a/libsolidity/analysis/experimental/Analysis.h +++ b/libsolidity/analysis/experimental/Analysis.h @@ -18,10 +18,12 @@ #pragma once #include +#include +#include namespace solidity::frontend { -class ASTNode; +class SourceUnit; } namespace solidity::langutil @@ -35,11 +37,8 @@ namespace solidity::frontend::experimental class Analysis { public: - Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): - m_errorReporter(_errorReporter), - m_maxAstId(_maxAstId) - {} - bool check(ASTNode const& _ast); + Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId); + bool check(std::vector> const& _sourceUnits); private: langutil::ErrorReporter& m_errorReporter; uint64_t m_maxAstId = 0; diff --git a/libsolidity/analysis/experimental/SyntaxRestrictor.cpp b/libsolidity/analysis/experimental/SyntaxRestrictor.cpp index 142d46011..17c4cc629 100644 --- a/libsolidity/analysis/experimental/SyntaxRestrictor.cpp +++ b/libsolidity/analysis/experimental/SyntaxRestrictor.cpp @@ -49,10 +49,10 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) { if (!_functionDefinition.isImplemented()) m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Functions must be implemented."); - if (!_functionDefinition.parameterList().parameters().empty()) - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have arguments."); - if (_functionDefinition.returnParameterList() && !_functionDefinition.returnParameterList()->parameters().empty()) - m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have return variables."); + 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."); if (_functionDefinition.isFree()) { if (_functionDefinition.stateMutability() != StateMutability::NonPayable) @@ -69,9 +69,7 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); } - _functionDefinition.body().accept(*this); - - return false; + return true; } bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement) diff --git a/libsolidity/analysis/experimental/SyntaxRestrictor.h b/libsolidity/analysis/experimental/SyntaxRestrictor.h index aff673626..ef018f5f2 100644 --- a/libsolidity/analysis/experimental/SyntaxRestrictor.h +++ b/libsolidity/analysis/experimental/SyntaxRestrictor.h @@ -42,12 +42,15 @@ private: 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(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(VariableDeclarationStatement const&) override; bool visit(VariableDeclaration const&) override; bool visit(ElementaryTypeName const&) override { return true; } + bool visit(ParameterList const&) override { return true; } langutil::ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/experimental/TypeInference.cpp b/libsolidity/analysis/experimental/TypeInference.cpp index db7ac2356..04f8ecae8 100644 --- a/libsolidity/analysis/experimental/TypeInference.cpp +++ b/libsolidity/analysis/experimental/TypeInference.cpp @@ -20,18 +20,119 @@ #include #include +#include +#include +#include + +using namespace std; using namespace solidity::frontend; using namespace solidity::frontend::experimental; using namespace solidity::langutil; +bool TypeInference::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeInference::visit(FunctionDefinition const& _functionDefinition) +{ + ScopedSaveAndRestore env{m_env, {}}; + _functionDefinition.parameterList().accept(*this); + if (_functionDefinition.returnParameterList()) + _functionDefinition.returnParameterList()->accept(*this); + + _functionDefinition.body().accept(*this); + + return false; +} + +bool TypeInference::visit(ParameterList const&) +{ + return true; +} + bool TypeInference::visitNode(ASTNode const& _node) { m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference."); return false; } -bool TypeInference::visit(VariableDeclaration const&) +experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) { -// m_env.assignType(&_varialeDeclaration, m_env.lookupType(_varialeDeclaration.typeName())); + if (auto const* elementaryTypeName = dynamic_cast(&_typeName)) + { + switch(elementaryTypeName->typeName().token()) + { + case Token::Word: + return WordType{}; + default: + m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); + break; + } + } + else + m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); + return m_env.freshFreeType(); + +} + +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) + { + // 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; + } + auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); + if (ref == _inlineAssembly.annotation().externalReferences.end()) + return false; + InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; + Declaration const* declaration = identifierInfo.declaration; + solAssert(!!declaration, ""); + + m_env.assignType(m_typeSystem, declaration, WordType{}); + 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(VariableDeclaration const& _variableDeclaration) +{ + Type type = _variableDeclaration.hasTypeName() ? fromTypeName(_variableDeclaration.typeName()) : m_typeSystem.freshTypeVariable(); + m_env.assignType(m_typeSystem, &_variableDeclaration, type); + return false; +} + +bool TypeInference::visit(Assignment const& _assignment) +{ + (void)_assignment; + return true; +} + +bool TypeInference::visit(Identifier const& _identifier) +{ + (void)_identifier; + return true; +} diff --git a/libsolidity/analysis/experimental/TypeInference.h b/libsolidity/analysis/experimental/TypeInference.h index f1decb8ff..06eab47c7 100644 --- a/libsolidity/analysis/experimental/TypeInference.h +++ b/libsolidity/analysis/experimental/TypeInference.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include @@ -26,84 +27,32 @@ namespace solidity::frontend::experimental { -class GlobalTypeContext; - -struct SumType; -struct TupleType; -struct FunctionType; -struct WordType; -struct UserDefinedType; -struct TypeVariable; -struct FreeType; - -using Type = std::variant; - -struct SumType -{ - std::vector alternatives; -}; - -struct TupleType -{ - std::vector components; -}; - -struct FunctionType -{ - Type const* codomain = nullptr; - Type const* domain = nullptr; -}; - -struct WordType -{ -}; - -struct UserDefinedType -{ - Declaration const* declaration = nullptr; - std::vector arguments; -}; - -struct TypeVariable -{ - uint64_t index = 0; -}; - -struct FreeType -{ - uint64_t index = 0; -}; - -Type unify(Type _a, Type _b) -{ - -} - -class TypeEnvironment -{ -public: - TypeEnvironment() {} - void assignType(Declaration const* _declaration, Type _typeAssignment) - { - m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment))); - } -private: - uint64_t m_numTypeVariables = 0; - std::map m_types; -}; - class TypeInference: public ASTConstVisitor { public: TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + + bool analyze(SourceUnit const& _sourceUnit); private: bool visit(Block const&) override { return true; } bool visit(VariableDeclarationStatement const&) override { return true; } bool visit(VariableDeclaration const& _variableDeclaration) override; + bool visit(FunctionDefinition const& _functionDefinition) override; + bool visit(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(ExpressionStatement const&) override { return true; } + bool visit(Assignment const&) override; + bool visit(Identifier const&) override; + bool visitNode(ASTNode const& _node) override; -private: + Type fromTypeName(TypeName const& _typeName); + TypeSystem m_typeSystem; langutil::ErrorReporter& m_errorReporter; TypeEnvironment m_env; }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9d754202d..07c9576fc 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1077,13 +1077,15 @@ public: m_overrides(std::move(_overrides)), m_location(_referenceLocation) { - solAssert(m_typeName, ""); + // TODO: consider still asserting unless we are in experimental solidity. + // solAssert(m_typeName, ""); } 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; } diff --git a/libsolidity/ast/experimental/TypeSystem.cpp b/libsolidity/ast/experimental/TypeSystem.cpp new file mode 100644 index 000000000..384968618 --- /dev/null +++ b/libsolidity/ast/experimental/TypeSystem.cpp @@ -0,0 +1,64 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +void TypeEnvironment::assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment) +{ + auto&& [type, newlyInserted] = m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment))); + if (!newlyInserted) + { + unify(_typeSystem, type->second, _typeAssignment); + } +} +Type TypeEnvironment::lookup(TypeSystem& _typeSystem, Declaration const* _declaration) +{ + if (m_types.count(_declaration)) + return m_types[_declaration]; + Type result = _typeSystem.freshTypeVariable(); + m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(result)); + return result; +} +Type TypeEnvironment::freshFreeType() +{ + return FreeType{m_numFreeTypes++}; +} + + +void TypeEnvironment::unify(TypeSystem& _context, Type _a, Type _b) +{ + _a = _context.resolve(_a); + _b = _context.resolve(_b); + if (auto* varA = get_if(&_a)) + _context.instantiate(*varA, _b); + else if (holds_alternative(_a)) + { + if (holds_alternative(_b)) + return; + else + solAssert(false, "unification failed"); + } + + solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b))); +} \ No newline at end of file diff --git a/libsolidity/ast/experimental/TypeSystem.h b/libsolidity/ast/experimental/TypeSystem.h new file mode 100644 index 000000000..da1d546a6 --- /dev/null +++ b/libsolidity/ast/experimental/TypeSystem.h @@ -0,0 +1,167 @@ +/* + 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 TypeSystem; + +struct SumType; +struct TupleType; +struct FunctionType; +struct WordType; +struct UserDefinedType; +struct TypeVariable; +struct FreeType; + +using Type = std::variant; + +struct SumType +{ + std::vector alternatives; + std::string toString() const + { + return "sum"; + } +}; + +struct TupleType +{ + std::vector components; + std::string toString() const + { + return "tuple"; + } +}; + +struct FunctionType +{ + Type const* codomain = nullptr; + Type const* domain = nullptr; + std::string toString() const + { + return "fun"; + } +}; + +struct WordType +{ + std::string toString() const + { + return "word"; + } +}; + +struct UserDefinedType +{ + Declaration const* declaration = nullptr; + std::vector arguments; + std::string toString() const + { + return "user_defined_type"; + } +}; + +struct TypeVariable +{ + std::string toString() const + { + return fmt::format("var<{}>", m_index); + } +private: + uint64_t index() const { return m_index; } + friend class TypeSystem; + uint64_t m_index = 0; + TypeVariable(uint64_t _index): m_index(_index) {} +}; + +struct FreeType +{ + uint64_t index = 0; + std::string toString() const + { + return fmt::format("free<{}>", index); + } +}; + +inline std::string typeToString(Type const& _type) +{ + return std::visit([](auto _type) { return _type.toString(); }, _type); +} + +class TypeEnvironment +{ +public: + void assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment); + Type lookup(TypeSystem& _typeSystem, Declaration const* _declaration); + Type freshFreeType(); + void unify(TypeSystem& _typeSystem, Type _a, Type _b); +private: + uint64_t m_numFreeTypes = 0; + std::map m_types; +}; + + +class TypeSystem +{ +public: + TypeSystem() {} + TypeSystem(TypeSystem const&) = delete; + TypeSystem const& operator=(TypeSystem const&) = delete; + Type freshTypeVariable() + { + uint64_t index = m_typeVariables.size(); + m_typeVariables.emplace_back(std::nullopt); + return TypeVariable(index); + } + void instantiate(TypeVariable _variable, Type _type) + { + solAssert(_variable.index() < m_typeVariables.size()); + solAssert(!m_typeVariables.at(_variable.index()).has_value()); + m_typeVariables[_variable.index()] = _type; + } + Type resolve(Type _type) + { + Type result = _type; + while(auto const* var = std::get_if(&result)) + if (auto value = m_typeVariables.at(var->index())) + result = *value; + else + break; + return result; + } +private: + std::vector> m_typeVariables; +}; + +} \ No newline at end of file diff --git a/libsolidity/codegen/experimental/IRGenerator.cpp b/libsolidity/codegen/experimental/IRGenerator.cpp index 0db02a163..857f0c48b 100644 --- a/libsolidity/codegen/experimental/IRGenerator.cpp +++ b/libsolidity/codegen/experimental/IRGenerator.cpp @@ -18,6 +18,8 @@ #include +#include + #include #include @@ -87,74 +89,9 @@ string IRGenerator::generate(FunctionDefinition const& _function) const 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."); + IRGeneratorForStatements statementGenerator{m_analysis}; + code << statementGenerator.generate(*_statement); } 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 index b2d1a7247..d7a36c57a 100644 --- a/libsolidity/codegen/experimental/IRGenerator.h +++ b/libsolidity/codegen/experimental/IRGenerator.h @@ -35,6 +35,7 @@ namespace solidity::frontend::experimental { class SourceUnit; +class Analysis; class IRGenerator { @@ -45,12 +46,14 @@ public: RevertStrings /*_revertStrings*/, std::map /*_sourceIndices*/, langutil::DebugInfoSelection const& _debugInfoSelection, - langutil::CharStreamProvider const* _soliditySourceProvider + langutil::CharStreamProvider const* _soliditySourceProvider, + Analysis const& _analysis ): m_evmVersion(_evmVersion), m_eofVersion(_eofVersion), m_debugInfoSelection(_debugInfoSelection), - m_soliditySourceProvider(_soliditySourceProvider) + m_soliditySourceProvider(_soliditySourceProvider), + m_analysis(_analysis) {} std::string run( @@ -61,13 +64,13 @@ public: 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; + Analysis const& m_analysis; }; } diff --git a/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp b/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp index 913c916ee..7051710f2 100644 --- a/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp @@ -18,9 +18,108 @@ #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( + 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& _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."); + // TODO: validate that variable is known and has word type. + string value = IRNames::localVariable(*varDecl); + return yul::Identifier{_identifier.debugData, yul::YulString{value}}; + } + + yul::Dialect const& m_dialect; + map m_references; +}; + +} + +bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly) +{ + CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences}; + yul::Statement modified = bodyCopier(_assembly.operations()); + solAssert(holds_alternative(modified)); + m_code << yul::AsmPrinter()(std::get(modified)); + return false; +} + +bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + solAssert(_variableDeclarationStatement.declarations().size() == 1, "multi variable declarations not supported"); + solAssert(!_variableDeclarationStatement.initialValue(), "initial values not yet 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) << "\n"; + return false; +} + +bool IRGeneratorForStatements::visitNode(ASTNode const&) +{ + solAssert(false, "Unsupported AST node during statement code generation."); +} \ No newline at end of file diff --git a/libsolidity/codegen/experimental/IRGeneratorForStatements.h b/libsolidity/codegen/experimental/IRGeneratorForStatements.h index 70f4f9c5b..948c6697d 100644 --- a/libsolidity/codegen/experimental/IRGeneratorForStatements.h +++ b/libsolidity/codegen/experimental/IRGeneratorForStatements.h @@ -21,18 +21,25 @@ #include #include +#include namespace solidity::frontend::experimental { +class Analysis; class IRGeneratorForStatements: public ASTConstVisitor { public: - IRGeneratorForStatements() {} + IRGeneratorForStatements(Analysis const& _analysis): m_analysis(_analysis) {} + std::string generate(ASTNode const& _node); private: + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; /// Default visit will reject all AST nodes that are not explicitly supported. bool visitNode(ASTNode const& _node) override; + Analysis const& m_analysis; + std::stringstream m_code; }; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9163cfada..0692e7637 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -670,10 +670,12 @@ bool CompilerStack::analyzeExperimental() bool noErrors = true; solAssert(m_maxAstId && *m_maxAstId >= 0); m_experimentalAnalysis = make_unique(m_errorReporter, static_cast(*m_maxAstId)); + vector> sourceAsts; for (Source const* source: m_sourceOrder) if (source->ast) - if (!m_experimentalAnalysis->check(*source->ast)) - noErrors = false; + sourceAsts.emplace_back(source->ast); + if (!m_experimentalAnalysis->check(sourceAsts)) + noErrors = false; return noErrors; } @@ -1519,7 +1521,8 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) m_revertStrings, sourceIndices(), m_debugInfoSelection, - this + this, + *m_experimentalAnalysis ); compiledContract.yulIR = generator.run( _contract, diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8fe853f31..86c7ccc3d 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -610,7 +610,9 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else break; } - if (m_scanner->currentToken() == Token::Returns) + if ( + m_scanner->currentToken() == (m_experimentalSolidityEnabledInCurrentSourceUnit ? Token::RightArrow : Token::Returns) + ) { bool const permitEmptyParameterList = false; advance(); @@ -1269,15 +1271,21 @@ ASTPointer Parser::parseParameterList( VarDeclParserOptions options(_options); options.allowEmptyName = true; 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(); @@ -1658,12 +1666,66 @@ 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); + + 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 = parseTypeName(); + nodeFactory.setEndPositionFromNode(type); + } + + return nodeFactory.createNode( + type, + identifier, + nameLocation, + nullptr, + Visibility::Default, + documentation + ); +} + 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); @@ -1766,7 +1828,10 @@ pair Parser::tryParseIndexAcce { case LookAheadInfo::VariableDeclaration: case LookAheadInfo::Expression: - return make_pair(statementType, IndexAccessedPath()); + return make_pair( + m_experimentalSolidityEnabledInCurrentSourceUnit ? LookAheadInfo::Expression : statementType, + IndexAccessedPath() + ); default: break; } @@ -1777,6 +1842,9 @@ pair Parser::tryParseIndexAcce // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + return make_pair(LookAheadInfo::Expression, std::move(iap)); + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return make_pair(LookAheadInfo::VariableDeclaration, std::move(iap)); else diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 4566a69e1..f607bcbc0 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -170,6 +170,14 @@ private: std::pair, langutil::SourceLocation> expectIdentifierWithLocation(); ///@} + ///@{ + ///@name Specialized parsing functions for the AST nodes of experimental solidity. + ASTPointer parsePostfixVariableDeclarationStatement( + ASTPointer const& _docString + ); + ASTPointer parsePostfixVariableDeclaration(); + ///@} + ///@{ ///@name Helper functions diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 8043c758a..a189745f2 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -116,8 +116,9 @@ unique_ptr Parser::parseInline(std::shared_ptr const& _scanner) { m_recursionDepth = 0; + auto previousScannerKind = _scanner->scannerKind(); _scanner->setScannerMode(ScannerKind::Yul); - ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); }); + ScopeGuard resetScanner([&]{ _scanner->setScannerMode(previousScannerKind); }); try { diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index 88b0e1199..5376569a3 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -1,10 +1,21 @@ pragma experimental solidity; +function f(a:word) -> (b:word) { + assembly { + b := a + } +} + contract C { fallback() external { - word x; + let x : word; + let y : word; assembly { - mstore(0, 42) + x := 0x42 + } + y = x; + assembly { + mstore(0, y) return(0, 32) } } @@ -12,4 +23,4 @@ contract C { // ==== // compileViaYul: true // ---- -// () -> 42 +// () -> 0x42