Using for for operators.

This commit is contained in:
chriseth
2022-09-28 11:32:03 +02:00
committed by wechman
parent 2201526a90
commit 3bd047f188
28 changed files with 607 additions and 93 deletions
+5
View File
@@ -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
View File
@@ -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;
+7 -1
View File
@@ -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).
+9 -2
View File
@@ -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;
+14
View File
@@ -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")
+2 -2
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -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);