diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index c331f1a4b..32869ba13 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -63,6 +63,7 @@ set(sources ast/CallGraph.cpp ast/CallGraph.h ast/ExperimentalFeatures.h + ast/UserDefinableOperators.h ast/Types.cpp ast/Types.h ast/TypeProvider.cpp diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8619ee6ae..fab1a20f8 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -30,7 +30,9 @@ #include #include +#include #include +#include #include @@ -367,6 +369,11 @@ TypeDeclarationAnnotation& UserDefinedValueTypeDefinition::annotation() const return initAnnotation(); } +std::vector, std::optional>> UsingForDirective::functionsAndOperators() const +{ + return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to; +} + Type const* StructDefinition::type() const { solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker."); @@ -895,6 +902,37 @@ MemberAccessAnnotation& MemberAccess::annotation() const return initAnnotation(); } +OperationAnnotation& UnaryOperation::annotation() const +{ + return initAnnotation(); +} + +FunctionType const* UnaryOperation::userDefinedFunctionType() const +{ + if (*annotation().userDefinedFunction == nullptr) + return nullptr; + + FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction; + return dynamic_cast( + userDefinedFunction->libraryFunction() ? + userDefinedFunction->typeViaContractName() : + userDefinedFunction->type() + ); +} + +FunctionType const* BinaryOperation::userDefinedFunctionType() const +{ + if (*annotation().userDefinedFunction == nullptr) + return nullptr; + + FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction; + return dynamic_cast( + userDefinedFunction->libraryFunction() ? + userDefinedFunction->typeViaContractName() : + userDefinedFunction->type() + ); +} + BinaryOperationAnnotation& BinaryOperation::annotation() const { return initAnnotation(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 06575beec..80fd68e0e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -663,16 +663,19 @@ public: int64_t _id, SourceLocation const& _location, std::vector> _functionsOrLibrary, + std::vector> _operators, bool _usesBraces, ASTPointer _typeName, bool _global ): ASTNode(_id, _location), m_functionsOrLibrary(std::move(_functionsOrLibrary)), + m_operators(std::move(_operators)), m_usesBraces(_usesBraces), m_typeName(std::move(_typeName)), m_global{_global} { + solAssert(m_functionsOrLibrary.size() == m_operators.size()); } void accept(ASTVisitor& _visitor) override; @@ -683,12 +686,18 @@ public: /// @returns a list of functions or the single library. std::vector> const& functionsOrLibrary() const { return m_functionsOrLibrary; } + std::vector, std::optional>> functionsAndOperators() const; bool usesBraces() const { return m_usesBraces; } bool global() const { return m_global; } private: /// Either the single library or a list of functions. std::vector> m_functionsOrLibrary; + /// Operators, the functions from @a m_functionsOrLibrary implement. + /// A token if the corresponding element in m_functionsOrLibrary + /// defines an operator, nullptr otherwise. + /// Note that this vector size must be equal to m_functionsOrLibrary size. + std::vector> m_operators; bool m_usesBraces; ASTPointer m_typeName; bool m_global = false; @@ -2073,6 +2082,10 @@ public: bool isPrefixOperation() const { return m_isPrefix; } Expression const& subExpression() const { return *m_subExpression; } + FunctionType const* userDefinedFunctionType() const; + + OperationAnnotation& annotation() const override; + private: Token m_operator; ASTPointer m_subExpression; @@ -2104,6 +2117,8 @@ public: Expression const& rightExpression() const { return *m_right; } Token getOperator() const { return m_operator; } + FunctionType const* userDefinedFunctionType() const; + BinaryOperationAnnotation& annotation() const override; private: diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 2615e6af7..b85eb3ad0 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -312,7 +312,12 @@ struct MemberAccessAnnotation: ExpressionAnnotation util::SetOnce requiredLookup; }; -struct BinaryOperationAnnotation: ExpressionAnnotation +struct OperationAnnotation: ExpressionAnnotation +{ + util::SetOnce userDefinedFunction; +}; + +struct BinaryOperationAnnotation: OperationAnnotation { /// The common type that is used for the operation, not necessarily the result type (which /// e.g. for comparisons is bool). diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index 8b46684bc..59b0fda1b 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -329,19 +329,27 @@ bool ASTJsonExporter::visit(UsingForDirective const& _node) vector> 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); + if (op.has_value()) + functionNode["operator"] = string(TokenTraits::toString(*op)); functionList.append(std::move(functionNode)); } attributes.emplace_back("functionList", std::move(functionList)); } else - attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front())); + { + auto const& functionAndOperators = _node.functionsAndOperators(); + solAssert(_node.functionsAndOperators().size() == 1); + solAssert(!functionAndOperators.front().second.has_value()); + attributes.emplace_back("libraryName", toJson(*(functionAndOperators.front().first))); + } attributes.emplace_back("global", _node.global()); setJsonNode(_node, "UsingForDirective", std::move(attributes)); @@ -829,6 +837,9 @@ bool ASTJsonExporter::visit(UnaryOperation const& _node) make_pair("operator", TokenTraits::toString(_node.getOperator())), make_pair("subExpression", toJson(_node.subExpression())) }; + // NOTE: This annotation is guaranteed to be set but only if we didn't stop at the parsing stage. + if (_node.annotation().userDefinedFunction.set() && *_node.annotation().userDefinedFunction != nullptr) + attributes.emplace_back("function", nodeId(**_node.annotation().userDefinedFunction)); appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "UnaryOperation", std::move(attributes)); return false; @@ -842,6 +853,9 @@ bool ASTJsonExporter::visit(BinaryOperation const& _node) make_pair("rightExpression", toJson(_node.rightExpression())), make_pair("commonType", typePointerToJson(_node.annotation().commonType)), }; + // NOTE: This annotation is guaranteed to be set but only if we didn't stop at the parsing stage. + if (_node.annotation().userDefinedFunction.set() && *_node.annotation().userDefinedFunction != nullptr) + attributes.emplace_back("function", nodeId(**_node.annotation().userDefinedFunction)); appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "BinaryOperation", std::move(attributes)); return false; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 079704946..9b1da3a1a 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -397,15 +398,32 @@ ASTPointer ASTJsonImporter::createInheritanceSpecifier(Jso ASTPointer ASTJsonImporter::createUsingForDirective(Json::Value const& _node) { vector> functions; + vector> operators; if (_node.isMember("libraryName")) + { + astAssert(!_node["libraryName"].isArray()); + astAssert(!_node["libraryName"]["operator"]); functions.emplace_back(createIdentifierPath(_node["libraryName"])); + operators.emplace_back(nullopt); + } else if (_node.isMember("functionList")) for (Json::Value const& function: _node["functionList"]) + { functions.emplace_back(createIdentifierPath(function["function"])); + if (function.isMember("operator")) + { + Token const token = scanSingleToken(function["operator"]); + astAssert(util::contains(frontend::userDefinableOperators, token)); + operators.emplace_back(token); + } + else + operators.emplace_back(nullopt); + } return createASTNode( _node, std::move(functions), + std::move(operators), !_node.isMember("libraryName"), _node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]), memberAsBool(_node, "global") diff --git a/libsolidity/ast/UserDefinableOperators.h b/libsolidity/ast/UserDefinableOperators.h new file mode 100644 index 000000000..a3895342f --- /dev/null +++ b/libsolidity/ast/UserDefinableOperators.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace solidity::frontend +{ + +std::vector const userDefinableOperators = { + // Bitwise + langutil::Token::BitOr, + langutil::Token::BitAnd, + langutil::Token::BitXor, + langutil::Token::BitNot, + langutil::Token::SHL, + langutil::Token::SAR, + // Arithmetic + langutil::Token::Add, + langutil::Token::Sub, + langutil::Token::Mul, + langutil::Token::Div, + langutil::Token::Mod, + langutil::Token::Exp, + // Comparison + langutil::Token::Equal, + langutil::Token::NotEqual, + langutil::Token::LessThan, + langutil::Token::GreaterThan, + langutil::Token::LessThanOrEqual, + langutil::Token::GreaterThanOrEqual, + // Boolean + langutil::Token::Not +}; + +}