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:
|
Language Features:
|
||||||
* Allow named parameters in mapping types.
|
* 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:
|
Compiler Features:
|
||||||
|
@ -8,7 +8,7 @@ Using For
|
|||||||
|
|
||||||
The directive ``using A for B;`` can be used to attach
|
The directive ``using A for B;`` can be used to attach
|
||||||
functions (``A``) as operators to user-defined value types
|
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
|
The member functions receive the object they are called on
|
||||||
as their first parameter (like the ``self`` variable in Python).
|
as their first parameter (like the ``self`` variable in Python).
|
||||||
The operator functions receive operands as parameters.
|
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.
|
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
|
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 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
|
the return value matching ``T``, except for comparison operators, where the return value must
|
||||||
be of type ``bool``.
|
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:
|
The following operators can be defined this way:
|
||||||
|
|
||||||
|
@ -1773,22 +1773,55 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string description = fmt::format(
|
set<FunctionDefinition const*> definitionsWithDifferentLocation = operandType->operatorDefinitions(
|
||||||
"Built-in unary operator {} cannot be applied to type {}.",
|
_operation.getOperator(),
|
||||||
TokenTraits::toString(op),
|
*currentDefinitionScope(),
|
||||||
operandType->humanReadableName()
|
true, // _unary
|
||||||
|
true // _anyDataLocation
|
||||||
);
|
);
|
||||||
if (!builtinResult.message().empty())
|
|
||||||
description += " " + builtinResult.message();
|
|
||||||
if (operandType->typeDefinition() && util::contains(userDefinableOperators, op))
|
|
||||||
description += " No matching user-defined operator found.";
|
|
||||||
|
|
||||||
if (modifying)
|
if (!definitionsWithDifferentLocation.empty())
|
||||||
// Cannot just report the error, ignore the unary operator, and continue,
|
{
|
||||||
// because the sub-expression was already processed with requireLValue()
|
auto const* referenceType = dynamic_cast<ReferenceType const*>(operandType);
|
||||||
m_errorReporter.fatalTypeError(9767_error, _operation.location(), description);
|
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
|
else
|
||||||
m_errorReporter.typeError(4907_error, _operation.location(), description);
|
{
|
||||||
|
string description = fmt::format(
|
||||||
|
"Built-in unary operator {} cannot be applied to type {}.",
|
||||||
|
TokenTraits::toString(op),
|
||||||
|
operandType->humanReadableName()
|
||||||
|
);
|
||||||
|
if (!builtinResult.message().empty())
|
||||||
|
description += " " + builtinResult.message();
|
||||||
|
if (operandType->typeDefinition() && util::contains(userDefinableOperators, op))
|
||||||
|
description += " No matching user-defined operator found.";
|
||||||
|
|
||||||
|
if (modifying)
|
||||||
|
// Cannot just report the error, ignore the unary operator, and continue,
|
||||||
|
// because the sub-expression was already processed with requireLValue()
|
||||||
|
m_errorReporter.fatalTypeError(9767_error, _operation.location(), description);
|
||||||
|
else
|
||||||
|
m_errorReporter.typeError(4907_error, _operation.location(), description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_operation.annotation().userDefinedFunction = operatorDefinition;
|
_operation.annotation().userDefinedFunction = operatorDefinition;
|
||||||
@ -1797,6 +1830,8 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
|||||||
if (operatorDefinition && !returnParameterTypes.empty())
|
if (operatorDefinition && !returnParameterTypes.empty())
|
||||||
// Use the actual result type from operator definition. Ignore all values but the
|
// Use the actual result type from operator definition. Ignore all values but the
|
||||||
// first one - in valid code there will be only one anyway.
|
// 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];
|
resultType = returnParameterTypes[0];
|
||||||
_operation.annotation().type = resultType;
|
_operation.annotation().type = resultType;
|
||||||
_operation.annotation().isConstant = false;
|
_operation.annotation().isConstant = false;
|
||||||
@ -1855,18 +1890,50 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string description = fmt::format(
|
set<FunctionDefinition const*> definitionsWithDifferentLocation = leftType->operatorDefinitions(
|
||||||
"Built-in binary operator {} cannot be applied to types {} and {}.",
|
_operation.getOperator(),
|
||||||
TokenTraits::toString(_operation.getOperator()),
|
*currentDefinitionScope(),
|
||||||
leftType->humanReadableName(),
|
false, // _unary
|
||||||
rightType->humanReadableName()
|
true // _anyDataLocation
|
||||||
);
|
);
|
||||||
if (!builtinResult.message().empty())
|
|
||||||
description += " " + builtinResult.message();
|
|
||||||
if (leftType->typeDefinition() && util::contains(userDefinableOperators, _operation.getOperator()))
|
|
||||||
description += " No matching user-defined operator found.";
|
|
||||||
|
|
||||||
m_errorReporter.typeError(2271_error, _operation.location(), description);
|
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 {}.",
|
||||||
|
TokenTraits::toString(_operation.getOperator()),
|
||||||
|
leftType->humanReadableName(),
|
||||||
|
rightType->humanReadableName()
|
||||||
|
);
|
||||||
|
if (!builtinResult.message().empty())
|
||||||
|
description += " " + builtinResult.message();
|
||||||
|
if (leftType->typeDefinition() && util::contains(userDefinableOperators, _operation.getOperator()))
|
||||||
|
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.
|
// Set common type to something we'd expect from correct code just so that we can continue analysis.
|
||||||
commonType = leftType;
|
commonType = leftType;
|
||||||
@ -1890,9 +1957,9 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|||||||
|
|
||||||
// operatorDefinitions() filters out definitions with non-matching first argument.
|
// operatorDefinitions() filters out definitions with non-matching first argument.
|
||||||
solAssert(parameterTypes.size() == 2);
|
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(
|
m_errorReporter.typeError(
|
||||||
5653_error,
|
5653_error,
|
||||||
_operation.location(),
|
_operation.location(),
|
||||||
@ -1907,6 +1974,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|||||||
if (!returnParameterTypes.empty())
|
if (!returnParameterTypes.empty())
|
||||||
// Use the actual result type from operator definition. Ignore all values but the
|
// Use the actual result type from operator definition. Ignore all values but the
|
||||||
// first one - in valid code there will be only one anyway.
|
// 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];
|
resultType = returnParameterTypes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3990,12 +4059,15 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
{
|
{
|
||||||
TypePointers const& parameterTypes = functionType->parameterTypesIncludingSelf();
|
TypePointers const& parameterTypes = functionType->parameterTypesIncludingSelf();
|
||||||
size_t const parameterCount = parameterTypes.size();
|
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(
|
m_errorReporter.typeError(
|
||||||
5332_error,
|
5332_error,
|
||||||
path->location(),
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
@ -4013,7 +4085,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
bool isBinaryOnlyOperator =
|
bool isBinaryOnlyOperator =
|
||||||
(TokenTraits::isBinaryOp(*operator_) && !TokenTraits::isUnaryOp(*operator_)) ||
|
(TokenTraits::isBinaryOp(*operator_) && !TokenTraits::isUnaryOp(*operator_)) ||
|
||||||
*operator_ == Token::Add;
|
*operator_ == Token::Add;
|
||||||
bool firstParameterMatchesUsingFor = parameterCount == 0 || *usingForType == *parameterTypes.front();
|
bool firstParameterMatchesUsingFor =
|
||||||
|
parameterCount == 0 ||
|
||||||
|
usingForType->sameTypeOrPointerTo(*parameterTypes.front(), true /* _excludeLocation */);
|
||||||
|
|
||||||
optional<string> wrongParametersMessage;
|
optional<string> wrongParametersMessage;
|
||||||
if (isBinaryOnlyOperator && (parameterCount != 2 || !identicalFirstTwoParameters))
|
if (isBinaryOnlyOperator && (parameterCount != 2 || !identicalFirstTwoParameters))
|
||||||
@ -4049,7 +4123,10 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
optional<string> wrongReturnParametersMessage;
|
optional<string> wrongReturnParametersMessage;
|
||||||
if (!TokenTraits::isCompareOp(*operator_) && *operator_ != Token::Not)
|
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();
|
wrongReturnParametersMessage = "exactly one value of type " + usingForType->canonicalName();
|
||||||
else if (*returnParameterTypes.front() != *parameterTypes.front())
|
else if (*returnParameterTypes.front() != *parameterTypes.front())
|
||||||
wrongReturnParametersMessage = "a value of the same type and data location as its parameters";
|
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
|
* 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.
|
* 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.
|
* 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
|
* 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.
|
* 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(
|
set<FunctionDefinition const*> Type::operatorDefinitions(
|
||||||
Token _token,
|
Token _token,
|
||||||
ASTNode const& _scope,
|
ASTNode const& _scope,
|
||||||
bool _unary
|
bool _unary,
|
||||||
|
bool _anyDataLocation
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
if (!typeDefinition())
|
if (!typeDefinition())
|
||||||
@ -428,7 +429,10 @@ set<FunctionDefinition const*> Type::operatorDefinitions(
|
|||||||
solAssert(functionType && !functionType->parameterTypes().empty());
|
solAssert(functionType && !functionType->parameterTypes().empty());
|
||||||
|
|
||||||
size_t parameterCount = functionDefinition.parameterList().parameters().size();
|
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);
|
matchingDefinitions.insert(&functionDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,17 +388,20 @@ public:
|
|||||||
|
|
||||||
/// Scans all "using for" directives in the @a _scope for functions implementing
|
/// 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
|
/// 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.
|
/// the function will find at most one definition for an operator.
|
||||||
///
|
///
|
||||||
/// @param _unary If true, only definitions that accept exactly one argument are included.
|
/// @param _unary If true, only definitions that accept exactly one argument are included.
|
||||||
/// Otherwise only definitions that accept exactly two arguments.
|
/// 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(
|
std::set<FunctionDefinition const*> operatorDefinitions(
|
||||||
Token _token,
|
Token _token,
|
||||||
ASTNode const& _scope,
|
ASTNode const& _scope,
|
||||||
bool _unary
|
bool _unary,
|
||||||
|
bool _anyDataLocation = false
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
private:
|
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) {}
|
function f(uint, uint) pure returns (uint) {}
|
||||||
// ----
|
// ----
|
||||||
// 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.
|
||||||
// TypeError 5332: (32-33): Operators can only be implemented for user-defined value types.
|
// 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.
|
// 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.
|
// 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.
|
// 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 {}
|
contract C {}
|
||||||
abstract contract A {}
|
abstract contract A {}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 5332: (7-9): 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.
|
// 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) {}
|
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 {}
|
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) {}
|
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