From 672951ccc74bd0f103f32fb04132d55f51100a39 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Mon, 11 Oct 2021 10:16:52 +0200 Subject: [PATCH] Extend using-for. --- Changelog.md | 1 + docs/contracts/using-for.rst | 105 +++++++----- docs/grammar/SolidityParser.g4 | 7 +- .../analysis/DeclarationTypeChecker.cpp | 38 ++++- libsolidity/analysis/SyntaxChecker.cpp | 24 +++ libsolidity/analysis/SyntaxChecker.h | 3 + libsolidity/analysis/TypeChecker.cpp | 66 +++++++- libsolidity/ast/AST.h | 29 +++- libsolidity/ast/ASTJsonConverter.cpp | 22 ++- libsolidity/ast/ASTJsonImporter.cpp | 10 +- libsolidity/ast/AST_accept.h | 4 +- libsolidity/ast/Types.cpp | 71 +++++--- libsolidity/parsing/Parser.cpp | 22 ++- .../ASTJSON/using_for_directive.json | 151 ++++++++++++++---- .../ASTJSON/using_for_directive.sol | 5 +- .../using_for_directive_parseOnly.json | 114 ++++++++++--- .../using/calldata_memory_copy.sol | 19 +++ .../using/free_function_braces.sol | 26 +++ .../using/free_function_multi.sol | 27 ++++ .../using/free_functions_individual.sol | 30 ++++ .../using/imported_functions.sol | 19 +++ .../using/library_through_module.sol | 34 ++++ .../semanticTests/using/module_renamed.sol | 26 +++ .../syntaxTests/bound/bound_to_struct.sol | 2 +- .../syntaxTests/bound/interface_using_for.sol | 2 +- .../252_using_for_not_library.sol | 2 +- .../514_using_for_with_non_library.sol | 2 +- .../syntaxTests/using/double_asterisk.sol | 19 +++ .../file_level_inactive_after_import.sol | 22 +++ .../free_functions_implicit_conversion.sol | 6 + ...free_functions_implicit_conversion_err.sol | 8 + .../using/free_functions_non_unique_err.sol | 12 ++ .../syntaxTests/using/free_overloads.sol | 10 ++ .../using/free_overloads_array.sol | 9 ++ .../syntaxTests/using/free_reference_type.sol | 11 ++ .../syntaxTests/using/library_import_as.sol | 16 ++ ...library_non_free_external_function_err.sol | 14 ++ .../using/library_non_free_function.sol | 13 ++ .../syntaxTests/using/module_1.sol | 12 ++ .../syntaxTests/using/module_2.sol | 14 ++ .../syntaxTests/using/module_3.sol | 14 ++ .../using/module_identifier_not_found.sol | 13 ++ .../syntaxTests/using/using_contract_err.sol | 13 ++ .../using/using_empty_list_err.sol | 5 + .../using/using_empty_list_file_level.sol | 3 + .../using/using_for_ast_file_level.sol | 7 + .../using/using_free_functions.sol | 16 ++ .../using/using_free_no_parameters_err.sol | 7 + .../using/using_functions_with_ast.sol | 7 + .../syntaxTests/using/using_lhs_asterisk.sol | 3 + .../using/using_lhs_asterisk_contract.sol | 5 + .../using/using_library_ast_file_level.sol | 4 + .../using/using_library_file_level.sol | 3 + .../using/using_non_free_function_err.sol | 9 ++ .../syntaxTests/using/using_non_function.sol | 6 + 55 files changed, 992 insertions(+), 150 deletions(-) create mode 100644 test/libsolidity/semanticTests/using/calldata_memory_copy.sol create mode 100644 test/libsolidity/semanticTests/using/free_function_braces.sol create mode 100644 test/libsolidity/semanticTests/using/free_function_multi.sol create mode 100644 test/libsolidity/semanticTests/using/free_functions_individual.sol create mode 100644 test/libsolidity/semanticTests/using/imported_functions.sol create mode 100644 test/libsolidity/semanticTests/using/library_through_module.sol create mode 100644 test/libsolidity/semanticTests/using/module_renamed.sol create mode 100644 test/libsolidity/syntaxTests/using/double_asterisk.sol create mode 100644 test/libsolidity/syntaxTests/using/file_level_inactive_after_import.sol create mode 100644 test/libsolidity/syntaxTests/using/free_functions_implicit_conversion.sol create mode 100644 test/libsolidity/syntaxTests/using/free_functions_implicit_conversion_err.sol create mode 100644 test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol create mode 100644 test/libsolidity/syntaxTests/using/free_overloads.sol create mode 100644 test/libsolidity/syntaxTests/using/free_overloads_array.sol create mode 100644 test/libsolidity/syntaxTests/using/free_reference_type.sol create mode 100644 test/libsolidity/syntaxTests/using/library_import_as.sol create mode 100644 test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol create mode 100644 test/libsolidity/syntaxTests/using/library_non_free_function.sol create mode 100644 test/libsolidity/syntaxTests/using/module_1.sol create mode 100644 test/libsolidity/syntaxTests/using/module_2.sol create mode 100644 test/libsolidity/syntaxTests/using/module_3.sol create mode 100644 test/libsolidity/syntaxTests/using/module_identifier_not_found.sol create mode 100644 test/libsolidity/syntaxTests/using/using_contract_err.sol create mode 100644 test/libsolidity/syntaxTests/using/using_empty_list_err.sol create mode 100644 test/libsolidity/syntaxTests/using/using_empty_list_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/using_for_ast_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/using_free_functions.sol create mode 100644 test/libsolidity/syntaxTests/using/using_free_no_parameters_err.sol create mode 100644 test/libsolidity/syntaxTests/using/using_functions_with_ast.sol create mode 100644 test/libsolidity/syntaxTests/using/using_lhs_asterisk.sol create mode 100644 test/libsolidity/syntaxTests/using/using_lhs_asterisk_contract.sol create mode 100644 test/libsolidity/syntaxTests/using/using_library_ast_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/using_library_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/using_non_free_function_err.sol create mode 100644 test/libsolidity/syntaxTests/using/using_non_function.sol diff --git a/Changelog.md b/Changelog.md index 39d22c88e..7171cfa42 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model. + * General: ``using M for Type;`` is allowed at file level and ``M`` can now also be a brace-enclosed list of free functions or library functions. Compiler Features: diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst index af6750f87..c95a419e6 100644 --- a/docs/contracts/using-for.rst +++ b/docs/contracts/using-for.rst @@ -6,71 +6,89 @@ Using For ********* -The directive ``using A for B;`` can be used to attach library -functions (from the library ``A``) to any type (``B``) -in the context of a contract. +The directive ``using A for B;`` can be used to attach +functions (``A``) as member functions to any type (``B``). These functions will receive the object they are called on as their first parameter (like the ``self`` variable in Python). -The effect of ``using A for *;`` is that the functions from -the library ``A`` are attached to *any* type. +It is valid either at file level or inside a contract, +at contract level. -In both situations, *all* functions in the library are attached, +The first part, ``A``, can be one of: + +- a list of file-level or library functions (``using {f, g, h, L.t} for uint;``) - + only those functions will be attached to the type. +- the name of a library (``using L for uint;``) - + all functions (both public and internal ones) of the library are attached to the type + +At file level, the second part, ``B``, has to be an explicit type (without data location specifier). +Inside contracts, you can also use ``using L for *;``, +which has the effect that all functions of the library ``L`` +are attached to *all* types. + +If you specify a library, *all* functions in the library are attached, even those where the type of the first parameter does not match the type of the object. The type is checked at the point the function is called and function overload resolution is performed. +If you use a list of functions (``using {f, g, h, L.t} for uint;``), +then the type (``uint``) has to be implicitly convertible to the +first parameter of each of these functions. This check is +performed even if none of these functions are called. + The ``using A for B;`` directive is active only within the current -contract, including within all of its functions, and has no effect -outside of the contract in which it is used. The directive -may only be used inside a contract, not inside any of its functions. +scope (either the contract or the current module/source unit), +including within all of its functions, and has no effect +outside of the contract or module in which it is used. Let us rewrite the set example from the -:ref:`libraries` in this way: +:ref:`libraries` section in this way, using file-level functions +instead of library functions. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.9.0; + pragma solidity ^0.8.13; - - // This is the same code as before, just without comments struct Data { mapping(uint => bool) flags; } + // Now we attach functions to the type. + // The attached functions can be used throughout the rest of the module. + // If you import the module, you have to + // repeat the using directive there, for example as + // import "flags.sol" as Flags; + // using {Flags.insert, Flags.remove, Flags.contains} + // for Flags.Data; + using {insert, remove, contains} for Data; - library Set { - function insert(Data storage self, uint value) - public - returns (bool) - { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; - } + function insert(Data storage self, uint value) + returns (bool) + { + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; + } - function remove(Data storage self, uint value) - public - returns (bool) - { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; - } + function remove(Data storage self, uint value) + returns (bool) + { + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; + } - function contains(Data storage self, uint value) - public - view - returns (bool) - { - return self.flags[value]; - } + function contains(Data storage self, uint value) + public + view + returns (bool) + { + return self.flags[value]; } contract C { - using Set for Data; // this is the crucial change Data knownValues; function register(uint value) public { @@ -82,12 +100,13 @@ Let us rewrite the set example from the } } -It is also possible to extend elementary types in that way: +It is also possible to extend built-in types in that way. +In this example, we will use a library. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.8 <0.9.0; + pragma solidity ^0.8.13; library Search { function indexOf(uint[] storage self, uint value) @@ -100,9 +119,9 @@ It is also possible to extend elementary types in that way: return type(uint).max; } } + using Search for uint[]; contract C { - using Search for uint[]; uint[] data; function append(uint value) public { diff --git a/docs/grammar/SolidityParser.g4 b/docs/grammar/SolidityParser.g4 index e41bb2ba9..415dd5985 100644 --- a/docs/grammar/SolidityParser.g4 +++ b/docs/grammar/SolidityParser.g4 @@ -12,6 +12,7 @@ options { tokenVocab=SolidityLexer; } sourceUnit: ( pragmaDirective | importDirective + | usingDirective | contractDefinition | interfaceDefinition | libraryDefinition @@ -311,10 +312,10 @@ errorDefinition: Semicolon; /** - * Using directive to bind library functions to types. - * Can occur within contracts and libraries. + * Using directive to bind library functions and free functions to types. + * Can occur within contracts and libraries and at the file level. */ -usingDirective: Using identifierPath For (Mul | typeName) Semicolon; +usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Semicolon; /** * A type name can be an elementary type, a function type, a mapping type, a user-defined type * (e.g. a contract or struct) or an array type. diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index ba989972c..4fb210435 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -451,12 +452,39 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor) { - ContractDefinition const* library = dynamic_cast( - _usingFor.libraryName().annotation().referencedDeclaration - ); + if (_usingFor.usesBraces()) + { + for (ASTPointer const& function: _usingFor.functionsOrLibrary()) + if (auto functionDefinition = dynamic_cast(function->annotation().referencedDeclaration)) + { + if (!functionDefinition->isFree() && !( + dynamic_cast(functionDefinition->scope()) && + dynamic_cast(functionDefinition->scope())->isLibrary() + )) + m_errorReporter.typeError( + 4167_error, + function->location(), + "Only file-level functions and library functions can be bound to a type in a \"using\" statement" + ); + } + else + m_errorReporter.fatalTypeError(8187_error, function->location(), "Expected function name." ); + } + else + { + ContractDefinition const* library = dynamic_cast( + _usingFor.functionsOrLibrary().front()->annotation().referencedDeclaration + ); + if (!library || !library->isLibrary()) + m_errorReporter.fatalTypeError( + 4357_error, + _usingFor.functionsOrLibrary().front()->location(), + "Library name expected. If you want to attach a function, use '{...}'." + ); + } - if (!library || !library->isLibrary()) - m_errorReporter.fatalTypeError(4357_error, _usingFor.libraryName().location(), "Library name expected."); + // We do not visit _usingFor.functions() because it will lead to an error since + // library names cannot be mentioned stand-alone. if (_usingFor.typeName()) _usingFor.typeName()->accept(*this); diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 589787a89..b6a30e664 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -403,6 +403,30 @@ void SyntaxChecker::endVisit(ContractDefinition const&) m_currentContractKind = std::nullopt; } +bool SyntaxChecker::visit(UsingForDirective const& _usingFor) +{ + if (!m_currentContractKind && !_usingFor.typeName()) + m_errorReporter.syntaxError( + 8118_error, + _usingFor.location(), + "The type has to be specified explicitly at file level (cannot use '*')." + ); + else if (_usingFor.usesBraces() && !_usingFor.typeName()) + m_errorReporter.syntaxError( + 3349_error, + _usingFor.location(), + "The type has to be specified explicitly when attaching specific functions." + ); + if (m_currentContractKind == ContractKind::Interface) + m_errorReporter.syntaxError( + 9088_error, + _usingFor.location(), + "The \"using for\" directive is not allowed inside interfaces." + ); + + return true; +} + bool SyntaxChecker::visit(FunctionDefinition const& _function) { solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index ca36ed992..f221df09f 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -88,6 +88,9 @@ private: bool visit(ContractDefinition const& _contract) override; void endVisit(ContractDefinition const& _contract) override; + + bool visit(UsingForDirective const& _usingFor) override; + bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 56edfc673..379033117 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -3626,12 +3627,67 @@ void TypeChecker::endVisit(Literal const& _literal) void TypeChecker::endVisit(UsingForDirective const& _usingFor) { - if (m_currentContract->isInterface()) - m_errorReporter.typeError( - 9088_error, - _usingFor.location(), - "The \"using for\" directive is not allowed inside interfaces." + if (!_usingFor.usesBraces()) + { + solAssert(_usingFor.functionsOrLibrary().size() == 1); + ContractDefinition const* library = dynamic_cast( + _usingFor.functionsOrLibrary().front()->annotation().referencedDeclaration ); + solAssert(library && library->isLibrary()); + // No type checking for libraries + return; + } + + if (!_usingFor.typeName()) + { + solAssert(m_errorReporter.hasErrors()); + return; + } + + solAssert(_usingFor.typeName()->annotation().type); + Type const* normalizedType = TypeProvider::withLocationIfReference( + DataLocation::Storage, + _usingFor.typeName()->annotation().type + ); + solAssert(normalizedType); + + for (ASTPointer const& path: _usingFor.functionsOrLibrary()) + { + solAssert(path->annotation().referencedDeclaration); + FunctionDefinition const& functionDefinition = + dynamic_cast(*path->annotation().referencedDeclaration); + + solAssert(functionDefinition.type()); + + if (functionDefinition.parameters().empty()) + m_errorReporter.fatalTypeError( + 4731_error, + path->location(), + "The function \"" + joinHumanReadable(path->path(), ".") + "\" " + + "does not have any parameters, and therefore cannot be bound to the type \"" + + (normalizedType ? normalizedType->toString(true) : "*") + "\"." + ); + + FunctionType const* functionType = dynamic_cast(*functionDefinition.type()).asBoundFunction(); + solAssert(functionType && functionType->selfType(), ""); + BoolResult result = normalizedType->isImplicitlyConvertibleTo( + *TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType()) + ); + if (!result) + m_errorReporter.typeError( + 3100_error, + path->location(), + "The function \"" + joinHumanReadable(path->path(), ".") + "\" "+ + "cannot be bound to the type \"" + _usingFor.typeName()->annotation().type->toString() + + "\" because the type cannot be implicitly converted to the first argument" + + " of the function (\"" + functionType->selfType()->toString() + "\")" + + ( + result.message().empty() ? + "." : + ": " + result.message() + ) + ); + } } void TypeChecker::checkErrorAndEventParameters(CallableDeclaration const& _callable) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4893c212f..613d91ad9 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -630,9 +631,16 @@ private: }; /** - * `using LibraryName for uint` will attach all functions from the library LibraryName - * to `uint` if the first parameter matches the type. `using LibraryName for *` attaches - * the function to any matching type. + * Using for directive: + * + * 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T` + * 2. `using LibraryName for *` attaches to all types. + * 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., + * `fn`, respectively to `T`. + * + * For version 3, T has to be implicitly convertible to the first parameter type of + * all functions, and this is checked at the point of the using statement. For versions 1 and + * 2, this check is only done when a function is called. */ class UsingForDirective: public ASTNode { @@ -640,23 +648,28 @@ public: UsingForDirective( int64_t _id, SourceLocation const& _location, - ASTPointer _libraryName, + std::vector> _functions, + bool _usesBraces, ASTPointer _typeName ): - ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName)) + ASTNode(_id, _location), m_functions(_functions), m_usesBraces(_usesBraces), m_typeName(std::move(_typeName)) { - solAssert(m_libraryName != nullptr, "Name cannot be null."); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - IdentifierPath const& libraryName() const { return *m_libraryName; } /// @returns the type name the library is attached to, null for `*`. TypeName const* typeName() const { return m_typeName.get(); } + /// @returns a list of functions or the single library. + std::vector> const& functionsOrLibrary() const { return m_functions; } + bool usesBraces() const { return m_usesBraces; } + private: - ASTPointer m_libraryName; + /// Either the single library or a list of functions. + std::vector> m_functions; + bool m_usesBraces; ASTPointer m_typeName; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 2616d7e23..bd9ce129a 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -311,10 +312,25 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) bool ASTJsonConverter::visit(UsingForDirective const& _node) { - setJsonNode(_node, "UsingForDirective", { - make_pair("libraryName", toJson(_node.libraryName())), + vector> attributes = { make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue) - }); + }; + if (_node.usesBraces()) + { + Json::Value functionList; + for (auto const& function: _node.functionsOrLibrary()) + { + Json::Value functionNode; + functionNode["function"] = toJson(*function); + functionList.append(move(functionNode)); + } + attributes.emplace_back("functionList", move(functionList)); + } + else + attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front())); + + setJsonNode(_node, "UsingForDirective", move(attributes)); + return false; } diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index d71864f34..ce0575fab 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -348,9 +348,17 @@ ASTPointer ASTJsonImporter::createInheritanceSpecifier(Jso ASTPointer ASTJsonImporter::createUsingForDirective(Json::Value const& _node) { + vector> functions; + if (_node.isMember("libraryName")) + functions.emplace_back(createIdentifierPath(_node["libraryName"])); + else if (_node.isMember("functionList")) + for (Json::Value const& function: _node["functionList"]) + functions.emplace_back(createIdentifierPath(function["function"])); + return createASTNode( _node, - createIdentifierPath(member(_node, "libraryName")), + move(functions), + !_node.isMember("libraryName"), _node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]) ); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index fa42c2dc6..6dab08f00 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -194,7 +194,7 @@ void UsingForDirective::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - m_libraryName->accept(_visitor); + listAccept(functionsOrLibrary(), _visitor); if (m_typeName) m_typeName->accept(_visitor); } @@ -205,7 +205,7 @@ void UsingForDirective::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { - m_libraryName->accept(_visitor); + listAccept(functionsOrLibrary(), _visitor); if (m_typeName) m_typeName->accept(_visitor); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 77e9af866..29475fc4a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -331,30 +332,48 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope) { vector usingForDirectives; - if (auto const* sourceUnit = dynamic_cast(&_scope)) - usingForDirectives += ASTNode::filteredNodes(sourceUnit->nodes()); - else if (auto const* contract = dynamic_cast(&_scope)) - usingForDirectives += - contract->usingForDirectives() + - ASTNode::filteredNodes(contract->sourceUnit().nodes()); + SourceUnit const* sourceUnit = dynamic_cast(&_scope); + if (auto const* contract = dynamic_cast(&_scope)) + { + sourceUnit = &contract->sourceUnit(); + usingForDirectives += contract->usingForDirectives(); + } else - solAssert(false, ""); + solAssert(sourceUnit, ""); + usingForDirectives += ASTNode::filteredNodes(sourceUnit->nodes()); // Normalise data location of type. DataLocation typeLocation = DataLocation::Storage; if (auto refType = dynamic_cast(&_type)) typeLocation = refType->location(); - set seenFunctions; MemberList::MemberMap members; + set> seenFunctions; + auto addFunction = [&](FunctionDefinition const& _function, optional _name = {}) + { + if (!_name) + _name = _function.name(); + Type const* functionType = + _function.libraryFunction() ? _function.typeViaContractName() : _function.type(); + solAssert(functionType, ""); + FunctionType const* asBoundFunction = + dynamic_cast(*functionType).asBoundFunction(); + solAssert(asBoundFunction, ""); + + if (_type.isImplicitlyConvertibleTo(*asBoundFunction->selfType())) + if (seenFunctions.insert(make_pair(*_name, &_function)).second) + members.emplace_back(&_function, asBoundFunction, *_name); + }; + for (UsingForDirective const* ufd: usingForDirectives) { // Convert both types to pointers for comparison to see if the `using for` // directive applies. // Further down, we check more detailed for each function if `_type` is // convertible to the function parameter type. - if (ufd->typeName() && + if ( + ufd->typeName() && *TypeProvider::withLocationIfReference(typeLocation, &_type, true) != *TypeProvider::withLocationIfReference( typeLocation, @@ -363,20 +382,28 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc ) ) continue; - auto const& library = dynamic_cast( - *ufd->libraryName().annotation().referencedDeclaration - ); - for (FunctionDefinition const* function: library.definedFunctions()) + + for (auto const& pathPointer: ufd->functionsOrLibrary()) { - if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || seenFunctions.count(function)) - continue; - seenFunctions.insert(function); - if (function->parameters().empty()) - continue; - FunctionTypePointer fun = - dynamic_cast(*function->typeViaContractName()).asBoundFunction(); - if (_type.isImplicitlyConvertibleTo(*fun->selfType())) - members.emplace_back(function, fun); + solAssert(pathPointer); + Declaration const* declaration = pathPointer->annotation().referencedDeclaration; + solAssert(declaration); + + if (ContractDefinition const* library = dynamic_cast(declaration)) + { + solAssert(library->isLibrary()); + for (FunctionDefinition const* function: library->definedFunctions()) + { + if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || function->parameters().empty()) + continue; + addFunction(*function); + } + } + else + addFunction( + dynamic_cast(*declaration), + pathPointer->path().back() + ); } } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b1d7e7cca..0ded8ea04 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -117,6 +117,9 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Type: nodes.push_back(parseUserDefinedValueTypeDefinition()); break; + case Token::Using: + nodes.push_back(parseUsingDirective()); + break; case Token::Function: nodes.push_back(parseFunctionDefinition(true)); break; @@ -962,7 +965,22 @@ ASTPointer Parser::parseUsingDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); - ASTPointer library(parseIdentifierPath()); + + vector> functions; + bool const usesBraces = m_scanner->currentToken() == Token::LBrace; + if (usesBraces) + { + do + { + advance(); + functions.emplace_back(parseIdentifierPath()); + } + while (m_scanner->currentToken() == Token::Comma); + expectToken(Token::RBrace); + } + else + functions.emplace_back(parseIdentifierPath()); + ASTPointer typeName; expectToken(Token::For); if (m_scanner->currentToken() == Token::Mul) @@ -971,7 +989,7 @@ ASTPointer Parser::parseUsingDirective() typeName = parseTypeName(); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - return nodeFactory.createNode(library, typeName); + return nodeFactory.createNode(move(functions), usesBraces, typeName); } ASTPointer Parser::parseModifierInvocation() diff --git a/test/libsolidity/ASTJSON/using_for_directive.json b/test/libsolidity/ASTJSON/using_for_directive.json index b935b262b..37877ff17 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.json +++ b/test/libsolidity/ASTJSON/using_for_directive.json @@ -4,17 +4,51 @@ { "C": [ - 5 + 13 ], "L": [ - 1 + 4 + ], + "f": + [ + 10 ] }, - "id": 6, + "id": 14, "nodeType": "SourceUnit", "nodes": [ + { + "functionList": + [ + { + "function": + { + "id": 1, + "name": "f", + "nodeType": "IdentifierPath", + "referencedDeclaration": 10, + "src": "7:1:1" + } + } + ], + "id": 3, + "nodeType": "UsingForDirective", + "src": "0:19:1", + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "14:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + }, { "abstract": false, "baseContracts": [], @@ -22,19 +56,86 @@ "contractDependencies": [], "contractKind": "library", "fullyImplemented": true, - "id": 1, + "id": 4, "linearizedBaseContracts": [ - 1 + 4 ], "name": "L", - "nameLocation": "8:1:1", + "nameLocation": "28:1:1", "nodeType": "ContractDefinition", "nodes": [], - "scope": 6, - "src": "0:12:1", + "scope": 14, + "src": "20:12:1", "usedErrors": [] }, + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "50:2:1", + "statements": [] + }, + "id": 10, + "implemented": true, + "kind": "freeFunction", + "modifiers": [], + "name": "f", + "nameLocation": "42:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 10, + "src": "44:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 5, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "44:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "43:6:1" + }, + "returnParameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "50:0:1" + }, + "scope": 14, + "src": "33:19:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + }, { "abstract": false, "baseContracts": [], @@ -42,46 +143,34 @@ "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, - "id": 5, + "id": 13, "linearizedBaseContracts": [ - 5 + 13 ], "name": "C", - "nameLocation": "22:1:1", + "nameLocation": "62:1:1", "nodeType": "ContractDefinition", "nodes": [ { - "id": 4, + "id": 12, "libraryName": { - "id": 2, + "id": 11, "name": "L", "nodeType": "IdentifierPath", - "referencedDeclaration": 1, - "src": "32:1:1" + "referencedDeclaration": 4, + "src": "72:1:1" }, "nodeType": "UsingForDirective", - "src": "26:17:1", - "typeName": - { - "id": 3, - "name": "uint", - "nodeType": "ElementaryTypeName", - "src": "38:4:1", - "typeDescriptions": - { - "typeIdentifier": "t_uint256", - "typeString": "uint256" - } - } + "src": "66:14:1" } ], - "scope": 6, - "src": "13:32:1", + "scope": 14, + "src": "53:29:1", "usedErrors": [] } ], - "src": "0:46:1" + "src": "0:83:1" } diff --git a/test/libsolidity/ASTJSON/using_for_directive.sol b/test/libsolidity/ASTJSON/using_for_directive.sol index be6e5288f..30e40252a 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.sol +++ b/test/libsolidity/ASTJSON/using_for_directive.sol @@ -1,3 +1,6 @@ -library L {} contract C { using L for uint; } +using {f} for uint; +library L {} +function f(uint) {} +contract C { using L for *; } // ---- diff --git a/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json b/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json index b4b016ac1..dcc5ce8d1 100644 --- a/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json +++ b/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json @@ -1,57 +1,131 @@ { "absolutePath": "a", - "id": 6, + "id": 14, "nodeType": "SourceUnit", "nodes": [ + { + "functionList": + [ + { + "function": + { + "id": 1, + "name": "f", + "nodeType": "IdentifierPath", + "src": "7:1:1" + } + } + ], + "id": 3, + "nodeType": "UsingForDirective", + "src": "0:19:1", + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "14:4:1", + "typeDescriptions": {} + } + }, { "abstract": false, "baseContracts": [], "contractDependencies": [], "contractKind": "library", - "id": 1, + "id": 4, "name": "L", - "nameLocation": "8:1:1", + "nameLocation": "28:1:1", "nodeType": "ContractDefinition", "nodes": [], - "src": "0:12:1", + "src": "20:12:1", "usedErrors": [] }, + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "50:2:1", + "statements": [] + }, + "id": 10, + "implemented": true, + "kind": "freeFunction", + "modifiers": [], + "name": "f", + "nameLocation": "42:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "src": "44:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 5, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "44:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "43:6:1" + }, + "returnParameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "50:0:1" + }, + "src": "33:19:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + }, { "abstract": false, "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "id": 5, + "id": 13, "name": "C", - "nameLocation": "22:1:1", + "nameLocation": "62:1:1", "nodeType": "ContractDefinition", "nodes": [ { - "id": 4, + "id": 12, "libraryName": { - "id": 2, + "id": 11, "name": "L", "nodeType": "IdentifierPath", - "src": "32:1:1" + "src": "72:1:1" }, "nodeType": "UsingForDirective", - "src": "26:17:1", - "typeName": - { - "id": 3, - "name": "uint", - "nodeType": "ElementaryTypeName", - "src": "38:4:1", - "typeDescriptions": {} - } + "src": "66:14:1" } ], - "src": "13:32:1", + "src": "53:29:1", "usedErrors": [] } ], - "src": "0:46:1" + "src": "0:83:1" } diff --git a/test/libsolidity/semanticTests/using/calldata_memory_copy.sol b/test/libsolidity/semanticTests/using/calldata_memory_copy.sol new file mode 100644 index 000000000..493cfc29b --- /dev/null +++ b/test/libsolidity/semanticTests/using/calldata_memory_copy.sol @@ -0,0 +1,19 @@ + +contract C { + function f(uint[] calldata arr) external returns (uint) { + return arr.sum(); + } +} + +function sum(uint[] memory arr) returns (uint result) { + for(uint i = 0; i < arr.length; i++) { + result += arr[i]; + } +} + +using {sum} for uint[]; + +// ==== +// compileViaYul: also +// ---- +// f(uint256[]): 0x20, 3, 1, 2, 8 -> 11 diff --git a/test/libsolidity/semanticTests/using/free_function_braces.sol b/test/libsolidity/semanticTests/using/free_function_braces.sol new file mode 100644 index 000000000..e522ff61b --- /dev/null +++ b/test/libsolidity/semanticTests/using/free_function_braces.sol @@ -0,0 +1,26 @@ +function id(uint x) pure returns (uint) { + return x; +} + +function zero(uint) pure returns (uint) { + return 0; +} + +contract C { + function f(uint z) pure external returns(uint) { + return z.id(); + } + + function g(uint z) pure external returns (uint) { + return z.zero(); + } + + using {id, zero} for uint; +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 10 -> 10 +// g(uint256): 10 -> 0 +// f(uint256): 256 -> 0x0100 +// g(uint256): 256 -> 0 diff --git a/test/libsolidity/semanticTests/using/free_function_multi.sol b/test/libsolidity/semanticTests/using/free_function_multi.sol new file mode 100644 index 000000000..bf71e4d44 --- /dev/null +++ b/test/libsolidity/semanticTests/using/free_function_multi.sol @@ -0,0 +1,27 @@ +contract C { + function f(uint z) pure external returns(uint) { + return z.id(); + } + + using {id, zero, zero, id} for uint; + + function g(uint z) pure external returns (uint) { + return z.zero(); + } +} + +function id(uint x) pure returns (uint) { + return x; +} + +function zero(uint) pure returns (uint) { + return 0; +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 10 -> 10 +// g(uint256): 10 -> 0 +// f(uint256): 256 -> 0x0100 +// g(uint256): 256 -> 0 diff --git a/test/libsolidity/semanticTests/using/free_functions_individual.sol b/test/libsolidity/semanticTests/using/free_functions_individual.sol new file mode 100644 index 000000000..334b427e1 --- /dev/null +++ b/test/libsolidity/semanticTests/using/free_functions_individual.sol @@ -0,0 +1,30 @@ +using {zero} for uint; + +contract C { + using {id} for uint; + + function f(uint z) pure external returns(uint) { + return z.id(); + } + + function g(uint z) pure external returns (uint) { + return z.zero(); + } +} + +function id(uint x) pure returns (uint) { + return x; +} + +function zero(uint) pure returns (uint) { + return 0; +} + + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 10 -> 10 +// g(uint256): 10 -> 0 +// f(uint256): 256 -> 0x0100 +// g(uint256): 256 -> 0 diff --git a/test/libsolidity/semanticTests/using/imported_functions.sol b/test/libsolidity/semanticTests/using/imported_functions.sol new file mode 100644 index 000000000..d99e558fd --- /dev/null +++ b/test/libsolidity/semanticTests/using/imported_functions.sol @@ -0,0 +1,19 @@ +==== Source: A ==== +function inc(uint x) pure returns (uint) { + return x + 1; +} + +==== Source: B ==== +contract C { + function f(uint x) public returns (uint) { + return x.f() + x.inc(); + } +} +using {A.inc, f} for uint; +import {inc as f} from "A"; +import "A" as A; +// ==== +// compileViaYul: also +// ---- +// f(uint256): 5 -> 12 +// f(uint256): 10 -> 0x16 diff --git a/test/libsolidity/semanticTests/using/library_through_module.sol b/test/libsolidity/semanticTests/using/library_through_module.sol new file mode 100644 index 000000000..6102f5a70 --- /dev/null +++ b/test/libsolidity/semanticTests/using/library_through_module.sol @@ -0,0 +1,34 @@ +==== Source: A ==== +library L { + function id(uint x) internal pure returns (uint) { + return x; + } + function one_ext(uint) pure external returns(uint) { + return 1; + } + function empty() pure internal { + } + +} + +==== Source: B ==== +contract C { + using M.L for uint; + function f(uint x) public pure returns (uint) { + return x.id(); + } + function g(uint x) public pure returns (uint) { + return x.one_ext(); + } +} + +import "A" as M; + +// ==== +// compileViaYul: also +// ---- +// library: "A":L +// f(uint256): 5 -> 5 +// f(uint256): 10 -> 10 +// g(uint256): 5 -> 1 +// g(uint256): 10 -> 1 diff --git a/test/libsolidity/semanticTests/using/module_renamed.sol b/test/libsolidity/semanticTests/using/module_renamed.sol new file mode 100644 index 000000000..d648e88d1 --- /dev/null +++ b/test/libsolidity/semanticTests/using/module_renamed.sol @@ -0,0 +1,26 @@ +==== Source: A ==== +function f(uint x) pure returns (uint) { + return x + 2; +} +function g(uint x) pure returns (uint) { + return x + 8; +} + +==== Source: B ==== +import {f as g, g as f} from "A"; + +==== Source: C ==== +contract C { + function test(uint x, uint y) public pure returns (uint, uint) { + return (x.f(), y.g()); + } +} + +using {M.g, M.f} for uint; + +import "B" as M; + +// ==== +// compileViaYul: also +// ---- +// test(uint256,uint256): 1, 1 -> 9, 3 diff --git a/test/libsolidity/syntaxTests/bound/bound_to_struct.sol b/test/libsolidity/syntaxTests/bound/bound_to_struct.sol index 0e1cbddb8..59c8e2aa7 100644 --- a/test/libsolidity/syntaxTests/bound/bound_to_struct.sol +++ b/test/libsolidity/syntaxTests/bound/bound_to_struct.sol @@ -7,4 +7,4 @@ contract C { using S for S; } // ---- -// TypeError 4357: (113-114): Library name expected. +// TypeError 4357: (113-114): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/bound/interface_using_for.sol b/test/libsolidity/syntaxTests/bound/interface_using_for.sol index 727ff2c46..415971017 100644 --- a/test/libsolidity/syntaxTests/bound/interface_using_for.sol +++ b/test/libsolidity/syntaxTests/bound/interface_using_for.sol @@ -7,4 +7,4 @@ interface I { function g() external; } // ---- -// TypeError 9088: (60-76): The "using for" directive is not allowed inside interfaces. +// SyntaxError 9088: (60-76): The "using for" directive is not allowed inside interfaces. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol index 83839f368..42de5ba41 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol @@ -3,4 +3,4 @@ contract C { using D for uint; } // ---- -// TypeError 4357: (38-39): Library name expected. +// TypeError 4357: (38-39): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol index ba141ce97..940a3b071 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol @@ -7,4 +7,4 @@ library L { } } // ---- -// TypeError 4357: (120-121): Library name expected. +// TypeError 4357: (120-121): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/double_asterisk.sol b/test/libsolidity/syntaxTests/using/double_asterisk.sol new file mode 100644 index 000000000..44cee5479 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/double_asterisk.sol @@ -0,0 +1,19 @@ +function id(uint x) pure returns (uint) { + return x; +} + +function zero(address) pure returns (address) { + return address(0); +} + +contract C { + using * for *; + function f(uint x) pure external returns (uint) { + return x.id(); + } + function g(address a) pure external returns (address) { + return a.zero(); + } +} +// ---- +// ParserError 2314: (156-157): Expected identifier but got '*' diff --git a/test/libsolidity/syntaxTests/using/file_level_inactive_after_import.sol b/test/libsolidity/syntaxTests/using/file_level_inactive_after_import.sol new file mode 100644 index 000000000..219954238 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/file_level_inactive_after_import.sol @@ -0,0 +1,22 @@ +==== Source: A ==== +function id(uint x) pure returns (uint) { + return x; +} +// we check that the effect of this directive is +// limited to this file. +using {id} for uint; + +function t() pure { + uint y = 2; + y = y.id(); +} + +==== Source: B ==== +import "A"; + +function f() pure { + uint y = 2; + y = y.id(); +} +// ---- +// TypeError 9582: (B:57-61): Member "id" not found or not visible after argument-dependent lookup in uint256. diff --git a/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion.sol b/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion.sol new file mode 100644 index 000000000..9df5e9c2c --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion.sol @@ -0,0 +1,6 @@ +function id(uint16 x) pure returns(uint16) { + return x; +} +contract C { + using {id} for uint8; +} diff --git a/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion_err.sol b/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion_err.sol new file mode 100644 index 000000000..36f3f3032 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_functions_implicit_conversion_err.sol @@ -0,0 +1,8 @@ +function id(uint16 x) pure returns(uint16) { + return x; +} +contract C { + using {id} for uint256; +} +// ---- +// TypeError 3100: (85-87): The function "id" cannot be bound to the type "uint256" because the type cannot be implicitly converted to the first argument of the function ("uint16"). diff --git a/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol b/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol new file mode 100644 index 000000000..7536dff07 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol @@ -0,0 +1,12 @@ +function id(int8 x) pure returns(int8) { + return x; +} +function id(uint256 x) pure returns(uint256) { + return x; +} + +contract C { + using {id} for uint256; +} +// ---- +// DeclarationError 7920: (145-147): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_overloads.sol b/test/libsolidity/syntaxTests/using/free_overloads.sol new file mode 100644 index 000000000..28a8e69ed --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_overloads.sol @@ -0,0 +1,10 @@ +function f(uint8 x) pure returns (uint) { + return x; +} +function f(int8 storage x) pure returns (int) { + return x[0]; +} +using {f} for uint8; +using {f} for int; +// ---- +// DeclarationError 7920: (132-133): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_overloads_array.sol b/test/libsolidity/syntaxTests/using/free_overloads_array.sol new file mode 100644 index 000000000..91b807397 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_overloads_array.sol @@ -0,0 +1,9 @@ +function f(uint x, uint[] y) pure returns (uint) { + return x; +} +function f(uint x, uint y) pure returns (int) { + return x; +} +using {f} for uint; +// ---- +// DeclarationError 7920: (138-139): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_reference_type.sol b/test/libsolidity/syntaxTests/using/free_reference_type.sol new file mode 100644 index 000000000..833b78266 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/free_reference_type.sol @@ -0,0 +1,11 @@ +function f(uint[] memory x) pure returns (uint) { + return x[0]; +} +function g(uint[] storage x) view returns (uint) { + return x[0]; +} +function h(uint[] calldata x) pure returns (uint) { + return x[0]; +} +using {f, g, h} for uint[]; +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_import_as.sol b/test/libsolidity/syntaxTests/using/library_import_as.sol new file mode 100644 index 000000000..7c3fc2891 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_import_as.sol @@ -0,0 +1,16 @@ +==== Source: A ==== +library L { + function id(uint x) pure internal returns (uint) { + return x; + } +} + +==== Source: B ==== +import {L as M} from "A"; + +contract C { + using M for uint; + function f(uint x) public pure returns (uint) { + return x.id(); + } +} diff --git a/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol b/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol new file mode 100644 index 000000000..76d53cf90 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol @@ -0,0 +1,14 @@ +library L { + function id_ext(uint x) external returns(uint) { + return x; + } +} + +contract C { + using L.id_ext for uint; + function f(uint x) external { + x.id_ext(); + } +} +// ---- +// DeclarationError 7920: (115-123): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/using/library_non_free_function.sol b/test/libsolidity/syntaxTests/using/library_non_free_function.sol new file mode 100644 index 000000000..2fdb471ab --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_non_free_function.sol @@ -0,0 +1,13 @@ +library L { + function id(uint x) internal pure returns(uint) { + return x; + } +} + +contract C { + using {L.id} for uint; + function f(uint x) external pure { + x.id(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/module_1.sol b/test/libsolidity/syntaxTests/using/module_1.sol new file mode 100644 index 000000000..a2d3b9f99 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/module_1.sol @@ -0,0 +1,12 @@ +==== Source: A ==== +function id(uint x) pure returns (uint) { + return x; +} + +==== Source: B ==== +import "A" as M; +contract C { + using M for uint; +} +// ---- +// TypeError 4357: (B:40-41): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/module_2.sol b/test/libsolidity/syntaxTests/using/module_2.sol new file mode 100644 index 000000000..924208222 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/module_2.sol @@ -0,0 +1,14 @@ +==== Source: A ==== +function id(uint x) pure returns (uint) { + return x; +} + +==== Source: B ==== +import {id as Id} from "A"; + +contract C { + using { Id } for uint; + function f(uint x) public pure returns (uint) { + return x.Id(); + } +} diff --git a/test/libsolidity/syntaxTests/using/module_3.sol b/test/libsolidity/syntaxTests/using/module_3.sol new file mode 100644 index 000000000..f017363e6 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/module_3.sol @@ -0,0 +1,14 @@ +==== Source: A ==== +function id(uint x) pure returns (uint) { + return x; +} + +==== Source: B ==== +import "A" as M; + +contract C { + using {M.id} for uint; + function f(uint x) public pure returns (uint) { + return x.id(); + } +} diff --git a/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol b/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol new file mode 100644 index 000000000..ef1688b22 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol @@ -0,0 +1,13 @@ +==== Source: A ==== +function id(uint x) pure returns (uint) { + return x; +} + +==== Source: B ==== +import "A" as M; + +contract C { + using { id } for uint; +} +// ---- +// DeclarationError 7920: (B:43-45): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/using/using_contract_err.sol b/test/libsolidity/syntaxTests/using/using_contract_err.sol new file mode 100644 index 000000000..e216e4cfc --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_contract_err.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint) external { + } +} +interface I { + function f(uint) external; +} + +contract Test { + using C for uint; +} +// ---- +// TypeError 4357: (127-128): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/using_empty_list_err.sol b/test/libsolidity/syntaxTests/using/using_empty_list_err.sol new file mode 100644 index 000000000..b8b9cb9a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_empty_list_err.sol @@ -0,0 +1,5 @@ +contract C { + using {} for uint; +} +// ---- +// ParserError 2314: (24-25): Expected identifier but got '}' diff --git a/test/libsolidity/syntaxTests/using/using_empty_list_file_level.sol b/test/libsolidity/syntaxTests/using/using_empty_list_file_level.sol new file mode 100644 index 000000000..b1dc8b706 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_empty_list_file_level.sol @@ -0,0 +1,3 @@ +using {} for uint; +// ---- +// ParserError 2314: (7-8): Expected identifier but got '}' diff --git a/test/libsolidity/syntaxTests/using/using_for_ast_file_level.sol b/test/libsolidity/syntaxTests/using/using_for_ast_file_level.sol new file mode 100644 index 000000000..2b5d6a0dd --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_for_ast_file_level.sol @@ -0,0 +1,7 @@ +function id(uint x) pure returns (uint) { + return x; +} + +using {id} for *; +// ---- +// SyntaxError 8118: (59-76): The type has to be specified explicitly at file level (cannot use '*'). diff --git a/test/libsolidity/syntaxTests/using/using_free_functions.sol b/test/libsolidity/syntaxTests/using/using_free_functions.sol new file mode 100644 index 000000000..5180cfd20 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_free_functions.sol @@ -0,0 +1,16 @@ +function id(uint x) pure returns (uint) { + return x; +} + +function zero(uint) pure returns (uint) { + return 0; +} +using {id} for uint; +contract C { + using {zero} for uint; + + function g(uint z) pure external { + z.zero(); + z.id(); + } +} diff --git a/test/libsolidity/syntaxTests/using/using_free_no_parameters_err.sol b/test/libsolidity/syntaxTests/using/using_free_no_parameters_err.sol new file mode 100644 index 000000000..e03ba5d10 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_free_no_parameters_err.sol @@ -0,0 +1,7 @@ +function one() pure returns(uint) { + return 1; +} + +using {one} for uint; +// ---- +// TypeError 4731: (60-63): The function "one" does not have any parameters, and therefore cannot be bound to the type "uint256". diff --git a/test/libsolidity/syntaxTests/using/using_functions_with_ast.sol b/test/libsolidity/syntaxTests/using/using_functions_with_ast.sol new file mode 100644 index 000000000..62f8e4736 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_functions_with_ast.sol @@ -0,0 +1,7 @@ +function f(uint) {} + +contract C { + using {f} for *; +} +// ---- +// SyntaxError 3349: (38-54): The type has to be specified explicitly when attaching specific functions. diff --git a/test/libsolidity/syntaxTests/using/using_lhs_asterisk.sol b/test/libsolidity/syntaxTests/using/using_lhs_asterisk.sol new file mode 100644 index 000000000..07b607004 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_lhs_asterisk.sol @@ -0,0 +1,3 @@ +using * for uint; +// ---- +// ParserError 2314: (6-7): Expected identifier but got '*' diff --git a/test/libsolidity/syntaxTests/using/using_lhs_asterisk_contract.sol b/test/libsolidity/syntaxTests/using/using_lhs_asterisk_contract.sol new file mode 100644 index 000000000..67eee5862 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_lhs_asterisk_contract.sol @@ -0,0 +1,5 @@ +contract C { + using * for uint; +} +// ---- +// ParserError 2314: (23-24): Expected identifier but got '*' diff --git a/test/libsolidity/syntaxTests/using/using_library_ast_file_level.sol b/test/libsolidity/syntaxTests/using/using_library_ast_file_level.sol new file mode 100644 index 000000000..dfc0b916e --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_library_ast_file_level.sol @@ -0,0 +1,4 @@ +library L { } +using L for *; +// ---- +// SyntaxError 8118: (14-28): The type has to be specified explicitly at file level (cannot use '*'). diff --git a/test/libsolidity/syntaxTests/using/using_library_file_level.sol b/test/libsolidity/syntaxTests/using/using_library_file_level.sol new file mode 100644 index 000000000..c4de282c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_library_file_level.sol @@ -0,0 +1,3 @@ +library L { } +using L for uint; +// ---- diff --git a/test/libsolidity/syntaxTests/using/using_non_free_function_err.sol b/test/libsolidity/syntaxTests/using/using_non_free_function_err.sol new file mode 100644 index 000000000..df3b3f65f --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_non_free_function_err.sol @@ -0,0 +1,9 @@ +contract C { + using {f, g} for uint; + + function f(uint) internal { } + function g(uint) public { } +} +// ---- +// TypeError 4167: (24-25): Only file-level functions and library functions can be bound to a type in a "using" statement +// TypeError 4167: (27-28): Only file-level functions and library functions can be bound to a type in a "using" statement diff --git a/test/libsolidity/syntaxTests/using/using_non_function.sol b/test/libsolidity/syntaxTests/using/using_non_function.sol new file mode 100644 index 000000000..32d97345b --- /dev/null +++ b/test/libsolidity/syntaxTests/using/using_non_function.sol @@ -0,0 +1,6 @@ +contract C { + function() internal pure x; + using {x} for uint; +} +// ---- +// TypeError 8187: (56-57): Expected function name.