mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12121 from ethereum/extend-using-statement
Extend using statement
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
@@ -451,12 +452,39 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
|
||||
bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor)
|
||||
{
|
||||
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
|
||||
_usingFor.libraryName().annotation().referencedDeclaration
|
||||
);
|
||||
if (_usingFor.usesBraces())
|
||||
{
|
||||
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())
|
||||
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);
|
||||
|
||||
@@ -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), "");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/Views.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -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<ContractDefinition const*>(
|
||||
_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)
|
||||
|
||||
+21
-8
@@ -33,6 +33,7 @@
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
#include <libsolutil/LazyInit.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
@@ -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<IdentifierPath> _libraryName,
|
||||
std::vector<ASTPointer<IdentifierPath>> _functions,
|
||||
bool _usesBraces,
|
||||
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(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<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
|
||||
bool usesBraces() const { return m_usesBraces; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <libsolutil/JSON.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@@ -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<pair<string, Json::Value>> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -348,9 +348,17 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
|
||||
|
||||
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>(
|
||||
_node,
|
||||
createIdentifierPath(member(_node, "libraryName")),
|
||||
move(functions),
|
||||
!_node.isMember("libraryName"),
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+49
-22
@@ -35,6 +35,7 @@
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/algorithm/string.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)
|
||||
{
|
||||
vector<UsingForDirective const*> usingForDirectives;
|
||||
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope))
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
|
||||
usingForDirectives +=
|
||||
contract->usingForDirectives() +
|
||||
ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
|
||||
SourceUnit const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope);
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
|
||||
{
|
||||
sourceUnit = &contract->sourceUnit();
|
||||
usingForDirectives += contract->usingForDirectives();
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
solAssert(sourceUnit, "");
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
|
||||
// Normalise data location of type.
|
||||
DataLocation typeLocation = DataLocation::Storage;
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||
typeLocation = refType->location();
|
||||
|
||||
set<Declaration const*> seenFunctions;
|
||||
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)
|
||||
{
|
||||
// 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<ContractDefinition const&>(
|
||||
*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<FunctionType const&>(*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<ContractDefinition const*>(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<FunctionDefinition const&>(*declaration),
|
||||
pathPointer->path().back()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ ASTPointer<SourceUnit> 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<UsingForDirective> Parser::parseUsingDirective()
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
|
||||
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;
|
||||
expectToken(Token::For);
|
||||
if (m_scanner->currentToken() == Token::Mul)
|
||||
@@ -971,7 +989,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
|
||||
typeName = parseTypeName();
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<UsingForDirective>(library, typeName);
|
||||
return nodeFactory.createNode<UsingForDirective>(move(functions), usesBraces, typeName);
|
||||
}
|
||||
|
||||
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
||||
|
||||
Reference in New Issue
Block a user