Merge pull request #13790 from ethereum/user-defined-operators-for-udvt

User-defined operators for UDVTs
This commit is contained in:
Daniel 2023-02-22 11:58:48 +01:00 committed by GitHub
commit 103bf7a71f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
141 changed files with 4817 additions and 72 deletions

View File

@ -1,6 +1,7 @@
### 0.8.19 (unreleased)
Language Features:
* Allow defining custom operators for user-defined value types via ``using {f as +} for T global`` syntax.
Compiler Features:
@ -16,6 +17,10 @@ Bugfixes:
* SMTChecker: Fix internal error caused by unhandled ``z3`` expressions that come from the solver when bitwise operators are used.
AST Changes:
* AST: Add ``function`` field to ``UnaryOperation`` and ``BinaryOperation`` AST nodes. ``functionList`` in ``UsingForDirective`` AST nodes will now contain ``operator`` and ``definition`` members instead of ``function`` when the list entry defines an operator.
### 0.8.18 (2023-02-01)
Language Features:

View File

@ -1,4 +1,4 @@
.. index:: ! using for, library
.. index:: ! using for, library, ! operator; user-defined
.. _using-for:
@ -6,37 +6,87 @@
Using For
*********
The directive ``using A for B;`` can be used to attach
functions (``A``) as member functions to any type (``B``).
These functions will receive the object they are called on
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``).
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.
It is valid either at file level or inside a contract,
at contract level.
The first part, ``A``, can be one of:
- A list of file-level or library functions (e.g. ``using {f, g, h, L.t} for uint;``) -
only those functions will be attached to the type as member functions.
Note that private library functions can only be specified when ``using for`` is inside the library.
- The name of a library (e.g. ``using L for uint;``) -
all non-private functions of the library are attached to the type.
- A list of functions, optionally with an operator name assigned (e.g.
``using {f, g as +, h, L.t} for uint``).
If no operator is specified, the function can be either a library function or a free function and
is attached to the type as a member function.
Otherwise it must be a free function and it becomes the definition of that operator on the type.
- The name of a library (e.g. ``using L for uint``) -
all non-private functions of the library are attached to the type
as member functions
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
Inside contracts, you can also use ``*`` in place of the type (e.g. ``using L for *;``),
which has the effect that all functions of the library ``L``
are attached to *all* types.
If you specify a library, *all* functions in the library get attached,
If you specify a library, *all* non-private functions in the library get attached,
even those where the type of the first parameter does not
match the type of the object. The type is checked at the
point the function is called and function overload
resolution is performed.
If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint;``),
If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint``),
then the type (``uint``) has to be implicitly convertible to the
first parameter of each of these functions. This check is
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>` and the definition must be a ``pure`` function.
Operator definitions must be global.
The following operators can be defined this way:
+------------+----------+---------------------------------------------+
| Category | Operator | Possible signatures |
+============+==========+=============================================+
| Bitwise | ``&`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``|`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``^`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``~`` | ``function (T) pure returns (T)`` |
+------------+----------+---------------------------------------------+
| Arithmetic | ``+`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``-`` | ``function (T, T) pure returns (T)`` |
| + +---------------------------------------------+
| | | ``function (T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``*`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``/`` | ``function (T, T) pure returns (T)`` |
| +----------+---------------------------------------------+
| | ``%`` | ``function (T, T) pure returns (T)`` |
+------------+----------+---------------------------------------------+
| Comparison | ``==`` | ``function (T, T) pure returns (bool)`` |
| +----------+---------------------------------------------+
| | ``!=`` | ``function (T, T) pure returns (bool)`` |
| +----------+---------------------------------------------+
| | ``<`` | ``function (T, T) pure returns (bool)`` |
| +----------+---------------------------------------------+
| | ``<=`` | ``function (T, T) pure returns (bool)`` |
| +----------+---------------------------------------------+
| | ``>`` | ``function (T, T) pure returns (bool)`` |
| +----------+---------------------------------------------+
| | ``>=`` | ``function (T, T) pure returns (bool)`` |
+------------+----------+---------------------------------------------+
Note that unary and binary ``-`` need separate definitions.
The compiler will choose the right definition based on how the operator is invoked.
The ``using A for B;`` directive is active only within the current
scope (either the contract or the current module/source unit),
@ -46,7 +96,7 @@ outside of the contract or module in which it is used.
When the directive is used at file level and applied to a
user-defined type which was defined at file level in the same file,
the word ``global`` can be added at the end. This will have the
effect that the functions are attached to the type everywhere
effect that the functions and operators are attached to the type everywhere
the type is available (including other files), not only in the
scope of the using statement.
@ -150,3 +200,37 @@ if you pass memory or value types, a copy will be performed, even in case of the
``self`` variable. The only situation where no copy will be performed
is when storage reference variables are used or when internal library
functions are called.
Another example shows how to define a custom operator for a user-defined type:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
type UFixed16x2 is uint16;
using {
add as +,
div as /
} for UFixed16x2 global;
uint32 constant SCALE = 100;
function add(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) {
return UFixed16x2.wrap(UFixed16x2.unwrap(a) + UFixed16x2.unwrap(b));
}
function div(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) {
uint32 a32 = UFixed16x2.unwrap(a);
uint32 b32 = UFixed16x2.unwrap(b);
uint32 result32 = a32 * SCALE / b32;
require(result32 <= type(uint16).max, "Divide overflow");
return UFixed16x2.wrap(uint16(a32 * SCALE / b32));
}
contract Math {
function avg(UFixed16x2 a, UFixed16x2 b) public pure returns (UFixed16x2) {
return (a + b) / UFixed16x2.wrap(200);
}
}

View File

