diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 92c818353..3bf6b2d82 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -158,7 +158,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()); @@ -168,13 +168,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()); @@ -184,8 +184,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) @@ -245,8 +245,9 @@ 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; } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index a708ef810..13752e9c9 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -99,8 +99,8 @@ 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; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 94e37a47d..7d47a637a 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -958,7 +958,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), @@ -967,10 +968,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; @@ -1028,12 +1031,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; }; /** diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index fbd59508a..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 diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 5f9d13606..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); diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp index 85c8b8a48..75ed77b2d 100644 --- a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp @@ -59,6 +59,7 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) 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) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 2738a4f98..f433451d8 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -68,13 +68,15 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) ScopedSaveAndRestore signatureRestore(m_currentFunctionType, nullopt); _functionDefinition.parameterList().accept(*this); - if (_functionDefinition.returnParameterList()) - _functionDefinition.returnParameterList()->accept(*this); + if (_functionDefinition.experimentalReturnExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _functionDefinition.experimentalReturnExpression()->accept(*this); + } - auto getListType = [&](ParameterList const* _list) { return _list ? getType(*_list) : m_unitType; }; Type functionType = TypeSystemHelpers{m_typeSystem}.functionType( - getListType(&_functionDefinition.parameterList()), - getListType(_functionDefinition.returnParameterList().get()) + getType(_functionDefinition.parameterList()), + _functionDefinition.experimentalReturnExpression() ? getType(*_functionDefinition.experimentalReturnExpression()) : m_unitType ); m_currentFunctionType = functionType; diff --git a/libsolidity/experimental/codegen/IRGenerator.cpp b/libsolidity/experimental/codegen/IRGenerator.cpp index 4b02592e4..a050ed07a 100644 --- a/libsolidity/experimental/codegen/IRGenerator.cpp +++ b/libsolidity/experimental/codegen/IRGenerator.cpp @@ -140,14 +140,15 @@ string IRGenerator::generate(FunctionDefinition const& _function, Type _type) if (!_function.parameters().empty()) code << IRNames::localVariable(*_function.parameters().back()); code << ")"; - if (_function.returnParameterList() && !_function.returnParameters().empty()) + if (_function.experimentalReturnExpression()) { - code << " -> "; - if (_function.returnParameters().size() > 1) - for (auto const& arg: _function.returnParameters() | ranges::views::drop_last(1)) - code << IRNames::localVariable(*arg) << ", "; - if (!_function.returnParameters().empty()) - code << IRNames::localVariable(*_function.returnParameters().back()); + 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()) diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index b4bbdcdfd..476e749db 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -161,11 +161,9 @@ void IRGeneratorForStatements::endVisit(Return const& _return) { if (Expression const* value = _return.expression()) { - solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); - vector> const& returnParameters = - _return.annotation().functionReturnParameters->parameters(); - solAssert(returnParameters.size() == 1, "Returning tuples not yet supported."); - m_code << IRNames::localVariable(*returnParameters.front()) << " := " << IRNames::localVariable(*value) << "\n"; + 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"; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d475f2c65..1fde36eec 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -621,16 +621,25 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else break; } - if ( - m_scanner->currentToken() == (m_experimentalSolidityEnabledInCurrentSourceUnit ? Token::RightArrow : Token::Returns) - ) + if (m_experimentalSolidityEnabledInCurrentSourceUnit) { - bool const permitEmptyParameterList = m_experimentalSolidityEnabledInCurrentSourceUnit; - 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; } @@ -682,6 +691,11 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _al FunctionHeaderParserResult header = parseFunctionHeader(false); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + solAssert(!header.returnParameters); + else + solAssert(!header.experimentalReturnExpression); + ASTPointer block; nodeFactory.markEndPosition(); if (!_allowBody) @@ -706,7 +720,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _al header.parameters, header.modifiers, header.returnParameters, - block + block, + header.experimentalReturnExpression ); } @@ -1224,10 +1239,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, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b7b9be0dd..0aabb6cf6 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -77,6 +77,7 @@ private: Visibility visibility = Visibility::Default; StateMutability stateMutability = StateMutability::NonPayable; std::vector> modifiers; + ASTPointer experimentalReturnExpression; }; /// Struct to share parsed function call arguments. diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index 7cbe94149..a4c0fe3ea 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -2,19 +2,8 @@ pragma experimental solidity; type uint256 = word; -instantiation uint256: * { - function mul(x, y) -> z { - let a = uint256.rep(x); - let b = uint256.rep(y); - assembly { - a := mul(a,b) - } - return uint256.abs(a); - } -} - instantiation uint256: + { - function add(x, y) -> z { + function add(x, y) -> uint256 { let a = uint256.rep(x); let b = uint256.rep(y); assembly { @@ -24,27 +13,52 @@ instantiation uint256: + { } } + +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) -> z { + function mul(x, y) -> word { + let z: word; assembly { z := mul(x,y) } + return z; } } instantiation word: integer { - function fromInteger(x:integer) -> y:word { + function fromInteger(x:integer) -> word { + //x + x; } } instantiation word: == { - function eq(x, y) -> z:bool { + 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 x : word; @@ -52,8 +66,10 @@ contract C { 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 {