Merge pull request #12121 from ethereum/extend-using-statement

Extend using statement
This commit is contained in:
chriseth 2022-03-14 14:10:16 +01:00 committed by GitHub
commit 3f6beaa0ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 992 additions and 150 deletions

View File

@ -2,6 +2,7 @@
Language Features: 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: 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: Compiler Features:

View File

@ -6,71 +6,89 @@
Using For Using For
********* *********
The directive ``using A for B;`` can be used to attach library The directive ``using A for B;`` can be used to attach
functions (from the library ``A``) to any type (``B``) functions (``A``) as member functions to any type (``B``).
in the context of a contract.
These functions will receive the object they are called on These functions will receive the object they are called on
as their first parameter (like the ``self`` variable in Python). as their first parameter (like the ``self`` variable in Python).
The effect of ``using A for *;`` is that the functions from It is valid either at file level or inside a contract,
the library ``A`` are attached to *any* type. 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 even those where the type of the first parameter does not
match the type of the object. The type is checked at the match the type of the object. The type is checked at the
point the function is called and function overload point the function is called and function overload
resolution is performed. 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 The ``using A for B;`` directive is active only within the current
contract, including within all of its functions, and has no effect scope (either the contract or the current module/source unit),
outside of the contract in which it is used. The directive including within all of its functions, and has no effect
may only be used inside a contract, not inside any of its functions. outside of the contract or module in which it is used.
Let us rewrite the set example from the 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 .. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0 // 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; } 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)
function insert(Data storage self, uint value) returns (bool)
public {
returns (bool) if (self.flags[value])
{ return false; // already there
if (self.flags[value]) self.flags[value] = true;
return false; // already there return true;
self.flags[value] = true; }
return true;
}
function remove(Data storage self, uint value) function remove(Data storage self, uint value)
public returns (bool)
returns (bool) {
{ if (!self.flags[value])
if (!self.flags[value]) return false; // not there
return false; // not there self.flags[value] = false;
self.flags[value] = false; return true;
return true; }
}
function contains(Data storage self, uint value) function contains(Data storage self, uint value)
public public
view view
returns (bool) returns (bool)
{ {
return self.flags[value]; return self.flags[value];
}
} }
contract C { contract C {
using Set for Data; // this is the crucial change
Data knownValues; Data knownValues;
function register(uint value) public { 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 .. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.8 <0.9.0; pragma solidity ^0.8.13;
library Search { library Search {
function indexOf(uint[] storage self, uint value) 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; return type(uint).max;
} }
} }
using Search for uint[];
contract C { contract C {
using Search for uint[];
uint[] data; uint[] data;
function append(uint value) public { function append(uint value) public {

View File

@ -12,6 +12,7 @@ options { tokenVocab=SolidityLexer; }
sourceUnit: ( sourceUnit: (
pragmaDirective pragmaDirective
| importDirective | importDirective
| usingDirective
| contractDefinition | contractDefinition
| interfaceDefinition | interfaceDefinition
| libraryDefinition | libraryDefinition
@ -311,10 +312,10 @@ errorDefinition:
Semicolon; Semicolon;
/** /**
* Using directive to bind library functions to types. * Using directive to bind library functions and free functions to types.
* Can occur within contracts and libraries. * 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 * 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. * (e.g. a contract or struct) or an array type.

View File

@ -25,6 +25,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <libsolutil/Algorithms.h> #include <libsolutil/Algorithms.h>
#include <libsolutil/Visitor.h>
#include <range/v3/view/transform.hpp> #include <range/v3/view/transform.hpp>
@ -451,12 +452,39 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor) bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor)
{ {
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>( if (_usingFor.usesBraces())
_usingFor.libraryName().annotation().referencedDeclaration {
); for (ASTPointer<IdentifierPath> const& function: _usingFor.functionsOrLibrary())
if (auto functionDefinition = dynamic_cast<FunctionDefinition const*>(function->annotation().referencedDeclaration))
{
if (!functionDefinition->isFree() && !(
dynamic_cast<ContractDefinition const*>(functionDefinition->scope()) &&
dynamic_cast<ContractDefinition const*>(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<ContractDefinition const*>(
_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()) // We do not visit _usingFor.functions() because it will lead to an error since
m_errorReporter.fatalTypeError(4357_error, _usingFor.libraryName().location(), "Library name expected."); // library names cannot be mentioned stand-alone.
if (_usingFor.typeName()) if (_usingFor.typeName())
_usingFor.typeName()->accept(*this); _usingFor.typeName()->accept(*this);

View File

@ -403,6 +403,30 @@ void SyntaxChecker::endVisit(ContractDefinition const&)
m_currentContractKind = std::nullopt; 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) bool SyntaxChecker::visit(FunctionDefinition const& _function)
{ {
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");

View File

@ -88,6 +88,9 @@ private:
bool visit(ContractDefinition const& _contract) override; bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override; void endVisit(ContractDefinition const& _contract) override;
bool visit(UsingForDirective const& _usingFor) override;
bool visit(FunctionDefinition const& _function) override; bool visit(FunctionDefinition const& _function) override;
bool visit(FunctionTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override;

View File

@ -35,6 +35,7 @@
#include <libsolutil/Algorithms.h> #include <libsolutil/Algorithms.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <libsolutil/Views.h> #include <libsolutil/Views.h>
#include <libsolutil/Visitor.h>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -3626,12 +3627,67 @@ void TypeChecker::endVisit(Literal const& _literal)
void TypeChecker::endVisit(UsingForDirective const& _usingFor) void TypeChecker::endVisit(UsingForDirective const& _usingFor)
{ {
if (m_currentContract->isInterface()) if (!_usingFor.usesBraces())
m_errorReporter.typeError( {
9088_error, solAssert(_usingFor.functionsOrLibrary().size() == 1);
_usingFor.location(), ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
"The \"using for\" directive is not allowed inside interfaces." _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<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
{
solAssert(path->annotation().referencedDeclaration);
FunctionDefinition const& functionDefinition =
dynamic_cast<FunctionDefinition const&>(*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<FunctionType const&>(*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) void TypeChecker::checkErrorAndEventParameters(CallableDeclaration const& _callable)

View File

@ -33,6 +33,7 @@
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <libsolutil/FixedHash.h> #include <libsolutil/FixedHash.h>
#include <libsolutil/LazyInit.h> #include <libsolutil/LazyInit.h>
#include <libsolutil/Visitor.h>
#include <json/json.h> #include <json/json.h>
@ -630,9 +631,16 @@ private:
}; };
/** /**
* `using LibraryName for uint` will attach all functions from the library LibraryName * Using for directive:
* to `uint` if the first parameter matches the type. `using LibraryName for *` attaches *
* the function to any matching type. * 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 class UsingForDirective: public ASTNode
{ {
@ -640,23 +648,28 @@ public:
UsingForDirective( UsingForDirective(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<IdentifierPath> _libraryName, std::vector<ASTPointer<IdentifierPath>> _functions,
bool _usesBraces,
ASTPointer<TypeName> _typeName ASTPointer<TypeName> _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(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const 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 `*`. /// @returns the type name the library is attached to, null for `*`.
TypeName const* typeName() const { return m_typeName.get(); } TypeName const* typeName() const { return m_typeName.get(); }
/// @returns a list of functions or the single library.
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
bool usesBraces() const { return m_usesBraces; }
private: private:
ASTPointer<IdentifierPath> m_libraryName; /// Either the single library or a list of functions.
std::vector<ASTPointer<IdentifierPath>> m_functions;
bool m_usesBraces;
ASTPointer<TypeName> m_typeName; ASTPointer<TypeName> m_typeName;
}; };

View File

@ -32,6 +32,7 @@
#include <libsolutil/JSON.h> #include <libsolutil/JSON.h>
#include <libsolutil/UTF8.h> #include <libsolutil/UTF8.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
@ -311,10 +312,25 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
bool ASTJsonConverter::visit(UsingForDirective const& _node) bool ASTJsonConverter::visit(UsingForDirective const& _node)
{ {
setJsonNode(_node, "UsingForDirective", { vector<pair<string, Json::Value>> attributes = {
make_pair("libraryName", toJson(_node.libraryName())),
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue) 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; return false;
} }

View File

@ -348,9 +348,17 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node) ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
{ {
vector<ASTPointer<IdentifierPath>> 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<UsingForDirective>( return createASTNode<UsingForDirective>(
_node, _node,
createIdentifierPath(member(_node, "libraryName")), move(functions),
!_node.isMember("libraryName"),
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]) _node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
); );
} }

View File

@ -194,7 +194,7 @@ void UsingForDirective::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_libraryName->accept(_visitor); listAccept(functionsOrLibrary(), _visitor);
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
} }
@ -205,7 +205,7 @@ void UsingForDirective::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_libraryName->accept(_visitor); listAccept(functionsOrLibrary(), _visitor);
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
} }

View File

@ -35,6 +35,7 @@
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <libsolutil/UTF8.h> #include <libsolutil/UTF8.h>
#include <libsolutil/Visitor.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
@ -331,30 +332,48 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope) MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
{ {
vector<UsingForDirective const*> usingForDirectives; vector<UsingForDirective const*> usingForDirectives;
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope)) SourceUnit const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope);
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()); if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope)) {
usingForDirectives += sourceUnit = &contract->sourceUnit();
contract->usingForDirectives() + usingForDirectives += contract->usingForDirectives();
ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes()); }
else else
solAssert(false, ""); solAssert(sourceUnit, "");
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
// Normalise data location of type. // Normalise data location of type.
DataLocation typeLocation = DataLocation::Storage; DataLocation typeLocation = DataLocation::Storage;
if (auto refType = dynamic_cast<ReferenceType const*>(&_type)) if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
typeLocation = refType->location(); typeLocation = refType->location();
set<Declaration const*> seenFunctions;
MemberList::MemberMap members; MemberList::MemberMap members;
set<pair<string, Declaration const*>> seenFunctions;
auto addFunction = [&](FunctionDefinition const& _function, optional<string> _name = {})
{
if (!_name)
_name = _function.name();
Type const* functionType =
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
solAssert(functionType, "");
FunctionType const* asBoundFunction =
dynamic_cast<FunctionType const&>(*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) for (UsingForDirective const* ufd: usingForDirectives)
{ {
// Convert both types to pointers for comparison to see if the `using for` // Convert both types to pointers for comparison to see if the `using for`
// directive applies. // directive applies.
// Further down, we check more detailed for each function if `_type` is // Further down, we check more detailed for each function if `_type` is
// convertible to the function parameter type. // convertible to the function parameter type.
if (ufd->typeName() && if (
ufd->typeName() &&
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) != *TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
*TypeProvider::withLocationIfReference( *TypeProvider::withLocationIfReference(
typeLocation, typeLocation,
@ -363,20 +382,28 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
) )
) )
continue; continue;
auto const& library = dynamic_cast<ContractDefinition const&>(
*ufd->libraryName().annotation().referencedDeclaration for (auto const& pathPointer: ufd->functionsOrLibrary())
);
for (FunctionDefinition const* function: library.definedFunctions())
{ {
if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || seenFunctions.count(function)) solAssert(pathPointer);
continue; Declaration const* declaration = pathPointer->annotation().referencedDeclaration;
seenFunctions.insert(function); solAssert(declaration);
if (function->parameters().empty())
continue; if (ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(declaration))
FunctionTypePointer fun = {
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction(); solAssert(library->isLibrary());
if (_type.isImplicitlyConvertibleTo(*fun->selfType())) for (FunctionDefinition const* function: library->definedFunctions())
members.emplace_back(function, fun); {
if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || function->parameters().empty())
continue;
addFunction(*function);
}
}
else
addFunction(
dynamic_cast<FunctionDefinition const&>(*declaration),
pathPointer->path().back()
);
} }
} }

View File

@ -117,6 +117,9 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
case Token::Type: case Token::Type:
nodes.push_back(parseUserDefinedValueTypeDefinition()); nodes.push_back(parseUserDefinedValueTypeDefinition());
break; break;
case Token::Using:
nodes.push_back(parseUsingDirective());
break;
case Token::Function: case Token::Function:
nodes.push_back(parseFunctionDefinition(true)); nodes.push_back(parseFunctionDefinition(true));
break; break;
@ -962,7 +965,22 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::Using); expectToken(Token::Using);
ASTPointer<IdentifierPath> library(parseIdentifierPath());
vector<ASTPointer<IdentifierPath>> 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> typeName; ASTPointer<TypeName> typeName;
expectToken(Token::For); expectToken(Token::For);
if (m_scanner->currentToken() == Token::Mul) if (m_scanner->currentToken() == Token::Mul)
@ -971,7 +989,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
typeName = parseTypeName(); typeName = parseTypeName();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return nodeFactory.createNode<UsingForDirective>(library, typeName); return nodeFactory.createNode<UsingForDirective>(move(functions), usesBraces, typeName);
} }
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation() ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()

View File

@ -4,17 +4,51 @@
{ {
"C": "C":
[ [
5 13
], ],
"L": "L":
[ [
1 4
],
"f":
[
10
] ]
}, },
"id": 6, "id": 14,
"nodeType": "SourceUnit", "nodeType": "SourceUnit",
"nodes": "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, "abstract": false,
"baseContracts": [], "baseContracts": [],
@ -22,19 +56,86 @@
"contractDependencies": [], "contractDependencies": [],
"contractKind": "library", "contractKind": "library",
"fullyImplemented": true, "fullyImplemented": true,
"id": 1, "id": 4,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
1 4
], ],
"name": "L", "name": "L",
"nameLocation": "8:1:1", "nameLocation": "28:1:1",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": [], "nodes": [],
"scope": 6, "scope": 14,
"src": "0:12:1", "src": "20:12:1",
"usedErrors": [] "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, "abstract": false,
"baseContracts": [], "baseContracts": [],
@ -42,46 +143,34 @@
"contractDependencies": [], "contractDependencies": [],
"contractKind": "contract", "contractKind": "contract",
"fullyImplemented": true, "fullyImplemented": true,
"id": 5, "id": 13,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
5 13
], ],
"name": "C", "name": "C",
"nameLocation": "22:1:1", "nameLocation": "62:1:1",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": "nodes":
[ [
{ {
"id": 4, "id": 12,
"libraryName": "libraryName":
{ {
"id": 2, "id": 11,
"name": "L", "name": "L",
"nodeType": "IdentifierPath", "nodeType": "IdentifierPath",
"referencedDeclaration": 1, "referencedDeclaration": 4,
"src": "32:1:1" "src": "72:1:1"
}, },
"nodeType": "UsingForDirective", "nodeType": "UsingForDirective",
"src": "26:17:1", "src": "66:14:1"
"typeName":
{
"id": 3,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "38:4:1",
"typeDescriptions":
{
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
}
} }
], ],
"scope": 6, "scope": 14,
"src": "13:32:1", "src": "53:29:1",
"usedErrors": [] "usedErrors": []
} }
], ],
"src": "0:46:1" "src": "0:83:1"
} }

View File

@ -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 *; }
// ---- // ----

View File

@ -1,57 +1,131 @@
{ {
"absolutePath": "a", "absolutePath": "a",
"id": 6, "id": 14,
"nodeType": "SourceUnit", "nodeType": "SourceUnit",
"nodes": "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, "abstract": false,
"baseContracts": [], "baseContracts": [],
"contractDependencies": [], "contractDependencies": [],
"contractKind": "library", "contractKind": "library",
"id": 1, "id": 4,
"name": "L", "name": "L",
"nameLocation": "8:1:1", "nameLocation": "28:1:1",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": [], "nodes": [],
"src": "0:12:1", "src": "20:12:1",
"usedErrors": [] "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, "abstract": false,
"baseContracts": [], "baseContracts": [],
"contractDependencies": [], "contractDependencies": [],
"contractKind": "contract", "contractKind": "contract",
"id": 5, "id": 13,
"name": "C", "name": "C",
"nameLocation": "22:1:1", "nameLocation": "62:1:1",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": "nodes":
[ [
{ {
"id": 4, "id": 12,
"libraryName": "libraryName":
{ {
"id": 2, "id": 11,
"name": "L", "name": "L",
"nodeType": "IdentifierPath", "nodeType": "IdentifierPath",
"src": "32:1:1" "src": "72:1:1"
}, },
"nodeType": "UsingForDirective", "nodeType": "UsingForDirective",
"src": "26:17:1", "src": "66:14:1"
"typeName":
{
"id": 3,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "38:4:1",
"typeDescriptions": {}
}
} }
], ],
"src": "13:32:1", "src": "53:29:1",
"usedErrors": [] "usedErrors": []
} }
], ],
"src": "0:46:1" "src": "0:83:1"
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -7,4 +7,4 @@ contract C {
using S for S; 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 '{...}'.

View File

@ -7,4 +7,4 @@ interface I {
function g() external; 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.

View File

@ -3,4 +3,4 @@ contract C {
using D for uint; 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 '{...}'.

View File

@ -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 '{...}'.

View File

@ -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 '*'

View File

@ -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.

View File

@ -0,0 +1,6 @@
function id(uint16 x) pure returns(uint16) {
return x;
}
contract C {
using {id} for uint8;
}

View File

@ -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").

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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[];
// ----

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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();
}
}
// ----

View File

@ -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 '{...}'.

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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 '{...}'.

View File

@ -0,0 +1,5 @@
contract C {
using {} for uint;
}
// ----
// ParserError 2314: (24-25): Expected identifier but got '}'

View File

@ -0,0 +1,3 @@
using {} for uint;
// ----
// ParserError 2314: (7-8): Expected identifier but got '}'

View File

@ -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 '*').

View File

@ -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();
}
}

View File

@ -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".

View File

@ -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.

View File

@ -0,0 +1,3 @@
using * for uint;
// ----
// ParserError 2314: (6-7): Expected identifier but got '*'

View File

@ -0,0 +1,5 @@
contract C {
using * for uint;
}
// ----
// ParserError 2314: (23-24): Expected identifier but got '*'

View File

@ -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 '*').

View File

@ -0,0 +1,3 @@
library L { }
using L for uint;
// ----

View File

@ -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

View File

@ -0,0 +1,6 @@
contract C {
function() internal pure x;
using {x} for uint;
}
// ----
// TypeError 8187: (56-57): Expected function name.