@ -311,11 +311,31 @@ errorDefinition:
LParen (parameters+=errorParameter (Comma parameters+=errorParameter)*)? RParen
Semicolon;
/**
* Operators that users are allowed to implement for some types with `using for`.
*/
userDefinableOperator:
BitAnd
| BitNot
| BitOr
| BitXor
| Add
| Div
| Mod
| Mul
| Sub
| Equal
| GreaterThan
| GreaterThanOrEqual
| LessThan
| LessThanOrEqual
| NotEqual;
/**
* Using directive to attach library functions and free functions to types.
* Can occur within contracts and libraries and at the file level.
*/
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
usingDirective: Using (identifierPath | (LBrace identifierPath (As userDefinableOperator)? (Comma identifierPath (As userDefinableOperator)?)* RBrace)) For (Mul | typeName) Global? Semicolon;
/**
* A type name can be an elementary type, a function type, a mapping type, a user-defined type
* (e.g. a contract or struct) or an array type.

View File

@ -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

View File

@ -64,17 +64,53 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
case Token::And:
{
visitNode(_operation);
solAssert(*_operation.annotation().userDefinedFunction == nullptr);
appendControlFlow(_operation.leftExpression());
auto nodes = splitFlow<2>();
nodes[0] = createFlow(nodes[0], _operation.rightExpression());
mergeFlow(nodes, nodes[1]);
return false;
}
default:
return ASTConstVisitor::visit(_operation);
{
if (*_operation.annotation().userDefinedFunction != nullptr)
{
visitNode(_operation);
_operation.leftExpression().accept(*this);
_operation.rightExpression().accept(*this);
m_currentNode->functionDefinition = *_operation.annotation().userDefinedFunction;
auto nextNode = newLabel();
connect(m_currentNode, nextNode);
m_currentNode = nextNode;
return false;
}
}
}
return ASTConstVisitor::visit(_operation);
}
bool ControlFlowBuilder::visit(UnaryOperation const& _operation)
{
solAssert(!!m_currentNode);
if (*_operation.annotation().userDefinedFunction != nullptr)
{
visitNode(_operation);
_operation.subExpression().accept(*this);
m_currentNode->functionDefinition = *_operation.annotation().userDefinedFunction;
auto nextNode = newLabel();
connect(m_currentNode, nextNode);
m_currentNode = nextNode;
return false;
}
return ASTConstVisitor::visit(_operation);
}
bool ControlFlowBuilder::visit(Conditional const& _conditional)

View File

@ -50,6 +50,7 @@ private:
// Visits for constructing the control flow.
bool visit(BinaryOperation const& _operation) override;
bool visit(UnaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(TryStatement const& _tryStatement) override;
bool visit(IfStatement const& _ifStatement) override;

View File

@ -204,6 +204,20 @@ bool FunctionCallGraphBuilder::visit(MemberAccess const& _memberAccess)
return true;
}
bool FunctionCallGraphBuilder::visit(BinaryOperation const& _binaryOperation)
{
if (*_binaryOperation.annotation().userDefinedFunction != nullptr)
functionReferenced(**_binaryOperation.annotation().userDefinedFunction, true /* called directly */);
return true;
}
bool FunctionCallGraphBuilder::visit(UnaryOperation const& _unaryOperation)
{
if (*_unaryOperation.annotation().userDefinedFunction != nullptr)
functionReferenced(**_unaryOperation.annotation().userDefinedFunction, true /* called directly */);
return true;
}
bool FunctionCallGraphBuilder::visit(ModifierInvocation const& _modifierInvocation)
{
if (auto const* modifier = dynamic_cast<ModifierDefinition const*>(_modifierInvocation.name().annotation().referencedDeclaration))

View File

@ -72,6 +72,8 @@ private:
bool visit(EmitStatement const& _emitStatement) override;
bool visit(Identifier const& _identifier) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(BinaryOperation const& _binaryOperation) override;
bool visit(UnaryOperation const& _unaryOperation) override;
bool visit(ModifierInvocation const& _modifierInvocation) override;
bool visit(NewExpression const& _newExpression) override;

View File

@ -411,6 +411,12 @@ void SyntaxChecker::endVisit(ContractDefinition const&)
bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
{
if (!_usingFor.usesBraces())
solAssert(
_usingFor.functionsAndOperators().size() == 1 &&
!get<1>(_usingFor.functionsAndOperators().front())
);
if (!m_currentContractKind && !_usingFor.typeName())
m_errorReporter.syntaxError(
8118_error,

View File

@ -24,6 +24,7 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/UserDefinableOperators.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libyul/AsmAnalysis.h>
@ -1606,7 +1607,7 @@ bool TypeChecker::visit(Assignment const& _assignment)
7366_error,
_assignment.location(),
"Operator " +
string(TokenTraits::toString(_assignment.assignmentOperator())) +
string(TokenTraits::friendlyName(_assignment.assignmentOperator())) +
" not compatible with types " +
t->humanReadableName() +
" and " +
@ -1729,16 +1730,45 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
requireLValue(_operation.subExpression(), false);
else
_operation.subExpression().accept(*this);
Type const* subExprType = type(_operation.subExpression());
TypeResult result = type(_operation.subExpression())->unaryOperatorResult(op);
if (!result)
Type const* operandType = type(_operation.subExpression());
// Check if the operator is built-in or user-defined.
TypeResult builtinResult = operandType->unaryOperatorResult(op);
set<FunctionDefinition const*, ASTNode::CompareByID> matchingDefinitions = operandType->operatorDefinitions(
op,
*currentDefinitionScope(),
true // _unary
);
// Operator can't be both user-defined and built-in at the same time.
solAssert(!builtinResult || matchingDefinitions.empty());
// By default use the type we'd expect from correct code. This way we can continue analysis
// of other expressions in a sensible way in case of a non-fatal error.
Type const* resultType = operandType;
FunctionDefinition const* operatorDefinition = nullptr;
if (builtinResult)
resultType = builtinResult;
else if (!matchingDefinitions.empty())
{
// This is checked along with `using for` directive but the error is not fatal.
if (matchingDefinitions.size() != 1)
solAssert(m_errorReporter.hasErrors());
operatorDefinition = *matchingDefinitions.begin();
}
else
{
string description = fmt::format(
"Built-in unary operator {} cannot be applied to type {}.{}",
TokenTraits::toString(op),
subExprType->humanReadableName(),
!result.message().empty() ? " " + result.message() : ""
"Built-in unary operator {} cannot be applied to type {}.",
TokenTraits::friendlyName(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,
@ -1746,14 +1776,21 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
m_errorReporter.fatalTypeError(9767_error, _operation.location(), description);
else
m_errorReporter.typeError(4907_error, _operation.location(), description);
_operation.annotation().type = subExprType;
}
else
_operation.annotation().type = result.get();
_operation.annotation().userDefinedFunction = operatorDefinition;
TypePointers const& returnParameterTypes = _operation.userDefinedFunctionType()->returnParameterTypes();
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.
resultType = returnParameterTypes[0];
_operation.annotation().type = resultType;
_operation.annotation().isConstant = false;
_operation.annotation().isPure =
!modifying &&
*_operation.subExpression().annotation().isPure;
*_operation.subExpression().annotation().isPure &&
(!_operation.userDefinedFunctionType() || _operation.userDefinedFunctionType()->isPure());
_operation.annotation().isLValue = false;
return false;
@ -1763,31 +1800,95 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
{
Type const* leftType = type(_operation.leftExpression());
Type const* rightType = type(_operation.rightExpression());
TypeResult result = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
Type const* commonType = result.get();
if (!commonType)
// Check if the operator is built-in or user-defined.
TypeResult builtinResult = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
set<FunctionDefinition const*, ASTNode::CompareByID> matchingDefinitions = leftType->operatorDefinitions(
_operation.getOperator(),
*currentDefinitionScope(),
false // _unary
);
// Operator can't be both user-defined and built-in at the same time.
solAssert(!builtinResult || matchingDefinitions.empty());
Type const* commonType = nullptr;
FunctionDefinition const* operatorDefinition = nullptr;
if (builtinResult)
commonType = builtinResult.get();
else if (!matchingDefinitions.empty())
{
m_errorReporter.typeError(
2271_error,
_operation.location(),
"Built-in binary operator " +
string(TokenTraits::toString(_operation.getOperator())) +
" cannot be applied to types " +
leftType->humanReadableName() +
" and " +
rightType->humanReadableName() + "." +
(!result.message().empty() ? " " + result.message() : "")
);
// This is checked along with `using for` directive but the error is not fatal.
if (matchingDefinitions.size() != 1)
solAssert(m_errorReporter.hasErrors());
operatorDefinition = *matchingDefinitions.begin();
// Set common type to the type used in the `using for` directive.
commonType = leftType;
}
else
{
string description = fmt::format(
"Built-in binary operator {} cannot be applied to types {} and {}.",
TokenTraits::friendlyName(_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.
commonType = leftType;
}
_operation.annotation().commonType = commonType;
_operation.annotation().type =
_operation.annotation().userDefinedFunction = operatorDefinition;
FunctionType const* userDefinedFunctionType = _operation.userDefinedFunctionType();
// By default use the type we'd expect from correct code. This way we can continue analysis
// of other expressions in a sensible way in case of a non-fatal error.
Type const* resultType =
TokenTraits::isCompareOp(_operation.getOperator()) ?
TypeProvider::boolean() :
commonType;
if (operatorDefinition)
{
TypePointers const& parameterTypes = userDefinedFunctionType->parameterTypes();
TypePointers const& returnParameterTypes = userDefinedFunctionType->returnParameterTypes();
// operatorDefinitions() filters out definitions with non-matching first argument.
solAssert(parameterTypes.size() == 2);
solAssert(parameterTypes[0] && *leftType == *parameterTypes[0]);
if (*rightType != *parameterTypes[0])
m_errorReporter.typeError(
5653_error,
_operation.location(),
fmt::format(
"The type of the second operand of this user-defined binary operator {} "
"does not match the type of the first operand, which is {}.",
TokenTraits::friendlyName(_operation.getOperator()),
parameterTypes[0]->humanReadableName()
)
);
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.
resultType = returnParameterTypes[0];
}
_operation.annotation().type = resultType;
_operation.annotation().isPure =
*_operation.leftExpression().annotation().isPure &&
*_operation.rightExpression().annotation().isPure;
*_operation.rightExpression().annotation().isPure &&
(!userDefinedFunctionType || userDefinedFunctionType->isPure());
_operation.annotation().isLValue = false;
_operation.annotation().isConstant = false;
@ -1814,14 +1915,14 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
m_errorReporter.warning(
3149_error,
_operation.location(),
"The result type of the " +
operation +
" operation is equal to the type of the first operand (" +
commonType->humanReadableName() +
") ignoring the (larger) type of the second operand (" +
rightType->humanReadableName() +
") which might be unexpected. Silence this warning by either converting "
"the first or the second operand to the type of the other."
fmt::format(
"The result type of the {} operation is equal to the type of the first operand ({}) "
"ignoring the (larger) type of the second operand ({}) which might be unexpected. "
"Silence this warning by either converting the first or the second operand to the type of the other.",
operation,
commonType->humanReadableName(),
rightType->humanReadableName()
)
);
}
}
@ -3820,7 +3921,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
);
solAssert(normalizedType);
for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
for (auto const& [path, operator_]: _usingFor.functionsAndOperators())
{
solAssert(path->annotation().referencedDeclaration);
FunctionDefinition const& functionDefinition =
@ -3839,8 +3940,8 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
4731_error,
path->location(),
SecondarySourceLocation().append(
"Function defined here:",
functionDefinition.location()
"Function defined here:",
functionDefinition.location()
),
fmt::format(
"The function \"{}\" does not have any parameters, and therefore cannot be attached to the type \"{}\".",
@ -3859,8 +3960,8 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
6772_error,
path->location(),
SecondarySourceLocation().append(
"Function defined here:",
functionDefinition.location()
"Function defined here:",
functionDefinition.location()
),
fmt::format(
"Function \"{}\" is private and therefore cannot be attached"
@ -3875,13 +3976,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionTypeWithBoundFirstArgument->selfType())
);
if (!result)
if (!result && !operator_)
m_errorReporter.typeError(
3100_error,
path->location(),
SecondarySourceLocation().append(
"Function defined here:",
functionDefinition.location()
"Function defined here:",
functionDefinition.location()
),
fmt::format(
"The function \"{}\" cannot be attached to the type \"{}\" because the type cannot "
@ -3892,6 +3993,144 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
result.message().empty() ? "." : ": " + result.message()
)
);
else if (operator_.has_value())
{
if (!_usingFor.global())
m_errorReporter.typeError(
3320_error,
path->location(),
"Operators can only be defined in a global 'using for' directive."
);
if (
functionType->stateMutability() != StateMutability::Pure ||
!functionDefinition.isFree()
)
m_errorReporter.typeError(
7775_error,
path->location(),
SecondarySourceLocation().append(
"Function defined as non-pure here:",
functionDefinition.location()
),
"Only pure free functions can be used to define operators."
);
solAssert(!functionType->hasBoundFirstArgument());
TypePointers const& parameterTypes = functionType->parameterTypes();
size_t const parameterCount = parameterTypes.size();
if (usingForType->category() != Type::Category::UserDefinedValueType)
{
m_errorReporter.typeError(
5332_error,
path->location(),
"Operators can only be implemented for user-defined value types."
);
continue;
}
solAssert(usingForType->typeDefinition());
bool identicalFirstTwoParameters = (parameterCount < 2 || *parameterTypes.at(0) == *parameterTypes.at(1));
bool isUnaryOnlyOperator = (!TokenTraits::isBinaryOp(operator_.value()) && TokenTraits::isUnaryOp(operator_.value()));
bool isBinaryOnlyOperator =
(TokenTraits::isBinaryOp(operator_.value()) && !TokenTraits::isUnaryOp(operator_.value())) ||
operator_.value() == Token::Add;
bool firstParameterMatchesUsingFor = parameterCount == 0 || *usingForType == *parameterTypes.front();
optional<string> wrongParametersMessage;
if (isBinaryOnlyOperator && (parameterCount != 2 || !identicalFirstTwoParameters))
wrongParametersMessage = fmt::format("two parameters of type {} and the same data location", usingForType->canonicalName());
else if (isUnaryOnlyOperator && (parameterCount != 1 || !firstParameterMatchesUsingFor))
wrongParametersMessage = fmt::format("exactly one parameter of type {}", usingForType->canonicalName());
else if (parameterCount >= 3 || !firstParameterMatchesUsingFor || !identicalFirstTwoParameters)
wrongParametersMessage = fmt::format("one or two parameters of type {} and the same data location", usingForType->canonicalName());
if (wrongParametersMessage.has_value())
m_errorReporter.typeError(
1884_error,
functionDefinition.parameterList().location(),
SecondarySourceLocation().append(
"Function was used to implement an operator here:",
path->location()
),
fmt::format(
"Wrong parameters in operator definition. "
"The function \"{}\" needs to have {} to be used for the operator {}.",
joinHumanReadable(path->path(), "."),
wrongParametersMessage.value(),
TokenTraits::friendlyName(operator_.value())
)
);
// This case is separately validated for all attached functions and is a fatal error
solAssert(parameterCount != 0);
TypePointers const& returnParameterTypes = functionType->returnParameterTypes();
size_t const returnParameterCount = returnParameterTypes.size();
optional<string> wrongReturnParametersMessage;
if (!TokenTraits::isCompareOp(operator_.value()) && operator_.value() != Token::Not)
{
if (returnParameterCount != 1 || *usingForType != *returnParameterTypes.front())
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";
}
else if (returnParameterCount != 1 || *returnParameterTypes.front() != *TypeProvider::boolean())
wrongReturnParametersMessage = "exactly one value of type bool";
solAssert(functionDefinition.returnParameterList());
if (wrongReturnParametersMessage.has_value())
m_errorReporter.typeError(
7743_error,
functionDefinition.returnParameterList()->location(),
SecondarySourceLocation().append(
"Function was used to implement an operator here:",
path->location()
),
fmt::format(
"Wrong return parameters in operator definition. "
"The function \"{}\" needs to return {} to be used for the operator {}.",
joinHumanReadable(path->path(), "."),
wrongReturnParametersMessage.value(),
TokenTraits::friendlyName(operator_.value())
)
);
if (parameterCount != 1 && parameterCount != 2)
solAssert(m_errorReporter.hasErrors());
else
{
// TODO: This is pretty inefficient. For every operator binding we find, we're
// traversing all bindings in all `using for` directives in the current scope.
set<FunctionDefinition const*, ASTNode::CompareByID> matchingDefinitions = usingForType->operatorDefinitions(
operator_.value(),
*currentDefinitionScope(),
parameterCount == 1 // _unary
);
if (matchingDefinitions.size() >= 2)
{
// TODO: We should point at other places that bind the operator rather than at
// the definitions they bind.
SecondarySourceLocation secondaryLocation;
for (FunctionDefinition const* definition: matchingDefinitions)
if (functionDefinition != *definition)
secondaryLocation.append("Conflicting definition:", definition->location());
m_errorReporter.typeError(
4705_error,
path->location(),
secondaryLocation,
fmt::format(
"User-defined {} operator {} has more than one definition matching the operand type visible in the current scope.",
parameterCount == 1 ? "unary" : "binary",
TokenTraits::friendlyName(operator_.value())
)
);
}
}
}
}
}

View File

@ -331,6 +331,18 @@ void ViewPureChecker::reportFunctionCallMutability(StateMutability _mutability,
reportMutability(_mutability, _location);
}
void ViewPureChecker::endVisit(BinaryOperation const& _binaryOperation)
{
if (*_binaryOperation.annotation().userDefinedFunction != nullptr)
reportFunctionCallMutability((*_binaryOperation.annotation().userDefinedFunction)->stateMutability(), _binaryOperation.location());
}
void ViewPureChecker::endVisit(UnaryOperation const& _unaryOperation)
{
if (*_unaryOperation.annotation().userDefinedFunction != nullptr)
reportFunctionCallMutability((*_unaryOperation.annotation().userDefinedFunction)->stateMutability(), _unaryOperation.location());
}
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
{
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)

View File

@ -54,6 +54,8 @@ private:
bool visit(FunctionDefinition const& _funDef) override;
void endVisit(FunctionDefinition const& _funDef) override;
void endVisit(BinaryOperation const& _binaryOperation) override;
void endVisit(UnaryOperation const& _unaryOperation) override;
bool visit(ModifierDefinition const& _modifierDef) override;
void endVisit(ModifierDefinition const& _modifierDef) override;
void endVisit(Identifier const& _identifier) override;

View File

@ -30,7 +30,9 @@
#include <libsolutil/FunctionSelector.h>
#include <libsolutil/Keccak256.h>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/tail.hpp>
#include <range/v3/view/zip.hpp>
#include <boost/algorithm/string.hpp>
@ -367,6 +369,11 @@ TypeDeclarationAnnotation& UserDefinedValueTypeDefinition::annotation() const
return initAnnotation<TypeDeclarationAnnotation>();
}
std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> UsingForDirective::functionsAndOperators() const
{
return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to<vector>;
}
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<MemberAccessAnnotation>();
}
OperationAnnotation& UnaryOperation::annotation() const
{
return initAnnotation<OperationAnnotation>();
}
FunctionType const* UnaryOperation::userDefinedFunctionType() const
{
if (*annotation().userDefinedFunction == nullptr)
return nullptr;
FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction;
return dynamic_cast<FunctionType const*>(
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<FunctionType const*>(
userDefinedFunction->libraryFunction() ?
userDefinedFunction->typeViaContractName() :
userDefinedFunction->type()
);
}
BinaryOperationAnnotation& BinaryOperation::annotation() const
{
return initAnnotation<BinaryOperationAnnotation>();

View File

@ -647,11 +647,17 @@ private:
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`.
* 2. `using LibraryName for *` attaches to all types.
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., `fn`, respectively to `T`.
* 4. `using {f1 as op1, f2 as op2, ..., fn as opn} for T` implements operator `opn` for type `T` with function `fn`.
*
* For version 3, T has to be implicitly convertible to the first parameter type of
* 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.
* All parameters and return value of all the functions have to be of type T.
* 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.
*
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
* a user-defined type defined in the same file at file level. In this case, the methods are
* attached to all objects of that type regardless of scope.
@ -663,16 +669,19 @@ public:
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<IdentifierPath>> _functionsOrLibrary,
std::vector<std::optional<Token>> _operators,
bool _usesBraces,
ASTPointer<TypeName> _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 +692,18 @@ public:
/// @returns a list of functions or the single library.
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functionsOrLibrary; }
std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> 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<ASTPointer<IdentifierPath>> 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<std::optional<Token>> m_operators;
bool m_usesBraces;
ASTPointer<TypeName> m_typeName;
bool m_global = false;
@ -2073,6 +2088,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<Expression> m_subExpression;
@ -2104,6 +2123,8 @@ public:
Expression const& rightExpression() const { return *m_right; }
Token getOperator() const { return m_operator; }
FunctionType const* userDefinedFunctionType() const;
BinaryOperationAnnotation& annotation() const override;
private:

View File

@ -312,7 +312,12 @@ struct MemberAccessAnnotation: ExpressionAnnotation
util::SetOnce<VirtualLookup> requiredLookup;
};
struct BinaryOperationAnnotation: ExpressionAnnotation
struct OperationAnnotation: ExpressionAnnotation
{
util::SetOnce<FunctionDefinition const*> 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).

View File

@ -330,19 +330,31 @@ bool ASTJsonExporter::visit(UsingForDirective const& _node)
vector<pair<string, Json::Value>> attributes = {
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
};
if (_node.usesBraces())
{
Json::Value functionList;
for (auto const& function: _node.functionsOrLibrary())
for (auto&& [function, op]: _node.functionsAndOperators())
{
Json::Value functionNode;
functionNode["function"] = toJson(*function);
if (!op.has_value())
functionNode["function"] = toJson(*function);
else
{
functionNode["definition"] = toJson(*function);
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));
@ -830,6 +842,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;
@ -843,6 +858,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;

View File

@ -22,6 +22,7 @@
*/
#include <libsolidity/ast/ASTJsonImporter.h>
#include <libsolidity/ast/UserDefinableOperators.h>
#include <libyul/AsmJsonImporter.h>
#include <libyul/AST.h>
@ -397,15 +398,42 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
{
vector<ASTPointer<IdentifierPath>> functions;
vector<optional<Token>> operators;
if (_node.isMember("libraryName"))
{
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("function"))
{
astAssert(!function.isMember("operator"));
astAssert(!function.isMember("definition"));
functions.emplace_back(createIdentifierPath(function["function"]));
operators.emplace_back(nullopt);
}
else
{
astAssert(function.isMember("operator"));
astAssert(function.isMember("definition"));
Token const operatorName = scanSingleToken(function["operator"]);
astAssert(util::contains(frontend::userDefinableOperators, operatorName));
functions.emplace_back(createIdentifierPath(function["definition"]));
operators.emplace_back(operatorName);
}
}
return createASTNode<UsingForDirective>(
_node,
std::move(functions),
std::move(operators),
!_node.isMember("libraryName"),
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
memberAsBool(_node, "global")

View File

@ -383,6 +383,38 @@ vector<UsingForDirective const*> usingForDirectivesForType(Type const& _type, AS
}
set<FunctionDefinition const*, ASTNode::CompareByID> Type::operatorDefinitions(
Token _token,
ASTNode const& _scope,
bool _unary
) const
{
if (!typeDefinition())
return {};
set<FunctionDefinition const*, ASTNode::CompareByID> matchingDefinitions;
for (UsingForDirective const* directive: usingForDirectivesForType(*this, _scope))
for (auto const& [identifierPath, operator_]: directive->functionsAndOperators())
{
if (operator_ != _token)
continue;
auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(
*identifierPath->annotation().referencedDeclaration
);
auto const* functionType = dynamic_cast<FunctionType const*>(
functionDefinition.libraryFunction() ? functionDefinition.typeViaContractName() : functionDefinition.type()
);
solAssert(functionType && !functionType->parameterTypes().empty());
size_t parameterCount = functionDefinition.parameterList().parameters().size();
if (*this == *functionType->parameterTypes().front() && (_unary ? parameterCount == 1 : parameterCount == 2))
matchingDefinitions.insert(&functionDefinition);
}
return matchingDefinitions;
}
MemberList::MemberMap Type::attachedFunctions(Type const& _type, ASTNode const& _scope)
{
MemberList::MemberMap members;
@ -405,8 +437,13 @@ MemberList::MemberMap Type::attachedFunctions(Type const& _type, ASTNode const&
};
for (UsingForDirective const* ufd: usingForDirectivesForType(_type, _scope))
for (auto const& identifierPath: ufd->functionsOrLibrary())
for (auto const& [identifierPath, operator_]: ufd->functionsAndOperators())
{
if (operator_.has_value())
// Functions used to define operators are not automatically attached to the type.
// I.e. `using {f, f as +} for T` allows `T x; x.f()` but `using {f as +} for T` does not.
continue;
solAssert(identifierPath);
Declaration const* declaration = identifierPath->annotation().referencedDeclaration;
solAssert(declaration);

View File

@ -377,6 +377,21 @@ public:
/// Clears all internally cached values (if any).
virtual void clearCache() const;
/// 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.
///
/// @note: If the AST has passed analysis without errors,
/// 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.
std::set<FunctionDefinition const*, ASTCompareByID<ASTNode>> operatorDefinitions(
Token _token,
ASTNode const& _scope,
bool _unary
) const;
private:
/// @returns a member list containing all members added to this type by `using for` directives.
static MemberList::MemberMap attachedFunctions(Type const& _type, ASTNode const& _scope);

View File

@ -0,0 +1,31 @@
#pragma once
#include <liblangutil/Token.h>
#include <vector>
namespace solidity::frontend
{
std::vector<langutil::Token> const userDefinableOperators = {
// Bitwise
langutil::Token::BitOr,
langutil::Token::BitAnd,
langutil::Token::BitXor,
langutil::Token::BitNot,
// Arithmetic
langutil::Token::Add,
langutil::Token::Sub,
langutil::Token::Mul,
langutil::Token::Div,
langutil::Token::Mod,
// Comparison
langutil::Token::Equal,
langutil::Token::NotEqual,
langutil::Token::LessThan,
langutil::Token::GreaterThan,
langutil::Token::LessThanOrEqual,
langutil::Token::GreaterThanOrEqual,
};
}

View File

@ -410,6 +410,38 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
{
CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction;
if (function)
{
solAssert(function->isFree());
FunctionType const* functionType = _unaryOperation.userDefinedFunctionType();
solAssert(functionType);
solAssert(functionType->parameterTypes().size() == 1);
solAssert(functionType->returnParameterTypes().size() == 1);
solAssert(functionType->kind() == FunctionType::Kind::Internal);
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
acceptAndConvert(
_unaryOperation.subExpression(),
*functionType->parameterTypes()[0],
false // _cleanupNeeded
);
m_context << m_context.functionEntryLabel(*function).pushTag();
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
unsigned parameterSize = CompilerUtils::sizeOnStack(functionType->parameterTypes());
unsigned returnParametersSize = CompilerUtils::sizeOnStack(functionType->returnParameterTypes());
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(static_cast<int>(returnParametersSize - parameterSize) - 1);
return false;
}
Type const& type = *_unaryOperation.annotation().type;
if (type.category() == Type::Category::RationalNumber)
{
@ -502,7 +534,42 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
Expression const& leftExpression = _binaryOperation.leftExpression();
Expression const& rightExpression = _binaryOperation.rightExpression();
solAssert(!!_binaryOperation.annotation().commonType, "");
FunctionDefinition const* function = *_binaryOperation.annotation().userDefinedFunction;
if (function)
{
solAssert(function->isFree());
FunctionType const* functionType = _binaryOperation.userDefinedFunctionType();
solAssert(functionType);
solAssert(functionType->parameterTypes().size() == 2);
solAssert(functionType->returnParameterTypes().size() == 1);
solAssert(functionType->kind() == FunctionType::Kind::Internal);
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
acceptAndConvert(
leftExpression,
*functionType->parameterTypes()[0],
false // _cleanupNeeded
);
acceptAndConvert(
rightExpression,
*functionType->parameterTypes()[1],
false // _cleanupNeeded
);
m_context << m_context.functionEntryLabel(*function).pushTag();
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
unsigned parameterSize = CompilerUtils::sizeOnStack(functionType->parameterTypes());
unsigned returnParametersSize = CompilerUtils::sizeOnStack(functionType->returnParameterTypes());
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(static_cast<int>(returnParametersSize - parameterSize) - 1);
return false;
}
solAssert(!!_binaryOperation.annotation().commonType);
Type const* commonType = _binaryOperation.annotation().commonType;
Token const c_op = _binaryOperation.getOperator();

View File

@ -672,6 +672,30 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation)
{
setLocation(_unaryOperation);
FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction;
if (function)
{
_unaryOperation.subExpression().accept(*this);
setLocation(_unaryOperation);
solAssert(function->isImplemented());
solAssert(function->isFree());
solAssert(function->parameters().size() == 1);
solAssert(function->returnParameters().size() == 1);
solAssert(*function->returnParameters()[0]->type() == *_unaryOperation.annotation().type);
string argument = expressionAsType(_unaryOperation.subExpression(), *function->parameters()[0]->type());
solAssert(!argument.empty());
solAssert(_unaryOperation.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal);
define(_unaryOperation) <<
m_context.enqueueFunctionForCodeGeneration(*function) <<
("(" + argument + ")\n");
return false;
}
Type const& resultType = type(_unaryOperation);
Token const op = _unaryOperation.getOperator();
@ -775,6 +799,31 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
{
setLocation(_binOp);
FunctionDefinition const* function = *_binOp.annotation().userDefinedFunction;
if (function)
{
_binOp.leftExpression().accept(*this);
_binOp.rightExpression().accept(*this);
setLocation(_binOp);
solAssert(function->isImplemented());
solAssert(function->isFree());
solAssert(function->parameters().size() == 2);
solAssert(function->returnParameters().size() == 1);
solAssert(*function->returnParameters()[0]->type() == *_binOp.annotation().type);
string left = expressionAsType(_binOp.leftExpression(), *function->parameters()[0]->type());
string right = expressionAsType(_binOp.rightExpression(), *function->parameters()[1]->type());
solAssert(!left.empty() && !right.empty());
solAssert(_binOp.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal);
define(_binOp) <<
m_context.enqueueFunctionForCodeGeneration(*function) <<
("(" + left + ", " + right + ")\n");
return false;
}
solAssert(!!_binOp.annotation().commonType);
Type const* commonType = _binOp.annotation().commonType;
langutil::Token op = _binOp.getOperator();

View File

@ -449,11 +449,25 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
if (_op.annotation().type->category() == Type::Category::RationalNumber)
return;
if (TokenTraits::isBitOp(_op.getOperator()))
if (TokenTraits::isBitOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction)
return bitwiseNotOperation(_op);
createExpr(_op);
// User-defined operators are essentially function calls.
if (*_op.annotation().userDefinedFunction)
{
// TODO: Implement user-defined operators properly.
m_errorReporter.warning(
6156_error,
_op.location(),
"User-defined operators are not yet supported by SMTChecker. "s +
"This invocation of operator " + TokenTraits::friendlyName(_op.getOperator()) +
" has been ignored, which may lead to incorrect results."
);
return;
}
auto const* subExpr = innermostTuple(_op.subExpression());
auto type = _op.annotation().type;
switch (_op.getOperator())
@ -524,7 +538,7 @@ bool SMTEncoder::visit(BinaryOperation const& _op)
{
if (shortcutRationalNumber(_op))
return false;
if (TokenTraits::isBooleanOp(_op.getOperator()))
if (TokenTraits::isBooleanOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction)
{
booleanOperation(_op);
return false;
@ -537,11 +551,25 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
/// If _op is const evaluated the RationalNumber shortcut was taken.
if (isConstant(_op))
return;
if (TokenTraits::isBooleanOp(_op.getOperator()))
if (TokenTraits::isBooleanOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction)
return;
createExpr(_op);
// User-defined operators are essentially function calls.
if (*_op.annotation().userDefinedFunction)
{
// TODO: Implement user-defined operators properly.
m_errorReporter.warning(
6756_error,
_op.location(),
"User-defined operators are not yet supported by SMTChecker. "s +
"This invocation of operator " + TokenTraits::friendlyName(_op.getOperator()) +
" has been ignored, which may lead to incorrect results."
);
return;
}
if (TokenTraits::isArithmeticOp(_op.getOperator()))
arithmeticOperation(_op);
else if (TokenTraits::isCompareOp(_op.getOperator()))

View File

@ -23,6 +23,7 @@
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/ast/UserDefinableOperators.h>
#include <libsolidity/interface/Version.h>
#include <libyul/AST.h>
#include <libyul/AsmParser.h>
@ -976,6 +977,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
expectToken(Token::Using);
vector<ASTPointer<IdentifierPath>> functions;
vector<optional<Token>> operators;
bool const usesBraces = m_scanner->currentToken() == Token::LBrace;
if (usesBraces)
{
@ -983,12 +985,35 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
{
advance();
functions.emplace_back(parseIdentifierPath());
if (m_scanner->currentToken() == Token::As)
{
advance();
Token operator_ = m_scanner->currentToken();
if (!util::contains(userDefinableOperators, operator_))
{
parserError(
4403_error,
fmt::format(
"Not a user-definable operator: {}. Only the following operators can be user-defined: {}",
(!m_scanner->currentLiteral().empty() ? m_scanner->currentLiteral() : string(TokenTraits::toString(operator_))),
util::joinHumanReadable(userDefinableOperators | ranges::views::transform([](Token _t) { return string{TokenTraits::toString(_t)}; }))
)
);
}
operators.emplace_back(operator_);
advance();
}
else
operators.emplace_back(nullopt);
}
while (m_scanner->currentToken() == Token::Comma);
expectToken(Token::RBrace);
}
else
{
functions.emplace_back(parseIdentifierPath());
operators.emplace_back(nullopt);
}
ASTPointer<TypeName> typeName;
expectToken(Token::For);
@ -1004,7 +1029,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
}
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
return nodeFactory.createNode<UsingForDirective>(std::move(functions), usesBraces, typeName, global);
return nodeFactory.createNode<UsingForDirective>(std::move(functions), std::move(operators), usesBraces, typeName, global);
}
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()

View File

@ -0,0 +1,663 @@
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
49
],
"I8":
[
2
],
"sub":
[
20
],
"unsub":
[
30
]
},
"id": 50,
"nodeType": "SourceUnit",
"nodes":
[
{
"canonicalName": "I8",
"id": 2,
"name": "I8",
"nameLocation": "5:2:1",
"nodeType": "UserDefinedValueTypeDefinition",
"src": "0:16:1",
"underlyingType":
{
"id": 1,
"name": "int8",
"nodeType": "ElementaryTypeName",
"src": "11:4:1",
"typeDescriptions":
{
"typeIdentifier": "t_int8",
"typeString": "int8"
}
}
},
{
"functionList":
[
{
"definition":
{
"id": 3,
"name": "sub",
"nameLocations":
[
"24:3:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 20,
"src": "24:3:1"
},
"operator": "-"
},
{
"definition":
{
"id": 4,
"name": "unsub",
"nameLocations":
[
"34:5:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 30,
"src": "34:5:1"
},
"operator": "-"
}
],
"global": true,
"id": 7,
"nodeType": "UsingForDirective",
"src": "17:43:1",
"typeName":
{
"id": 6,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 5,
"name": "I8",
"nameLocations":
[
"50:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "50:2:1"
},
"referencedDeclaration": 2,
"src": "50:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
}
},
{
"body":
{
"id": 19,
"nodeType": "Block",
"src": "100:2:1",
"statements": []
},
"id": 20,
"implemented": true,
"kind": "freeFunction",
"modifiers": [],
"name": "sub",
"nameLocation": "70:3:1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 14,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 10,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 20,
"src": "74:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 9,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 8,
"name": "I8",
"nameLocations":
[
"74:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "74:2:1"
},
"referencedDeclaration": 2,
"src": "74:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
},
{
"constant": false,
"id": 13,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 20,
"src": "78:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 12,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 11,
"name": "I8",
"nameLocations":
[
"78:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "78:2:1"
},
"referencedDeclaration": 2,
"src": "78:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "73:8:1"
},
"returnParameters":
{
"id": 18,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 17,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 20,
"src": "96:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 16,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 15,
"name": "I8",
"nameLocations":
[
"96:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "96:2:1"
},
"referencedDeclaration": 2,
"src": "96:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "95:4:1"
},
"scope": 50,
"src": "61:41:1",
"stateMutability": "pure",
"virtual": false,
"visibility": "internal"
},
{
"body":
{
"id": 29,
"nodeType": "Block",
"src": "140:2:1",
"statements": []
},
"id": 30,
"implemented": true,
"kind": "freeFunction",
"modifiers": [],
"name": "unsub",
"nameLocation": "112:5:1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 24,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 23,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 30,
"src": "118:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 22,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 21,
"name": "I8",
"nameLocations":
[
"118:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "118:2:1"
},
"referencedDeclaration": 2,
"src": "118:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "117:4:1"
},
"returnParameters":
{
"id": 28,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 27,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 30,
"src": "136:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 26,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 25,
"name": "I8",
"nameLocations":
[
"136:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "136:2:1"
},
"referencedDeclaration": 2,
"src": "136:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "135:4:1"
},
"scope": 50,
"src": "103:39:1",
"stateMutability": "pure",
"virtual": false,
"visibility": "internal"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "C",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 49,
"linearizedBaseContracts":
[
49
],
"name": "C",
"nameLocation": "152:1:1",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 47,
"nodeType": "Block",
"src": "208:30:1",
"statements":
[
{
"expression":
{
"commonType":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"function": 20,
"id": 45,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"leftExpression":
{
"function": 30,
"id": 43,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"nodeType": "UnaryOperation",
"operator": "-",
"prefix": true,
"src": "225:2:1",
"subExpression":
{
"id": 42,
"name": "a",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 33,
"src": "226:1:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"nodeType": "BinaryOperation",
"operator": "-",
"rightExpression":
{
"id": 44,
"name": "b",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 36,
"src": "230:1:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"src": "225:6:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"functionReturnParameters": 41,
"id": 46,
"nodeType": "Return",
"src": "218:13:1"
}
]
},
"functionSelector": "ac9fe858",
"id": 48,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "f",
"nameLocation": "169:1:1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 37,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 33,
"mutability": "mutable",
"name": "a",
"nameLocation": "174:1:1",
"nodeType": "VariableDeclaration",
"scope": 48,
"src": "171:4:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 32,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 31,
"name": "I8",
"nameLocations":
[
"171:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "171:2:1"
},
"referencedDeclaration": 2,
"src": "171:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
},
{
"constant": false,
"id": 36,
"mutability": "mutable",
"name": "b",
"nameLocation": "180:1:1",
"nodeType": "VariableDeclaration",
"scope": 48,
"src": "177:4:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 35,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 34,
"name": "I8",
"nameLocations":
[
"177:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "177:2:1"
},
"referencedDeclaration": 2,
"src": "177:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "170:12:1"
},
"returnParameters":
{
"id": 41,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 40,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 48,
"src": "204:2:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
},
"typeName":
{
"id": 39,
"nodeType": "UserDefinedTypeName",
"pathNode":
{
"id": 38,
"name": "I8",
"nameLocations":
[
"204:2:1"
],
"nodeType": "IdentifierPath",
"referencedDeclaration": 2,
"src": "204:2:1"
},
"referencedDeclaration": 2,
"src": "204:2:1",
"typeDescriptions":
{
"typeIdentifier": "t_userDefinedValueType$_I8_$2",
"typeString": "I8"
}
},
"visibility": "internal"
}
],
"src": "203:4:1"
},
"scope": 49,
"src": "160:78:1",
"stateMutability": "pure",
"virtual": false,
"visibility": "public"
}
],
"scope": 50,
"src": "143:97:1",
"usedErrors": []
}
],
"src": "0:241:1"
}

View File

@ -0,0 +1,11 @@
type I8 is int8;
using {sub as -, unsub as -} for I8 global;
function sub(I8, I8) pure returns (I8) {}
function unsub(I8) pure returns (I8) {}
contract C {
function f(I8 a, I8 b) public pure returns (I8) {
return -a - b;
}
}
// ----

View File

@ -1702,6 +1702,72 @@ BOOST_AUTO_TEST_CASE(using_for)
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_CASE(user_defined_binary_operator)
{
unique_ptr<CompilerStack> compilerStack = parseAndAnalyzeContracts(R"(
type Int is int128;
using {add as +} for Int global;
function add(Int, Int) pure returns (Int) {
return Int.wrap(0);
}
contract C {
function pub() public {
Int.wrap(0) + Int.wrap(1);
}
}
)"s);
tuple<CallGraphMap, CallGraphMap> graphs = collectGraphs(*compilerStack);
map<string, EdgeNames> expectedCreationEdges = {
{"C", {}},
};
map<string, EdgeNames> expectedDeployedEdges = {
{"C", {
{"Entry", "function C.pub()"},
{"function C.pub()", "function add(Int,Int)"},
}},
};
checkCallGraphExpectations(get<0>(graphs), expectedCreationEdges);
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_CASE(user_defined_unary_operator)
{
unique_ptr<CompilerStack> compilerStack = parseAndAnalyzeContracts(R"(
type Int is int128;
using {sub as -} for Int global;
function sub(Int) pure returns (Int) {
return Int.wrap(0);
}
contract C {
function pub() public {
-Int.wrap(1);
}
}
)"s);
tuple<CallGraphMap, CallGraphMap> graphs = collectGraphs(*compilerStack);
map<string, EdgeNames> expectedCreationEdges = {
{"C", {}},
};
map<string, EdgeNames> expectedDeployedEdges = {
{"C", {
{"Entry", "function C.pub()"},
{"function C.pub()", "function sub(Int)"},
}},
};
checkCallGraphExpectations(get<0>(graphs), expectedCreationEdges);
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_CASE(getters)
{
unique_ptr<CompilerStack> compilerStack = parseAndAnalyzeContracts(R"(

View File

@ -0,0 +1,92 @@
type Int is int8;
using {
bitor as |, bitand as &, bitxor as ^, bitnot as ~,
add as +, sub as -, unsub as -, mul as *, div as /, mod as %,
eq as ==, noteq as !=, lt as <, gt as >, leq as <=, geq as >=
} for Int global;
function bitor(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) | Int.unwrap(y)); }
function bitand(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) & Int.unwrap(y)); }
function bitxor(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) ^ Int.unwrap(y)); }
function bitnot(Int x) pure returns (Int) { return Int.wrap(~Int.unwrap(x)); }
function add(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) + Int.unwrap(y)); }
function sub(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) - Int.unwrap(y)); }
function unsub(Int x) pure returns (Int) { return Int.wrap(-Int.unwrap(x)); }
function mul(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) * Int.unwrap(y)); }
function div(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) / Int.unwrap(y)); }
function mod(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) % Int.unwrap(y)); }
function eq(Int x, Int y) pure returns (bool) { return Int.unwrap(x) == Int.unwrap(y); }
function noteq(Int x, Int y) pure returns (bool) { return Int.unwrap(x) != Int.unwrap(y); }
function lt(Int x, Int y) pure returns (bool) { return Int.unwrap(x) < Int.unwrap(y); }
function gt(Int x, Int y) pure returns (bool) { return Int.unwrap(x) > Int.unwrap(y); }
function leq(Int x, Int y) pure returns (bool) { return Int.unwrap(x) <= Int.unwrap(y); }
function geq(Int x, Int y) pure returns (bool) { return Int.unwrap(x) >= Int.unwrap(y); }
contract C {
Int constant ZERO = Int.wrap(0);
Int constant ONE = Int.wrap(1);
Int constant TWO = Int.wrap(2);
Int constant THREE = Int.wrap(3);
Int constant SIX = Int.wrap(6);
function testBitwise() public pure {
assert(Int.unwrap(ONE | TWO) == 3);
assert(Int.unwrap(ONE | ZERO) == 1);
assert(Int.unwrap(ONE & THREE) == 1);
assert(Int.unwrap(ONE & ONE) == 1);
assert(Int.unwrap(TWO ^ TWO) == 0);
assert(Int.unwrap(TWO ^ ONE) == 3);
assert(Int.unwrap(~ZERO) == -1);
assert(Int.unwrap(~ONE) == -2);
assert(Int.unwrap(~TWO) == -3);
}
function testArithmetic() public pure {
assert(Int.unwrap(ONE + TWO) == 3);
assert(Int.unwrap(ONE + ZERO) == 1);
assert(Int.unwrap(TWO - ONE) == 1);
assert(Int.unwrap(THREE - THREE) == 0);
assert(Int.unwrap(-TWO) == -2);
assert(Int.unwrap(-ZERO) == 0);
assert(Int.unwrap(ONE * ONE) == 1);
assert(Int.unwrap(THREE * TWO) == 6);
assert(Int.unwrap(SIX / TWO) == 3);
assert(Int.unwrap(THREE / TWO) == 1);
assert(Int.unwrap(SIX % TWO) == 0);
assert(Int.unwrap(THREE % TWO) == 1);
}
function testComparison() public pure {
assert((ONE == ONE) == true);
assert((ONE == TWO) == false);
assert((ONE != ONE) == false);
assert((ONE != TWO) == true);
assert((ONE < TWO) == true);
assert((TWO < ONE) == false);
assert((ONE <= TWO) == true);
assert((TWO <= ONE) == false);
assert((ONE > TWO) == false);
assert((TWO > ONE) == true);
assert((ONE >= TWO) == false);
assert((TWO >= ONE) == true);
}
}
// ----
// testBitwise() ->
// testArithmetic() ->
// testComparison() ->

View File

@ -0,0 +1,670 @@
type Int8 is int8;
type Int16 is int16;
type Int24 is int24;
type Int32 is int32;
type Int40 is int40;
type Int48 is int48;
type Int56 is int56;
type Int64 is int64;
type Int72 is int72;
type Int80 is int80;
type Int88 is int88;
type Int96 is int96;
type Int104 is int104;
type Int112 is int112;
type Int120 is int120;
type Int128 is int128;
type Int136 is int136;
type Int144 is int144;
type Int152 is int152;
type Int160 is int160;
type Int168 is int168;
type Int176 is int176;
type Int184 is int184;
type Int192 is int192;
type Int200 is int200;
type Int208 is int208;
type Int216 is int216;
type Int224 is int224;
type Int232 is int232;
type Int240 is int240;
type Int248 is int248;
type Int256 is int256;
type Int is int;
type Uint8 is uint8;
type Uint16 is uint16;
type Uint24 is uint24;
type Uint32 is uint32;
type Uint40 is uint40;
type Uint48 is uint48;
type Uint56 is uint56;
type Uint64 is uint64;
type Uint72 is uint72;
type Uint80 is uint80;
type Uint88 is uint88;
type Uint96 is uint96;
type Uint104 is uint104;
type Uint112 is uint112;
type Uint120 is uint120;
type Uint128 is uint128;
type Uint136 is uint136;
type Uint144 is uint144;
type Uint152 is uint152;
type Uint160 is uint160;
type Uint168 is uint168;
type Uint176 is uint176;
type Uint184 is uint184;
type Uint192 is uint192;
type Uint200 is uint200;
type Uint208 is uint208;
type Uint216 is uint216;
type Uint224 is uint224;
type Uint232 is uint232;
type Uint240 is uint240;
type Uint248 is uint248;
type Uint256 is uint256;
type Uint is uint;
type Bytes1 is bytes1;
type Bytes2 is bytes2;
type Bytes3 is bytes3;
type Bytes4 is bytes4;
type Bytes5 is bytes5;
type Bytes6 is bytes6;
type Bytes7 is bytes7;
type Bytes8 is bytes8;
type Bytes9 is bytes9;
type Bytes10 is bytes10;
type Bytes11 is bytes11;
type Bytes12 is bytes12;
type Bytes13 is bytes13;
type Bytes14 is bytes14;
type Bytes15 is bytes15;
type Bytes16 is bytes16;
type Bytes17 is bytes17;
type Bytes18 is bytes18;
type Bytes19 is bytes19;
type Bytes20 is bytes20;
type Bytes21 is bytes21;
type Bytes22 is bytes22;
type Bytes23 is bytes23;
type Bytes24 is bytes24;
type Bytes25 is bytes25;
type Bytes26 is bytes26;
type Bytes27 is bytes27;
type Bytes28 is bytes28;
type Bytes29 is bytes29;
type Bytes30 is bytes30;
type Bytes31 is bytes31;
type Bytes32 is bytes32;
type Address is address;
type AddressPayable is address payable;
type Bool is bool;
using {bitorInt8 as |, unsubInt8 as -} for Int8 global;
using {bitorInt16 as |, unsubInt16 as -} for Int16 global;
using {bitorInt24 as |, unsubInt24 as -} for Int24 global;
using {bitorInt32 as |, unsubInt32 as -} for Int32 global;
using {bitorInt40 as |, unsubInt40 as -} for Int40 global;
using {bitorInt48 as |, unsubInt48 as -} for Int48 global;
using {bitorInt56 as |, unsubInt56 as -} for Int56 global;
using {bitorInt64 as |, unsubInt64 as -} for Int64 global;
using {bitorInt72 as |, unsubInt72 as -} for Int72 global;
using {bitorInt80 as |, unsubInt80 as -} for Int80 global;
using {bitorInt88 as |, unsubInt88 as -} for Int88 global;
using {bitorInt96 as |, unsubInt96 as -} for Int96 global;
using {bitorInt104 as |, unsubInt104 as -} for Int104 global;
using {bitorInt112 as |, unsubInt112 as -} for Int112 global;
using {bitorInt120 as |, unsubInt120 as -} for Int120 global;
using {bitorInt128 as |, unsubInt128 as -} for Int128 global;
using {bitorInt136 as |, unsubInt136 as -} for Int136 global;
using {bitorInt144 as |, unsubInt144 as -} for Int144 global;
using {bitorInt152 as |, unsubInt152 as -} for Int152 global;
using {bitorInt160 as |, unsubInt160 as -} for Int160 global;
using {bitorInt168 as |, unsubInt168 as -} for Int168 global;
using {bitorInt176 as |, unsubInt176 as -} for Int176 global;
using {bitorInt184 as |, unsubInt184 as -} for Int184 global;
using {bitorInt192 as |, unsubInt192 as -} for Int192 global;
using {bitorInt200 as |, unsubInt200 as -} for Int200 global;
using {bitorInt208 as |, unsubInt208 as -} for Int208 global;
using {bitorInt216 as |, unsubInt216 as -} for Int216 global;
using {bitorInt224 as |, unsubInt224 as -} for Int224 global;
using {bitorInt232 as |, unsubInt232 as -} for Int232 global;
using {bitorInt240 as |, unsubInt240 as -} for Int240 global;
using {bitorInt248 as |, unsubInt248 as -} for Int248 global;
using {bitorInt256 as |, unsubInt256 as -} for Int256 global;
using {bitorInt as |, unsubInt as -} for Int global;
using {bitorUint8 as |, bitnotUint8 as ~} for Uint8 global;
using {bitorUint16 as |, bitnotUint16 as ~} for Uint16 global;
using {bitorUint24 as |, bitnotUint24 as ~} for Uint24 global;
using {bitorUint32 as |, bitnotUint32 as ~} for Uint32 global;
using {bitorUint40 as |, bitnotUint40 as ~} for Uint40 global;
using {bitorUint48 as |, bitnotUint48 as ~} for Uint48 global;
using {bitorUint56 as |, bitnotUint56 as ~} for Uint56 global;
using {bitorUint64 as |, bitnotUint64 as ~} for Uint64 global;
using {bitorUint72 as |, bitnotUint72 as ~} for Uint72 global;
using {bitorUint80 as |, bitnotUint80 as ~} for Uint80 global;
using {bitorUint88 as |, bitnotUint88 as ~} for Uint88 global;
using {bitorUint96 as |, bitnotUint96 as ~} for Uint96 global;
using {bitorUint104 as |, bitnotUint104 as ~} for Uint104 global;
using {bitorUint112 as |, bitnotUint112 as ~} for Uint112 global;
using {bitorUint120 as |, bitnotUint120 as ~} for Uint120 global;
using {bitorUint128 as |, bitnotUint128 as ~} for Uint128 global;
using {bitorUint136 as |, bitnotUint136 as ~} for Uint136 global;
using {bitorUint144 as |, bitnotUint144 as ~} for Uint144 global;
using {bitorUint152 as |, bitnotUint152 as ~} for Uint152 global;
using {bitorUint160 as |, bitnotUint160 as ~} for Uint160 global;
using {bitorUint168 as |, bitnotUint168 as ~} for Uint168 global;
using {bitorUint176 as |, bitnotUint176 as ~} for Uint176 global;
using {bitorUint184 as |, bitnotUint184 as ~} for Uint184 global;
using {bitorUint192 as |, bitnotUint192 as ~} for Uint192 global;
using {bitorUint200 as |, bitnotUint200 as ~} for Uint200 global;
using {bitorUint208 as |, bitnotUint208 as ~} for Uint208 global;
using {bitorUint216 as |, bitnotUint216 as ~} for Uint216 global;
using {bitorUint224 as |, bitnotUint224 as ~} for Uint224 global;
using {bitorUint232 as |, bitnotUint232 as ~} for Uint232 global;
using {bitorUint240 as |, bitnotUint240 as ~} for Uint240 global;
using {bitorUint248 as |, bitnotUint248 as ~} for Uint248 global;
using {bitorUint256 as |, bitnotUint256 as ~} for Uint256 global;
using {bitorUint as |, bitnotUint as ~} for Uint global;
using {bitorBytes1 as |, bitnotBytes1 as ~} for Bytes1 global;
using {bitorBytes2 as |, bitnotBytes2 as ~} for Bytes2 global;
using {bitorBytes3 as |, bitnotBytes3 as ~} for Bytes3 global;
using {bitorBytes4 as |, bitnotBytes4 as ~} for Bytes4 global;
using {bitorBytes5 as |, bitnotBytes5 as ~} for Bytes5 global;
using {bitorBytes6 as |, bitnotBytes6 as ~} for Bytes6 global;
using {bitorBytes7 as |, bitnotBytes7 as ~} for Bytes7 global;
using {bitorBytes8 as |, bitnotBytes8 as ~} for Bytes8 global;
using {bitorBytes9 as |, bitnotBytes9 as ~} for Bytes9 global;
using {bitorBytes10 as |, bitnotBytes10 as ~} for Bytes10 global;
using {bitorBytes11 as |, bitnotBytes11 as ~} for Bytes11 global;
using {bitorBytes12 as |, bitnotBytes12 as ~} for Bytes12 global;
using {bitorBytes13 as |, bitnotBytes13 as ~} for Bytes13 global;
using {bitorBytes14 as |, bitnotBytes14 as ~} for Bytes14 global;
using {bitorBytes15 as |, bitnotBytes15 as ~} for Bytes15 global;
using {bitorBytes16 as |, bitnotBytes16 as ~} for Bytes16 global;
using {bitorBytes17 as |, bitnotBytes17 as ~} for Bytes17 global;
using {bitorBytes18 as |, bitnotBytes18 as ~} for Bytes18 global;
using {bitorBytes19 as |, bitnotBytes19 as ~} for Bytes19 global;
using {bitorBytes20 as |, bitnotBytes20 as ~} for Bytes20 global;
using {bitorBytes21 as |, bitnotBytes21 as ~} for Bytes21 global;
using {bitorBytes22 as |, bitnotBytes22 as ~} for Bytes22 global;
using {bitorBytes23 as |, bitnotBytes23 as ~} for Bytes23 global;
using {bitorBytes24 as |, bitnotBytes24 as ~} for Bytes24 global;
using {bitorBytes25 as |, bitnotBytes25 as ~} for Bytes25 global;
using {bitorBytes26 as |, bitnotBytes26 as ~} for Bytes26 global;
using {bitorBytes27 as |, bitnotBytes27 as ~} for Bytes27 global;
using {bitorBytes28 as |, bitnotBytes28 as ~} for Bytes28 global;
using {bitorBytes29 as |, bitnotBytes29 as ~} for Bytes29 global;
using {bitorBytes30 as |, bitnotBytes30 as ~} for Bytes30 global;
using {bitorBytes31 as |, bitnotBytes31 as ~} for Bytes31 global;
using {bitorBytes32 as |, bitnotBytes32 as ~} for Bytes32 global;
function bitorInt8(Int8 x, Int8 y) pure returns (Int8) { return Int8.wrap(Int8.unwrap(x) | Int8.unwrap(y)); }
function bitorInt16(Int16 x, Int16 y) pure returns (Int16) { return Int16.wrap(Int16.unwrap(x) | Int16.unwrap(y)); }
function bitorInt24(Int24 x, Int24 y) pure returns (Int24) { return Int24.wrap(Int24.unwrap(x) | Int24.unwrap(y)); }
function bitorInt32(Int32 x, Int32 y) pure returns (Int32) { return Int32.wrap(Int32.unwrap(x) | Int32.unwrap(y)); }
function bitorInt40(Int40 x, Int40 y) pure returns (Int40) { return Int40.wrap(Int40.unwrap(x) | Int40.unwrap(y)); }
function bitorInt48(Int48 x, Int48 y) pure returns (Int48) { return Int48.wrap(Int48.unwrap(x) | Int48.unwrap(y)); }
function bitorInt56(Int56 x, Int56 y) pure returns (Int56) { return Int56.wrap(Int56.unwrap(x) | Int56.unwrap(y)); }
function bitorInt64(Int64 x, Int64 y) pure returns (Int64) { return Int64.wrap(Int64.unwrap(x) | Int64.unwrap(y)); }
function bitorInt72(Int72 x, Int72 y) pure returns (Int72) { return Int72.wrap(Int72.unwrap(x) | Int72.unwrap(y)); }
function bitorInt80(Int80 x, Int80 y) pure returns (Int80) { return Int80.wrap(Int80.unwrap(x) | Int80.unwrap(y)); }
function bitorInt88(Int88 x, Int88 y) pure returns (Int88) { return Int88.wrap(Int88.unwrap(x) | Int88.unwrap(y)); }
function bitorInt96(Int96 x, Int96 y) pure returns (Int96) { return Int96.wrap(Int96.unwrap(x) | Int96.unwrap(y)); }
function bitorInt104(Int104 x, Int104 y) pure returns (Int104) { return Int104.wrap(Int104.unwrap(x) | Int104.unwrap(y)); }
function bitorInt112(Int112 x, Int112 y) pure returns (Int112) { return Int112.wrap(Int112.unwrap(x) | Int112.unwrap(y)); }
function bitorInt120(Int120 x, Int120 y) pure returns (Int120) { return Int120.wrap(Int120.unwrap(x) | Int120.unwrap(y)); }
function bitorInt128(Int128 x, Int128 y) pure returns (Int128) { return Int128.wrap(Int128.unwrap(x) | Int128.unwrap(y)); }
function bitorInt136(Int136 x, Int136 y) pure returns (Int136) { return Int136.wrap(Int136.unwrap(x) | Int136.unwrap(y)); }
function bitorInt144(Int144 x, Int144 y) pure returns (Int144) { return Int144.wrap(Int144.unwrap(x) | Int144.unwrap(y)); }
function bitorInt152(Int152 x, Int152 y) pure returns (Int152) { return Int152.wrap(Int152.unwrap(x) | Int152.unwrap(y)); }
function bitorInt160(Int160 x, Int160 y) pure returns (Int160) { return Int160.wrap(Int160.unwrap(x) | Int160.unwrap(y)); }
function bitorInt168(Int168 x, Int168 y) pure returns (Int168) { return Int168.wrap(Int168.unwrap(x) | Int168.unwrap(y)); }
function bitorInt176(Int176 x, Int176 y) pure returns (Int176) { return Int176.wrap(Int176.unwrap(x) | Int176.unwrap(y)); }
function bitorInt184(Int184 x, Int184 y) pure returns (Int184) { return Int184.wrap(Int184.unwrap(x) | Int184.unwrap(y)); }
function bitorInt192(Int192 x, Int192 y) pure returns (Int192) { return Int192.wrap(Int192.unwrap(x) | Int192.unwrap(y)); }
function bitorInt200(Int200 x, Int200 y) pure returns (Int200) { return Int200.wrap(Int200.unwrap(x) | Int200.unwrap(y)); }
function bitorInt208(Int208 x, Int208 y) pure returns (Int208) { return Int208.wrap(Int208.unwrap(x) | Int208.unwrap(y)); }
function bitorInt216(Int216 x, Int216 y) pure returns (Int216) { return Int216.wrap(Int216.unwrap(x) | Int216.unwrap(y)); }
function bitorInt224(Int224 x, Int224 y) pure returns (Int224) { return Int224.wrap(Int224.unwrap(x) | Int224.unwrap(y)); }
function bitorInt232(Int232 x, Int232 y) pure returns (Int232) { return Int232.wrap(Int232.unwrap(x) | Int232.unwrap(y)); }
function bitorInt240(Int240 x, Int240 y) pure returns (Int240) { return Int240.wrap(Int240.unwrap(x) | Int240.unwrap(y)); }
function bitorInt248(Int248 x, Int248 y) pure returns (Int248) { return Int248.wrap(Int248.unwrap(x) | Int248.unwrap(y)); }
function bitorInt256(Int256 x, Int256 y) pure returns (Int256) { return Int256.wrap(Int256.unwrap(x) | Int256.unwrap(y)); }
function bitorInt(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) | Int.unwrap(y)); }
function unsubInt8(Int8 x) pure returns (Int8) { return Int8.wrap(-Int8.unwrap(x)); }
function unsubInt16(Int16 x) pure returns (Int16) { return Int16.wrap(-Int16.unwrap(x)); }
function unsubInt24(Int24 x) pure returns (Int24) { return Int24.wrap(-Int24.unwrap(x)); }
function unsubInt32(Int32 x) pure returns (Int32) { return Int32.wrap(-Int32.unwrap(x)); }
function unsubInt40(Int40 x) pure returns (Int40) { return Int40.wrap(-Int40.unwrap(x)); }
function unsubInt48(Int48 x) pure returns (Int48) { return Int48.wrap(-Int48.unwrap(x)); }
function unsubInt56(Int56 x) pure returns (Int56) { return Int56.wrap(-Int56.unwrap(x)); }
function unsubInt64(Int64 x) pure returns (Int64) { return Int64.wrap(-Int64.unwrap(x)); }
function unsubInt72(Int72 x) pure returns (Int72) { return Int72.wrap(-Int72.unwrap(x)); }
function unsubInt80(Int80 x) pure returns (Int80) { return Int80.wrap(-Int80.unwrap(x)); }
function unsubInt88(Int88 x) pure returns (Int88) { return Int88.wrap(-Int88.unwrap(x)); }
function unsubInt96(Int96 x) pure returns (Int96) { return Int96.wrap(-Int96.unwrap(x)); }
function unsubInt104(Int104 x) pure returns (Int104) { return Int104.wrap(-Int104.unwrap(x)); }
function unsubInt112(Int112 x) pure returns (Int112) { return Int112.wrap(-Int112.unwrap(x)); }
function unsubInt120(Int120 x) pure returns (Int120) { return Int120.wrap(-Int120.unwrap(x)); }
function unsubInt128(Int128 x) pure returns (Int128) { return Int128.wrap(-Int128.unwrap(x)); }
function unsubInt136(Int136 x) pure returns (Int136) { return Int136.wrap(-Int136.unwrap(x)); }
function unsubInt144(Int144 x) pure returns (Int144) { return Int144.wrap(-Int144.unwrap(x)); }
function unsubInt152(Int152 x) pure returns (Int152) { return Int152.wrap(-Int152.unwrap(x)); }
function unsubInt160(Int160 x) pure returns (Int160) { return Int160.wrap(-Int160.unwrap(x)); }
function unsubInt168(Int168 x) pure returns (Int168) { return Int168.wrap(-Int168.unwrap(x)); }
function unsubInt176(Int176 x) pure returns (Int176) { return Int176.wrap(-Int176.unwrap(x)); }
function unsubInt184(Int184 x) pure returns (Int184) { return Int184.wrap(-Int184.unwrap(x)); }
function unsubInt192(Int192 x) pure returns (Int192) { return Int192.wrap(-Int192.unwrap(x)); }
function unsubInt200(Int200 x) pure returns (Int200) { return Int200.wrap(-Int200.unwrap(x)); }
function unsubInt208(Int208 x) pure returns (Int208) { return Int208.wrap(-Int208.unwrap(x)); }
function unsubInt216(Int216 x) pure returns (Int216) { return Int216.wrap(-Int216.unwrap(x)); }
function unsubInt224(Int224 x) pure returns (Int224) { return Int224.wrap(-Int224.unwrap(x)); }
function unsubInt232(Int232 x) pure returns (Int232) { return Int232.wrap(-Int232.unwrap(x)); }
function unsubInt240(Int240 x) pure returns (Int240) { return Int240.wrap(-Int240.unwrap(x)); }
function unsubInt248(Int248 x) pure returns (Int248) { return Int248.wrap(-Int248.unwrap(x)); }
function unsubInt256(Int256 x) pure returns (Int256) { return Int256.wrap(-Int256.unwrap(x)); }
function unsubInt(Int x) pure returns (Int) { return Int.wrap(-Int.unwrap(x)); }
function bitorUint8(Uint8 x, Uint8 y) pure returns (Uint8) { return Uint8.wrap(Uint8.unwrap(x) | Uint8.unwrap(y)); }
function bitorUint16(Uint16 x, Uint16 y) pure returns (Uint16) { return Uint16.wrap(Uint16.unwrap(x) | Uint16.unwrap(y)); }
function bitorUint24(Uint24 x, Uint24 y) pure returns (Uint24) { return Uint24.wrap(Uint24.unwrap(x) | Uint24.unwrap(y)); }
function bitorUint32(Uint32 x, Uint32 y) pure returns (Uint32) { return Uint32.wrap(Uint32.unwrap(x) | Uint32.unwrap(y)); }
function bitorUint40(Uint40 x, Uint40 y) pure returns (Uint40) { return Uint40.wrap(Uint40.unwrap(x) | Uint40.unwrap(y)); }
function bitorUint48(Uint48 x, Uint48 y) pure returns (Uint48) { return Uint48.wrap(Uint48.unwrap(x) | Uint48.unwrap(y)); }
function bitorUint56(Uint56 x, Uint56 y) pure returns (Uint56) { return Uint56.wrap(Uint56.unwrap(x) | Uint56.unwrap(y)); }
function bitorUint64(Uint64 x, Uint64 y) pure returns (Uint64) { return Uint64.wrap(Uint64.unwrap(x) | Uint64.unwrap(y)); }
function bitorUint72(Uint72 x, Uint72 y) pure returns (Uint72) { return Uint72.wrap(Uint72.unwrap(x) | Uint72.unwrap(y)); }
function bitorUint80(Uint80 x, Uint80 y) pure returns (Uint80) { return Uint80.wrap(Uint80.unwrap(x) | Uint80.unwrap(y)); }
function bitorUint88(Uint88 x, Uint88 y) pure returns (Uint88) { return Uint88.wrap(Uint88.unwrap(x) | Uint88.unwrap(y)); }
function bitorUint96(Uint96 x, Uint96 y) pure returns (Uint96) { return Uint96.wrap(Uint96.unwrap(x) | Uint96.unwrap(y)); }
function bitorUint104(Uint104 x, Uint104 y) pure returns (Uint104) { return Uint104.wrap(Uint104.unwrap(x) | Uint104.unwrap(y)); }
function bitorUint112(Uint112 x, Uint112 y) pure returns (Uint112) { return Uint112.wrap(Uint112.unwrap(x) | Uint112.unwrap(y)); }
function bitorUint120(Uint120 x, Uint120 y) pure returns (Uint120) { return Uint120.wrap(Uint120.unwrap(x) | Uint120.unwrap(y)); }
function bitorUint128(Uint128 x, Uint128 y) pure returns (Uint128) { return Uint128.wrap(Uint128.unwrap(x) | Uint128.unwrap(y)); }
function bitorUint136(Uint136 x, Uint136 y) pure returns (Uint136) { return Uint136.wrap(Uint136.unwrap(x) | Uint136.unwrap(y)); }
function bitorUint144(Uint144 x, Uint144 y) pure returns (Uint144) { return Uint144.wrap(Uint144.unwrap(x) | Uint144.unwrap(y)); }
function bitorUint152(Uint152 x, Uint152 y) pure returns (Uint152) { return Uint152.wrap(Uint152.unwrap(x) | Uint152.unwrap(y)); }
function bitorUint160(Uint160 x, Uint160 y) pure returns (Uint160) { return Uint160.wrap(Uint160.unwrap(x) | Uint160.unwrap(y)); }
function bitorUint168(Uint168 x, Uint168 y) pure returns (Uint168) { return Uint168.wrap(Uint168.unwrap(x) | Uint168.unwrap(y)); }
function bitorUint176(Uint176 x, Uint176 y) pure returns (Uint176) { return Uint176.wrap(Uint176.unwrap(x) | Uint176.unwrap(y)); }
function bitorUint184(Uint184 x, Uint184 y) pure returns (Uint184) { return Uint184.wrap(Uint184.unwrap(x) | Uint184.unwrap(y)); }
function bitorUint192(Uint192 x, Uint192 y) pure returns (Uint192) { return Uint192.wrap(Uint192.unwrap(x) | Uint192.unwrap(y)); }
function bitorUint200(Uint200 x, Uint200 y) pure returns (Uint200) { return Uint200.wrap(Uint200.unwrap(x) | Uint200.unwrap(y)); }
function bitorUint208(Uint208 x, Uint208 y) pure returns (Uint208) { return Uint208.wrap(Uint208.unwrap(x) | Uint208.unwrap(y)); }
function bitorUint216(Uint216 x, Uint216 y) pure returns (Uint216) { return Uint216.wrap(Uint216.unwrap(x) | Uint216.unwrap(y)); }
function bitorUint224(Uint224 x, Uint224 y) pure returns (Uint224) { return Uint224.wrap(Uint224.unwrap(x) | Uint224.unwrap(y)); }
function bitorUint232(Uint232 x, Uint232 y) pure returns (Uint232) { return Uint232.wrap(Uint232.unwrap(x) | Uint232.unwrap(y)); }
function bitorUint240(Uint240 x, Uint240 y) pure returns (Uint240) { return Uint240.wrap(Uint240.unwrap(x) | Uint240.unwrap(y)); }
function bitorUint248(Uint248 x, Uint248 y) pure returns (Uint248) { return Uint248.wrap(Uint248.unwrap(x) | Uint248.unwrap(y)); }
function bitorUint256(Uint256 x, Uint256 y) pure returns (Uint256) { return Uint256.wrap(Uint256.unwrap(x) | Uint256.unwrap(y)); }
function bitorUint(Uint x, Uint y) pure returns (Uint) { return Uint.wrap(Uint.unwrap(x) | Uint.unwrap(y)); }
function bitnotUint8(Uint8 x) pure returns (Uint8) { return Uint8.wrap(~Uint8.unwrap(x)); }
function bitnotUint16(Uint16 x) pure returns (Uint16) { return Uint16.wrap(~Uint16.unwrap(x)); }
function bitnotUint24(Uint24 x) pure returns (Uint24) { return Uint24.wrap(~Uint24.unwrap(x)); }
function bitnotUint32(Uint32 x) pure returns (Uint32) { return Uint32.wrap(~Uint32.unwrap(x)); }
function bitnotUint40(Uint40 x) pure returns (Uint40) { return Uint40.wrap(~Uint40.unwrap(x)); }
function bitnotUint48(Uint48 x) pure returns (Uint48) { return Uint48.wrap(~Uint48.unwrap(x)); }
function bitnotUint56(Uint56 x) pure returns (Uint56) { return Uint56.wrap(~Uint56.unwrap(x)); }
function bitnotUint64(Uint64 x) pure returns (Uint64) { return Uint64.wrap(~Uint64.unwrap(x)); }
function bitnotUint72(Uint72 x) pure returns (Uint72) { return Uint72.wrap(~Uint72.unwrap(x)); }
function bitnotUint80(Uint80 x) pure returns (Uint80) { return Uint80.wrap(~Uint80.unwrap(x)); }
function bitnotUint88(Uint88 x) pure returns (Uint88) { return Uint88.wrap(~Uint88.unwrap(x)); }
function bitnotUint96(Uint96 x) pure returns (Uint96) { return Uint96.wrap(~Uint96.unwrap(x)); }
function bitnotUint104(Uint104 x) pure returns (Uint104) { return Uint104.wrap(~Uint104.unwrap(x)); }
function bitnotUint112(Uint112 x) pure returns (Uint112) { return Uint112.wrap(~Uint112.unwrap(x)); }
function bitnotUint120(Uint120 x) pure returns (Uint120) { return Uint120.wrap(~Uint120.unwrap(x)); }
function bitnotUint128(Uint128 x) pure returns (Uint128) { return Uint128.wrap(~Uint128.unwrap(x)); }
function bitnotUint136(Uint136 x) pure returns (Uint136) { return Uint136.wrap(~Uint136.unwrap(x)); }
function bitnotUint144(Uint144 x) pure returns (Uint144) { return Uint144.wrap(~Uint144.unwrap(x)); }
function bitnotUint152(Uint152 x) pure returns (Uint152) { return Uint152.wrap(~Uint152.unwrap(x)); }
function bitnotUint160(Uint160 x) pure returns (Uint160) { return Uint160.wrap(~Uint160.unwrap(x)); }
function bitnotUint168(Uint168 x) pure returns (Uint168) { return Uint168.wrap(~Uint168.unwrap(x)); }
function bitnotUint176(Uint176 x) pure returns (Uint176) { return Uint176.wrap(~Uint176.unwrap(x)); }
function bitnotUint184(Uint184 x) pure returns (Uint184) { return Uint184.wrap(~Uint184.unwrap(x)); }
function bitnotUint192(Uint192 x) pure returns (Uint192) { return Uint192.wrap(~Uint192.unwrap(x)); }
function bitnotUint200(Uint200 x) pure returns (Uint200) { return Uint200.wrap(~Uint200.unwrap(x)); }
function bitnotUint208(Uint208 x) pure returns (Uint208) { return Uint208.wrap(~Uint208.unwrap(x)); }
function bitnotUint216(Uint216 x) pure returns (Uint216) { return Uint216.wrap(~Uint216.unwrap(x)); }
function bitnotUint224(Uint224 x) pure returns (Uint224) { return Uint224.wrap(~Uint224.unwrap(x)); }
function bitnotUint232(Uint232 x) pure returns (Uint232) { return Uint232.wrap(~Uint232.unwrap(x)); }
function bitnotUint240(Uint240 x) pure returns (Uint240) { return Uint240.wrap(~Uint240.unwrap(x)); }
function bitnotUint248(Uint248 x) pure returns (Uint248) { return Uint248.wrap(~Uint248.unwrap(x)); }
function bitnotUint256(Uint256 x) pure returns (Uint256) { return Uint256.wrap(~Uint256.unwrap(x)); }
function bitnotUint(Uint x) pure returns (Uint) { return Uint.wrap(~Uint.unwrap(x)); }
function bitorBytes1(Bytes1 x, Bytes1 y) pure returns (Bytes1) { return Bytes1.wrap(Bytes1.unwrap(x) | Bytes1.unwrap(y)); }
function bitorBytes2(Bytes2 x, Bytes2 y) pure returns (Bytes2) { return Bytes2.wrap(Bytes2.unwrap(x) | Bytes2.unwrap(y)); }
function bitorBytes3(Bytes3 x, Bytes3 y) pure returns (Bytes3) { return Bytes3.wrap(Bytes3.unwrap(x) | Bytes3.unwrap(y)); }
function bitorBytes4(Bytes4 x, Bytes4 y) pure returns (Bytes4) { return Bytes4.wrap(Bytes4.unwrap(x) | Bytes4.unwrap(y)); }
function bitorBytes5(Bytes5 x, Bytes5 y) pure returns (Bytes5) { return Bytes5.wrap(Bytes5.unwrap(x) | Bytes5.unwrap(y)); }
function bitorBytes6(Bytes6 x, Bytes6 y) pure returns (Bytes6) { return Bytes6.wrap(Bytes6.unwrap(x) | Bytes6.unwrap(y)); }
function bitorBytes7(Bytes7 x, Bytes7 y) pure returns (Bytes7) { return Bytes7.wrap(Bytes7.unwrap(x) | Bytes7.unwrap(y)); }
function bitorBytes8(Bytes8 x, Bytes8 y) pure returns (Bytes8) { return Bytes8.wrap(Bytes8.unwrap(x) | Bytes8.unwrap(y)); }
function bitorBytes9(Bytes9 x, Bytes9 y) pure returns (Bytes9) { return Bytes9.wrap(Bytes9.unwrap(x) | Bytes9.unwrap(y)); }
function bitorBytes10(Bytes10 x, Bytes10 y) pure returns (Bytes10) { return Bytes10.wrap(Bytes10.unwrap(x) | Bytes10.unwrap(y)); }
function bitorBytes11(Bytes11 x, Bytes11 y) pure returns (Bytes11) { return Bytes11.wrap(Bytes11.unwrap(x) | Bytes11.unwrap(y)); }
function bitorBytes12(Bytes12 x, Bytes12 y) pure returns (Bytes12) { return Bytes12.wrap(Bytes12.unwrap(x) | Bytes12.unwrap(y)); }
function bitorBytes13(Bytes13 x, Bytes13 y) pure returns (Bytes13) { return Bytes13.wrap(Bytes13.unwrap(x) | Bytes13.unwrap(y)); }
function bitorBytes14(Bytes14 x, Bytes14 y) pure returns (Bytes14) { return Bytes14.wrap(Bytes14.unwrap(x) | Bytes14.unwrap(y)); }
function bitorBytes15(Bytes15 x, Bytes15 y) pure returns (Bytes15) { return Bytes15.wrap(Bytes15.unwrap(x) | Bytes15.unwrap(y)); }
function bitorBytes16(Bytes16 x, Bytes16 y) pure returns (Bytes16) { return Bytes16.wrap(Bytes16.unwrap(x) | Bytes16.unwrap(y)); }
function bitorBytes17(Bytes17 x, Bytes17 y) pure returns (Bytes17) { return Bytes17.wrap(Bytes17.unwrap(x) | Bytes17.unwrap(y)); }
function bitorBytes18(Bytes18 x, Bytes18 y) pure returns (Bytes18) { return Bytes18.wrap(Bytes18.unwrap(x) | Bytes18.unwrap(y)); }
function bitorBytes19(Bytes19 x, Bytes19 y) pure returns (Bytes19) { return Bytes19.wrap(Bytes19.unwrap(x) | Bytes19.unwrap(y)); }
function bitorBytes20(Bytes20 x, Bytes20 y) pure returns (Bytes20) { return Bytes20.wrap(Bytes20.unwrap(x) | Bytes20.unwrap(y)); }
function bitorBytes21(Bytes21 x, Bytes21 y) pure returns (Bytes21) { return Bytes21.wrap(Bytes21.unwrap(x) | Bytes21.unwrap(y)); }
function bitorBytes22(Bytes22 x, Bytes22 y) pure returns (Bytes22) { return Bytes22.wrap(Bytes22.unwrap(x) | Bytes22.unwrap(y)); }
function bitorBytes23(Bytes23 x, Bytes23 y) pure returns (Bytes23) { return Bytes23.wrap(Bytes23.unwrap(x) | Bytes23.unwrap(y)); }
function bitorBytes24(Bytes24 x, Bytes24 y) pure returns (Bytes24) { return Bytes24.wrap(Bytes24.unwrap(x) | Bytes24.unwrap(y)); }
function bitorBytes25(Bytes25 x, Bytes25 y) pure returns (Bytes25) { return Bytes25.wrap(Bytes25.unwrap(x) | Bytes25.unwrap(y)); }
function bitorBytes26(Bytes26 x, Bytes26 y) pure returns (Bytes26) { return Bytes26.wrap(Bytes26.unwrap(x) | Bytes26.unwrap(y)); }
function bitorBytes27(Bytes27 x, Bytes27 y) pure returns (Bytes27) { return Bytes27.wrap(Bytes27.unwrap(x) | Bytes27.unwrap(y)); }
function bitorBytes28(Bytes28 x, Bytes28 y) pure returns (Bytes28) { return Bytes28.wrap(Bytes28.unwrap(x) | Bytes28.unwrap(y)); }
function bitorBytes29(Bytes29 x, Bytes29 y) pure returns (Bytes29) { return Bytes29.wrap(Bytes29.unwrap(x) | Bytes29.unwrap(y)); }
function bitorBytes30(Bytes30 x, Bytes30 y) pure returns (Bytes30) { return Bytes30.wrap(Bytes30.unwrap(x) | Bytes30.unwrap(y)); }
function bitorBytes31(Bytes31 x, Bytes31 y) pure returns (Bytes31) { return Bytes31.wrap(Bytes31.unwrap(x) | Bytes31.unwrap(y)); }
function bitorBytes32(Bytes32 x, Bytes32 y) pure returns (Bytes32) { return Bytes32.wrap(Bytes32.unwrap(x) | Bytes32.unwrap(y)); }
function bitnotBytes1(Bytes1 x) pure returns (Bytes1) { return Bytes1.wrap(~Bytes1.unwrap(x)); }
function bitnotBytes2(Bytes2 x) pure returns (Bytes2) { return Bytes2.wrap(~Bytes2.unwrap(x)); }
function bitnotBytes3(Bytes3 x) pure returns (Bytes3) { return Bytes3.wrap(~Bytes3.unwrap(x)); }
function bitnotBytes4(Bytes4 x) pure returns (Bytes4) { return Bytes4.wrap(~Bytes4.unwrap(x)); }
function bitnotBytes5(Bytes5 x) pure returns (Bytes5) { return Bytes5.wrap(~Bytes5.unwrap(x)); }
function bitnotBytes6(Bytes6 x) pure returns (Bytes6) { return Bytes6.wrap(~Bytes6.unwrap(x)); }
function bitnotBytes7(Bytes7 x) pure returns (Bytes7) { return Bytes7.wrap(~Bytes7.unwrap(x)); }
function bitnotBytes8(Bytes8 x) pure returns (Bytes8) { return Bytes8.wrap(~Bytes8.unwrap(x)); }
function bitnotBytes9(Bytes9 x) pure returns (Bytes9) { return Bytes9.wrap(~Bytes9.unwrap(x)); }
function bitnotBytes10(Bytes10 x) pure returns (Bytes10) { return Bytes10.wrap(~Bytes10.unwrap(x)); }
function bitnotBytes11(Bytes11 x) pure returns (Bytes11) { return Bytes11.wrap(~Bytes11.unwrap(x)); }
function bitnotBytes12(Bytes12 x) pure returns (Bytes12) { return Bytes12.wrap(~Bytes12.unwrap(x)); }
function bitnotBytes13(Bytes13 x) pure returns (Bytes13) { return Bytes13.wrap(~Bytes13.unwrap(x)); }
function bitnotBytes14(Bytes14 x) pure returns (Bytes14) { return Bytes14.wrap(~Bytes14.unwrap(x)); }
function bitnotBytes15(Bytes15 x) pure returns (Bytes15) { return Bytes15.wrap(~Bytes15.unwrap(x)); }
function bitnotBytes16(Bytes16 x) pure returns (Bytes16) { return Bytes16.wrap(~Bytes16.unwrap(x)); }
function bitnotBytes17(Bytes17 x) pure returns (Bytes17) { return Bytes17.wrap(~Bytes17.unwrap(x)); }
function bitnotBytes18(Bytes18 x) pure returns (Bytes18) { return Bytes18.wrap(~Bytes18.unwrap(x)); }
function bitnotBytes19(Bytes19 x) pure returns (Bytes19) { return Bytes19.wrap(~Bytes19.unwrap(x)); }
function bitnotBytes20(Bytes20 x) pure returns (Bytes20) { return Bytes20.wrap(~Bytes20.unwrap(x)); }
function bitnotBytes21(Bytes21 x) pure returns (Bytes21) { return Bytes21.wrap(~Bytes21.unwrap(x)); }
function bitnotBytes22(Bytes22 x) pure returns (Bytes22) { return Bytes22.wrap(~Bytes22.unwrap(x)); }
function bitnotBytes23(Bytes23 x) pure returns (Bytes23) { return Bytes23.wrap(~Bytes23.unwrap(x)); }
function bitnotBytes24(Bytes24 x) pure returns (Bytes24) { return Bytes24.wrap(~Bytes24.unwrap(x)); }
function bitnotBytes25(Bytes25 x) pure returns (Bytes25) { return Bytes25.wrap(~Bytes25.unwrap(x)); }
function bitnotBytes26(Bytes26 x) pure returns (Bytes26) { return Bytes26.wrap(~Bytes26.unwrap(x)); }
function bitnotBytes27(Bytes27 x) pure returns (Bytes27) { return Bytes27.wrap(~Bytes27.unwrap(x)); }
function bitnotBytes28(Bytes28 x) pure returns (Bytes28) { return Bytes28.wrap(~Bytes28.unwrap(x)); }
function bitnotBytes29(Bytes29 x) pure returns (Bytes29) { return Bytes29.wrap(~Bytes29.unwrap(x)); }
function bitnotBytes30(Bytes30 x) pure returns (Bytes30) { return Bytes30.wrap(~Bytes30.unwrap(x)); }
function bitnotBytes31(Bytes31 x) pure returns (Bytes31) { return Bytes31.wrap(~Bytes31.unwrap(x)); }
function bitnotBytes32(Bytes32 x) pure returns (Bytes32) { return Bytes32.wrap(~Bytes32.unwrap(x)); }
using {bitorAddress as |, bitnotAddress as ~} for Address global;
using {bitorAddressPayable as |, bitnotAddressPayable as ~} for AddressPayable global;
using {bitorBool as |, bitnotBool as ~} for Bool global;
function bitorAddress(Address x, Address y) pure returns (Address) {
return Address.wrap(address(bytes20(Address.unwrap(x)) | bytes20(Address.unwrap(y))));
}
function bitnotAddress(Address x) pure returns (Address) {
return Address.wrap(address(~bytes20(Address.unwrap(x))));
}
function bitorAddressPayable(AddressPayable x, AddressPayable y) pure returns (AddressPayable) {
return AddressPayable.wrap(payable(address(bytes20(address(AddressPayable.unwrap(x))) | bytes20(address(AddressPayable.unwrap(y))))));
}
function bitnotAddressPayable(AddressPayable x) pure returns (AddressPayable) {
return AddressPayable.wrap(payable(address(~bytes20(address(AddressPayable.unwrap(x))))));
}
function bitorBool(Bool x, Bool y) pure returns (Bool) {
return Bool.wrap(Bool.unwrap(x) || Bool.unwrap(y));
}
function bitnotBool(Bool x) pure returns (Bool) {
return Bool.wrap(!Bool.unwrap(x));
}
contract C {
function testIntBinary() public pure {
assert(Int8.unwrap(Int8.wrap(1) | Int8.wrap(2)) == 3);
assert(Int16.unwrap(Int16.wrap(1) | Int16.wrap(2)) == 3);
assert(Int24.unwrap(Int24.wrap(1) | Int24.wrap(2)) == 3);
assert(Int32.unwrap(Int32.wrap(1) | Int32.wrap(2)) == 3);
assert(Int40.unwrap(Int40.wrap(1) | Int40.wrap(2)) == 3);
assert(Int48.unwrap(Int48.wrap(1) | Int48.wrap(2)) == 3);
assert(Int56.unwrap(Int56.wrap(1) | Int56.wrap(2)) == 3);
assert(Int64.unwrap(Int64.wrap(1) | Int64.wrap(2)) == 3);
assert(Int72.unwrap(Int72.wrap(1) | Int72.wrap(2)) == 3);
assert(Int80.unwrap(Int80.wrap(1) | Int80.wrap(2)) == 3);
assert(Int88.unwrap(Int88.wrap(1) | Int88.wrap(2)) == 3);
assert(Int96.unwrap(Int96.wrap(1) | Int96.wrap(2)) == 3);
assert(Int104.unwrap(Int104.wrap(1) | Int104.wrap(2)) == 3);
assert(Int112.unwrap(Int112.wrap(1) | Int112.wrap(2)) == 3);
assert(Int120.unwrap(Int120.wrap(1) | Int120.wrap(2)) == 3);
assert(Int128.unwrap(Int128.wrap(1) | Int128.wrap(2)) == 3);
assert(Int136.unwrap(Int136.wrap(1) | Int136.wrap(2)) == 3);
assert(Int144.unwrap(Int144.wrap(1) | Int144.wrap(2)) == 3);
assert(Int152.unwrap(Int152.wrap(1) | Int152.wrap(2)) == 3);
assert(Int160.unwrap(Int160.wrap(1) | Int160.wrap(2)) == 3);
assert(Int168.unwrap(Int168.wrap(1) | Int168.wrap(2)) == 3);
assert(Int176.unwrap(Int176.wrap(1) | Int176.wrap(2)) == 3);
assert(Int184.unwrap(Int184.wrap(1) | Int184.wrap(2)) == 3);
assert(Int192.unwrap(Int192.wrap(1) | Int192.wrap(2)) == 3);
assert(Int200.unwrap(Int200.wrap(1) | Int200.wrap(2)) == 3);
assert(Int208.unwrap(Int208.wrap(1) | Int208.wrap(2)) == 3);
assert(Int216.unwrap(Int216.wrap(1) | Int216.wrap(2)) == 3);
assert(Int224.unwrap(Int224.wrap(1) | Int224.wrap(2)) == 3);
assert(Int232.unwrap(Int232.wrap(1) | Int232.wrap(2)) == 3);
assert(Int240.unwrap(Int240.wrap(1) | Int240.wrap(2)) == 3);
assert(Int248.unwrap(Int248.wrap(1) | Int248.wrap(2)) == 3);
assert(Int256.unwrap(Int256.wrap(1) | Int256.wrap(2)) == 3);
assert(Int.unwrap(Int.wrap(1) | Int.wrap(2)) == 3);
}
function testIntUnary() public pure {
assert(Int8.unwrap(-Int8.wrap(1)) == -1);
assert(Int16.unwrap(-Int16.wrap(1)) == -1);
assert(Int24.unwrap(-Int24.wrap(1)) == -1);
assert(Int32.unwrap(-Int32.wrap(1)) == -1);
assert(Int40.unwrap(-Int40.wrap(1)) == -1);
assert(Int48.unwrap(-Int48.wrap(1)) == -1);
assert(Int56.unwrap(-Int56.wrap(1)) == -1);
assert(Int64.unwrap(-Int64.wrap(1)) == -1);
assert(Int72.unwrap(-Int72.wrap(1)) == -1);
assert(Int80.unwrap(-Int80.wrap(1)) == -1);
assert(Int88.unwrap(-Int88.wrap(1)) == -1);
assert(Int96.unwrap(-Int96.wrap(1)) == -1);
assert(Int104.unwrap(-Int104.wrap(1)) == -1);
assert(Int112.unwrap(-Int112.wrap(1)) == -1);
assert(Int120.unwrap(-Int120.wrap(1)) == -1);
assert(Int128.unwrap(-Int128.wrap(1)) == -1);
assert(Int136.unwrap(-Int136.wrap(1)) == -1);
assert(Int144.unwrap(-Int144.wrap(1)) == -1);
assert(Int152.unwrap(-Int152.wrap(1)) == -1);
assert(Int160.unwrap(-Int160.wrap(1)) == -1);
assert(Int168.unwrap(-Int168.wrap(1)) == -1);
assert(Int176.unwrap(-Int176.wrap(1)) == -1);
assert(Int184.unwrap(-Int184.wrap(1)) == -1);
assert(Int192.unwrap(-Int192.wrap(1)) == -1);
assert(Int200.unwrap(-Int200.wrap(1)) == -1);
assert(Int208.unwrap(-Int208.wrap(1)) == -1);
assert(Int216.unwrap(-Int216.wrap(1)) == -1);
assert(Int224.unwrap(-Int224.wrap(1)) == -1);
assert(Int232.unwrap(-Int232.wrap(1)) == -1);
assert(Int240.unwrap(-Int240.wrap(1)) == -1);
assert(Int248.unwrap(-Int248.wrap(1)) == -1);
assert(Int256.unwrap(-Int256.wrap(1)) == -1);
assert(Int.unwrap(-Int.wrap(1)) == -1);
}
function testUintBinary() public pure {
assert(Uint8.unwrap(Uint8.wrap(1) | Uint8.wrap(2)) == 3);
assert(Uint16.unwrap(Uint16.wrap(1) | Uint16.wrap(2)) == 3);
assert(Uint24.unwrap(Uint24.wrap(1) | Uint24.wrap(2)) == 3);
assert(Uint32.unwrap(Uint32.wrap(1) | Uint32.wrap(2)) == 3);
assert(Uint40.unwrap(Uint40.wrap(1) | Uint40.wrap(2)) == 3);
assert(Uint48.unwrap(Uint48.wrap(1) | Uint48.wrap(2)) == 3);
assert(Uint56.unwrap(Uint56.wrap(1) | Uint56.wrap(2)) == 3);
assert(Uint64.unwrap(Uint64.wrap(1) | Uint64.wrap(2)) == 3);
assert(Uint72.unwrap(Uint72.wrap(1) | Uint72.wrap(2)) == 3);
assert(Uint80.unwrap(Uint80.wrap(1) | Uint80.wrap(2)) == 3);
assert(Uint88.unwrap(Uint88.wrap(1) | Uint88.wrap(2)) == 3);
assert(Uint96.unwrap(Uint96.wrap(1) | Uint96.wrap(2)) == 3);
assert(Uint104.unwrap(Uint104.wrap(1) | Uint104.wrap(2)) == 3);
assert(Uint112.unwrap(Uint112.wrap(1) | Uint112.wrap(2)) == 3);
assert(Uint120.unwrap(Uint120.wrap(1) | Uint120.wrap(2)) == 3);
assert(Uint128.unwrap(Uint128.wrap(1) | Uint128.wrap(2)) == 3);
assert(Uint136.unwrap(Uint136.wrap(1) | Uint136.wrap(2)) == 3);
assert(Uint144.unwrap(Uint144.wrap(1) | Uint144.wrap(2)) == 3);
assert(Uint152.unwrap(Uint152.wrap(1) | Uint152.wrap(2)) == 3);
assert(Uint160.unwrap(Uint160.wrap(1) | Uint160.wrap(2)) == 3);
assert(Uint168.unwrap(Uint168.wrap(1) | Uint168.wrap(2)) == 3);
assert(Uint176.unwrap(Uint176.wrap(1) | Uint176.wrap(2)) == 3);
assert(Uint184.unwrap(Uint184.wrap(1) | Uint184.wrap(2)) == 3);
assert(Uint192.unwrap(Uint192.wrap(1) | Uint192.wrap(2)) == 3);
assert(Uint200.unwrap(Uint200.wrap(1) | Uint200.wrap(2)) == 3);
assert(Uint208.unwrap(Uint208.wrap(1) | Uint208.wrap(2)) == 3);
assert(Uint216.unwrap(Uint216.wrap(1) | Uint216.wrap(2)) == 3);
assert(Uint224.unwrap(Uint224.wrap(1) | Uint224.wrap(2)) == 3);
assert(Uint232.unwrap(Uint232.wrap(1) | Uint232.wrap(2)) == 3);
assert(Uint240.unwrap(Uint240.wrap(1) | Uint240.wrap(2)) == 3);
assert(Uint248.unwrap(Uint248.wrap(1) | Uint248.wrap(2)) == 3);
assert(Uint256.unwrap(Uint256.wrap(1) | Uint256.wrap(2)) == 3);
assert(Uint.unwrap(Uint.wrap(1) | Uint.wrap(2)) == 3);
}
function testUintUnary() public pure {
assert(Uint8.unwrap(~Uint8.wrap(1)) == ~uint8(1));
assert(Uint16.unwrap(~Uint16.wrap(1)) == ~uint16(1));
assert(Uint24.unwrap(~Uint24.wrap(1)) == ~uint24(1));
assert(Uint32.unwrap(~Uint32.wrap(1)) == ~uint32(1));
assert(Uint40.unwrap(~Uint40.wrap(1)) == ~uint40(1));
assert(Uint48.unwrap(~Uint48.wrap(1)) == ~uint48(1));
assert(Uint56.unwrap(~Uint56.wrap(1)) == ~uint56(1));
assert(Uint64.unwrap(~Uint64.wrap(1)) == ~uint64(1));
assert(Uint72.unwrap(~Uint72.wrap(1)) == ~uint72(1));
assert(Uint80.unwrap(~Uint80.wrap(1)) == ~uint80(1));
assert(Uint88.unwrap(~Uint88.wrap(1)) == ~uint88(1));
assert(Uint96.unwrap(~Uint96.wrap(1)) == ~uint96(1));
assert(Uint104.unwrap(~Uint104.wrap(1)) == ~uint104(1));
assert(Uint112.unwrap(~Uint112.wrap(1)) == ~uint112(1));
assert(Uint120.unwrap(~Uint120.wrap(1)) == ~uint120(1));
assert(Uint128.unwrap(~Uint128.wrap(1)) == ~uint128(1));
assert(Uint136.unwrap(~Uint136.wrap(1)) == ~uint136(1));
assert(Uint144.unwrap(~Uint144.wrap(1)) == ~uint144(1));
assert(Uint152.unwrap(~Uint152.wrap(1)) == ~uint152(1));
assert(Uint160.unwrap(~Uint160.wrap(1)) == ~uint160(1));
assert(Uint168.unwrap(~Uint168.wrap(1)) == ~uint168(1));
assert(Uint176.unwrap(~Uint176.wrap(1)) == ~uint176(1));
assert(Uint184.unwrap(~Uint184.wrap(1)) == ~uint184(1));
assert(Uint192.unwrap(~Uint192.wrap(1)) == ~uint192(1));
assert(Uint200.unwrap(~Uint200.wrap(1)) == ~uint200(1));
assert(Uint208.unwrap(~Uint208.wrap(1)) == ~uint208(1));
assert(Uint216.unwrap(~Uint216.wrap(1)) == ~uint216(1));
assert(Uint224.unwrap(~Uint224.wrap(1)) == ~uint224(1));
assert(Uint232.unwrap(~Uint232.wrap(1)) == ~uint232(1));
assert(Uint240.unwrap(~Uint240.wrap(1)) == ~uint240(1));
assert(Uint248.unwrap(~Uint248.wrap(1)) == ~uint248(1));
assert(Uint256.unwrap(~Uint256.wrap(1)) == ~uint256(1));
assert(Uint.unwrap(~Uint.wrap(1)) == ~uint(1));
}
function testBytesBinary() public pure {
assert(Bytes1.unwrap(Bytes1.wrap(0x01) | Bytes1.wrap(0x02)) == bytes1(0x03));
assert(Bytes2.unwrap(Bytes2.wrap(bytes2(bytes1(0x01))) | Bytes2.wrap(bytes2(bytes1(0x02)))) == bytes2(bytes1(0x03)));
assert(Bytes3.unwrap(Bytes3.wrap(bytes3(bytes1(0x01))) | Bytes3.wrap(bytes3(bytes1(0x02)))) == bytes3(bytes1(0x03)));
assert(Bytes4.unwrap(Bytes4.wrap(bytes4(bytes1(0x01))) | Bytes4.wrap(bytes4(bytes1(0x02)))) == bytes4(bytes1(0x03)));
assert(Bytes5.unwrap(Bytes5.wrap(bytes5(bytes1(0x01))) | Bytes5.wrap(bytes5(bytes1(0x02)))) == bytes5(bytes1(0x03)));
assert(Bytes6.unwrap(Bytes6.wrap(bytes6(bytes1(0x01))) | Bytes6.wrap(bytes6(bytes1(0x02)))) == bytes6(bytes1(0x03)));
assert(Bytes7.unwrap(Bytes7.wrap(bytes7(bytes1(0x01))) | Bytes7.wrap(bytes7(bytes1(0x02)))) == bytes7(bytes1(0x03)));
assert(Bytes8.unwrap(Bytes8.wrap(bytes8(bytes1(0x01))) | Bytes8.wrap(bytes8(bytes1(0x02)))) == bytes8(bytes1(0x03)));
assert(Bytes9.unwrap(Bytes9.wrap(bytes9(bytes1(0x01))) | Bytes9.wrap(bytes9(bytes1(0x02)))) == bytes9(bytes1(0x03)));
assert(Bytes10.unwrap(Bytes10.wrap(bytes10(bytes1(0x01))) | Bytes10.wrap(bytes10(bytes1(0x02)))) == bytes10(bytes1(0x03)));
assert(Bytes11.unwrap(Bytes11.wrap(bytes11(bytes1(0x01))) | Bytes11.wrap(bytes11(bytes1(0x02)))) == bytes11(bytes1(0x03)));
assert(Bytes12.unwrap(Bytes12.wrap(bytes12(bytes1(0x01))) | Bytes12.wrap(bytes12(bytes1(0x02)))) == bytes12(bytes1(0x03)));
assert(Bytes13.unwrap(Bytes13.wrap(bytes13(bytes1(0x01))) | Bytes13.wrap(bytes13(bytes1(0x02)))) == bytes13(bytes1(0x03)));
assert(Bytes14.unwrap(Bytes14.wrap(bytes14(bytes1(0x01))) | Bytes14.wrap(bytes14(bytes1(0x02)))) == bytes14(bytes1(0x03)));
assert(Bytes15.unwrap(Bytes15.wrap(bytes15(bytes1(0x01))) | Bytes15.wrap(bytes15(bytes1(0x02)))) == bytes15(bytes1(0x03)));
assert(Bytes16.unwrap(Bytes16.wrap(bytes16(bytes1(0x01))) | Bytes16.wrap(bytes16(bytes1(0x02)))) == bytes16(bytes1(0x03)));
assert(Bytes17.unwrap(Bytes17.wrap(bytes17(bytes1(0x01))) | Bytes17.wrap(bytes17(bytes1(0x02)))) == bytes17(bytes1(0x03)));
assert(Bytes18.unwrap(Bytes18.wrap(bytes18(bytes1(0x01))) | Bytes18.wrap(bytes18(bytes1(0x02)))) == bytes18(bytes1(0x03)));
assert(Bytes19.unwrap(Bytes19.wrap(bytes19(bytes1(0x01))) | Bytes19.wrap(bytes19(bytes1(0x02)))) == bytes19(bytes1(0x03)));
assert(Bytes20.unwrap(Bytes20.wrap(bytes20(bytes1(0x01))) | Bytes20.wrap(bytes20(bytes1(0x02)))) == bytes20(bytes1(0x03)));
assert(Bytes21.unwrap(Bytes21.wrap(bytes21(bytes1(0x01))) | Bytes21.wrap(bytes21(bytes1(0x02)))) == bytes21(bytes1(0x03)));
assert(Bytes22.unwrap(Bytes22.wrap(bytes22(bytes1(0x01))) | Bytes22.wrap(bytes22(bytes1(0x02)))) == bytes22(bytes1(0x03)));
assert(Bytes23.unwrap(Bytes23.wrap(bytes23(bytes1(0x01))) | Bytes23.wrap(bytes23(bytes1(0x02)))) == bytes23(bytes1(0x03)));
assert(Bytes24.unwrap(Bytes24.wrap(bytes24(bytes1(0x01))) | Bytes24.wrap(bytes24(bytes1(0x02)))) == bytes24(bytes1(0x03)));
assert(Bytes25.unwrap(Bytes25.wrap(bytes25(bytes1(0x01))) | Bytes25.wrap(bytes25(bytes1(0x02)))) == bytes25(bytes1(0x03)));
assert(Bytes26.unwrap(Bytes26.wrap(bytes26(bytes1(0x01))) | Bytes26.wrap(bytes26(bytes1(0x02)))) == bytes26(bytes1(0x03)));
assert(Bytes27.unwrap(Bytes27.wrap(bytes27(bytes1(0x01))) | Bytes27.wrap(bytes27(bytes1(0x02)))) == bytes27(bytes1(0x03)));
assert(Bytes28.unwrap(Bytes28.wrap(bytes28(bytes1(0x01))) | Bytes28.wrap(bytes28(bytes1(0x02)))) == bytes28(bytes1(0x03)));
assert(Bytes29.unwrap(Bytes29.wrap(bytes29(bytes1(0x01))) | Bytes29.wrap(bytes29(bytes1(0x02)))) == bytes29(bytes1(0x03)));
assert(Bytes30.unwrap(Bytes30.wrap(bytes30(bytes1(0x01))) | Bytes30.wrap(bytes30(bytes1(0x02)))) == bytes30(bytes1(0x03)));
assert(Bytes31.unwrap(Bytes31.wrap(bytes31(bytes1(0x01))) | Bytes31.wrap(bytes31(bytes1(0x02)))) == bytes31(bytes1(0x03)));
assert(Bytes32.unwrap(Bytes32.wrap(bytes32(bytes1(0x01))) | Bytes32.wrap(bytes32(bytes1(0x02)))) == bytes32(bytes1(0x03)));
}
function testBytesUnary() public pure {
assert(Bytes1.unwrap(~Bytes1.wrap(bytes1(0x01))) == ~bytes1(0x01));
assert(Bytes2.unwrap(~Bytes2.wrap(bytes2(bytes1(0x01)))) == ~bytes2(bytes1(0x01)));
assert(Bytes3.unwrap(~Bytes3.wrap(bytes3(bytes1(0x01)))) == ~bytes3(bytes1(0x01)));
assert(Bytes4.unwrap(~Bytes4.wrap(bytes4(bytes1(0x01)))) == ~bytes4(bytes1(0x01)));
assert(Bytes5.unwrap(~Bytes5.wrap(bytes5(bytes1(0x01)))) == ~bytes5(bytes1(0x01)));
assert(Bytes6.unwrap(~Bytes6.wrap(bytes6(bytes1(0x01)))) == ~bytes6(bytes1(0x01)));
assert(Bytes7.unwrap(~Bytes7.wrap(bytes7(bytes1(0x01)))) == ~bytes7(bytes1(0x01)));
assert(Bytes8.unwrap(~Bytes8.wrap(bytes8(bytes1(0x01)))) == ~bytes8(bytes1(0x01)));
assert(Bytes9.unwrap(~Bytes9.wrap(bytes9(bytes1(0x01)))) == ~bytes9(bytes1(0x01)));
assert(Bytes10.unwrap(~Bytes10.wrap(bytes10(bytes1(0x01)))) == ~bytes10(bytes1(0x01)));
assert(Bytes11.unwrap(~Bytes11.wrap(bytes11(bytes1(0x01)))) == ~bytes11(bytes1(0x01)));
assert(Bytes12.unwrap(~Bytes12.wrap(bytes12(bytes1(0x01)))) == ~bytes12(bytes1(0x01)));
assert(Bytes13.unwrap(~Bytes13.wrap(bytes13(bytes1(0x01)))) == ~bytes13(bytes1(0x01)));
assert(Bytes14.unwrap(~Bytes14.wrap(bytes14(bytes1(0x01)))) == ~bytes14(bytes1(0x01)));
assert(Bytes15.unwrap(~Bytes15.wrap(bytes15(bytes1(0x01)))) == ~bytes15(bytes1(0x01)));
assert(Bytes16.unwrap(~Bytes16.wrap(bytes16(bytes1(0x01)))) == ~bytes16(bytes1(0x01)));
assert(Bytes17.unwrap(~Bytes17.wrap(bytes17(bytes1(0x01)))) == ~bytes17(bytes1(0x01)));
assert(Bytes18.unwrap(~Bytes18.wrap(bytes18(bytes1(0x01)))) == ~bytes18(bytes1(0x01)));
assert(Bytes19.unwrap(~Bytes19.wrap(bytes19(bytes1(0x01)))) == ~bytes19(bytes1(0x01)));
assert(Bytes20.unwrap(~Bytes20.wrap(bytes20(bytes1(0x01)))) == ~bytes20(bytes1(0x01)));
assert(Bytes21.unwrap(~Bytes21.wrap(bytes21(bytes1(0x01)))) == ~bytes21(bytes1(0x01)));
assert(Bytes22.unwrap(~Bytes22.wrap(bytes22(bytes1(0x01)))) == ~bytes22(bytes1(0x01)));
assert(Bytes23.unwrap(~Bytes23.wrap(bytes23(bytes1(0x01)))) == ~bytes23(bytes1(0x01)));
assert(Bytes24.unwrap(~Bytes24.wrap(bytes24(bytes1(0x01)))) == ~bytes24(bytes1(0x01)));
assert(Bytes25.unwrap(~Bytes25.wrap(bytes25(bytes1(0x01)))) == ~bytes25(bytes1(0x01)));
assert(Bytes26.unwrap(~Bytes26.wrap(bytes26(bytes1(0x01)))) == ~bytes26(bytes1(0x01)));
assert(Bytes27.unwrap(~Bytes27.wrap(bytes27(bytes1(0x01)))) == ~bytes27(bytes1(0x01)));
assert(Bytes28.unwrap(~Bytes28.wrap(bytes28(bytes1(0x01)))) == ~bytes28(bytes1(0x01)));
assert(Bytes29.unwrap(~Bytes29.wrap(bytes29(bytes1(0x01)))) == ~bytes29(bytes1(0x01)));
assert(Bytes30.unwrap(~Bytes30.wrap(bytes30(bytes1(0x01)))) == ~bytes30(bytes1(0x01)));
assert(Bytes31.unwrap(~Bytes31.wrap(bytes31(bytes1(0x01)))) == ~bytes31(bytes1(0x01)));
assert(Bytes32.unwrap(~Bytes32.wrap(bytes32(bytes1(0x01)))) == ~bytes32(bytes1(0x01)));
}
function testOtherBinary() public pure {
assert(Address.unwrap(Address.wrap(address(0x01)) | Address.wrap(address(0x02))) == address(0x03));
assert(AddressPayable.unwrap(AddressPayable.wrap(payable(address(0x01))) | AddressPayable.wrap(payable(address(0x02)))) == payable(address(0x03)));
assert(Bool.unwrap(~Bool.wrap(true)) == false);
}
function testOtherUnary() public pure {
assert(Address.unwrap(~Address.wrap(address(0))) == address(~bytes20(0)));
assert(AddressPayable.unwrap(~AddressPayable.wrap(payable(address(0)))) == payable(address(~bytes20(0))));
assert(Bool.unwrap(~Bool.wrap(true)) == false);
}
}
// ----
// testIntBinary() ->
// testIntUnary() ->
// testUintBinary() ->
// testUintUnary() ->
// testBytesBinary() ->
// testBytesUnary() ->
// testOtherBinary() ->
// testOtherUnary() ->

View File

@ -0,0 +1,20 @@
type Int is int16;
using {add as +, add} for Int global;
function add(Int _a, Int _b) pure returns (Int) {
return Int.wrap(Int.unwrap(_a) + Int.unwrap(_b));
}
contract C {
function f() pure public returns (Int) {
return Int.wrap(5) + Int.wrap(5);
}
function g() pure public returns (Int) {
return Int.wrap(7).add(Int.wrap(6));
}
}
// ----
// f() -> 10
// g() -> 13

View File

@ -0,0 +1,22 @@
type U8 is uint8;
function checkedAdd(U8 x, U8 y) pure returns (U8) {
return U8.wrap(U8.unwrap(x) + U8.unwrap(y));
}
using {checkedAdd as +} for U8 global;
contract C {
function testCheckedOperator() public pure returns (U8) {
return U8.wrap(250) + U8.wrap(10);
}
function testCheckedOperatorInUncheckedBlock() public pure returns (U8) {
unchecked {
return U8.wrap(250) + U8.wrap(10);
}
}
}
// ----
// testCheckedOperator() -> FAILURE, hex"4e487b71", 0x11
// testCheckedOperatorInUncheckedBlock() -> FAILURE, hex"4e487b71", 0x11

View File

@ -0,0 +1,18 @@
type A is address;
using {add as +} for A global;
function add(A a, A b) pure returns (A) {
return A.wrap(address(uint160(A.unwrap(a)) + uint160(A.unwrap(b))));
}
contract C {
function g() public pure returns (A) {
A a = A.wrap(0x3333333333333333333333333333333333333333);
A b = A.wrap(0x1111111111111111111111111111111111111111);
A c = A.wrap(0x5555555555555555555555555555555555555555);
return a + b + c;
}
}
// ----
// g() -> 0x9999999999999999999999999999999999999999

View File

@ -0,0 +1,22 @@
type Fixed is int128;
using {add as +, mul as *} for Fixed global;
int constant MULTIPLIER = 10**18;
function add(Fixed a, Fixed b) pure returns (Fixed) {
return Fixed.wrap(Fixed.unwrap(a) + Fixed.unwrap(b));
}
function mul(Fixed a, Fixed b) pure returns (Fixed) {
int intermediate = (int(Fixed.unwrap(a)) * int(Fixed.unwrap(b))) / MULTIPLIER;
if (int128(intermediate) != intermediate) { revert("Overflow"); }
return Fixed.wrap(int128(intermediate));
}
contract C {
function applyInterest(Fixed value, Fixed percentage) public pure returns (Fixed result) {
return value + value * percentage;
}
}
// ----
// applyInterest(int128,int128): 500000000000000000000, 100000000000000000 -> 550000000000000000000

View File

@ -0,0 +1,26 @@
type SmallInt is int;
type BigInt is int;
using {addSmall as +} for SmallInt global;
using {addBig as +} for BigInt global;
function addSmall(SmallInt a, SmallInt b) pure returns (SmallInt) {
return SmallInt.wrap(SmallInt.unwrap(a) + SmallInt.unwrap(b));
}
function addBig(BigInt a, BigInt b) pure returns (BigInt) {
return BigInt.wrap(10 * (BigInt.unwrap(a) + BigInt.unwrap(b)));
}
contract C {
function small() public pure returns (SmallInt) {
return SmallInt.wrap(1) + SmallInt.wrap(2);
}
function big() public pure returns (BigInt) {
return BigInt.wrap(3) + BigInt.wrap(4);
}
}
// ----
// small() -> 3
// big() -> 70

View File

@ -0,0 +1,20 @@
type Int is int32;
using {foo as +, foo as -} for Int global;
function foo(Int a, Int b) pure returns(Int) {
return Int.wrap(Int.unwrap(a) + Int.unwrap(b));
}
contract C {
function f() pure public returns (Int) {
return Int.wrap(2) + Int.wrap(3);
}
function g() pure public returns (Int) {
return Int.wrap(6) - Int.wrap(1);
}
}
// ----
// f() -> 5
// g() -> 7

View File

@ -0,0 +1,15 @@
type Int is int16;
using {keccak256 as +} for Int global;
function keccak256(Int a, Int b) pure returns (Int) {
return Int.wrap(Int.unwrap(a) + Int.unwrap(b));
}
contract C {
function test() public returns (Int) {
return Int.wrap(3) + Int.wrap(4);
}
}
// ----
// test() -> 7

View File

@ -0,0 +1,83 @@
type Bool is bool;
using {add as +, mul as *, unsub as -} for Bool global;
function add(Bool x, Bool y) pure returns (Bool) {
return Bool.wrap(Bool.unwrap(x) || Bool.unwrap(y));
}
function mul(Bool x, Bool y) pure returns (Bool) {
return Bool.wrap(Bool.unwrap(x) && Bool.unwrap(y));
}
function unsub(Bool x) pure returns (Bool) {
return Bool.wrap(!Bool.unwrap(x));
}
contract C {
event Wrapped(uint);
event Probe(Bool);
function toBool(uint x) public returns (Bool) {
emit Wrapped(x);
return Bool.wrap(x > 0);
}
function probe(Bool x) public returns (Bool) {
emit Probe(x);
return x;
}
function testSingleOperator() public {
toBool(0) +
(toBool(1) + toBool(2)) +
toBool(3);
}
function testTwoBinaryOperators() public {
toBool(0) * toBool(1) +
(toBool(2) * toBool(3)) +
toBool(4) * toBool(5);
}
function testBinaryAndUnaryOperators() public {
-toBool(0) * -toBool(1) +
(-toBool(2) * -toBool(3)) +
-toBool(4) * -toBool(5);
}
function testOperatorsNestedInCalls() public {
-probe(toBool(0) * -toBool(1)) +
(-probe(toBool(2) * -toBool(3))) +
-probe(toBool(4) * -toBool(5));
}
}
// ----
// testSingleOperator() ->
// ~ emit Wrapped(uint256): 0x00
// ~ emit Wrapped(uint256): 0x01
// ~ emit Wrapped(uint256): 0x02
// ~ emit Wrapped(uint256): 0x03
// testTwoBinaryOperators() ->
// ~ emit Wrapped(uint256): 0x00
// ~ emit Wrapped(uint256): 0x01
// ~ emit Wrapped(uint256): 0x02
// ~ emit Wrapped(uint256): 0x03
// ~ emit Wrapped(uint256): 0x04
// ~ emit Wrapped(uint256): 0x05
// testBinaryAndUnaryOperators() ->
// ~ emit Wrapped(uint256): 0x00
// ~ emit Wrapped(uint256): 0x01
// ~ emit Wrapped(uint256): 0x02
// ~ emit Wrapped(uint256): 0x03
// ~ emit Wrapped(uint256): 0x04
// ~ emit Wrapped(uint256): 0x05
// testOperatorsNestedInCalls() ->
// ~ emit Wrapped(uint256): 0x00
// ~ emit Wrapped(uint256): 0x01
// ~ emit Probe(bool): 0x00
// ~ emit Wrapped(uint256): 0x02
// ~ emit Wrapped(uint256): 0x03
// ~ emit Probe(bool): 0x00
// ~ emit Wrapped(uint256): 0x04
// ~ emit Wrapped(uint256): 0x05
// ~ emit Probe(bool): 0x00

View File

@ -0,0 +1,61 @@
type Int32 is int32;
using {add as +, unsub as -} for Int32 global;
function add(Int32 x, Int32 y) pure returns (Int32) {
return loadAdder().mul(x, y);
}
function unsub(Int32 x) pure returns (Int32) {
return loadAdder().inc(x);
}
interface IAdder {
function mul(Int32, Int32) external pure returns (Int32);
function inc(Int32) external pure returns (Int32);
}
contract Adder is IAdder {
function mul(Int32 x, Int32 y) external pure override returns (Int32) {
return Int32.wrap(Int32.unwrap(x) * Int32.unwrap(y));
}
function inc(Int32 x) external pure override returns (Int32) {
return Int32.wrap(Int32.unwrap(x) + 1);
}
}
function storeAdder(IAdder adder) pure {
assembly {
// This test would also work without assembly if we could hard-code an address here.
mstore(0, adder)
}
}
function loadAdder() pure returns (IAdder adder) {
assembly {
adder := mload(0)
}
}
contract C {
function testMul(Int32 x, Int32 y) public returns (Int32) {
storeAdder(new Adder());
return x + y;
}
function testInc(Int32 x) public returns (Int32) {
storeAdder(new Adder());
return -x;
}
}
// ----
// testMul(int32,int32): 42, 10 -> 420
// gas irOptimized: 103347
// gas legacy: 188203
// gas legacyOptimized: 126164
// testInc(int32): 42 -> 43
// gas irOptimized: 103173
// gas legacy: 187452
// gas legacyOptimized: 125851

View File

@ -0,0 +1,67 @@
type Int32 is int32;
using {add as +, unsub as -} for Int32 global;
function add(Int32 x, Int32 y) pure returns (Int32) {
return loadAdder().mul(x, y);
}
function unsub(Int32 x) pure returns (Int32) {
return loadAdder().inc(x);
}
interface IAdderPure {
function mul(Int32, Int32) external pure returns (Int32);
function inc(Int32) external pure returns (Int32);
}
interface IAdderView {
function mul(Int32, Int32) external view returns (Int32);
function inc(Int32) external view returns (Int32);
}
contract Adder is IAdderView {
function mul(Int32 x, Int32 y) external view override returns (Int32) {
return Int32.wrap(Int32.unwrap(x) * Int32.unwrap(y));
}
function inc(Int32 x) external view override returns (Int32) {
return Int32.wrap(Int32.unwrap(x) + 1);
}
}
function storeAdder(IAdderView adder) pure {
assembly {
// This test would also work without assembly if we could hard-code an address here.
mstore(0, adder)
}
}
function loadAdder() pure returns (IAdderPure adder) {
assembly {
// The adder we stored is view but we cheat by using a modified version with pure functions
adder := mload(0)
}
}
contract C {
function testMul(Int32 x, Int32 y) public returns (Int32) {
storeAdder(new Adder());
return x + y;
}
function testInc(Int32 x) public returns (Int32) {
storeAdder(new Adder());
return -x;
}
}
// ----
// testMul(int32,int32): 42, 10 -> 420
// gas irOptimized: 103347
// gas legacy: 188203
// gas legacyOptimized: 126164
// testInc(int32): 42 -> 43
// gas irOptimized: 103173
// gas legacy: 187452
// gas legacyOptimized: 125851

View File

@ -0,0 +1,40 @@
type U8 is uint8;
using {f as ~, add as +} for U8 global;
function f(U8 x) pure returns (U8 z) {
assembly {
// NOTE: Not using shr so that the test works pre-constantinople too
z := div(x, 256)
}
}
function add(U8 x, U8 y) pure returns (U8 z) {
assembly {
z := add(div(x, 256), div(x, 256))
}
}
contract C {
function testUnary() external pure returns (U8, U8) {
U8 a;
assembly {
a := 0x4200
}
// If the result is not 0, no cleanup was performed.
return (~a, f(a));
}
function testBinary() external pure returns (U8, U8) {
U8 a;
U8 b;
assembly {
a := 0x4200
b := 0x4200
}
// If the result is not 0, no cleanup was performed.
return (a + b, add(a, b));
}
}
// ----
// testUnary() -> 0x42, 0x42
// testBinary() -> 0x84, 0x84

View File

@ -0,0 +1,71 @@
type Int is int64;
using {
bitor as |, bitand as &, bitxor as ^, bitnot as ~,
add as +, sub as -, unsub as -, mul as *, div as /, mod as %
} for Int global;
function bitor(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) | Int.unwrap(y)); }
function bitand(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) & Int.unwrap(y)); }
function bitxor(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) ^ Int.unwrap(y)); }
function bitnot(Int x) pure returns (Int) { return Int.wrap(~Int.unwrap(x)); }
function add(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) + Int.unwrap(y)); }
function sub(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) - Int.unwrap(y)); }
function unsub(Int x) pure returns (Int) { return Int.wrap(-Int.unwrap(x)); }
function mul(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) * Int.unwrap(y)); }
function div(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) / Int.unwrap(y)); }
function mod(Int x, Int y) pure returns (Int) { return Int.wrap(Int.unwrap(x) % Int.unwrap(y)); }
contract C {
Int constant I0 = Int.wrap(0);
Int constant I1 = Int.wrap(1);
Int constant I2 = Int.wrap(2);
Int constant I3 = Int.wrap(3);
Int constant I4 = Int.wrap(4);
Int constant I5 = Int.wrap(5);
Int constant I6 = Int.wrap(6);
Int constant I7 = Int.wrap(7);
Int constant I8 = Int.wrap(8);
Int constant I10 = Int.wrap(10);
Int constant I13 = Int.wrap(13);
Int constant I15 = Int.wrap(15);
Int constant I20 = Int.wrap(20);
Int constant I128 = Int.wrap(128);
function testBitwise() public pure {
assert(Int.unwrap(I0 & I0 | I1) == (0 & 0 | 1));
assert(Int.unwrap(I0 & I0 | I1) == ((0 & 0) | 1));
}
function testBitwise_arithmetic() public pure {
assert(Int.unwrap(I2 + I2 & ~I1 | I6 * I6 - I4 & ~I3) == (2 + 2 & ~1 | 6 * 6 - 4 & ~3));
assert(Int.unwrap(I2 + I2 & ~I1 | I6 * I6 - I4 & ~I3) == (((2 + 2) & (~1)) | (((6 * 6) - 4) & (~3))));
}
function testArithmetic() public pure {
assert(Int.unwrap(I1 + I8 / I4 - I5 % I6 * I7) == (1 + 8 / 4 - 5 % 6 * 7));
assert(Int.unwrap(I1 + I8 / I4 - I5 % I6 * I7) == ((1 + (8 / 4)) - ((5 % 6) * 7)));
}
function testAll() public pure {
assert(
Int.unwrap(I128 + I1 - I10 + I4 & ~I1 ^ ~I1 * I2 | -I15 % -I10 * I20 / I2 + I13 & ~I3) ==
(128 + 1 - 10 + 4 & ~1 ^ ~1 * 2 | -15 % -10 * 20 / 2 + 13 & ~3)
);
assert(
Int.unwrap(I128 + I1 - I10 + I4 & ~I1 ^ ~I1 * I2 | -I15 % -I10 * I20 / I2 + I13 & ~I3) ==
(
(
((((128 + 1) - 10) + 4) & (~1)) ^
((~1) * 2)
) |
((((((-15) % (-10)) * 20) / 2) + 13) & (~3))
)
);
}
}
// ----
// testBitwise() ->
// testBitwise_arithmetic() ->
// testArithmetic() ->
// testAll() ->

View File

@ -0,0 +1,58 @@
type U8 is uint8;
using {f as ~, g as +} for U8 global;
function f(U8) pure returns (U8 z) {
assembly {
// Return a value with dirty bytes outside of uint8
z := 0xffff
}
}
function g(U8, U8) pure returns (U8 z) {
assembly {
// Return a value with dirty bytes outside of uint8
z := 0xffff
}
}
contract C {
function testUnary() external pure returns (uint, uint) {
U8 a; // Value does not matter
U8 opResult = ~a;
U8 fResult = f(a);
// Get the slot, including bytes outside of uint8
uint opResultFull;
uint fResultFull;
assembly {
opResultFull := opResult
fResultFull := fResult
}
// If the result is not 0xff, no cleanup was performed.
return (opResultFull, fResultFull);
}
function testBinary() external pure returns (uint, uint) {
U8 a; // Value does not matter
U8 b; // Value does not matter
U8 opResult = a + b;
U8 fResult = g(a, b);
// Get the slot, including bytes outside of uint8
uint opResultFull;
uint fResultFull;
assembly {
opResultFull := opResult
fResultFull := fResult
}
// If the result is not 0xff, no cleanup was performed.
return (opResultFull, fResultFull);
}
}
// ----
// testUnary() -> 0xffff, 0xffff
// testBinary() -> 0xffff, 0xffff

View File

@ -0,0 +1,41 @@
type Uint is uint;
using {unaryCountdown as ~, binaryCountdown as ^, eq as ==} for Uint global;
function unaryCountdown(Uint x) pure returns (Uint) {
if (x == Uint.wrap(0))
return Uint.wrap(0);
return ~Uint.wrap(Uint.unwrap(x) - 1);
}
function binaryCountdown(Uint x, Uint y) pure returns (Uint) {
if (x == Uint.wrap(0) && y == Uint.wrap(0))
return Uint.wrap(0);
if (x == Uint.wrap(0))
return y ^ x;
return Uint.wrap(Uint.unwrap(x) - 1) ^ y;
}
function eq(Uint x, Uint y) pure returns (bool) {
return Uint.unwrap(x) == Uint.unwrap(y);
}
contract C {
function testUnary(Uint x) public pure returns (Uint) {
return ~x;
}
function testBinary(Uint x, Uint y) public pure returns (Uint) {
return x ^ y;
}
}
// ----
// testUnary(uint256): 0 -> 0
// testUnary(uint256): 1 -> 0
// testUnary(uint256): 99999999999 -> FAILURE
// testBinary(uint256,uint256): 0, 0 -> 0
// testBinary(uint256,uint256): 1, 0 -> 0
// testBinary(uint256,uint256): 0, 1 -> 0
// testBinary(uint256,uint256): 1, 1 -> 0
// testBinary(uint256,uint256): 99999999999, 99999999999 -> FAILURE

View File

@ -0,0 +1,25 @@
type U8 is uint8;
function uncheckedAdd(U8 x, U8 y) pure returns (U8) {
unchecked {
return U8.wrap(U8.unwrap(x) + U8.unwrap(y));
}
}
using {uncheckedAdd as +} for U8 global;
contract D {
function testUncheckedOperator() public pure returns (U8) {
return U8.wrap(250) + U8.wrap(10);
}
function testUncheckedOperatorInUncheckedBlock() public pure returns (U8) {
unchecked {
return U8.wrap(250) + U8.wrap(10);
}
}
}
// ----
// testUncheckedOperator() -> 4
// testUncheckedOperatorInUncheckedBlock() -> 4

View File

@ -0,0 +1,16 @@
type U is uint;
using {div as /} for U global;
function div(U x, U y) pure returns (U) {
return U.wrap(U.unwrap(x) / U.unwrap(y));
}
contract C {
function f(U x, U y) public pure returns (U) {
return x / y; // FIXME: should detect div by zero
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (218-223): User-defined operators are not yet supported by SMTChecker. This invocation of operator / has been ignored, which may lead to incorrect results.

View File

@ -0,0 +1,19 @@
type U is uint;
using {div as /} for U global;
function div(U x, U y) pure returns (U) {
if (U.unwrap(y) == 0)
return U.wrap(0);
return U.wrap(U.unwrap(x) / U.unwrap(y));
}
contract C {
function f(U x, U y) public pure returns (U) {
return x / y; // no div by zero possible here
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (271-276): User-defined operators are not yet supported by SMTChecker. This invocation of operator / has been ignored, which may lead to incorrect results.

View File

@ -0,0 +1,104 @@
type I16 is int16;
using {
bitor as |, bitand as &, bitxor as ^, bitnot as ~,
add as +, sub as -, unsub as -, mul as *, div as /, mod as %,
eq as ==, noteq as !=, lt as <, gt as >, leq as <=, geq as >=
} for I16 global;
function bitor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) | I16.unwrap(y)); }
function bitand(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) & I16.unwrap(y)); }
function bitxor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) ^ I16.unwrap(y)); }
function bitnot(I16 x) pure returns (I16) { return I16.wrap(~I16.unwrap(x)); }
function add(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) + I16.unwrap(y)); }
function sub(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) - I16.unwrap(y)); }
function unsub(I16 x) pure returns (I16) { return I16.wrap(-I16.unwrap(x)); }
function mul(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) * I16.unwrap(y)); }
function div(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) / I16.unwrap(y)); }
function mod(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) % I16.unwrap(y)); }
function eq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) == I16.unwrap(y); }
function noteq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) != I16.unwrap(y); }
function lt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) < I16.unwrap(y); }
function gt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) > I16.unwrap(y); }
function leq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) <= I16.unwrap(y); }
function geq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) >= I16.unwrap(y); }
contract C {
I16 constant MINUS_TWO = I16.wrap(-2);
I16 constant ZERO = I16.wrap(0);
I16 constant ONE = I16.wrap(1);
I16 constant TWO = I16.wrap(2);
I16 constant THREE = I16.wrap(3);
I16 constant FOUR = I16.wrap(4);
function testBitwise() public pure {
assert(ONE | TWO == THREE); // FIXME: should hold
assert(ONE & THREE == ZERO); // FIXME: should hold
assert(TWO ^ TWO == ZERO); // FIXME: should hold
assert(~ONE == MINUS_TWO); // FIXME: should hold
}
function testArithmetic() public pure {
assert(TWO + TWO == FOUR); // FIXME: should hold
assert(TWO - TWO == ZERO); // FIXME: should hold
assert(-TWO == MINUS_TWO); // FIXME: should hold
assert(TWO * TWO == FOUR); // FIXME: should hold
assert(TWO / TWO == ONE); // FIXME: should hold
assert(TWO % TWO == ZERO); // FIXME: should hold
}
function testComparison() public pure {
assert(TWO == TWO); // FIXME: should hold
assert(!(TWO != TWO)); // FIXME: should hold
assert(!(TWO < TWO)); // FIXME: should hold
assert(!(TWO > TWO)); // FIXME: should hold
assert(TWO <= TWO); // FIXME: should hold
assert(TWO >= TWO); // FIXME: should hold
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (2019-2028): User-defined operators are not yet supported by SMTChecker. This invocation of operator | has been ignored, which may lead to incorrect results.
// Warning 6756: (2019-2037): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2077-2088): User-defined operators are not yet supported by SMTChecker. This invocation of operator & has been ignored, which may lead to incorrect results.
// Warning 6756: (2077-2096): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2136-2145): User-defined operators are not yet supported by SMTChecker. This invocation of operator ^ has been ignored, which may lead to incorrect results.
// Warning 6756: (2136-2153): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (2193-2197): User-defined operators are not yet supported by SMTChecker. This invocation of operator ~ has been ignored, which may lead to incorrect results.
// Warning 6756: (2193-2210): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2301-2310): User-defined operators are not yet supported by SMTChecker. This invocation of operator + has been ignored, which may lead to incorrect results.
// Warning 6756: (2301-2318): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2358-2367): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2358-2375): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (2415-2419): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2415-2432): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2472-2481): User-defined operators are not yet supported by SMTChecker. This invocation of operator * has been ignored, which may lead to incorrect results.
// Warning 6756: (2472-2489): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2529-2538): User-defined operators are not yet supported by SMTChecker. This invocation of operator / has been ignored, which may lead to incorrect results.
// Warning 6756: (2529-2545): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2585-2594): User-defined operators are not yet supported by SMTChecker. This invocation of operator % has been ignored, which may lead to incorrect results.
// Warning 6756: (2585-2602): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2693-2703): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2745-2755): User-defined operators are not yet supported by SMTChecker. This invocation of operator != has been ignored, which may lead to incorrect results.
// Warning 6756: (2798-2807): User-defined operators are not yet supported by SMTChecker. This invocation of operator < has been ignored, which may lead to incorrect results.
// Warning 6756: (2850-2859): User-defined operators are not yet supported by SMTChecker. This invocation of operator > has been ignored, which may lead to incorrect results.
// Warning 6756: (2900-2910): User-defined operators are not yet supported by SMTChecker. This invocation of operator <= has been ignored, which may lead to incorrect results.
// Warning 6756: (2950-2960): User-defined operators are not yet supported by SMTChecker. This invocation of operator >= has been ignored, which may lead to incorrect results.
// Warning 6328: (2012-2038): CHC: Assertion violation happens here.
// Warning 6328: (2070-2097): CHC: Assertion violation happens here.
// Warning 6328: (2129-2154): CHC: Assertion violation happens here.
// Warning 6328: (2186-2211): CHC: Assertion violation happens here.
// Warning 6328: (2294-2319): CHC: Assertion violation happens here.
// Warning 6328: (2351-2376): CHC: Assertion violation happens here.
// Warning 6328: (2408-2433): CHC: Assertion violation happens here.
// Warning 6328: (2465-2490): CHC: Assertion violation happens here.
// Warning 6328: (2522-2546): CHC: Assertion violation happens here.
// Warning 6328: (2578-2603): CHC: Assertion violation happens here.
// Warning 6328: (2686-2704): CHC: Assertion violation happens here.
// Warning 6328: (2736-2757): CHC: Assertion violation happens here.
// Warning 6328: (2789-2809): CHC: Assertion violation happens here.
// Warning 6328: (2841-2861): CHC: Assertion violation happens here.
// Warning 6328: (2893-2911): CHC: Assertion violation happens here.
// Warning 6328: (2943-2961): CHC: Assertion violation happens here.

View File

@ -0,0 +1,104 @@
type I16 is int16;
using {
bitor as |, bitand as &, bitxor as ^, bitnot as ~,
add as +, sub as -, unsub as -, mul as *, div as /, mod as %,
eq as ==, noteq as !=, lt as <, gt as >, leq as <=, geq as >=
} for I16 global;
function bitor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) | I16.unwrap(y)); }
function bitand(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) & I16.unwrap(y)); }
function bitxor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) ^ I16.unwrap(y)); }
function bitnot(I16 x) pure returns (I16) { return I16.wrap(~I16.unwrap(x)); }
function add(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) + I16.unwrap(y)); }
function sub(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) - I16.unwrap(y)); }
function unsub(I16 x) pure returns (I16) { return I16.wrap(-I16.unwrap(x)); }
function mul(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) * I16.unwrap(y)); }
function div(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) / I16.unwrap(y)); }
function mod(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) % I16.unwrap(y)); }
function eq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) == I16.unwrap(y); }
function noteq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) != I16.unwrap(y); }
function lt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) < I16.unwrap(y); }
function gt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) > I16.unwrap(y); }
function leq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) <= I16.unwrap(y); }
function geq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) >= I16.unwrap(y); }
contract C {
I16 constant MINUS_TWO = I16.wrap(-2);
I16 constant ZERO = I16.wrap(0);
I16 constant ONE = I16.wrap(1);
I16 constant TWO = I16.wrap(2);
I16 constant THREE = I16.wrap(3);
I16 constant FOUR = I16.wrap(4);
function testBitwise() public pure {
assert(ONE | TWO == FOUR); // should fail
assert(ONE & THREE == FOUR); // should fail
assert(TWO ^ TWO == FOUR); // should fail
assert(~ONE == FOUR); // should fail
}
function testArithmetic() public pure {
assert(TWO + THREE == FOUR); // should fail
assert(TWO - TWO == FOUR); // should fail
assert(-TWO == FOUR); // should fail
assert(TWO * THREE == FOUR); // should fail
assert(TWO / TWO == FOUR); // should fail
assert(TWO % TWO == FOUR); // should fail
}
function testComparison() public pure {
assert(!(TWO == TWO)); // should fail
assert(TWO != TWO); // should fail
assert(TWO < TWO); // should fail
assert(TWO > TWO); // should fail
assert(!(TWO <= TWO)); // should fail
assert(!(TWO >= TWO)); // should fail
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (2019-2028): User-defined operators are not yet supported by SMTChecker. This invocation of operator | has been ignored, which may lead to incorrect results.
// Warning 6756: (2019-2036): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2069-2080): User-defined operators are not yet supported by SMTChecker. This invocation of operator & has been ignored, which may lead to incorrect results.
// Warning 6756: (2069-2088): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2121-2130): User-defined operators are not yet supported by SMTChecker. This invocation of operator ^ has been ignored, which may lead to incorrect results.
// Warning 6756: (2121-2138): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (2171-2175): User-defined operators are not yet supported by SMTChecker. This invocation of operator ~ has been ignored, which may lead to incorrect results.
// Warning 6756: (2171-2183): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2267-2278): User-defined operators are not yet supported by SMTChecker. This invocation of operator + has been ignored, which may lead to incorrect results.
// Warning 6756: (2267-2286): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2319-2328): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2319-2336): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (2369-2373): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2369-2381): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2414-2425): User-defined operators are not yet supported by SMTChecker. This invocation of operator * has been ignored, which may lead to incorrect results.
// Warning 6756: (2414-2433): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2466-2475): User-defined operators are not yet supported by SMTChecker. This invocation of operator / has been ignored, which may lead to incorrect results.
// Warning 6756: (2466-2483): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2516-2525): User-defined operators are not yet supported by SMTChecker. This invocation of operator % has been ignored, which may lead to incorrect results.
// Warning 6756: (2516-2533): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2619-2629): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2663-2673): User-defined operators are not yet supported by SMTChecker. This invocation of operator != has been ignored, which may lead to incorrect results.
// Warning 6756: (2706-2715): User-defined operators are not yet supported by SMTChecker. This invocation of operator < has been ignored, which may lead to incorrect results.
// Warning 6756: (2748-2757): User-defined operators are not yet supported by SMTChecker. This invocation of operator > has been ignored, which may lead to incorrect results.
// Warning 6756: (2792-2802): User-defined operators are not yet supported by SMTChecker. This invocation of operator <= has been ignored, which may lead to incorrect results.
// Warning 6756: (2838-2848): User-defined operators are not yet supported by SMTChecker. This invocation of operator >= has been ignored, which may lead to incorrect results.
// Warning 6328: (2012-2037): CHC: Assertion violation happens here.
// Warning 6328: (2062-2089): CHC: Assertion violation happens here.
// Warning 6328: (2114-2139): CHC: Assertion violation happens here.
// Warning 6328: (2164-2184): CHC: Assertion violation happens here.
// Warning 6328: (2260-2287): CHC: Assertion violation happens here.
// Warning 6328: (2312-2337): CHC: Assertion violation happens here.
// Warning 6328: (2362-2382): CHC: Assertion violation happens here.
// Warning 6328: (2407-2434): CHC: Assertion violation happens here.
// Warning 6328: (2459-2484): CHC: Assertion violation happens here.
// Warning 6328: (2509-2534): CHC: Assertion violation happens here.
// Warning 6328: (2610-2631): CHC: Assertion violation happens here.
// Warning 6328: (2656-2674): CHC: Assertion violation happens here.
// Warning 6328: (2699-2716): CHC: Assertion violation happens here.
// Warning 6328: (2741-2758): CHC: Assertion violation happens here.
// Warning 6328: (2783-2804): CHC: Assertion violation happens here.
// Warning 6328: (2829-2850): CHC: Assertion violation happens here.

View File

@ -0,0 +1,105 @@
type I16 is int16;
using {
bitor as |, bitand as &, bitxor as ^, bitnot as ~,
add as +, sub as -, unsub as -, mul as *, div as /, mod as %,
eq as ==, noteq as !=, lt as <, gt as >, leq as <=, geq as >=
} for I16 global;
function bitor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) | I16.unwrap(y)); }
function bitand(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) & I16.unwrap(y)); }
function bitxor(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) ^ I16.unwrap(y)); }
function bitnot(I16 x) pure returns (I16) { return I16.wrap(~I16.unwrap(x)); }
function add(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) + I16.unwrap(y)); }
function sub(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) - I16.unwrap(y)); }
function unsub(I16 x) pure returns (I16) { return I16.wrap(-I16.unwrap(x)); }
function mul(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) * I16.unwrap(y)); }
function div(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) / I16.unwrap(y)); }
function mod(I16 x, I16 y) pure returns (I16) { return I16.wrap(I16.unwrap(x) % I16.unwrap(y)); }
function eq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) == I16.unwrap(y); }
function noteq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) != I16.unwrap(y); }
function lt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) < I16.unwrap(y); }
function gt(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) > I16.unwrap(y); }
function leq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) <= I16.unwrap(y); }
function geq(I16 x, I16 y) pure returns (bool) { return I16.unwrap(x) >= I16.unwrap(y); }
contract C {
function testBitwise(I16 x, I16 y) public pure {
assert(x | y == bitor(x, y)); // FIXME: should hold
assert(x & y == bitand(x, y)); // FIXME: should hold
assert(x ^ y == bitxor(x, y)); // FIXME: should hold
assert(~x == bitnot(x)); // FIXME: should hold
}
function testArithmetic(I16 x, I16 y) public pure {
assert(x + y == add(x, y)); // FIXME: should hold
assert(x - y == sub(x, y)); // FIXME: should hold
assert(-x == unsub(x)); // FIXME: should hold
assert(x * y == mul(x, y)); // FIXME: should hold
assert(x / y == div(x, y)); // FIXME: should hold
assert(x % y == mod(x, y)); // FIXME: should hold
}
function testComparison(I16 x, I16 y) public pure {
assert((x == y) == eq(x, y)); // FIXME: should hold
assert((x != y) == noteq(x, y)); // FIXME: should hold
assert((x < y) == lt(x, y)); // FIXME: should hold
assert((x > y) == gt(x, y)); // FIXME: should hold
assert((x <= y) == leq(x, y)); // FIXME: should hold
assert((x >= y) == geq(x, y)); // FIXME: should hold
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (1803-1808): User-defined operators are not yet supported by SMTChecker. This invocation of operator | has been ignored, which may lead to incorrect results.
// Warning 6756: (1803-1823): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (1863-1868): User-defined operators are not yet supported by SMTChecker. This invocation of operator & has been ignored, which may lead to incorrect results.
// Warning 6756: (1863-1884): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (1924-1929): User-defined operators are not yet supported by SMTChecker. This invocation of operator ^ has been ignored, which may lead to incorrect results.
// Warning 6756: (1924-1945): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (1985-1987): User-defined operators are not yet supported by SMTChecker. This invocation of operator ~ has been ignored, which may lead to incorrect results.
// Warning 6756: (1985-2000): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2103-2108): User-defined operators are not yet supported by SMTChecker. This invocation of operator + has been ignored, which may lead to incorrect results.
// Warning 6756: (2103-2121): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2161-2166): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2161-2179): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6156: (2219-2221): User-defined operators are not yet supported by SMTChecker. This invocation of operator - has been ignored, which may lead to incorrect results.
// Warning 6756: (2219-2233): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2273-2278): User-defined operators are not yet supported by SMTChecker. This invocation of operator * has been ignored, which may lead to incorrect results.
// Warning 6756: (2273-2291): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2331-2336): User-defined operators are not yet supported by SMTChecker. This invocation of operator / has been ignored, which may lead to incorrect results.
// Warning 6756: (2331-2349): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2389-2394): User-defined operators are not yet supported by SMTChecker. This invocation of operator % has been ignored, which may lead to incorrect results.
// Warning 6756: (2389-2407): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2511-2517): User-defined operators are not yet supported by SMTChecker. This invocation of operator == has been ignored, which may lead to incorrect results.
// Warning 6756: (2571-2577): User-defined operators are not yet supported by SMTChecker. This invocation of operator != has been ignored, which may lead to incorrect results.
// Warning 6756: (2634-2639): User-defined operators are not yet supported by SMTChecker. This invocation of operator < has been ignored, which may lead to incorrect results.
// Warning 6756: (2693-2698): User-defined operators are not yet supported by SMTChecker. This invocation of operator > has been ignored, which may lead to incorrect results.
// Warning 6756: (2752-2758): User-defined operators are not yet supported by SMTChecker. This invocation of operator <= has been ignored, which may lead to incorrect results.
// Warning 6756: (2813-2819): User-defined operators are not yet supported by SMTChecker. This invocation of operator >= has been ignored, which may lead to incorrect results.
// Warning 3944: (679-708): CHC: Underflow (resulting value less than -32768) happens here.
// Warning 4984: (679-708): CHC: Overflow (resulting value larger than 32767) happens here.
// Warning 3944: (777-806): CHC: Underflow (resulting value less than -32768) happens here.
// Warning 4984: (777-806): CHC: Overflow (resulting value larger than 32767) happens here.
// Warning 3944: (953-982): CHC: Underflow (resulting value less than -32768) happens here.
// Warning 4984: (953-982): CHC: Overflow (resulting value larger than 32767) happens here.
// Warning 4281: (1051-1080): CHC: Division by zero happens here.
// Warning 4281: (1149-1178): CHC: Division by zero happens here.
// Warning 6328: (1796-1824): CHC: Assertion violation happens here.
// Warning 6328: (1856-1885): CHC: Assertion violation happens here.
// Warning 6328: (1917-1946): CHC: Assertion violation happens here.
// Warning 6328: (1978-2001): CHC: Assertion violation happens here.
// Warning 6328: (2096-2122): CHC: Assertion violation happens here.
// Warning 6328: (2154-2180): CHC: Assertion violation happens here.
// Warning 6328: (2212-2234): CHC: Assertion violation happens here.
// Warning 6328: (2266-2292): CHC: Assertion violation happens here.
// Warning 6328: (2324-2350): CHC: Assertion violation happens here.
// Warning 6328: (2382-2408): CHC: Assertion violation happens here.
// Warning 6328: (2503-2531): CHC: Assertion violation happens here.
// Warning 6328: (2563-2594): CHC: Assertion violation happens here.
// Warning 6328: (2626-2653): CHC: Assertion violation happens here.
// Warning 6328: (2685-2712): CHC: Assertion violation happens here.
// Warning 6328: (2744-2773): CHC: Assertion violation happens here.
// Warning 6328: (2805-2834): CHC: Assertion violation happens here.

View File

@ -0,0 +1,23 @@
type U8 is uint8;
using {add as +} for U8 global;
function add(U8 x, U8 y) pure returns (U8) {
return U8.wrap(U8.unwrap(x) + U8.unwrap(y)); // FIXME: should detect possible overflow here
}
contract C {
U8 x = U8.wrap(254);
function inc() public {
x = x + U8.wrap(1); // FIXME: should detect possible overflow here
}
function check() view public {
U8 y = x;
assert(U8.unwrap(y) < 256);
}
}
// ====
// SMTEngine: all
// ----
// Warning 6756: (274-288): User-defined operators are not yet supported by SMTChecker. This invocation of operator + has been ignored, which may lead to incorrect results.

View File

@ -0,0 +1,10 @@
type Type is uint;
using {f as +} for Type global;
function f(Type, Type) pure returns (Type) {}
Type constant t = Type.wrap(1);
Type constant u = v + t;
Type constant v = u + t;
// ----
// TypeError 8349: (148-153): Initial value for constant variable has to be compile-time constant.
// TypeError 8349: (173-178): Initial value for constant variable has to be compile-time constant.

View File

@ -0,0 +1,17 @@
type X is uint24;
type Y is uint16;
using {addX as +} for X global;
using {addY as +} for Y global;
function addX(X, X) pure returns (X) {}
function addY(Y, Y) pure returns (Y) { revert(); }
contract C {
function f() public pure {
X.wrap(1) + X.wrap(Y.unwrap(Y.wrap(2) + Y.wrap(3)));
X.wrap(4) + X.wrap(5); // Unreachable
}
}
// ----
// Warning 5740: (307-328): Unreachable code.

View File

@ -0,0 +1,17 @@
type X is uint24;
type Y is uint16;
using {unsubX as -} for X global;
using {unsubY as -} for Y global;
function unsubX(X) pure returns (X) {}
function unsubY(Y) pure returns (Y) { revert(); }
contract C {
function f() public pure {
-X.wrap(Y.unwrap(-Y.wrap(1)));
-X.wrap(Y.unwrap(Y.wrap(2))); // Unreachable
}
}
// ----
// Warning 5740: (287-315): Unreachable code.

View File

@ -7,4 +7,4 @@ contract test {
}
}
// ----
// TypeError 2271: (79-85): Built-in binary operator == cannot be applied to types struct test.s storage ref and struct test.s storage ref.
// TypeError 2271: (79-85): Built-in binary operator == cannot be applied to types struct test.s storage ref and struct test.s storage ref. No matching user-defined operator found.

View File

@ -0,0 +1,11 @@
type Int is int256;
function f() pure {
Int a = Int.wrap(0);
a + a;
a >>> a;
}
// ----
// TypeError 2271: (70-75): Built-in binary operator + cannot be applied to types Int and Int. No matching user-defined operator found.
// TypeError 2271: (81-88): Built-in binary operator >>> cannot be applied to types Int and Int.

View File

@ -0,0 +1,11 @@
type Int is int256;
function f() pure {
Int a = Int.wrap(0);
-a;
a++;
}
// ----
// TypeError 4907: (70-72): Built-in unary operator - cannot be applied to type Int. No matching user-defined operator found.
// TypeError 9767: (78-81): Built-in unary operator ++ cannot be applied to type Int.

View File

@ -0,0 +1,11 @@
type Int is int;
using {add as +} for Int global;
using {unsub as -} for Int global;
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
function f() pure {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}

View File

@ -0,0 +1,12 @@
type Int is int16;
using {add as +} for Int global;
function add(Int, Int) pure returns (Int) {}
function f() {
Int a;
a.add(a);
}
// ----
// TypeError 9582: (130-135): Member "add" not found or not visible after argument-dependent lookup in Int.

View File

@ -0,0 +1,12 @@
type Int is int16;
using {add as +} for Int global;
function add(Int, Int) pure returns (Int) {}
function f() {
Int a;
a.+(a);
}
// ----
// ParserError 2314: (132-133): Expected identifier but got '+'

View File

@ -0,0 +1,23 @@
==== Source: definition.sol ====
import "type-and-binding.sol";
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
==== Source: type-and-binding.sol ====
import "definition.sol";
type Int is int;
using {add as +} for Int global;
using {unsub as -} for Int global;
==== Source: use.sol ====
import "type-and-binding.sol";
contract C {
function f() pure public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}

View File

@ -0,0 +1,30 @@
==== Source: binding.sol ====
import "definition.sol";
import "type.sol";
using {add as +} for Int global;
using {unsub as -} for Int global;
==== Source: definition.sol ====
import "type.sol";
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
==== Source: type.sol ====
type Int is int;
==== Source: use.sol ====
import "type.sol";
contract C {
function f() pure public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}
// ----
// TypeError 4117: (binding.sol:45-77): Can only use "global" with types defined in the same source unit at file level.
// TypeError 4117: (binding.sol:78-112): Can only use "global" with types defined in the same source unit at file level.
// TypeError 2271: (use.sol:72-97): Built-in binary operator + cannot be applied to types Int and Int. No matching user-defined operator found.
// TypeError 4907: (use.sol:107-119): Built-in unary operator - cannot be applied to type Int. No matching user-defined operator found.

View File

@ -0,0 +1,17 @@
==== Source: s1.sol ====
type Int is int;
using {add as +} for Int global;
using {unsub as -} for Int global;
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
==== Source: s2.sol ====
import "s1.sol";
contract C {
function f() pure public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}

View File

@ -0,0 +1,22 @@
==== Source: s1.sol ====
type Int is int;
using {add as +} for Int;
using {unsub as -} for Int;
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
==== Source: s2.sol ====
import "s1.sol";
contract C {
function f() pure public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}
// ----
// TypeError 3320: (s1.sol:24-27): Operators can only be defined in a global 'using for' directive.
// TypeError 3320: (s1.sol:50-55): Operators can only be defined in a global 'using for' directive.
// TypeError 2271: (s2.sol:70-95): Built-in binary operator + cannot be applied to types Int and Int. No matching user-defined operator found.
// TypeError 4907: (s2.sol:105-117): Built-in unary operator - cannot be applied to type Int. No matching user-defined operator found.

View File

@ -0,0 +1,35 @@
==== Source: s0.sol ====
type Int is int;
==== Source: s1.sol ====
import "s0.sol";
using {add1 as +} for Int global;
using {unsub1 as -} for Int global;
function add1(Int, Int) pure returns (Int) {}
function unsub1(Int) pure returns (Int) {}
==== Source: s2.sol ====
import "s0.sol";
using {add2 as +} for Int global;
using {unsub2 as -} for Int global;
function add2(Int, Int) pure returns (Int) {}
function unsub2(Int) pure returns (Int) {}
==== Source: s3.sol ====
import "s1.sol";
import "s2.sol";
contract C {
function f() public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}
// ----
// TypeError 4117: (s1.sol:17-50): Can only use "global" with types defined in the same source unit at file level.
// TypeError 4117: (s1.sol:51-86): Can only use "global" with types defined in the same source unit at file level.
// TypeError 4117: (s2.sol:17-50): Can only use "global" with types defined in the same source unit at file level.
// TypeError 4117: (s2.sol:51-86): Can only use "global" with types defined in the same source unit at file level.
// TypeError 2271: (s3.sol:81-106): Built-in binary operator + cannot be applied to types Int and Int. No matching user-defined operator found.
// TypeError 4907: (s3.sol:116-128): Built-in unary operator - cannot be applied to type Int. No matching user-defined operator found.

View File

@ -0,0 +1,35 @@
==== Source: s0.sol ====
type Int is int;
==== Source: s1.sol ====
import "s0.sol";
using {add1 as +} for Int;
using {unsub1 as -} for Int;
function add1(Int, Int) pure returns (Int) {}
function unsub1(Int) pure returns (Int) {}
==== Source: s2.sol ====
import "s0.sol";
using {add2 as +} for Int;
using {unsub2 as -} for Int;
function add2(Int, Int) pure returns (Int) {}
function unsub2(Int) pure returns (Int) {}
==== Source: s3.sol ====
import "s1.sol";
import "s2.sol";
contract C {
function f() public {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
}
// ----
// TypeError 3320: (s1.sol:24-28): Operators can only be defined in a global 'using for' directive.
// TypeError 3320: (s1.sol:51-57): Operators can only be defined in a global 'using for' directive.
// TypeError 3320: (s2.sol:24-28): Operators can only be defined in a global 'using for' directive.
// TypeError 3320: (s2.sol:51-57): Operators can only be defined in a global 'using for' directive.
// TypeError 2271: (s3.sol:81-106): Built-in binary operator + cannot be applied to types Int and Int. No matching user-defined operator found.
// TypeError 4907: (s3.sol:116-128): Built-in unary operator - cannot be applied to type Int. No matching user-defined operator found.

View File

@ -0,0 +1,24 @@
type B4 is bytes4;
using {bitor as |, bitnot as ~} for B4 global;
function bitor(B4, B4) pure returns (B4) {}
function bitnot(B4) pure returns (B4) {}
B4 constant X = B4.wrap(0x12345678) | B4.wrap(0xaabbccdd);
contract C {
B4 constant Y = B4.wrap(0x12345678) | B4.wrap(0xaabbccdd);
}
library L {
B4 constant Z = ~B4.wrap(0x12345678);
}
interface I {
B4 constant W = ~B4.wrap(0x12345678);
}
// ----
// TypeError 8349: (169-210): Initial value for constant variable has to be compile-time constant.
// TypeError 8349: (246-287): Initial value for constant variable has to be compile-time constant.
// TypeError 8349: (324-344): Initial value for constant variable has to be compile-time constant.
// TypeError 8349: (383-403): Initial value for constant variable has to be compile-time constant.

View File

@ -0,0 +1,14 @@
type Int is int;
using {add as +} for Int;
using {unsub as -} for Int;
function add(Int, Int) pure returns (Int) {}
function unsub(Int) pure returns (Int) {}
function f() pure {
Int.wrap(0) + Int.wrap(0);
-Int.wrap(0);
}
// ----
// TypeError 3320: (24-27): Operators can only be defined in a global 'using for' directive.
// TypeError 3320: (50-55): Operators can only be defined in a global 'using for' directive.

View File

@ -0,0 +1,24 @@
type Int is int16;
using {add as +} for Int global;
function add(Int, Int) returns (Int) {
B b = new B();
return b.f();
}
contract B {
Int s;
function f() external returns (Int) {
s = Int.wrap(3);
return s;
}
}
contract C {
function test() public returns (Int) {
return Int.wrap(0) + Int.wrap(0);
}
}
// ----
// TypeError 7775: (27-30): Only pure free functions can be used to define operators.

View File

@ -0,0 +1,20 @@
type Int is int16;
using {add as +, unsub as -} for Int global;
IAdder constant ADDER = IAdder(address(0));
function add(Int x, Int y) pure returns (Int) {
return ADDER.mul(x, y);
}
function unsub(Int x) pure returns (Int) {
return ADDER.inc(x);
}
interface IAdder {
function mul(Int, Int) external returns (Int);
function inc(Int) external returns (Int);
}
// ----
// TypeError 8961: (169-184): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (243-255): Function cannot be declared as pure because this expression (potentially) modifies the state.

View File

@ -0,0 +1,17 @@
type Int is int16;
using {add as +, unsub as -} for Int global;
IAdder constant ADDER = IAdder(address(0));
function add(Int x, Int y) pure returns (Int) {
return ADDER.mul(x, y);
}
function unsub(Int x) pure returns (Int) {
return ADDER.inc(x);
}
interface IAdder {
function mul(Int, Int) external pure returns (Int);
function inc(Int) external pure returns (Int);
}

View File

@ -0,0 +1,20 @@
type Int is int16;
using {add as +, unsub as -} for Int global;
IAdder constant ADDER = IAdder(address(0));
function add(Int x, Int y) pure returns (Int) {
return ADDER.mul(x, y);
}
function unsub(Int x) pure returns (Int) {
return ADDER.inc(x);
}
interface IAdder {
function mul(Int, Int) external view returns (Int);
function inc(Int) external view returns (Int);
}
// ----
// TypeError 2527: (169-184): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (243-255): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

View File

@ -0,0 +1,32 @@
using {add as +, unsub as -} for U global;
type U is uint;
function add(U, U) pure returns (U) {}
function unsub(U) pure returns (U) {}
contract C {
function fromBool() public {
U u;
u + true;
true + u;
-true;
}
function fromUint() public {
U u;
uint32 u32;
u + u32;
u32 + u;
-u32;
}
}
// ----
// TypeError 5653: (207-215): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is U.
// TypeError 2271: (225-233): Built-in binary operator + cannot be applied to types bool and U.
// TypeError 4907: (243-248): Built-in unary operator - cannot be applied to type bool.
// TypeError 5653: (332-339): The type of the second operand of this user-defined binary operator + does not match the type of the first operand, which is U.
// TypeError 2271: (349-356): Built-in binary operator + cannot be applied to types uint32 and U.
// TypeError 4907: (366-370): Built-in unary operator - cannot be applied to type uint32. Unary negation is only allowed for signed integers.

View File

@ -0,0 +1,18 @@
using {f as +} for uint global;
using {f as +} for uint[2] global;
using {f as +} for mapping(uint => uint) global;
using {f as +} for function (uint) pure returns (uint) global;
using {f as +} for string global;
function f(uint, uint) pure returns (uint) {}
// ----
// TypeError 8841: (0-31): Can only use "global" with user-defined types.
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types.
// TypeError 8841: (32-66): Can only use "global" with user-defined types.
// TypeError 5332: (39-40): Operators can only be implemented for user-defined value types.
// TypeError 8841: (67-115): Can only use "global" with user-defined types.
// TypeError 5332: (74-75): Operators can only be implemented for user-defined value types.
// TypeError 8841: (116-178): Can only use "global" with user-defined types.
// TypeError 5332: (123-124): Operators can only be implemented for user-defined value types.
// TypeError 8841: (179-212): Can only use "global" with user-defined types.
// TypeError 5332: (186-187): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,13 @@
using {fc as +} for C global;
using {fa as +} for A global;
function fc(C, C) pure returns (C) {}
function fa(A, A) pure returns (A) {}
contract C {}
abstract contract A {}
// ----
// TypeError 8841: (0-29): Can only use "global" with user-defined types.
// TypeError 5332: (7-9): Operators can only be implemented for user-defined value types.
// TypeError 8841: (30-59): Can only use "global" with user-defined types.
// TypeError 5332: (37-39): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,10 @@
using {add as +} for E global;
enum E {
E1,
E2
}
function add(E, E) pure returns (E) {}
// ----
// TypeError 5332: (7-10): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,10 @@
using {add as +} for E global;
error E();
function add(E, E) pure returns (E) {
return E.E1;
}
// ----
// TypeError 5172: (21-22): Name has to refer to a user-defined type.

View File

@ -0,0 +1,10 @@
using {add as +} for C.Event global;
contract C {
event Event();
}
function add(C.Event, C.Event) pure returns (C.Event) {}
// ----
// TypeError 5172: (21-28): Name has to refer to a user-defined type.

View File

@ -0,0 +1,8 @@
using {f as +} for I global;
function f(I, I) pure returns (I) {}
interface I {}
// ----
// TypeError 8841: (0-28): Can only use "global" with user-defined types.
// TypeError 5332: (7-8): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,6 @@
using {f as +} for L global;
function f() pure {}
library L {}
// ----
// TypeError 1130: (19-20): Invalid use of a library name.

View File

@ -0,0 +1,9 @@
using {add as +} for S global;
struct S {
uint x;
}
function add(S memory, S memory) pure returns (S memory) {}
// ----
// TypeError 5332: (7-10): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,16 @@
type Int is int;
function add(Int, Int) pure returns (Int) {}
contract C {
using {add as +} for *;
}
contract D {
using {add as +} for * global;
}
// ----
// SyntaxError 3349: (81-104): The type has to be specified explicitly when attaching specific functions.
// SyntaxError 3349: (125-155): The type has to be specified explicitly when attaching specific functions.
// SyntaxError 2854: (125-155): Can only globally attach functions to specific types.
// SyntaxError 3367: (125-155): "global" can only be used at file level.

View File

@ -0,0 +1,6 @@
using {add as +} for * global;
function add(int, int) returns (int) {}
// ----
// SyntaxError 8118: (0-30): The type has to be specified explicitly at file level (cannot use '*').
// SyntaxError 2854: (0-30): Can only globally attach functions to specific types.

View File

@ -0,0 +1,9 @@
type Int is uint;
using {f} for Int;
Int constant v;
using {v.f as +} for Int global;
function f(Int) pure returns (Int) {}
// ----
// DeclarationError 9589: (61-64): Identifier is not a function name or not unique.

View File

@ -0,0 +1,8 @@
type Int is int16;
using {abi.encode as +} for Int global;
function f(Int, Int) pure returns (Int) {}
// ----
// DeclarationError 9589: (27-37): Identifier is not a function name or not unique.

View File

@ -0,0 +1,5 @@
type Int is int16;
using {keccak256 as +} for Int global;
// ----
// TypeError 8187: (27-36): Expected function name.

View File

@ -0,0 +1,5 @@
type Int is int16;
using {revert as +} for Int global;
// ----
// DeclarationError 9589: (27-33): Identifier is not a function name or not unique.

View File

@ -0,0 +1,9 @@
type Int is int;
contract C {
using {add as +} for Int;
function add(Int, Int) public pure returns (Int) {}
}
// ----
// TypeError 4167: (42-45): Only file-level functions and library functions can be attached to a type in a "using" statement

View File

@ -0,0 +1,11 @@
type Int is int;
using {C.add as +} for Int global;
contract C {
function add(Int, Int) public pure returns (Int) {
return 0;
}
}
// ----
// TypeError 4167: (25-30): Only file-level functions and library functions can be attached to a type in a "using" statement

View File

@ -0,0 +1,7 @@
type Int is int16;
using {IntError as +} for Int global;
error IntError(Int a, Int b);
// ----
// TypeError 8187: (27-35): Expected function name.

View File

@ -0,0 +1,9 @@
type Int is int16;
using {C.IntEvent as +} for Int global;
contract C {
event IntEvent(Int a, Int b);
}
// ----
// TypeError 8187: (27-37): Expected function name.

View File

@ -0,0 +1,8 @@
type Int is int8;
contract C {
function(Int, Int) external pure returns (Int) ptr;
using {ptr as +} for Int;
}
// ----
// TypeError 8187: (99-102): Expected function name.

View File

@ -0,0 +1,9 @@
type Int is int16;
using {keccak256 as +} for Int global;
function keccak256(Int, Int) pure returns (Int) {
return Int.wrap(0);
}
// ----
// Warning 2319: (60-135): This declaration shadows a builtin symbol.

View File

@ -0,0 +1,7 @@
type Int is int16;
using {L as +} for Int global;
library L {}
// ----
// TypeError 8187: (27-28): Expected function name.

View File

@ -0,0 +1,113 @@
==== Source: external.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) external pure returns (Int) {}
function unaryOperator(Int) external pure returns (Int) {}
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
contract C {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
library X {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
==== Source: internal.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) internal pure returns (Int) {}
function unaryOperator(Int) internal pure returns (Int) {}
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
contract C {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
library X {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
==== Source: private.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) private pure returns (Int) {}
function unaryOperator(Int) private pure returns (Int) {}
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
==== Source: public.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) public pure returns (Int) {}
function unaryOperator(Int) public pure returns (Int) {}
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
contract C {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
library X {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
// ----
// TypeError 3320: (external.sol:177-193): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:177-193): Only pure free functions can be used to define operators.
// TypeError 3320: (external.sol:220-235): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:220-235): Only pure free functions can be used to define operators.
// TypeError 3320: (external.sol:278-294): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:278-294): Only pure free functions can be used to define operators.
// TypeError 3320: (external.sol:321-336): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:321-336): Only pure free functions can be used to define operators.
// TypeError 3320: (external.sol:378-394): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:378-394): Only pure free functions can be used to define operators.
// TypeError 3320: (external.sol:421-436): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (external.sol:421-436): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:177-193): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:177-193): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:220-235): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:220-235): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:278-294): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:278-294): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:321-336): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:321-336): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:378-394): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:378-394): Only pure free functions can be used to define operators.
// TypeError 3320: (internal.sol:421-436): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (internal.sol:421-436): Only pure free functions can be used to define operators.
// TypeError 3320: (private.sol:175-191): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (private.sol:175-191): Only pure free functions can be used to define operators.
// TypeError 3320: (private.sol:218-233): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (private.sol:218-233): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:173-189): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:173-189): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:216-231): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:216-231): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:274-290): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:274-290): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:317-332): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:317-332): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:374-390): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:374-390): Only pure free functions can be used to define operators.
// TypeError 3320: (public.sol:417-432): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (public.sol:417-432): Only pure free functions can be used to define operators.

View File

@ -0,0 +1,39 @@
==== Source: external.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) external pure returns (Int) {}
function unaryOperator(Int) external pure returns (Int) {}
}
using {L.binaryOperator as +} for Int global;
using {L.unaryOperator as -} for Int global;
==== Source: internal.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) internal pure returns (Int) {}
function unaryOperator(Int) internal pure returns (Int) {}
}
using {L.binaryOperator as +} for Int global;
using {L.unaryOperator as -} for Int global;
==== Source: public.sol ====
type Int is int128;
library L {
function binaryOperator(Int, Int) public pure returns (Int) {}
function unaryOperator(Int) public pure returns (Int) {}
}
using {L.binaryOperator as +} for Int global;
using {L.unaryOperator as -} for Int global;
// ----
// TypeError 7775: (external.sol:175-191): Only pure free functions can be used to define operators.
// TypeError 7775: (external.sol:221-236): Only pure free functions can be used to define operators.
// TypeError 7775: (internal.sol:175-191): Only pure free functions can be used to define operators.
// TypeError 7775: (internal.sol:221-236): Only pure free functions can be used to define operators.
// TypeError 7775: (public.sol:171-187): Only pure free functions can be used to define operators.
// TypeError 7775: (public.sol:217-232): Only pure free functions can be used to define operators.

View File

@ -0,0 +1,36 @@
type Int is int128;
library L {
function binaryOperator(Int, Int) private pure returns (Int) {}
function unaryOperator(Int) private pure returns (Int) {}
}
using {L.binaryOperator as +} for Int global;
using {L.unaryOperator as -} for Int global;
contract C {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
library X {
using {L.binaryOperator as *} for Int;
using {L.unaryOperator as ~} for Int;
}
// ----
// TypeError 6772: (173-189): Function "L.binaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 7775: (173-189): Only pure free functions can be used to define operators.
// TypeError 6772: (219-234): Function "L.unaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 7775: (219-234): Only pure free functions can be used to define operators.
// TypeError 6772: (282-298): Function "L.binaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 3320: (282-298): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (282-298): Only pure free functions can be used to define operators.
// TypeError 6772: (325-340): Function "L.unaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 3320: (325-340): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (325-340): Only pure free functions can be used to define operators.
// TypeError 6772: (382-398): Function "L.binaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 3320: (382-398): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (382-398): Only pure free functions can be used to define operators.
// TypeError 6772: (425-440): Function "L.unaryOperator" is private and therefore cannot be attached to a type outside of the library where it is defined.
// TypeError 3320: (425-440): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (425-440): Only pure free functions can be used to define operators.

View File

@ -0,0 +1,12 @@
using {add as +, sub as -, mul as *} for A global;
function add(A, A) view returns (A) {}
function sub(A, A) returns (A) {}
function mul(A, A) payable returns (A) {}
type A is address payable;
// ----
// TypeError 7775: (7-10): Only pure free functions can be used to define operators.
// TypeError 7775: (17-20): Only pure free functions can be used to define operators.
// TypeError 7775: (27-30): Only pure free functions can be used to define operators.
// TypeError 9559: (125-166): Free functions cannot be payable.

View File

@ -0,0 +1,8 @@
// This should point out all 3 errors, rather than give up after the first one.
using {add as +} for address;
function add(address, address) view returns (address) {}
// ----
// TypeError 3320: (87-90): Operators can only be defined in a global 'using for' directive.
// TypeError 7775: (87-90): Only pure free functions can be used to define operators.
// TypeError 5332: (87-90): Operators can only be implemented for user-defined value types.

View File

@ -0,0 +1,10 @@
using {add as +} for A global;
using {add as +} for AP global;
function add(A, A) pure returns (A) {}
function add(AP, AP) pure returns (AP) {}
type A is address;
type AP is address payable;
// ----
// DeclarationError 9589: (7-10): Identifier is not a function name or not unique.

View File

@ -0,0 +1,12 @@
using {L.add as +} for A global;
using {L.add as +} for AP global;
library L {
function add(A, A) private pure returns (A) {}
function add(AP, AP) internal pure returns (AP) {}
}
type A is address;
type AP is address payable;
// ----
// DeclarationError 9589: (7-12): Identifier is not a function name or not unique.

Some files were not shown because too many files have changed in this diff Show More