diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 42756a8de..490ec3d1b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -413,7 +413,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) if (FunctionDefinition const* function = _unaryOperation.annotation().userDefinedFunction) { - solAssert(function->isFree(), "Only free functions can be bound to a user type operator."); + solAssert( + function->isFree() || function->libraryFunction(), + "Only file-level functions and library functions can be bound to a user type operator." + ); FunctionType const* functionType = dynamic_cast( function->libraryFunction() ? function->typeViaContractName() : function->type() @@ -537,7 +540,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Expression const& rightExpression = _binaryOperation.rightExpression(); if (FunctionDefinition const* function =_binaryOperation.annotation().userDefinedFunction) { - solAssert(function->isFree(), "Only free function can be bound to a user type operator."); + solAssert( + function->isFree() || function->libraryFunction(), + "Only file-level functions and library functions can be bound to a user type operator." + ); FunctionType const* functionType = dynamic_cast( function->libraryFunction() ? function->typeViaContractName() : function->type() ); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5beddf3a3..70b3aaf59 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -679,7 +679,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) setLocation(_unaryOperation); solAssert( - dynamic_cast(function->scope()), + function->isFree() || function->libraryFunction(), "Only file-level functions and library functions can be bound to a user type operator." ); @@ -824,7 +824,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) setLocation(_binOp); solAssert( - dynamic_cast(function->scope()), + function->isFree() || function->libraryFunction(), "Only file-level functions and library functions can be bound to a user type operator." ); diff --git a/test/libsolidity/semanticTests/operators/custom/operator_bound_to_library_function.sol b/test/libsolidity/semanticTests/operators/custom/operator_bound_to_library_function.sol new file mode 100644 index 000000000..c36fe2ed3 --- /dev/null +++ b/test/libsolidity/semanticTests/operators/custom/operator_bound_to_library_function.sol @@ -0,0 +1,33 @@ +==== Source: a.sol ==== +library L { + type Int is int128; + + function add(Int, Int) pure public returns (Int) { + return Int.wrap(7); + } + + function sub(Int) pure public returns (Int) { + return Int.wrap(5); + } +} +==== Source: b.sol ==== +import "a.sol" as a; + +contract C { + using {a.L.add as +} for a.L.Int; + using {a.L.sub as -} for a.L.Int; + + function f() pure public returns (a.L.Int) { + return a.L.Int.wrap(0) + a.L.Int.wrap(0); + } + + function g() pure public returns (a.L.Int) { + return - a.L.Int.wrap(0); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 7 +// g() -> 5