mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Using for for operators.
This commit is contained in:
@@ -895,6 +895,11 @@ MemberAccessAnnotation& MemberAccess::annotation() const
|
||||
return initAnnotation<MemberAccessAnnotation>();
|
||||
}
|
||||
|
||||
OperationAnnotation& UnaryOperation::annotation() const
|
||||
{
|
||||
return initAnnotation<OperationAnnotation>();
|
||||
}
|
||||
|
||||
BinaryOperationAnnotation& BinaryOperation::annotation() const
|
||||
{
|
||||
return initAnnotation<BinaryOperationAnnotation>();
|
||||
|
||||
+10
-1
@@ -38,6 +38,7 @@
|
||||
#include <json/json.h>
|
||||
|
||||
#include <range/v3/view/subrange.hpp>
|
||||
#include <range/v3/view/zip.hpp>
|
||||
#include <range/v3/view/map.hpp>
|
||||
|
||||
#include <memory>
|
||||
@@ -664,16 +665,19 @@ public:
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
std::vector<ASTPointer<IdentifierPath>> _functions,
|
||||
std::vector<std::optional<Token>> _operators,
|
||||
bool _usesBraces,
|
||||
ASTPointer<TypeName> _typeName,
|
||||
bool _global
|
||||
):
|
||||
ASTNode(_id, _location),
|
||||
m_functions(_functions),
|
||||
m_functions(std::move(_functions)),
|
||||
m_operators(std::move(_operators)),
|
||||
m_usesBraces(_usesBraces),
|
||||
m_typeName(std::move(_typeName)),
|
||||
m_global{_global}
|
||||
{
|
||||
solAssert(m_functions.size() == m_operators.size());
|
||||
}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
@@ -684,12 +688,15 @@ public:
|
||||
|
||||
/// @returns a list of functions or the single library.
|
||||
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
|
||||
auto functionsAndOperators() const { return ranges::zip_view(m_functions, m_operators); }
|
||||
bool usesBraces() const { return m_usesBraces; }
|
||||
bool global() const { return m_global; }
|
||||
|
||||
private:
|
||||
/// Either the single library or a list of functions.
|
||||
std::vector<ASTPointer<IdentifierPath>> m_functions;
|
||||
/// Operators, the functions are applied to.
|
||||
std::vector<std::optional<Token>> m_operators;
|
||||
bool m_usesBraces;
|
||||
ASTPointer<TypeName> m_typeName;
|
||||
bool m_global = false;
|
||||
@@ -2055,6 +2062,8 @@ public:
|
||||
bool isPrefixOperation() const { return m_isPrefix; }
|
||||
Expression const& subExpression() const { return *m_subExpression; }
|
||||
|
||||
OperationAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
Token m_operator;
|
||||
ASTPointer<Expression> m_subExpression;
|
||||
|
||||
@@ -312,7 +312,13 @@ struct MemberAccessAnnotation: ExpressionAnnotation
|
||||
util::SetOnce<VirtualLookup> requiredLookup;
|
||||
};
|
||||
|
||||
struct BinaryOperationAnnotation: ExpressionAnnotation
|
||||
struct OperationAnnotation: ExpressionAnnotation
|
||||
{
|
||||
// TODO should this be more like "referencedDeclaration"?
|
||||
FunctionDefinition const* userDefinedFunction = nullptr;
|
||||
};
|
||||
|
||||
struct BinaryOperationAnnotation: OperationAnnotation
|
||||
{
|
||||
/// The common type that is used for the operation, not necessarily the result type (which
|
||||
/// e.g. for comparisons is bool).
|
||||
|
||||
@@ -329,14 +329,17 @@ bool ASTJsonExporter::visit(UsingForDirective const& _node)
|
||||
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())
|
||||
for (auto&& [function, op]: _node.functionsAndOperators())
|
||||
{
|
||||
Json::Value functionNode;
|
||||
functionNode["function"] = toJson(*function);
|
||||
functionList.append(std::move(functionNode));
|
||||
if (op)
|
||||
functionNode["operator"] = string(TokenTraits::toString(*op));
|
||||
functionList.append(move(functionNode));
|
||||
}
|
||||
attributes.emplace_back("functionList", std::move(functionList));
|
||||
}
|
||||
@@ -825,6 +828,8 @@ bool ASTJsonExporter::visit(UnaryOperation const& _node)
|
||||
make_pair("operator", TokenTraits::toString(_node.getOperator())),
|
||||
make_pair("subExpression", toJson(_node.subExpression()))
|
||||
};
|
||||
if (FunctionDefinition const* function = _node.annotation().userDefinedFunction)
|
||||
attributes.emplace_back("function", nodeId(*function));
|
||||
appendExpressionAttributes(attributes, _node.annotation());
|
||||
setJsonNode(_node, "UnaryOperation", std::move(attributes));
|
||||
return false;
|
||||
@@ -838,6 +843,8 @@ bool ASTJsonExporter::visit(BinaryOperation const& _node)
|
||||
make_pair("rightExpression", toJson(_node.rightExpression())),
|
||||
make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
|
||||
};
|
||||
if (FunctionDefinition const* function = _node.annotation().userDefinedFunction)
|
||||
attributes.emplace_back("function", nodeId(*function));
|
||||
appendExpressionAttributes(attributes, _node.annotation());
|
||||
setJsonNode(_node, "BinaryOperation", std::move(attributes));
|
||||
return false;
|
||||
|
||||
@@ -383,15 +383,29 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
|
||||
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
|
||||
{
|
||||
vector<ASTPointer<IdentifierPath>> functions;
|
||||
vector<optional<Token>> operators;
|
||||
if (_node.isMember("libraryName"))
|
||||
{
|
||||
solAssert(!_node["libraryName"].isArray());
|
||||
solAssert(!_node["libraryName"]["operator"]);
|
||||
functions.emplace_back(createIdentifierPath(_node["libraryName"]));
|
||||
operators.emplace_back();
|
||||
}
|
||||
else if (_node.isMember("functionList"))
|
||||
for (Json::Value const& function: _node["functionList"])
|
||||
{
|
||||
functions.emplace_back(createIdentifierPath(function["function"]));
|
||||
operators.emplace_back(
|
||||
function.isMember("operator") ?
|
||||
optional<Token>{scanSingleToken(function["operator"])} :
|
||||
nullopt
|
||||
);
|
||||
}
|
||||
|
||||
return createASTNode<UsingForDirective>(
|
||||
_node,
|
||||
std::move(functions),
|
||||
move(operators),
|
||||
!_node.isMember("libraryName"),
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
|
||||
memberAsBool(_node, "global")
|
||||
|
||||
@@ -194,7 +194,7 @@ void UsingForDirective::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
listAccept(functionsOrLibrary(), _visitor);
|
||||
listAccept(m_functions, _visitor);
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ void UsingForDirective::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
listAccept(functionsOrLibrary(), _visitor);
|
||||
listAccept(m_functions, _visitor);
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
}
|
||||
|
||||
+61
-20
@@ -48,6 +48,7 @@
|
||||
#include <range/v3/view/reverse.hpp>
|
||||
#include <range/v3/view/tail.hpp>
|
||||
#include <range/v3/view/transform.hpp>
|
||||
#include <range/v3/view/filter.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
@@ -337,7 +338,10 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
|
||||
namespace
|
||||
{
|
||||
|
||||
vector<UsingForDirective const*> usingForDirectivesForType(Type const& _type, ASTNode const& _scope)
|
||||
{
|
||||
vector<UsingForDirective const*> usingForDirectives;
|
||||
SourceUnit const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope);
|
||||
@@ -362,6 +366,57 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||
typeLocation = refType->location();
|
||||
|
||||
return usingForDirectives | ranges::views::filter([&](UsingForDirective const* _directive) -> bool {
|
||||
// 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.
|
||||
return
|
||||
!_directive->typeName() ||
|
||||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) ==
|
||||
*TypeProvider::withLocationIfReference(
|
||||
typeLocation,
|
||||
_directive->typeName()->annotation().type,
|
||||
true
|
||||
);
|
||||
}) | ranges::to<vector<UsingForDirective const*>>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FunctionDefinition const* Type::userDefinedOperator(Token _token, ASTNode const& _scope) const
|
||||
{
|
||||
// Check if it is a user-defined type.
|
||||
if (!typeDefinition())
|
||||
return nullptr;
|
||||
|
||||
set<FunctionDefinition const*> seenFunctions;
|
||||
for (UsingForDirective const* ufd: usingForDirectivesForType(*this, _scope))
|
||||
for (auto const& [pathPointer, operator_]: ufd->functionsAndOperators())
|
||||
{
|
||||
if (operator_ != _token)
|
||||
continue;
|
||||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||
*pathPointer->annotation().referencedDeclaration
|
||||
);
|
||||
FunctionType const* functionType = dynamic_cast<FunctionType const*>(
|
||||
function.libraryFunction() ? function.typeViaContractName() : function.type()
|
||||
);
|
||||
solAssert(functionType && !functionType->parameterTypes().empty());
|
||||
// TODO does this work (data location)?
|
||||
solAssert(isImplicitlyConvertibleTo(*functionType->parameterTypes().front()));
|
||||
seenFunctions.insert(&function);
|
||||
}
|
||||
// TODO proper error handling.
|
||||
if (seenFunctions.size() == 1)
|
||||
return *seenFunctions.begin();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
|
||||
set<pair<string, Declaration const*>> seenFunctions;
|
||||
@@ -381,25 +436,12 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
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() &&
|
||||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
|
||||
*TypeProvider::withLocationIfReference(
|
||||
typeLocation,
|
||||
ufd->typeName()->annotation().type,
|
||||
true
|
||||
)
|
||||
)
|
||||
continue;
|
||||
|
||||
for (auto const& pathPointer: ufd->functionsOrLibrary())
|
||||
for (UsingForDirective const* ufd: usingForDirectivesForType(_type, _scope))
|
||||
for (auto const& [pathPointer, operator_]: ufd->functionsAndOperators())
|
||||
{
|
||||
if (operator_)
|
||||
continue;
|
||||
|
||||
solAssert(pathPointer);
|
||||
Declaration const* declaration = pathPointer->annotation().referencedDeclaration;
|
||||
solAssert(declaration);
|
||||
@@ -420,7 +462,6 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
pathPointer->path().back()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
@@ -377,6 +377,8 @@ public:
|
||||
/// Clears all internally cached values (if any).
|
||||
virtual void clearCache() const;
|
||||
|
||||
FunctionDefinition const* userDefinedOperator(Token _token, ASTNode const& _scope) const;
|
||||
|
||||
private:
|
||||
/// @returns a member list containing all members added to this type by `using for` directives.
|
||||
static MemberList::MemberMap boundFunctions(Type const& _type, ASTNode const& _scope);
|
||||
|
||||
Reference in New Issue
Block a user