mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined operators on structs
This commit is contained in:
parent
5657515f45
commit
05f4617275
@ -2,7 +2,7 @@
|
||||
|
||||
Language Features:
|
||||
* Allow named parameters in mapping types.
|
||||
* Allow defining custom operators for user-defined value types via ``using {f as +} for Typename;`` syntax.
|
||||
* Allow defining custom operators for user-defined value types and structs via ``using {f as +} for Typename;`` syntax.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -8,7 +8,7 @@ Using For
|
||||
|
||||
The directive ``using A for B;`` can be used to attach
|
||||
functions (``A``) as operators to user-defined value types
|
||||
or as member functions to any type (``B``).
|
||||
and structs or as member functions to any type (``B``).
|
||||
The member functions receive the object they are called on
|
||||
as their first parameter (like the ``self`` variable in Python).
|
||||
The operator functions receive operands as parameters.
|
||||
@ -44,10 +44,12 @@ performed even if none of these functions are called.
|
||||
Note that private library functions can only be specified when ``using for`` is inside a library.
|
||||
|
||||
If you define an operator (e.g. ``using {f as +} for T``), then the type (``T``) must be a
|
||||
:ref:`user-defined value type <user-defined-value-types>`.
|
||||
:ref:`user-defined value type <user-defined-value-types>` or a struct.
|
||||
The definition of an operator must be a ``pure`` function with the types of all parameters and
|
||||
the return value matching ``T``, except for comparison operators, where the return value must
|
||||
be of type ``bool``.
|
||||
``T`` does not include data location.
|
||||
The locations of the parameters and the return value of the definition do and must be the same.
|
||||
|
||||
The following operators can be defined this way:
|
||||
|
||||
|
@ -1772,6 +1772,38 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
||||
operatorDefinition = *matchingDefinitions.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
set<FunctionDefinition const*> definitionsWithDifferentLocation = operandType->operatorDefinitions(
|
||||
_operation.getOperator(),
|
||||
*currentDefinitionScope(),
|
||||
true, // _unary
|
||||
true // _anyDataLocation
|
||||
);
|
||||
|
||||
if (!definitionsWithDifferentLocation.empty())
|
||||
{
|
||||
auto const* referenceType = dynamic_cast<ReferenceType const*>(operandType);
|
||||
solAssert(referenceType);
|
||||
solAssert(!modifying);
|
||||
|
||||
SecondarySourceLocation secondaryLocation;
|
||||
for (FunctionDefinition const* definition: definitionsWithDifferentLocation)
|
||||
secondaryLocation.append("Candidate definition:", definition->location());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
5652_error,
|
||||
_operation.location(),
|
||||
secondaryLocation,
|
||||
fmt::format(
|
||||
"User-defined unary operator {} cannot be applied to type {}. "
|
||||
"None of the available definitions accepts {} arguments.",
|
||||
TokenTraits::toString(op),
|
||||
operandType->humanReadableName(),
|
||||
referenceType->stringForReferencePart(false /* _showIndirection */)
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
string description = fmt::format(
|
||||
"Built-in unary operator {} cannot be applied to type {}.",
|
||||
@ -1790,6 +1822,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
||||
else
|
||||
m_errorReporter.typeError(4907_error, _operation.location(), description);
|
||||
}
|
||||
}
|
||||
|
||||
_operation.annotation().userDefinedFunction = operatorDefinition;
|
||||
|
||||
@ -1797,6 +1830,8 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
||||
if (operatorDefinition && !returnParameterTypes.empty())
|
||||
// Use the actual result type from operator definition. Ignore all values but the
|
||||
// first one - in valid code there will be only one anyway.
|
||||
// NOTE: Watch out for the difference between storage ref and ptr. Even in valid code the
|
||||
// types will differ when the argument is a ref and the result is a ptr.
|
||||
resultType = returnParameterTypes[0];
|
||||
_operation.annotation().type = resultType;
|
||||
_operation.annotation().isConstant = false;
|
||||
@ -1854,6 +1889,37 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
commonType = leftType;
|
||||
}
|
||||
else
|
||||
{
|
||||
set<FunctionDefinition const*> definitionsWithDifferentLocation = leftType->operatorDefinitions(
|
||||
_operation.getOperator(),
|
||||
*currentDefinitionScope(),
|
||||
false, // _unary
|
||||
true // _anyDataLocation
|
||||
);
|
||||
|
||||
if (!definitionsWithDifferentLocation.empty())
|
||||
{
|
||||
auto const* referenceType = dynamic_cast<ReferenceType const*>(leftType);
|
||||
solAssert(referenceType);
|
||||
|
||||
SecondarySourceLocation secondaryLocation;
|
||||
for (FunctionDefinition const* definition: definitionsWithDifferentLocation)
|
||||
secondaryLocation.append("Candidate definition:", definition->location());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
1349_error,
|
||||
_operation.location(),
|
||||
secondaryLocation,
|
||||
fmt::format(
|
||||
"User-defined binary operator {} cannot be applied to type {}. "
|
||||
"None of the available definitions accepts {} arguments.",
|
||||
TokenTraits::toString(_operation.getOperator()),
|
||||
leftType->humanReadableName(),
|
||||
referenceType->stringForReferencePart(false /* _showIndirection */)
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
string description = fmt::format(
|
||||
"Built-in binary operator {} cannot be applied to types {} and {}.",
|
||||
@ -1867,6 +1933,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
description += " No matching user-defined operator found.";
|
||||
|
||||
m_errorReporter.typeError(2271_error, _operation.location(), description);
|
||||
}
|
||||
|
||||
// Set common type to something we'd expect from correct code just so that we can continue analysis.
|
||||
commonType = leftType;
|
||||
@ -1890,9 +1957,9 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
|
||||
// operatorDefinitions() filters out definitions with non-matching first argument.
|
||||
solAssert(parameterTypes.size() == 2);
|
||||
solAssert(parameterTypes[0] && *leftType == *parameterTypes[0]);
|
||||
solAssert(parameterTypes[0] && leftType->sameTypeOrPointerTo(*parameterTypes[0]));
|
||||
|
||||
if (*rightType != *parameterTypes[0])
|
||||
if (!rightType->sameTypeOrPointerTo(*parameterTypes[0]))
|
||||
m_errorReporter.typeError(
|
||||
5653_error,
|
||||
_operation.location(),
|
||||
@ -1907,6 +1974,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
if (!returnParameterTypes.empty())
|
||||
// Use the actual result type from operator definition. Ignore all values but the
|
||||
// first one - in valid code there will be only one anyway.
|
||||
// NOTE: Watch out for the difference between storage ref and ptr. Even in valid code the
|
||||
// types will differ when the argument is a ref and the result is a ptr.
|
||||
resultType = returnParameterTypes[0];
|
||||
}
|
||||
|
||||
@ -3990,12 +4059,15 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
{
|
||||
TypePointers const& parameterTypes = functionType->parameterTypesIncludingSelf();
|
||||
size_t const parameterCount = parameterTypes.size();
|
||||
if (usingForType->category() != Type::Category::UserDefinedValueType)
|
||||
if (
|
||||
usingForType->category() != Type::Category::UserDefinedValueType &&
|
||||
usingForType->category() != Type::Category::Struct
|
||||
)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
5332_error,
|
||||
path->location(),
|
||||
"Operators can only be implemented for user-defined value types."
|
||||
"Operators can only be implemented for user-defined value types and structs."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@ -4013,7 +4085,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
bool isBinaryOnlyOperator =
|
||||
(TokenTraits::isBinaryOp(*operator_) && !TokenTraits::isUnaryOp(*operator_)) ||
|
||||
*operator_ == Token::Add;
|
||||
bool firstParameterMatchesUsingFor = parameterCount == 0 || *usingForType == *parameterTypes.front();
|
||||
bool firstParameterMatchesUsingFor =
|
||||
parameterCount == 0 ||
|
||||
usingForType->sameTypeOrPointerTo(*parameterTypes.front(), true /* _excludeLocation */);
|
||||
|
||||
optional<string> wrongParametersMessage;
|
||||
if (isBinaryOnlyOperator && (parameterCount != 2 || !identicalFirstTwoParameters))
|
||||
@ -4049,7 +4123,10 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
optional<string> wrongReturnParametersMessage;
|
||||
if (!TokenTraits::isCompareOp(*operator_) && *operator_ != Token::Not)
|
||||
{
|
||||
if (returnParameterCount != 1 || *usingForType != *returnParameterTypes.front())
|
||||
if (
|
||||
returnParameterCount != 1 ||
|
||||
!usingForType->sameTypeOrPointerTo(*returnParameterTypes.front(), true /* _excludeLocation */)
|
||||
)
|
||||
wrongReturnParametersMessage = "exactly one value of type " + usingForType->canonicalName();
|
||||
else if (*returnParameterTypes.front() != *parameterTypes.front())
|
||||
wrongReturnParametersMessage = "a value of the same type and data location as its parameters";
|
||||
|
@ -653,8 +653,9 @@ private:
|
||||
* 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.
|
||||
*
|
||||
* For version 4, T has to be user-defined value type and the function must be pure.
|
||||
* For version 4, T has to be user-defined value type or struct and the function must be pure.
|
||||
* All parameters and return value of all the functions have to be of type T.
|
||||
* Locations of all parameters and the return value must be identical.
|
||||
* This version can be combined with version 3 - a single directive may attach functions to the
|
||||
* type and define operators on it at the same time.
|
||||
*
|
||||
|
@ -406,7 +406,8 @@ vector<UsingForDirective const*> usingForDirectivesForType(Type const& _type, AS
|
||||
set<FunctionDefinition const*> Type::operatorDefinitions(
|
||||
Token _token,
|
||||
ASTNode const& _scope,
|
||||
bool _unary
|
||||
bool _unary,
|
||||
bool _anyDataLocation
|
||||
) const
|
||||
{
|
||||
if (!typeDefinition())
|
||||
@ -428,7 +429,10 @@ set<FunctionDefinition const*> Type::operatorDefinitions(
|
||||
solAssert(functionType && !functionType->parameterTypes().empty());
|
||||
|
||||
size_t parameterCount = functionDefinition.parameterList().parameters().size();
|
||||
if (*this == *functionType->parameterTypes().front() && (_unary ? parameterCount == 1 : parameterCount == 2))
|
||||
if (
|
||||
sameTypeOrPointerTo(*functionType->parameterTypes().front(), _anyDataLocation) &&
|
||||
(_unary ? parameterCount == 1 : parameterCount == 2)
|
||||
)
|
||||
matchingDefinitions.insert(&functionDefinition);
|
||||
}
|
||||
|
||||
|
@ -388,17 +388,20 @@ public:
|
||||
|
||||
/// Scans all "using for" directives in the @a _scope for functions implementing
|
||||
/// the operator represented by @a _token. Returns the set of all definitions where the type
|
||||
/// of the first argument matches this type object.
|
||||
/// of the first argument matches this type object. Accepts both pointer and non-pointer types.
|
||||
///
|
||||
/// @note: If the AST has passed analysis without errors,
|
||||
/// @note: If the AST has passed analysis without errors and @a _anyDataLocation is false,
|
||||
/// the function will find at most one definition for an operator.
|
||||
///
|
||||
/// @param _unary If true, only definitions that accept exactly one argument are included.
|
||||
/// Otherwise only definitions that accept exactly two arguments.
|
||||
/// @param _anyDataLocation If true, ignores data location when comparing types.
|
||||
/// Use this if you want to get all the overloads of the operator.
|
||||
std::set<FunctionDefinition const*> operatorDefinitions(
|
||||
Token _token,
|
||||
ASTNode const& _scope,
|
||||
bool _unary
|
||||
bool _unary,
|
||||
bool _anyDataLocation = false
|
||||
) const;
|
||||
|
||||
private:
|
||||
|
@ -0,0 +1,37 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
struct S {
|
||||
int value;
|
||||
}
|
||||
|
||||
using {add as +, mul as *, unsub as -} for S;
|
||||
|
||||
function add(S calldata a, S calldata) returns (S calldata) {
|
||||
return a;
|
||||
}
|
||||
|
||||
function mul(S calldata, S calldata b) returns (S calldata) {
|
||||
return b;
|
||||
}
|
||||
|
||||
function unsub(S calldata a) returns (S calldata) {
|
||||
return a;
|
||||
}
|
||||
|
||||
contract C {
|
||||
function testAdd(S calldata a, S calldata b) public returns (int) {
|
||||
return (a + b).value;
|
||||
}
|
||||
|
||||
function testMul(S calldata a, S calldata b) public returns (int) {
|
||||
return (a * b).value;
|
||||
}
|
||||
|
||||
function testUnsub(S calldata a) public returns (int) {
|
||||
return (-a).value;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// testAdd((int256),(int256)): 3, 7 -> 3
|
||||
// testMul((int256),(int256)): 3, 7 -> 7
|
||||
// testUnsub((int256)): 3 -> 3
|
@ -0,0 +1,55 @@
|
||||
struct S {
|
||||
int value;
|
||||
}
|
||||
|
||||
using {add as +, unsub as -} for S;
|
||||
|
||||
function add(S storage a, S storage b) returns (S storage) {
|
||||
a.value = a.value + b.value;
|
||||
return a;
|
||||
}
|
||||
|
||||
function unsub(S storage a) returns (S storage) {
|
||||
a.value = -a.value;
|
||||
return a;
|
||||
}
|
||||
|
||||
contract C {
|
||||
S a = S(10);
|
||||
S b = S(3);
|
||||
|
||||
function addStorageRef() public returns (int) {
|
||||
return (a + b).value;
|
||||
}
|
||||
|
||||
function addStoragePtr() public returns (int) {
|
||||
// Storage pointers are technically a different type internally. The point of this test is
|
||||
// to make sure they go find through both the codegen and the analysis.
|
||||
S storage c = a;
|
||||
S storage d = b;
|
||||
|
||||
return (c + d).value;
|
||||
}
|
||||
|
||||
function addStoragePtrAndRef() public returns (int) {
|
||||
S storage c = a;
|
||||
|
||||
return (a + c).value;
|
||||
}
|
||||
|
||||
function unsubStorageRef() public returns (int) {
|
||||
return (-a).value;
|
||||
}
|
||||
|
||||
function unsubStoragePtr() public returns (int) {
|
||||
S storage c = a;
|
||||
|
||||
return (-c).value;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// addStorageRef() -> 13
|
||||
// addStoragePtr() -> 16
|
||||
// addStoragePtrAndRef() -> 32
|
||||
// unsubStorageRef() -> -32
|
||||
// unsubStoragePtr() -> 32
|
@ -0,0 +1,54 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
using {
|
||||
addC as +,
|
||||
addM as +,
|
||||
addS as +
|
||||
} for S;
|
||||
|
||||
struct S {
|
||||
uint v;
|
||||
}
|
||||
|
||||
function addC(S calldata _s, S calldata) pure returns (S calldata) {
|
||||
return _s;
|
||||
}
|
||||
|
||||
function addM(S memory _s, S memory) pure returns (S memory) {
|
||||
_s.v = 7;
|
||||
return _s;
|
||||
}
|
||||
|
||||
function addS(S storage _s, S storage) returns (S storage) {
|
||||
_s.v = 13;
|
||||
return _s;
|
||||
}
|
||||
|
||||
contract C {
|
||||
S s;
|
||||
|
||||
function testC(S calldata _s) public returns (S calldata) {
|
||||
return _s + _s;
|
||||
}
|
||||
|
||||
function testM() public returns (S memory) {
|
||||
S memory sTmp;
|
||||
return sTmp + sTmp;
|
||||
}
|
||||
|
||||
function testS() public returns (uint) {
|
||||
s + s;
|
||||
return s.v;
|
||||
}
|
||||
|
||||
function testSTmp() public returns (uint) {
|
||||
S storage sTmp = s;
|
||||
sTmp + sTmp;
|
||||
return sTmp.v;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// testC((uint256)): 3 -> 3
|
||||
// testM()-> 7
|
||||
// testS() -> 13
|
||||
// testSTmp() -> 13
|
@ -0,0 +1,31 @@
|
||||
type UncheckedCounter is uint;
|
||||
|
||||
using {
|
||||
add as +,
|
||||
lt as <
|
||||
} for UncheckedCounter;
|
||||
|
||||
UncheckedCounter constant ONE = UncheckedCounter.wrap(1);
|
||||
|
||||
function add(UncheckedCounter x, UncheckedCounter y) pure returns (UncheckedCounter) {
|
||||
unchecked {
|
||||
return UncheckedCounter.wrap(UncheckedCounter.unwrap(x) + UncheckedCounter.unwrap(y));
|
||||
}
|
||||
}
|
||||
|
||||
function lt(UncheckedCounter x, UncheckedCounter y) pure returns (bool) {
|
||||
return UncheckedCounter.unwrap(x) < UncheckedCounter.unwrap(y);
|
||||
}
|
||||
|
||||
contract C {
|
||||
uint internalCounter = 12;
|
||||
|
||||
function testCounter() public returns (uint) {
|
||||
for (UncheckedCounter i = UncheckedCounter.wrap(12); i < UncheckedCounter.wrap(20); i = i + ONE) {
|
||||
++internalCounter;
|
||||
}
|
||||
return internalCounter;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// testCounter() -> 20
|
@ -0,0 +1,22 @@
|
||||
struct S {
|
||||
uint v;
|
||||
}
|
||||
|
||||
using {bitand as &} for S;
|
||||
|
||||
function bitand(S storage, S storage) returns (S storage) {
|
||||
S storage rTmp;
|
||||
return rTmp;
|
||||
}
|
||||
|
||||
contract C {
|
||||
S s;
|
||||
function f() public {
|
||||
S storage sTmp;
|
||||
sTmp & s;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// TypeError 3464: (145-149): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
|
||||
// TypeError 3464: (234-238): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
|
@ -0,0 +1,22 @@
|
||||
struct S {
|
||||
uint v;
|
||||
}
|
||||
|
||||
using {bitor as |} for S;
|
||||
|
||||
function bitor(S storage, S storage) returns (S storage) {
|
||||
S storage rTmp;
|
||||
return rTmp;
|
||||
}
|
||||
|
||||
contract C {
|
||||
S s;
|
||||
function f() public {
|
||||
S storage sTmp;
|
||||
sTmp | s;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// TypeError 3464: (143-147): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
|
||||
// TypeError 3464: (232-236): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
|
@ -0,0 +1,17 @@
|
||||
struct S { Z z; }
|
||||
struct Z { int x; }
|
||||
|
||||
using {addS as +} for S;
|
||||
using {addZ as +} for Z;
|
||||
|
||||
function addS(S memory, S memory) pure returns (S memory) {}
|
||||
function addZ(Z memory, Z memory) pure returns (Z memory) { revert(); }
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
S(Z(1)) + S(Z(2) + Z(3));
|
||||
S(Z(4)) + S(Z(5)); // Unreachable
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5740: (310-327): Unreachable code.
|
@ -0,0 +1,17 @@
|
||||
struct S { Z z; }
|
||||
struct Z { int x; }
|
||||
|
||||
using {unsubS as -} for S;
|
||||
using {unsubZ as -} for Z;
|
||||
|
||||
function unsubS(S memory) pure returns (S memory) {}
|
||||
function unsubZ(Z memory) pure returns (Z memory) { revert(); }
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
-S(-Z(1));
|
||||
-S(Z(2)); // Unreachable
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5740: (283-291): Unreachable code.
|
@ -0,0 +1,36 @@
|
||||
struct S {
|
||||
uint8 a;
|
||||
}
|
||||
|
||||
using {
|
||||
add as +,
|
||||
sub as -,
|
||||
mul as *
|
||||
} for S;
|
||||
|
||||
function add(S memory, S memory) pure returns (S memory) {}
|
||||
function sub(S calldata, S calldata) pure returns (S calldata) {}
|
||||
function mul(S storage, S storage) pure returns (S storage) {}
|
||||
|
||||
contract C {
|
||||
S s;
|
||||
function test(S calldata c) public {
|
||||
S memory m;
|
||||
|
||||
c + c; // operator accepts memory, arguments are calldata
|
||||
s + s; // operator accepts memory, arguments are storage
|
||||
|
||||
m - m; // operator accepts calldata, arguments are memory
|
||||
s - s; // operator accepts calldata, arguments are storage
|
||||
|
||||
c * c; // operator accepts storage, arguments are calldata
|
||||
m * m; // operator accepts storage, arguments are memory
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1349: (368-373): User-defined binary operator + cannot be applied to type struct S calldata. None of the available definitions accepts calldata arguments.
|
||||
// TypeError 1349: (434-439): User-defined binary operator + cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (500-505): User-defined binary operator - cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 1349: (566-571): User-defined binary operator - cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (634-639): User-defined binary operator * cannot be applied to type struct S calldata. None of the available definitions accepts calldata arguments.
|
||||
// TypeError 1349: (701-706): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
@ -0,0 +1,51 @@
|
||||
struct S {
|
||||
uint8 a;
|
||||
}
|
||||
|
||||
using {
|
||||
add as +,
|
||||
sub as -,
|
||||
mul as *
|
||||
} for S;
|
||||
|
||||
function add(S memory, S memory) pure returns (S memory) {}
|
||||
function sub(S calldata, S calldata) pure returns (S calldata) {}
|
||||
function mul(S storage, S storage) pure returns (S storage) {}
|
||||
|
||||
contract C {
|
||||
S s;
|
||||
function test(S calldata c) public {
|
||||
S memory m;
|
||||
|
||||
// operator accepts only memory
|
||||
m + c;
|
||||
m + s;
|
||||
c + m;
|
||||
s + m;
|
||||
|
||||
// operator accepts only calldata
|
||||
c - m;
|
||||
c - s;
|
||||
m - c;
|
||||
s - c;
|
||||
|
||||
// operator accepts only storage
|
||||
s * c;
|
||||
s * m;
|
||||
c * s;
|
||||
m * s;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 5653: (408-413): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is struct S memory.
|
||||
// TypeError 5653: (423-428): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is struct S memory.
|
||||
// TypeError 1349: (438-443): User-defined binary operator + cannot be applied to type struct S calldata. None of the available definitions accepts calldata arguments.
|
||||
// TypeError 1349: (453-458): User-defined binary operator + cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 5653: (511-516): The type of the second operand of this user-defined binary operator - does not match the type of the first operand, which is struct S calldata.
|
||||
// TypeError 5653: (526-531): The type of the second operand of this user-defined binary operator - does not match the type of the first operand, which is struct S calldata.
|
||||
// TypeError 1349: (541-546): User-defined binary operator - cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 1349: (556-561): User-defined binary operator - cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 5653: (613-618): The type of the second operand of this user-defined binary operator * does not match the type of the first operand, which is struct S storage pointer.
|
||||
// TypeError 5653: (628-633): The type of the second operand of this user-defined binary operator * does not match the type of the first operand, which is struct S storage pointer.
|
||||
// TypeError 1349: (643-648): User-defined binary operator * cannot be applied to type struct S calldata. None of the available definitions accepts calldata arguments.
|
||||
// TypeError 1349: (658-663): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
@ -0,0 +1,70 @@
|
||||
using {add as +, unsub as -} for S;
|
||||
using {mul as *, not as !} for S;
|
||||
|
||||
struct S {
|
||||
uint x;
|
||||
}
|
||||
|
||||
function add(S memory, S memory) returns (S memory) {}
|
||||
function mul(S storage, S storage) returns (S storage) {}
|
||||
function unsub(S memory) returns (S memory) {}
|
||||
function not(S storage) returns (S storage) {}
|
||||
|
||||
contract C {
|
||||
S sRef;
|
||||
|
||||
function storageToMemory() public {
|
||||
S storage sPtr;
|
||||
S memory sMem;
|
||||
|
||||
sMem + sPtr;
|
||||
sPtr + sMem;
|
||||
sPtr + sPtr;
|
||||
|
||||
sMem + sRef;
|
||||
sRef + sMem;
|
||||
sRef + sRef;
|
||||
|
||||
sRef + sPtr;
|
||||
sPtr + sRef;
|
||||
|
||||
-sPtr;
|
||||
-sRef;
|
||||
}
|
||||
|
||||
function memoryToStorage() public {
|
||||
S memory sMem;
|
||||
S storage sPtr;
|
||||
|
||||
sMem * sPtr;
|
||||
sPtr * sMem;
|
||||
sMem * sMem;
|
||||
|
||||
sMem * sRef;
|
||||
sRef * sMem;
|
||||
sMem * sMem;
|
||||
|
||||
sRef * sPtr;
|
||||
sPtr * sRef;
|
||||
|
||||
!sMem;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 5653: (427-438): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is struct S memory.
|
||||
// TypeError 1349: (448-459): User-defined binary operator + cannot be applied to type struct S storage pointer. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (469-480): User-defined binary operator + cannot be applied to type struct S storage pointer. None of the available definitions accepts storage arguments.
|
||||
// TypeError 5653: (491-502): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is struct S memory.
|
||||
// TypeError 1349: (512-523): User-defined binary operator + cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (533-544): User-defined binary operator + cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (555-566): User-defined binary operator + cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (576-587): User-defined binary operator + cannot be applied to type struct S storage pointer. None of the available definitions accepts storage arguments.
|
||||
// TypeError 5652: (598-603): User-defined unary operator - cannot be applied to type struct S storage pointer. None of the available definitions accepts storage arguments.
|
||||
// TypeError 5652: (613-618): User-defined unary operator - cannot be applied to type struct S storage ref. None of the available definitions accepts storage arguments.
|
||||
// TypeError 1349: (723-734): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 5653: (744-755): The type of the second operand of this user-defined binary operator * does not match the type of the first operand, which is struct S storage pointer.
|
||||
// TypeError 1349: (765-776): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 1349: (787-798): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 5653: (808-819): The type of the second operand of this user-defined binary operator * does not match the type of the first operand, which is struct S storage pointer.
|
||||
// TypeError 1349: (829-840): User-defined binary operator * cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
||||
// TypeError 5652: (894-899): User-defined unary operator ! cannot be applied to type struct S memory. None of the available definitions accepts memory arguments.
|
@ -6,8 +6,8 @@ using {f as +} for string;
|
||||
|
||||
function f(uint, uint) pure returns (uint) {}
|
||||
// ----
|
||||
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (32-33): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (60-61): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (102-103): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (158-159): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types and structs.
|
||||
// TypeError 5332: (32-33): Operators can only be implemented for user-defined value types and structs.
|
||||
// TypeError 5332: (60-61): Operators can only be implemented for user-defined value types and structs.
|
||||
// TypeError 5332: (102-103): Operators can only be implemented for user-defined value types and structs.
|
||||
// TypeError 5332: (158-159): Operators can only be implemented for user-defined value types and structs.
|
||||
|
@ -7,5 +7,5 @@ function fa(A, A) pure returns (A) {}
|
||||
contract C {}
|
||||
abstract contract A {}
|
||||
// ----
|
||||
// TypeError 5332: (7-9): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (30-32): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (7-9): Operators can only be implemented for user-defined value types and structs.
|
||||
// TypeError 5332: (30-32): Operators can only be implemented for user-defined value types and structs.
|
||||
|
@ -7,4 +7,4 @@ enum E {
|
||||
|
||||
function add(E, E) pure returns (E) {}
|
||||
// ----
|
||||
// TypeError 5332: (7-10): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (7-10): Operators can only be implemented for user-defined value types and structs.
|
||||
|
@ -4,4 +4,4 @@ function f(I, I) pure returns (I) {}
|
||||
|
||||
interface I {}
|
||||
// ----
|
||||
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types.
|
||||
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types and structs.
|
||||
|
@ -5,5 +5,3 @@ struct S {
|
||||
}
|
||||
|
||||
function add(S memory, S memory) pure returns (S memory) {}
|
||||
// ----
|
||||
// TypeError 5332: (7-10): Operators can only be implemented for user-defined value types.
|
||||
|
@ -0,0 +1,8 @@
|
||||
using {add as +} for S;
|
||||
|
||||
function add(S memory, S memory) pure returns (S memory) {}
|
||||
function add(S storage, S storage) pure returns (S storage) {}
|
||||
|
||||
struct S { int x; }
|
||||
// ----
|
||||
// DeclarationError 7920: (7-10): Identifier not found or not unique.
|
@ -0,0 +1,10 @@
|
||||
using {add as +} for S;
|
||||
using {add as +} for Z;
|
||||
|
||||
function add(S memory, S memory) pure returns (S memory) {}
|
||||
function add(Z memory, Z memory) pure returns (Z memory) {}
|
||||
|
||||
struct S { int x; }
|
||||
struct Z { int x; }
|
||||
// ----
|
||||
// DeclarationError 7920: (7-10): Identifier not found or not unique.
|
@ -0,0 +1,24 @@
|
||||
struct S { uint128 x; }
|
||||
|
||||
using {add as +} for S;
|
||||
using {sub as -} for S;
|
||||
using {mul as *} for S;
|
||||
using {div as *} for S;
|
||||
using {bitor as |} for S;
|
||||
using {unsub as -} for S;
|
||||
|
||||
function add(S memory, S storage) returns (S memory) {}
|
||||
function sub(S memory, S storage) returns (S storage) {}
|
||||
function mul(S storage, S memory) returns (S memory) {}
|
||||
function div(S storage, S memory) returns (S storage) {}
|
||||
function bitor(S storage, S storage) pure returns (S memory) {}
|
||||
function unsub(S memory, S memory) pure returns (S storage) {}
|
||||
// ----
|
||||
// TypeError 1884: (186-207): Wrong parameters in operator definition. The function "add" needs to have two parameters of type S and the same data location to be used for the operator +.
|
||||
// TypeError 1884: (242-263): Wrong parameters in operator definition. The function "sub" needs to have one or two parameters of type S and the same data location to be used for the operator -.
|
||||
// TypeError 7743: (272-283): Wrong return parameters in operator definition. The function "sub" needs to return a value of the same type and data location as its parameters to be used for the operator -.
|
||||
// TypeError 1884: (299-320): Wrong parameters in operator definition. The function "mul" needs to have two parameters of type S and the same data location to be used for the operator *.
|
||||
// TypeError 7743: (329-339): Wrong return parameters in operator definition. The function "mul" needs to return a value of the same type and data location as its parameters to be used for the operator *.
|
||||
// TypeError 1884: (355-376): Wrong parameters in operator definition. The function "div" needs to have two parameters of type S and the same data location to be used for the operator *.
|
||||
// TypeError 7743: (450-460): Wrong return parameters in operator definition. The function "bitor" needs to return a value of the same type and data location as its parameters to be used for the operator |.
|
||||
// TypeError 7743: (512-523): Wrong return parameters in operator definition. The function "unsub" needs to return a value of the same type and data location as its parameters to be used for the operator -.
|
@ -0,0 +1,38 @@
|
||||
using {
|
||||
sub as -,
|
||||
mul as *,
|
||||
div as /,
|
||||
mod as %,
|
||||
unsub as -,
|
||||
bitnot as ~
|
||||
} for S;
|
||||
|
||||
struct S {
|
||||
uint x;
|
||||
}
|
||||
|
||||
function sub(S calldata, uint) pure returns (S calldata r) {}
|
||||
function mul(S calldata) pure returns (S calldata r) {}
|
||||
function div(S calldata, S calldata) pure returns (uint) {}
|
||||
function mod(S calldata, S calldata) pure {}
|
||||
function unsub(uint) pure returns (S calldata r) {}
|
||||
function bitnot(S calldata) pure {}
|
||||
|
||||
function test(S calldata s) pure {
|
||||
s - s;
|
||||
s * s;
|
||||
s / s;
|
||||
s % s;
|
||||
-s;
|
||||
~s;
|
||||
}
|
||||
// ----
|
||||
// TypeError 1884: (144-162): Wrong parameters in operator definition. The function "sub" needs to have one or two parameters of type S and the same data location to be used for the operator -.
|
||||
// TypeError 1884: (206-218): Wrong parameters in operator definition. The function "mul" needs to have two parameters of type S and the same data location to be used for the operator *.
|
||||
// TypeError 7743: (300-306): Wrong return parameters in operator definition. The function "div" needs to return exactly one value of type S to be used for the operator /.
|
||||
// TypeError 7743: (352-352): Wrong return parameters in operator definition. The function "mod" needs to return exactly one value of type S to be used for the operator %.
|
||||
// TypeError 1884: (369-375): Wrong parameters in operator definition. The function "unsub" needs to have one or two parameters of type S and the same data location to be used for the operator -.
|
||||
// TypeError 7743: (389-403): Wrong return parameters in operator definition. The function "unsub" needs to return a value of the same type and data location as its parameters to be used for the operator -.
|
||||
// TypeError 7743: (440-440): Wrong return parameters in operator definition. The function "bitnot" needs to return exactly one value of type S to be used for the operator ~.
|
||||
// TypeError 2271: (494-499): Built-in binary operator * cannot be applied to types struct S calldata and struct S calldata. No matching user-defined operator found.
|
||||
// TypeError 4907: (527-529): Built-in unary operator - cannot be applied to type struct S calldata. No matching user-defined operator found.
|
@ -0,0 +1,59 @@
|
||||
using {
|
||||
add as +,
|
||||
sub as -,
|
||||
mul as *,
|
||||
div as /,
|
||||
mod as %,
|
||||
unsub as -,
|
||||
bitnot as ~
|
||||
} for S;
|
||||
|
||||
struct S {
|
||||
uint x;
|
||||
}
|
||||
|
||||
function add(S storage a, S storage) pure returns (S storage) {}
|
||||
function sub(S storage a, uint) pure returns (S storage) {}
|
||||
function mul(S storage a) pure returns (S storage) {}
|
||||
function div(S storage a, S storage) pure returns (uint) {}
|
||||
function mod(S storage a, S storage) pure {}
|
||||
function unsub(S storage a) pure {}
|
||||
function bitnot(S storage a, S storage) pure returns (S storage) {}
|
||||
|
||||
contract C {
|
||||
S a;
|
||||
S b;
|
||||
|
||||
function test() public view {
|
||||
S storage c;
|
||||
S storage d;
|
||||
|
||||
// storage ref
|
||||
a + b; // OK
|
||||
a - b;
|
||||
a * b;
|
||||
a / b;
|
||||
a % b;
|
||||
-a;
|
||||
~a;
|
||||
|
||||
// storage ptr
|
||||
c + d; // OK
|
||||
c - d;
|
||||
c * a;
|
||||
a / d;
|
||||
-c;
|
||||
~c;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1884: (223-242): Wrong parameters in operator definition. The function "sub" needs to have one or two parameters of type S and the same data location to be used for the operator -.
|
||||
// TypeError 1884: (283-296): Wrong parameters in operator definition. The function "mul" needs to have two parameters of type S and the same data location to be used for the operator *.
|
||||
// TypeError 7743: (375-381): Wrong return parameters in operator definition. The function "div" needs to return exactly one value of type S to be used for the operator /.
|
||||
// TypeError 7743: (427-427): Wrong return parameters in operator definition. The function "mod" needs to return exactly one value of type S to be used for the operator %.
|
||||
// TypeError 7743: (463-463): Wrong return parameters in operator definition. The function "unsub" needs to return exactly one value of type S to be used for the operator -.
|
||||
// TypeError 1884: (481-505): Wrong parameters in operator definition. The function "bitnot" needs to have exactly one parameter of type S to be used for the operator ~.
|
||||
// TypeError 2271: (711-716): Built-in binary operator * cannot be applied to types struct S storage ref and struct S storage ref. No matching user-defined operator found.
|
||||
// TypeError 4907: (768-770): Built-in unary operator ~ cannot be applied to type struct S storage ref. No matching user-defined operator found.
|
||||
// TypeError 2271: (840-845): Built-in binary operator * cannot be applied to types struct S storage pointer and struct S storage ref. No matching user-defined operator found.
|
||||
// TypeError 4907: (882-884): Built-in unary operator ~ cannot be applied to type struct S storage pointer. No matching user-defined operator found.
|
Loading…
Reference in New Issue
Block a user