mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11806 from ethereum/user-defined-types
User defined value types
This commit is contained in:
commit
8a37f56e98
@ -3,6 +3,7 @@
|
||||
Language Features:
|
||||
* Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier.
|
||||
* Type System: Support ``type().min`` and ``type().max`` for enums.
|
||||
* User Defined Value Type: allows creating a zero cost abstraction over a value type with stricter type requirements.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -113,6 +113,9 @@ them.
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`enum<enums>` |``uint8`` |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`user defined value types |its underlying value type |
|
||||
|<user-defined-value-types>` | |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`struct<structs>` |``tuple`` |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|
||||
|
@ -19,6 +19,7 @@ sourceUnit: (
|
||||
| constantVariableDeclaration
|
||||
| structDefinition
|
||||
| enumDefinition
|
||||
| userDefinedValueTypeDefinition
|
||||
| errorDefinition
|
||||
)* EOF;
|
||||
|
||||
@ -89,6 +90,7 @@ contractBodyElement:
|
||||
| receiveFunctionDefinition
|
||||
| structDefinition
|
||||
| enumDefinition
|
||||
| userDefinedValueTypeDefinition
|
||||
| stateVariableDeclaration
|
||||
| eventDefinition
|
||||
| errorDefinition
|
||||
@ -247,6 +249,11 @@ structMember: type=typeName name=identifier Semicolon;
|
||||
* Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface.
|
||||
*/
|
||||
enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace;
|
||||
/**
|
||||
* Definition of a user defined value type. Can occur at top-level within a source unit or within a contract, library or interface.
|
||||
*/
|
||||
userDefinedValueTypeDefinition:
|
||||
Type name=identifier Is elementaryTypeName[true] Semicolon;
|
||||
|
||||
/**
|
||||
* The declaration of a state variable.
|
||||
|
@ -628,6 +628,69 @@ smallest and respectively largest value of the given enum.
|
||||
.. note::
|
||||
Enums can also be declared on the file level, outside of contract or library definitions.
|
||||
|
||||
.. index:: ! user defined value type, custom type
|
||||
|
||||
.. _user-defined-value-types:
|
||||
|
||||
User Defined Value Types
|
||||
------------------------
|
||||
|
||||
A user defined value type allows creating a zero cost abstraction over an elementary value type.
|
||||
This is similar to an alias, but with stricter type requirements.
|
||||
|
||||
A user defined value type is defined using ``type C is V``, where ``C`` is the name of the newly
|
||||
introduced type and ``V`` has to be a built-in value type (the "underlying type"). The function
|
||||
``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the
|
||||
function ``C.unwrap`` is used to convert from the custom type to the underlying type.
|
||||
|
||||
The type ``C`` does not have any operators or bound member functions. In particular, even the
|
||||
operator ``==`` is not defined. Explicit and implicit conversions to and from other types are
|
||||
disallowed.
|
||||
|
||||
The data-representation of values of such types are inherited from the underlying type
|
||||
and the underlying type is also used in the ABI.
|
||||
|
||||
The following example illustrates a custom type ``UFixed256x18`` representing a decimal fixed point
|
||||
type with 18 decimals and a minimal library to do arithmetic operations on the type.
|
||||
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type.
|
||||
type UFixed256x18 is uint256;
|
||||
|
||||
/// A minimal library to do fixed point operations on UFixed256x18.
|
||||
library FixedMath {
|
||||
uint constant multiplier = 10**18;
|
||||
|
||||
/// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked
|
||||
/// arithmetic on uint256.
|
||||
function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) {
|
||||
return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
|
||||
}
|
||||
/// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked
|
||||
/// arithmetic on uint256.
|
||||
function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) {
|
||||
return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
|
||||
}
|
||||
/// Truncates UFixed256x18 to the nearest uint256 number.
|
||||
function truncate(UFixed256x18 a) internal pure returns (uint256) {
|
||||
return UFixed256x18.unwrap(a) / multiplier;
|
||||
}
|
||||
/// Turns a uint256 into a UFixed256x18 of the same value.
|
||||
/// Reverts if the integer is too large.
|
||||
function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) {
|
||||
return UFixed256x18.wrap(a * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
Notice how ``UFixed256x18.wrap`` and ``FixedMath.toUFixed256x18`` have the same signature but
|
||||
perform two very different operations: The ``UFixed256x18.wrap`` function returns a ``UFixed256x18``
|
||||
that has the same data representation as the input, whereas ``toUFixed256x18`` returns a
|
||||
``UFixed256x18`` that has the same numerical value.
|
||||
|
||||
.. index:: ! function type, ! type; function
|
||||
|
||||
|
@ -140,6 +140,30 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(UserDefinedValueTypeDefinition const& _userDefined)
|
||||
{
|
||||
TypeName const* typeName = _userDefined.underlyingType();
|
||||
solAssert(typeName, "");
|
||||
if (!dynamic_cast<ElementaryTypeName const*>(typeName))
|
||||
m_errorReporter.fatalTypeError(
|
||||
8657_error,
|
||||
typeName->location(),
|
||||
"The underlying type for a user defined value type has to be an elementary value type."
|
||||
);
|
||||
|
||||
Type const* type = typeName->annotation().type;
|
||||
solAssert(type, "");
|
||||
solAssert(!dynamic_cast<UserDefinedValueType const*>(type), "");
|
||||
if (!type->isValueType())
|
||||
m_errorReporter.typeError(
|
||||
8129_error,
|
||||
_userDefined.location(),
|
||||
"The underlying type of the user defined value type \"" +
|
||||
_userDefined.name() +
|
||||
"\" is not a value type."
|
||||
);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
@ -158,6 +182,8 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
|
||||
_typeName.annotation().type = TypeProvider::enumType(*enumDef);
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::contract(*contract);
|
||||
else if (auto userDefinedValueType = dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::userDefinedValueType(*userDefinedValueType);
|
||||
else
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::emptyTuple();
|
||||
|
@ -60,6 +60,7 @@ private:
|
||||
void endVisit(VariableDeclaration const& _variable) override;
|
||||
bool visit(EnumDefinition const& _enum) override;
|
||||
bool visit(StructDefinition const& _struct) override;
|
||||
void endVisit(UserDefinedValueTypeDefinition const& _userDefined) override;
|
||||
bool visit(UsingForDirective const& _usingForDirective) override;
|
||||
bool visit(InheritanceSpecifier const& _inheritanceSpecifier) override;
|
||||
|
||||
|
@ -2481,6 +2481,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
returnTypes = functionType->returnParameterTypes();
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Wrap:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
{
|
||||
typeCheckFunctionGeneralChecks(_functionCall, functionType);
|
||||
returnTypes = functionType->returnParameterTypes();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
typeCheckFunctionCall(_functionCall, functionType);
|
||||
|
@ -335,6 +335,12 @@ TypeNameAnnotation& TypeName::annotation() const
|
||||
return initAnnotation<TypeNameAnnotation>();
|
||||
}
|
||||
|
||||
Type const* UserDefinedValueTypeDefinition::type() const
|
||||
{
|
||||
solAssert(m_underlyingType->annotation().type, "");
|
||||
return TypeProvider::typeType(TypeProvider::userDefinedValueType(*this));
|
||||
}
|
||||
|
||||
Type const* StructDefinition::type() const
|
||||
{
|
||||
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
||||
|
@ -726,6 +726,37 @@ public:
|
||||
Type const* type() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* User defined value types, i.e., custom types, for example, `type MyInt is int`. Allows creating a
|
||||
* zero cost abstraction over value type with stricter type requirements.
|
||||
*/
|
||||
class UserDefinedValueTypeDefinition: public Declaration
|
||||
{
|
||||
public:
|
||||
UserDefinedValueTypeDefinition(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> _name,
|
||||
SourceLocation _nameLocation,
|
||||
ASTPointer<TypeName> _underlyingType
|
||||
):
|
||||
Declaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default),
|
||||
m_underlyingType(std::move(_underlyingType))
|
||||
{
|
||||
}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
Type const* type() const override;
|
||||
|
||||
TypeName const* underlyingType() const { return m_underlyingType.get(); }
|
||||
|
||||
private:
|
||||
/// The name of the underlying type
|
||||
ASTPointer<TypeName> m_underlyingType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameter list, used as function parameter list, return list and for try and catch.
|
||||
* None of the parameters is allowed to contain mappings (not even recursively
|
||||
|
@ -51,6 +51,7 @@ class UsingForDirective;
|
||||
class StructDefinition;
|
||||
class EnumDefinition;
|
||||
class EnumValue;
|
||||
class UserDefinedValueTypeDefinition;
|
||||
class ParameterList;
|
||||
class FunctionDefinition;
|
||||
class VariableDeclaration;
|
||||
|
@ -354,6 +354,20 @@ bool ASTJsonConverter::visit(EnumValue const& _node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(UserDefinedValueTypeDefinition const& _node)
|
||||
{
|
||||
solAssert(_node.underlyingType(), "");
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("name", _node.name()),
|
||||
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
|
||||
make_pair("underlyingType", toJson(*_node.underlyingType()))
|
||||
};
|
||||
|
||||
setJsonNode(_node, "UserDefinedValueTypeDefinition", std::move(attributes));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(ParameterList const& _node)
|
||||
{
|
||||
setJsonNode(_node, "ParameterList", {
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
bool visit(StructDefinition const& _node) override;
|
||||
bool visit(EnumDefinition const& _node) override;
|
||||
bool visit(EnumValue const& _node) override;
|
||||
bool visit(UserDefinedValueTypeDefinition const& _node) override;
|
||||
bool visit(ParameterList const& _node) override;
|
||||
bool visit(OverrideSpecifier const& _node) override;
|
||||
bool visit(FunctionDefinition const& _node) override;
|
||||
|
@ -133,6 +133,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
|
||||
return createEnumDefinition(_json);
|
||||
if (nodeType == "EnumValue")
|
||||
return createEnumValue(_json);
|
||||
if (nodeType == "UserDefinedValueTypeDefinition")
|
||||
return createUserDefinedValueTypeDefinition(_json);
|
||||
if (nodeType == "ParameterList")
|
||||
return createParameterList(_json);
|
||||
if (nodeType == "OverrideSpecifier")
|
||||
@ -387,6 +389,16 @@ ASTPointer<EnumValue> ASTJsonImporter::createEnumValue(Json::Value const& _node)
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<UserDefinedValueTypeDefinition> ASTJsonImporter::createUserDefinedValueTypeDefinition(Json::Value const& _node)
|
||||
{
|
||||
return createASTNode<UserDefinedValueTypeDefinition>(
|
||||
_node,
|
||||
memberAsASTString(_node, "name"),
|
||||
createNameSourceLocation(_node),
|
||||
convertJsonToASTNode<TypeName>(member(_node, "underlyingType"))
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<ParameterList> ASTJsonImporter::createParameterList(Json::Value const& _node)
|
||||
{
|
||||
std::vector<ASTPointer<VariableDeclaration>> parameters;
|
||||
|
@ -81,6 +81,7 @@ private:
|
||||
ASTPointer<ASTNode> createStructDefinition(Json::Value const& _node);
|
||||
ASTPointer<EnumDefinition> createEnumDefinition(Json::Value const& _node);
|
||||
ASTPointer<EnumValue> createEnumValue(Json::Value const& _node);
|
||||
ASTPointer<UserDefinedValueTypeDefinition> createUserDefinedValueTypeDefinition(Json::Value const& _node);
|
||||
ASTPointer<ParameterList> createParameterList(Json::Value const& _node);
|
||||
ASTPointer<OverrideSpecifier> createOverrideSpecifier(Json::Value const& _node);
|
||||
ASTPointer<FunctionDefinition> createFunctionDefinition(Json::Value const& _node);
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
virtual bool visit(IdentifierPath& _node) { return visitNode(_node); }
|
||||
virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); }
|
||||
virtual bool visit(UsingForDirective& _node) { return visitNode(_node); }
|
||||
virtual bool visit(UserDefinedValueTypeDefinition& _node) { return visitNode(_node); }
|
||||
virtual bool visit(StructDefinition& _node) { return visitNode(_node); }
|
||||
virtual bool visit(EnumDefinition& _node) { return visitNode(_node); }
|
||||
virtual bool visit(EnumValue& _node) { return visitNode(_node); }
|
||||
@ -116,6 +117,7 @@ public:
|
||||
virtual void endVisit(IdentifierPath& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(UserDefinedValueTypeDefinition& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(EnumValue& _node) { endVisitNode(_node); }
|
||||
@ -194,6 +196,7 @@ public:
|
||||
virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(StructDefinition const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(UsingForDirective const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(UserDefinedValueTypeDefinition const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(EnumValue const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(ParameterList const& _node) { return visitNode(_node); }
|
||||
@ -248,6 +251,7 @@ public:
|
||||
virtual void endVisit(IdentifierPath const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(UsingForDirective const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(UserDefinedValueTypeDefinition const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); }
|
||||
|
@ -164,6 +164,26 @@ void EnumValue::accept(ASTConstVisitor& _visitor) const
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void UserDefinedValueTypeDefinition::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
if (m_underlyingType)
|
||||
m_underlyingType->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void UserDefinedValueTypeDefinition::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
if (m_underlyingType)
|
||||
m_underlyingType->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void UsingForDirective::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
|
@ -578,3 +578,8 @@ MappingType const* TypeProvider::mapping(Type const* _keyType, Type const* _valu
|
||||
{
|
||||
return createAndGet<MappingType>(_keyType, _valueType);
|
||||
}
|
||||
|
||||
UserDefinedValueType const* TypeProvider::userDefinedValueType(UserDefinedValueTypeDefinition const& _definition)
|
||||
{
|
||||
return createAndGet<UserDefinedValueType>(_definition);
|
||||
}
|
||||
|
@ -201,6 +201,8 @@ public:
|
||||
|
||||
static MappingType const* mapping(Type const* _keyType, Type const* _valueType);
|
||||
|
||||
static UserDefinedValueType const* userDefinedValueType(UserDefinedValueTypeDefinition const& _definition);
|
||||
|
||||
private:
|
||||
/// Global TypeProvider instance.
|
||||
static TypeProvider& instance()
|
||||
|
@ -2533,6 +2533,36 @@ unsigned EnumType::memberValue(ASTString const& _member) const
|
||||
solAssert(false, "Requested unknown enum value " + _member);
|
||||
}
|
||||
|
||||
Type const& UserDefinedValueType::underlyingType() const
|
||||
{
|
||||
Type const* type = m_definition.underlyingType()->annotation().type;
|
||||
solAssert(type, "");
|
||||
return *type;
|
||||
}
|
||||
|
||||
string UserDefinedValueType::richIdentifier() const
|
||||
{
|
||||
return "t_userDefinedValueType" + parenthesizeIdentifier(m_definition.name()) + to_string(m_definition.id());
|
||||
}
|
||||
|
||||
bool UserDefinedValueType::operator==(Type const& _other) const
|
||||
{
|
||||
if (_other.category() != category())
|
||||
return false;
|
||||
UserDefinedValueType const& other = dynamic_cast<UserDefinedValueType const&>(_other);
|
||||
return other.definition() == definition();
|
||||
}
|
||||
|
||||
string UserDefinedValueType::toString(bool /* _short */) const
|
||||
{
|
||||
return "user defined type " + definition().name();
|
||||
}
|
||||
|
||||
vector<tuple<string, Type const*>> UserDefinedValueType::makeStackItems() const
|
||||
{
|
||||
return underlyingType().stackItems();
|
||||
}
|
||||
|
||||
BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||
{
|
||||
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
|
||||
@ -2884,6 +2914,8 @@ string FunctionType::richIdentifier() const
|
||||
case Kind::GasLeft: id += "gasleft"; break;
|
||||
case Kind::Event: id += "event"; break;
|
||||
case Kind::Error: id += "error"; break;
|
||||
case Kind::Wrap: id += "wrap"; break;
|
||||
case Kind::Unwrap: id += "unwrap"; break;
|
||||
case Kind::SetGas: id += "setgas"; break;
|
||||
case Kind::SetValue: id += "setvalue"; break;
|
||||
case Kind::BlockHash: id += "blockhash"; break;
|
||||
@ -3754,6 +3786,34 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
||||
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
|
||||
members.emplace_back(enumValue.get(), enumType);
|
||||
}
|
||||
else if (m_actualType->category() == Category::UserDefinedValueType)
|
||||
{
|
||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(*m_actualType);
|
||||
members.emplace_back(
|
||||
"wrap",
|
||||
TypeProvider::function(
|
||||
TypePointers{&userDefined.underlyingType()},
|
||||
TypePointers{&userDefined},
|
||||
strings{string{}},
|
||||
strings{string{}},
|
||||
FunctionType::Kind::Wrap,
|
||||
false, /*_arbitraryParameters */
|
||||
StateMutability::Pure
|
||||
)
|
||||
);
|
||||
members.emplace_back(
|
||||
"unwrap",
|
||||
TypeProvider::function(
|
||||
TypePointers{&userDefined},
|
||||
TypePointers{&userDefined.underlyingType()},
|
||||
strings{string{}},
|
||||
strings{string{}},
|
||||
FunctionType::Kind::Unwrap,
|
||||
false, /* _arbitraryParameters */
|
||||
StateMutability::Pure
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (
|
||||
auto const* arrayType = dynamic_cast<ArrayType const*>(m_actualType);
|
||||
arrayType && arrayType->isByteArray()
|
||||
|
@ -174,7 +174,7 @@ public:
|
||||
enum class Category
|
||||
{
|
||||
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice,
|
||||
FixedBytes, Contract, Struct, Function, Enum, Tuple,
|
||||
FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple,
|
||||
Mapping, TypeType, Modifier, Magic, Module,
|
||||
InaccessibleDynamic
|
||||
};
|
||||
@ -1082,6 +1082,53 @@ private:
|
||||
EnumDefinition const& m_enum;
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of a UserDefinedValueType.
|
||||
*/
|
||||
class UserDefinedValueType: public Type
|
||||
{
|
||||
public:
|
||||
explicit UserDefinedValueType(UserDefinedValueTypeDefinition const& _definition):
|
||||
m_definition(_definition)
|
||||
{}
|
||||
|
||||
Category category() const override { return Category::UserDefinedValueType; }
|
||||
Type const& underlyingType() const;
|
||||
UserDefinedValueTypeDefinition const& definition() const { return m_definition; }
|
||||
|
||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||
Type const* encodingType() const override { return &underlyingType(); }
|
||||
TypeResult interfaceType(bool /* _inLibrary */) const override {return &underlyingType(); }
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
|
||||
unsigned calldataEncodedSize(bool _padded) const override { return underlyingType().calldataEncodedSize(_padded); }
|
||||
|
||||
bool leftAligned() const override { return underlyingType().leftAligned(); }
|
||||
bool canBeStored() const override { return underlyingType().canBeStored(); }
|
||||
u256 storageSize() const override { return underlyingType().storageSize(); }
|
||||
bool isValueType() const override
|
||||
{
|
||||
solAssert(underlyingType().isValueType(), "");
|
||||
return true;
|
||||
}
|
||||
bool nameable() const override
|
||||
{
|
||||
solAssert(underlyingType().nameable(), "");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override { solAssert(false, ""); }
|
||||
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, Type const*>> makeStackItems() const override;
|
||||
|
||||
private:
|
||||
UserDefinedValueTypeDefinition const& m_definition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type that can hold a finite sequence of values of different types.
|
||||
* In some cases, the components are empty pointers (when used as placeholders).
|
||||
@ -1150,6 +1197,8 @@ public:
|
||||
RIPEMD160, ///< CALL to special contract for ripemd160
|
||||
Event, ///< syntactic sugar for LOG*
|
||||
Error, ///< creating an error instance in revert or require
|
||||
Wrap, ///< customType.wrap(...) for user defined value types
|
||||
Unwrap, ///< customType.unwrap(...) for user defined value types
|
||||
SetGas, ///< modify the default gas value for the function call
|
||||
SetValue, ///< modify the default value transfer for the function call
|
||||
BlockHash, ///< BLOCKHASH
|
||||
|
@ -772,6 +772,33 @@ void CompilerUtils::convertType(
|
||||
Type::Category stackTypeCategory = _typeOnStack.category();
|
||||
Type::Category targetTypeCategory = _targetType.category();
|
||||
|
||||
if (stackTypeCategory == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_cleanupNeeded, "");
|
||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack);
|
||||
solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType(), "");
|
||||
return convertType(
|
||||
userDefined.underlyingType(),
|
||||
_targetType,
|
||||
_cleanupNeeded,
|
||||
_chopSignBits,
|
||||
_asPartOfArgumentDecoding
|
||||
);
|
||||
}
|
||||
if (targetTypeCategory == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_cleanupNeeded, "");
|
||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType);
|
||||
solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()), "");
|
||||
return convertType(
|
||||
_typeOnStack,
|
||||
userDefined.underlyingType(),
|
||||
_cleanupNeeded,
|
||||
_chopSignBits,
|
||||
_asPartOfArgumentDecoding
|
||||
);
|
||||
}
|
||||
|
||||
if (auto contrType = dynamic_cast<ContractType const*>(&_typeOnStack))
|
||||
solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\"");
|
||||
|
||||
|
@ -957,6 +957,35 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Wrap:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
Type const* argumentType = arguments.at(0)->annotation().type;
|
||||
Type const* functionCallType = _functionCall.annotation().type;
|
||||
solAssert(argumentType, "");
|
||||
solAssert(functionCallType, "");
|
||||
FunctionType::Kind kind = functionType->kind();
|
||||
if (kind == FunctionType::Kind::Wrap)
|
||||
{
|
||||
solAssert(
|
||||
argumentType->isImplicitlyConvertibleTo(
|
||||
dynamic_cast<UserDefinedValueType const&>(*functionCallType).underlyingType()
|
||||
),
|
||||
""
|
||||
);
|
||||
solAssert(argumentType->isImplicitlyConvertibleTo(*function.parameterTypes()[0]), "");
|
||||
}
|
||||
else
|
||||
solAssert(
|
||||
dynamic_cast<UserDefinedValueType const&>(*argumentType) ==
|
||||
dynamic_cast<UserDefinedValueType const&>(*function.parameterTypes()[0]),
|
||||
""
|
||||
);
|
||||
|
||||
acceptAndConvert(*arguments[0], *function.parameterTypes()[0]);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::BlockHash:
|
||||
{
|
||||
acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
|
||||
@ -2157,6 +2186,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else if (dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else if (dynamic_cast<StructDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
|
@ -3168,6 +3168,16 @@ string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType co
|
||||
|
||||
string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
{
|
||||
if (_from.category() == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_from == _to || _to == dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), "");
|
||||
return conversionFunction(dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), _to);
|
||||
}
|
||||
if (_to.category() == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_from == _to || _from.isImplicitlyConvertibleTo(dynamic_cast<UserDefinedValueType const&>(_to).underlyingType()), "");
|
||||
return conversionFunction(_from, dynamic_cast<UserDefinedValueType const&>(_to).underlyingType());
|
||||
}
|
||||
if (_from.category() == Type::Category::Function)
|
||||
{
|
||||
solAssert(_to.category() == Type::Category::Function, "");
|
||||
@ -3696,6 +3706,9 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
|
||||
|
||||
string YulUtilFunctions::cleanupFunction(Type const& _type)
|
||||
{
|
||||
if (auto userDefinedValueType = dynamic_cast<UserDefinedValueType const*>(&_type))
|
||||
return cleanupFunction(userDefinedValueType->underlyingType());
|
||||
|
||||
string functionName = string("cleanup_") + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
@ -3816,6 +3829,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
|
||||
case Type::Category::Mapping:
|
||||
case Type::Category::FixedBytes:
|
||||
case Type::Category::Contract:
|
||||
case Type::Category::UserDefinedValueType:
|
||||
{
|
||||
templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))");
|
||||
break;
|
||||
|
@ -1049,6 +1049,24 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Wrap:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
FunctionType::Kind kind = functionType->kind();
|
||||
if (kind == FunctionType::Kind::Wrap)
|
||||
solAssert(
|
||||
type(*arguments.at(0)).isImplicitlyConvertibleTo(
|
||||
dynamic_cast<UserDefinedValueType const&>(type(_functionCall)).underlyingType()
|
||||
),
|
||||
""
|
||||
);
|
||||
else
|
||||
solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType, "");
|
||||
|
||||
define(_functionCall, *arguments.at(0));
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Assert:
|
||||
case FunctionType::Kind::Require:
|
||||
{
|
||||
@ -2001,6 +2019,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
}
|
||||
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
|
||||
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
|
||||
else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
|
||||
solAssert(member == "wrap" || member == "unwrap", "");
|
||||
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
|
||||
solAssert(arrayType->isByteArray() && member == "concat", "");
|
||||
else
|
||||
@ -2312,6 +2332,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else if (dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(false, "Identifier type not expected in expression context.");
|
||||
|
@ -113,6 +113,9 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
|
||||
case Token::Enum:
|
||||
nodes.push_back(parseEnumDefinition());
|
||||
break;
|
||||
case Token::Type:
|
||||
nodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||
break;
|
||||
case Token::Function:
|
||||
nodes.push_back(parseFunctionDefinition(true));
|
||||
break;
|
||||
@ -364,6 +367,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
||||
subNodes.push_back(parseStructDefinition());
|
||||
else if (currentTokenValue == Token::Enum)
|
||||
subNodes.push_back(parseEnumDefinition());
|
||||
else if (currentTokenValue == Token::Type)
|
||||
subNodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||
else if (
|
||||
// Workaround because `error` is not a keyword.
|
||||
currentTokenValue == Token::Identifier &&
|
||||
@ -1010,6 +1015,22 @@ ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
|
||||
return nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
|
||||
}
|
||||
|
||||
ASTPointer<UserDefinedValueTypeDefinition> Parser::parseUserDefinedValueTypeDefinition()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::Type);
|
||||
auto&& [name, nameLocation] = expectIdentifierWithLocation();
|
||||
expectToken(Token::Is);
|
||||
ASTPointer<TypeName> typeName = parseTypeName();
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<UserDefinedValueTypeDefinition>(
|
||||
name,
|
||||
move(nameLocation),
|
||||
typeName
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<IdentifierPath> Parser::parseIdentifierPath()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
|
@ -95,6 +95,7 @@ private:
|
||||
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
|
||||
ASTPointer<StructDefinition> parseStructDefinition();
|
||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||
ASTPointer<UserDefinedValueTypeDefinition> parseUserDefinedValueTypeDefinition();
|
||||
ASTPointer<EnumValue> parseEnumValue();
|
||||
ASTPointer<VariableDeclaration> parseVariableDeclaration(
|
||||
VarDeclParserOptions const& _options = {},
|
||||
|
173
test/libsolidity/ABIJson/user_defined_value_type.sol
Normal file
173
test/libsolidity/ABIJson/user_defined_value_type.sol
Normal file
@ -0,0 +1,173 @@
|
||||
type MyInt is int;
|
||||
type MyByte1 is bytes1;
|
||||
contract C {
|
||||
type MyAddress is address;
|
||||
type MyUInt8 is uint8;
|
||||
type MyBytes32 is bytes32;
|
||||
|
||||
MyInt public myInt;
|
||||
MyByte1 public myByte1;
|
||||
MyAddress public myAddress;
|
||||
MyUInt8 public myUInt8;
|
||||
MyBytes32 public myBytes32;
|
||||
|
||||
function setMyInt(MyInt a) external {
|
||||
myInt = a;
|
||||
}
|
||||
function setMyByte1(MyByte1 a) external {
|
||||
myByte1 = a;
|
||||
}
|
||||
function setMyAddress(MyAddress a) external {
|
||||
myAddress = a;
|
||||
}
|
||||
function setMyUInt8(MyUInt8 a) external {
|
||||
myUInt8 = a;
|
||||
}
|
||||
function setMyBytes32(MyBytes32 a) external {
|
||||
myBytes32 = a;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// :C
|
||||
// [
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "myAddress",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyAddress",
|
||||
// "name": "",
|
||||
// "type": "address"
|
||||
// }
|
||||
// ],
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "myByte1",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyByte1",
|
||||
// "name": "",
|
||||
// "type": "bytes1"
|
||||
// }
|
||||
// ],
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "myBytes32",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyBytes32",
|
||||
// "name": "",
|
||||
// "type": "bytes32"
|
||||
// }
|
||||
// ],
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "myInt",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyInt",
|
||||
// "name": "",
|
||||
// "type": "int256"
|
||||
// }
|
||||
// ],
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs": [],
|
||||
// "name": "myUInt8",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyUInt8",
|
||||
// "name": "",
|
||||
// "type": "uint8"
|
||||
// }
|
||||
// ],
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyAddress",
|
||||
// "name": "a",
|
||||
// "type": "address"
|
||||
// }
|
||||
// ],
|
||||
// "name": "setMyAddress",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyByte1",
|
||||
// "name": "a",
|
||||
// "type": "bytes1"
|
||||
// }
|
||||
// ],
|
||||
// "name": "setMyByte1",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyBytes32",
|
||||
// "name": "a",
|
||||
// "type": "bytes32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "setMyBytes32",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyInt",
|
||||
// "name": "a",
|
||||
// "type": "int256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "setMyInt",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "user defined type MyUInt8",
|
||||
// "name": "a",
|
||||
// "type": "uint8"
|
||||
// }
|
||||
// ],
|
||||
// "name": "setMyUInt8",
|
||||
// "outputs": [],
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
264
test/libsolidity/ASTJSON/userDefinedValueType.json
Normal file
264
test/libsolidity/ASTJSON/userDefinedValueType.json
Normal file
@ -0,0 +1,264 @@
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
21
|
||||
],
|
||||
"MyAddress":
|
||||
[
|
||||
2
|
||||
],
|
||||
"MyUInt":
|
||||
[
|
||||
4
|
||||
],
|
||||
"f":
|
||||
[
|
||||
16
|
||||
]
|
||||
},
|
||||
"id": 22,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"name": "MyAddress",
|
||||
"nameLocation": "5:9:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "0:26:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 1,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "18:7:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "MyUInt",
|
||||
"nameLocation": "32:6:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "27:20:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 3,
|
||||
"name": "uint",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "42:4:1",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"body":
|
||||
{
|
||||
"id": 15,
|
||||
"nodeType": "Block",
|
||||
"src": "61:34:1",
|
||||
"statements":
|
||||
[
|
||||
{
|
||||
"assignments":
|
||||
[
|
||||
9
|
||||
],
|
||||
"declarations":
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"id": 9,
|
||||
"mutability": "mutable",
|
||||
"name": "a",
|
||||
"nameLocation": "77:1:1",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 15,
|
||||
"src": "67:11:1",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_userDefinedValueType$_MyAddress_$2",
|
||||
"typeString": "user defined type MyAddress"
|
||||
},
|
||||
"typeName":
|
||||
{
|
||||
"id": 8,
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"pathNode":
|
||||
{
|
||||
"id": 7,
|
||||
"name": "MyAddress",
|
||||
"nodeType": "IdentifierPath",
|
||||
"referencedDeclaration": 2,
|
||||
"src": "67:9:1"
|
||||
},
|
||||
"referencedDeclaration": 2,
|
||||
"src": "67:9:1",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_userDefinedValueType$_MyAddress_$2",
|
||||
"typeString": "user defined type MyAddress"
|
||||
}
|
||||
},
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 10,
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "67:11:1"
|
||||
},
|
||||
{
|
||||
"assignments":
|
||||
[
|
||||
13
|
||||
],
|
||||
"declarations":
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"id": 13,
|
||||
"mutability": "mutable",
|
||||
"name": "b",
|
||||
"nameLocation": "91:1:1",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 15,
|
||||
"src": "84:8:1",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_userDefinedValueType$_MyUInt_$4",
|
||||
"typeString": "user defined type MyUInt"
|
||||
},
|
||||
"typeName":
|
||||
{
|
||||
"id": 12,
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"pathNode":
|
||||
{
|
||||
"id": 11,
|
||||
"name": "MyUInt",
|
||||
"nodeType": "IdentifierPath",
|
||||
"referencedDeclaration": 4,
|
||||
"src": "84:6:1"
|
||||
},
|
||||
"referencedDeclaration": 4,
|
||||
"src": "84:6:1",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_userDefinedValueType$_MyUInt_$4",
|
||||
"typeString": "user defined type MyUInt"
|
||||
}
|
||||
},
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 14,
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "84:8:1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": 16,
|
||||
"implemented": true,
|
||||
"kind": "freeFunction",
|
||||
"modifiers": [],
|
||||
"name": "f",
|
||||
"nameLocation": "57:1:1",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters":
|
||||
{
|
||||
"id": 5,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "58:2:1"
|
||||
},
|
||||
"returnParameters":
|
||||
{
|
||||
"id": 6,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "61:0:1"
|
||||
},
|
||||
"scope": 22,
|
||||
"src": "48:47:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"virtual": false,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"fullyImplemented": true,
|
||||
"id": 21,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
21
|
||||
],
|
||||
"name": "C",
|
||||
"nameLocation": "105:1:1",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 18,
|
||||
"name": "MyAddress",
|
||||
"nameLocation": "118:9:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "113:26:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 17,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "131:7:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "MyUInt",
|
||||
"nameLocation": "149:6:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "144:20:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 19,
|
||||
"name": "uint",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "159:4:1",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"scope": 22,
|
||||
"src": "96:70:1",
|
||||
"usedErrors": []
|
||||
}
|
||||
],
|
||||
"src": "0:167:1"
|
||||
}
|
12
test/libsolidity/ASTJSON/userDefinedValueType.sol
Normal file
12
test/libsolidity/ASTJSON/userDefinedValueType.sol
Normal file
@ -0,0 +1,12 @@
|
||||
type MyAddress is address;
|
||||
type MyUInt is uint;
|
||||
function f() {
|
||||
MyAddress a;
|
||||
MyUInt b;
|
||||
}
|
||||
contract C {
|
||||
type MyAddress is address;
|
||||
type MyUInt is uint;
|
||||
}
|
||||
|
||||
// ----
|
200
test/libsolidity/ASTJSON/userDefinedValueType_parseOnly.json
Normal file
200
test/libsolidity/ASTJSON/userDefinedValueType_parseOnly.json
Normal file
@ -0,0 +1,200 @@
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"id": 22,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"name": "MyAddress",
|
||||
"nameLocation": "5:9:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "0:26:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 1,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "18:7:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"typeDescriptions": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "MyUInt",
|
||||
"nameLocation": "32:6:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "27:20:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 3,
|
||||
"name": "uint",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "42:4:1",
|
||||
"typeDescriptions": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"body":
|
||||
{
|
||||
"id": 15,
|
||||
"nodeType": "Block",
|
||||
"src": "61:34:1",
|
||||
"statements":
|
||||
[
|
||||
{
|
||||
"assignments":
|
||||
[
|
||||
9
|
||||
],
|
||||
"declarations":
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"id": 9,
|
||||
"mutability": "mutable",
|
||||
"name": "a",
|
||||
"nameLocation": "77:1:1",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"src": "67:11:1",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {},
|
||||
"typeName":
|
||||
{
|
||||
"id": 8,
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"pathNode":
|
||||
{
|
||||
"id": 7,
|
||||
"name": "MyAddress",
|
||||
"nodeType": "IdentifierPath",
|
||||
"src": "67:9:1"
|
||||
},
|
||||
"src": "67:9:1",
|
||||
"typeDescriptions": {}
|
||||
},
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 10,
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "67:11:1"
|
||||
},
|
||||
{
|
||||
"assignments":
|
||||
[
|
||||
13
|
||||
],
|
||||
"declarations":
|
||||
[
|
||||
{
|
||||
"constant": false,
|
||||
"id": 13,
|
||||
"mutability": "mutable",
|
||||
"name": "b",
|
||||
"nameLocation": "91:1:1",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"src": "84:8:1",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {},
|
||||
"typeName":
|
||||
{
|
||||
"id": 12,
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"pathNode":
|
||||
{
|
||||
"id": 11,
|
||||
"name": "MyUInt",
|
||||
"nodeType": "IdentifierPath",
|
||||
"src": "84:6:1"
|
||||
},
|
||||
"src": "84:6:1",
|
||||
"typeDescriptions": {}
|
||||
},
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 14,
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "84:8:1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": 16,
|
||||
"implemented": true,
|
||||
"kind": "freeFunction",
|
||||
"modifiers": [],
|
||||
"name": "f",
|
||||
"nameLocation": "57:1:1",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters":
|
||||
{
|
||||
"id": 5,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "58:2:1"
|
||||
},
|
||||
"returnParameters":
|
||||
{
|
||||
"id": 6,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "61:0:1"
|
||||
},
|
||||
"src": "48:47:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"virtual": false,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"id": 21,
|
||||
"name": "C",
|
||||
"nameLocation": "105:1:1",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 18,
|
||||
"name": "MyAddress",
|
||||
"nameLocation": "118:9:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "113:26:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 17,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "131:7:1",
|
||||
"stateMutability": "nonpayable",
|
||||
"typeDescriptions": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "MyUInt",
|
||||
"nameLocation": "149:6:1",
|
||||
"nodeType": "UserDefinedValueTypeDefinition",
|
||||
"src": "144:20:1",
|
||||
"underlyingType":
|
||||
{
|
||||
"id": 19,
|
||||
"name": "uint",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "159:4:1",
|
||||
"typeDescriptions": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"src": "96:70:1",
|
||||
"usedErrors": []
|
||||
}
|
||||
],
|
||||
"src": "0:167:1"
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
pragma abicoder v2;
|
||||
type MyAddress is address;
|
||||
|
||||
contract C {
|
||||
MyAddress[] public addresses;
|
||||
function f(MyAddress[] calldata _addresses) external {
|
||||
for (uint i = 0; i < _addresses.length; i++) {
|
||||
MyAddress.unwrap(_addresses[i]).call("");
|
||||
}
|
||||
addresses = _addresses;
|
||||
}
|
||||
function g(MyAddress[] memory _addresses) external {
|
||||
for (uint i = 0; i < _addresses.length; i++) {
|
||||
MyAddress.unwrap(_addresses[i]).call("");
|
||||
}
|
||||
addresses = _addresses;
|
||||
}
|
||||
function test_f() external returns (bool) {
|
||||
clean();
|
||||
MyAddress[] memory test = new MyAddress[](3);
|
||||
test[0] = MyAddress.wrap(address(21));
|
||||
test[1] = MyAddress.wrap(address(22));
|
||||
test[2] = MyAddress.wrap(address(23));
|
||||
this.f(test);
|
||||
test_equality(test);
|
||||
return true;
|
||||
}
|
||||
function test_g() external returns (bool) {
|
||||
clean();
|
||||
MyAddress[] memory test = new MyAddress[](5);
|
||||
test[0] = MyAddress.wrap(address(24));
|
||||
test[1] = MyAddress.wrap(address(25));
|
||||
test[2] = MyAddress.wrap(address(26));
|
||||
test[3] = MyAddress.wrap(address(27));
|
||||
test[4] = MyAddress.wrap(address(28));
|
||||
this.g(test);
|
||||
test_equality(test);
|
||||
return true;
|
||||
}
|
||||
function clean() internal {
|
||||
delete addresses;
|
||||
}
|
||||
function test_equality(MyAddress[] memory _addresses) internal view {
|
||||
require (_addresses.length == addresses.length);
|
||||
for (uint i = 0; i < _addresses.length; i++) {
|
||||
require(MyAddress.unwrap(_addresses[i]) == MyAddress.unwrap(addresses[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test_f() -> true
|
||||
// gas irOptimized: 122655
|
||||
// gas legacy: 125037
|
||||
// gas legacyOptimized: 122605
|
||||
// test_g() -> true
|
||||
// gas irOptimized: 95940
|
||||
// gas legacy: 100656
|
||||
// gas legacyOptimized: 96057
|
||||
// addresses(uint256): 0 -> 0x18
|
||||
// addresses(uint256): 1 -> 0x19
|
||||
// addresses(uint256): 3 -> 0x1b
|
||||
// addresses(uint256): 4 -> 0x1c
|
||||
// addresses(uint256): 5 -> FAILURE
|
@ -0,0 +1,44 @@
|
||||
pragma abicoder v2;
|
||||
type MyUInt8 is uint8;
|
||||
|
||||
// Note that this wraps from a uint256
|
||||
function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } }
|
||||
function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } }
|
||||
|
||||
contract C {
|
||||
uint8 a;
|
||||
MyUInt8 b;
|
||||
uint8 c;
|
||||
function ret() external returns(MyUInt8) {
|
||||
return wrap(0x1ff);
|
||||
}
|
||||
function f(MyUInt8 x) external returns(MyUInt8) {
|
||||
return x;
|
||||
}
|
||||
function mem() external returns (MyUInt8[] memory) {
|
||||
MyUInt8[] memory x = new MyUInt8[](2);
|
||||
x[0] = wrap(0x1ff);
|
||||
x[1] = wrap(0xff);
|
||||
require(unwrap(x[0]) == unwrap(x[1]));
|
||||
assembly {
|
||||
mstore(add(x, 0x20), 0x1ff)
|
||||
}
|
||||
require(unwrap(x[0]) == unwrap(x[1]));
|
||||
return x;
|
||||
}
|
||||
function stor() external returns (uint8, MyUInt8, uint8) {
|
||||
a = 1;
|
||||
c = 2;
|
||||
b = wrap(0x1ff);
|
||||
return (a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// ret() -> 0xff
|
||||
// f(uint8): 0x1ff -> FAILURE
|
||||
// f(uint8): 0xff -> 0xff
|
||||
// mem() -> 0x20, 2, 0xff, 0xff
|
||||
// stor() -> 1, 0xff, 2
|
@ -0,0 +1,44 @@
|
||||
pragma abicoder v1;
|
||||
type MyUInt8 is uint8;
|
||||
|
||||
// Note that this wraps from a uint256
|
||||
function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } }
|
||||
function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } }
|
||||
|
||||
contract C {
|
||||
uint8 a;
|
||||
MyUInt8 b;
|
||||
uint8 c;
|
||||
function ret() external returns(MyUInt8) {
|
||||
return wrap(0x1ff);
|
||||
}
|
||||
function f(MyUInt8 x) external returns(MyUInt8) {
|
||||
return x;
|
||||
}
|
||||
function mem() external returns (MyUInt8[] memory) {
|
||||
MyUInt8[] memory x = new MyUInt8[](2);
|
||||
x[0] = wrap(0x1ff);
|
||||
x[1] = wrap(0xff);
|
||||
require(unwrap(x[0]) == unwrap(x[1]));
|
||||
assembly {
|
||||
mstore(add(x, 0x20), 0x1ff)
|
||||
}
|
||||
require(unwrap(x[0]) == unwrap(x[1]));
|
||||
return x;
|
||||
}
|
||||
function stor() external returns (uint8, MyUInt8, uint8) {
|
||||
a = 1;
|
||||
c = 2;
|
||||
b = wrap(0x1ff);
|
||||
return (a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: false
|
||||
// ----
|
||||
// ret() -> 0xff
|
||||
// f(uint8): 0x1ff -> 0xff
|
||||
// f(uint8): 0xff -> 0xff
|
||||
// mem() -> 0x20, 2, 0x01ff, 0xff
|
||||
// stor() -> 1, 0xff, 2
|
@ -0,0 +1,57 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
type MyUInt8 is uint8;
|
||||
type MyInt8 is int8;
|
||||
type MyUInt16 is uint16;
|
||||
|
||||
contract C {
|
||||
function f(uint a) external returns(MyUInt8) {
|
||||
return MyUInt8.wrap(uint8(a));
|
||||
}
|
||||
function g(uint a) external returns(MyInt8) {
|
||||
return MyInt8.wrap(int8(int(a)));
|
||||
}
|
||||
function h(MyUInt8 a) external returns (MyInt8) {
|
||||
return MyInt8.wrap(int8(MyUInt8.unwrap(a)));
|
||||
}
|
||||
function i(MyUInt8 a) external returns(MyUInt16) {
|
||||
return MyUInt16.wrap(MyUInt8.unwrap(a));
|
||||
}
|
||||
function j(MyUInt8 a) external returns (uint) {
|
||||
return MyUInt8.unwrap(a);
|
||||
}
|
||||
function k(MyUInt8 a) external returns (MyUInt16) {
|
||||
return MyUInt16.wrap(MyUInt8.unwrap(a));
|
||||
}
|
||||
function m(MyUInt16 a) external returns (MyUInt8) {
|
||||
return MyUInt8.wrap(uint8(MyUInt16.unwrap(a)));
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256): 1 -> 1
|
||||
// f(uint256): 2 -> 2
|
||||
// f(uint256): 257 -> 1
|
||||
// g(uint256): 1 -> 1
|
||||
// g(uint256): 2 -> 2
|
||||
// g(uint256): 255 -> -1
|
||||
// g(uint256): 257 -> 1
|
||||
// h(uint8): 1 -> 1
|
||||
// h(uint8): 2 -> 2
|
||||
// h(uint8): 255 -> -1
|
||||
// h(uint8): 257 -> FAILURE
|
||||
// i(uint8): 250 -> 250
|
||||
// j(uint8): 1 -> 1
|
||||
// j(uint8): 2 -> 2
|
||||
// j(uint8): 255 -> 0xff
|
||||
// j(uint8): 257 -> FAILURE
|
||||
// k(uint8): 1 -> 1
|
||||
// k(uint8): 2 -> 2
|
||||
// k(uint8): 255 -> 0xff
|
||||
// k(uint8): 257 -> FAILURE
|
||||
// m(uint16): 1 -> 1
|
||||
// m(uint16): 2 -> 2
|
||||
// m(uint16): 255 -> 0xff
|
||||
// m(uint16): 257 -> 1
|
@ -0,0 +1,57 @@
|
||||
pragma abicoder v1;
|
||||
|
||||
type MyUInt8 is uint8;
|
||||
type MyInt8 is int8;
|
||||
type MyUInt16 is uint16;
|
||||
|
||||
contract C {
|
||||
function f(uint a) external returns(MyUInt8) {
|
||||
return MyUInt8.wrap(uint8(a));
|
||||
}
|
||||
function g(uint a) external returns(MyInt8) {
|
||||
return MyInt8.wrap(int8(int(a)));
|
||||
}
|
||||
function h(MyUInt8 a) external returns (MyInt8) {
|
||||
return MyInt8.wrap(int8(MyUInt8.unwrap(a)));
|
||||
}
|
||||
function i(MyUInt8 a) external returns(MyUInt16) {
|
||||
return MyUInt16.wrap(MyUInt8.unwrap(a));
|
||||
}
|
||||
function j(MyUInt8 a) external returns (uint) {
|
||||
return MyUInt8.unwrap(a);
|
||||
}
|
||||
function k(MyUInt8 a) external returns (MyUInt16) {
|
||||
return MyUInt16.wrap(MyUInt8.unwrap(a));
|
||||
}
|
||||
function m(MyUInt16 a) external returns (MyUInt8) {
|
||||
return MyUInt8.wrap(uint8(MyUInt16.unwrap(a)));
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: false
|
||||
// ----
|
||||
// f(uint256): 1 -> 1
|
||||
// f(uint256): 2 -> 2
|
||||
// f(uint256): 257 -> 1
|
||||
// g(uint256): 1 -> 1
|
||||
// g(uint256): 2 -> 2
|
||||
// g(uint256): 255 -> -1
|
||||
// g(uint256): 257 -> 1
|
||||
// h(uint8): 1 -> 1
|
||||
// h(uint8): 2 -> 2
|
||||
// h(uint8): 255 -> -1
|
||||
// h(uint8): 257 -> 1
|
||||
// i(uint8): 250 -> 250
|
||||
// j(uint8): 1 -> 1
|
||||
// j(uint8): 2 -> 2
|
||||
// j(uint8): 255 -> 0xff
|
||||
// j(uint8): 257 -> 1
|
||||
// k(uint8): 1 -> 1
|
||||
// k(uint8): 2 -> 2
|
||||
// k(uint8): 255 -> 0xff
|
||||
// k(uint8): 257 -> 1
|
||||
// m(uint16): 1 -> 1
|
||||
// m(uint16): 2 -> 2
|
||||
// m(uint16): 255 -> 0xff
|
||||
// m(uint16): 257 -> 1
|
147
test/libsolidity/semanticTests/userDefinedValueType/erc20.sol
Normal file
147
test/libsolidity/semanticTests/userDefinedValueType/erc20.sol
Normal file
@ -0,0 +1,147 @@
|
||||
pragma abicoder v2;
|
||||
// A rewrite of the test/libsolidity/semanticTests/various/erc20.sol, but using user defined value
|
||||
// types.
|
||||
|
||||
// User defined type name. Indicating a type with 18 decimals.
|
||||
type UFixed18 is uint256;
|
||||
|
||||
library FixedMath
|
||||
{
|
||||
function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) {
|
||||
return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b));
|
||||
}
|
||||
function sub(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) {
|
||||
return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.unwrap(b));
|
||||
}
|
||||
}
|
||||
|
||||
contract ERC20 {
|
||||
using FixedMath for UFixed18;
|
||||
|
||||
event Transfer(address indexed from, address indexed to, UFixed18 value);
|
||||
event Approval(address indexed owner, address indexed spender, UFixed18 value);
|
||||
|
||||
mapping (address => UFixed18) private _balances;
|
||||
mapping (address => mapping (address => UFixed18)) private _allowances;
|
||||
UFixed18 private _totalSupply;
|
||||
|
||||
constructor() {
|
||||
_mint(msg.sender, UFixed18.wrap(20));
|
||||
}
|
||||
|
||||
function totalSupply() public view returns (UFixed18) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
function balanceOf(address owner) public view returns (UFixed18) {
|
||||
return _balances[owner];
|
||||
}
|
||||
|
||||
function allowance(address owner, address spender) public view returns (UFixed18) {
|
||||
return _allowances[owner][spender];
|
||||
}
|
||||
|
||||
function transfer(address to, UFixed18 value) public returns (bool) {
|
||||
_transfer(msg.sender, to, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function approve(address spender, UFixed18 value) public returns (bool) {
|
||||
_approve(msg.sender, spender, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(address from, address to, UFixed18 value) public returns (bool) {
|
||||
_transfer(from, to, value);
|
||||
// The subtraction here will revert on overflow.
|
||||
_approve(from, msg.sender, _allowances[from][msg.sender].sub(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
function increaseAllowance(address spender, UFixed18 addedValue) public returns (bool) {
|
||||
// The addition here will revert on overflow.
|
||||
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
function decreaseAllowance(address spender, UFixed18 subtractedValue) public returns (bool) {
|
||||
// The subtraction here will revert on overflow.
|
||||
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
function _transfer(address from, address to, UFixed18 value) internal {
|
||||
require(to != address(0), "ERC20: transfer to the zero address");
|
||||
|
||||
// The subtraction and addition here will revert on overflow.
|
||||
_balances[from] = _balances[from].sub(value);
|
||||
_balances[to] = _balances[to].add(value);
|
||||
emit Transfer(from, to, value);
|
||||
}
|
||||
|
||||
function _mint(address account, UFixed18 value) internal {
|
||||
require(account != address(0), "ERC20: mint to the zero address");
|
||||
|
||||
// The additions here will revert on overflow.
|
||||
_totalSupply = _totalSupply.add(value);
|
||||
_balances[account] = _balances[account].add(value);
|
||||
emit Transfer(address(0), account, value);
|
||||
}
|
||||
|
||||
function _burn(address account, UFixed18 value) internal {
|
||||
require(account != address(0), "ERC20: burn from the zero address");
|
||||
|
||||
// The subtractions here will revert on overflow.
|
||||
_totalSupply = _totalSupply.sub(value);
|
||||
_balances[account] = _balances[account].sub(value);
|
||||
emit Transfer(account, address(0), value);
|
||||
}
|
||||
|
||||
function _approve(address owner, address spender, UFixed18 value) internal {
|
||||
require(owner != address(0), "ERC20: approve from the zero address");
|
||||
require(spender != address(0), "ERC20: approve to the zero address");
|
||||
|
||||
_allowances[owner][spender] = value;
|
||||
emit Approval(owner, spender, value);
|
||||
}
|
||||
|
||||
function _burnFrom(address account, UFixed18 value) internal {
|
||||
_burn(account, value);
|
||||
_approve(account, msg.sender, _allowances[account][msg.sender].sub(value));
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor()
|
||||
// ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14
|
||||
// gas irOptimized: 460447
|
||||
// gas legacy: 861547
|
||||
// gas legacyOptimized: 420959
|
||||
// totalSupply() -> 20
|
||||
// gas irOptimized: 23378
|
||||
// gas legacy: 23653
|
||||
// gas legacyOptimized: 23368
|
||||
// transfer(address,uint256): 2, 5 -> true
|
||||
// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05
|
||||
// gas irOptimized: 48514
|
||||
// gas legacy: 49572
|
||||
// gas legacyOptimized: 48575
|
||||
// decreaseAllowance(address,uint256): 2, 0 -> true
|
||||
// ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00
|
||||
// gas irOptimized: 26316
|
||||
// gas legacy: 27204
|
||||
// gas legacyOptimized: 26317
|
||||
// decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11
|
||||
// gas irOptimized: 24040
|
||||
// gas legacy: 24506
|
||||
// gas legacyOptimized: 24077
|
||||
// transfer(address,uint256): 2, 14 -> true
|
||||
// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e
|
||||
// gas irOptimized: 28614
|
||||
// gas legacy: 29672
|
||||
// gas legacyOptimized: 28675
|
||||
// transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11
|
||||
// gas irOptimized: 24052
|
||||
// gas legacy: 24492
|
||||
// gas legacyOptimized: 24074
|
@ -0,0 +1,42 @@
|
||||
// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type.
|
||||
type UFixed256x18 is uint256;
|
||||
|
||||
/// A minimal library to do fixed point operations on UFixed256x18.
|
||||
library FixedMath {
|
||||
/// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked arithmetic on
|
||||
/// uint256.
|
||||
function add(UFixed256x18 a, UFixed256x18 b) internal returns (UFixed256x18) {
|
||||
return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
|
||||
}
|
||||
/// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked arithmetic on
|
||||
/// uint256.
|
||||
function mul(UFixed256x18 a, uint256 b) internal returns (UFixed256x18) {
|
||||
return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
|
||||
}
|
||||
/// Truncates UFixed256x18 to the nearest uint256 number.
|
||||
function truncate(UFixed256x18 a) internal returns (uint256) {
|
||||
return UFixed256x18.unwrap(a) / 10**18;
|
||||
}
|
||||
}
|
||||
|
||||
contract TestFixedMath {
|
||||
function add(UFixed256x18 a, UFixed256x18 b) external returns (UFixed256x18) {
|
||||
return FixedMath.add(a, b);
|
||||
}
|
||||
function mul(UFixed256x18 a, uint256 b) external returns (UFixed256x18) {
|
||||
return FixedMath.mul(a, b);
|
||||
}
|
||||
function truncate(UFixed256x18 a) external returns (uint256) {
|
||||
return FixedMath.truncate(a);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// add(uint256,uint256): 0, 0 -> 0
|
||||
// add(uint256,uint256): 25, 45 -> 0x46
|
||||
// add(uint256,uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935, 10 -> FAILURE, hex"4e487b71", 0x11
|
||||
// mul(uint256,uint256): 340282366920938463463374607431768211456, 45671926166590716193865151022383844364247891968 -> FAILURE, hex"4e487b71", 0x11
|
||||
// mul(uint256,uint256): 340282366920938463463374607431768211456, 20 -> 6805647338418769269267492148635364229120
|
||||
// truncate(uint256): 11579208923731619542357098500868790785326998665640564039457584007913129639930 -> 11579208923731619542357098500868790785326998665640564039457
|
||||
// truncate(uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> 115792089237316195423570985008687907853269984665640564039457
|
@ -0,0 +1,32 @@
|
||||
// Implementation of OpenZepplin's
|
||||
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
|
||||
// using user defined value types.
|
||||
|
||||
contract Ownable {
|
||||
type Owner is address;
|
||||
Owner public owner = Owner.wrap(msg.sender);
|
||||
error OnlyOwner();
|
||||
modifier onlyOwner() {
|
||||
if (Owner.unwrap(owner) != msg.sender)
|
||||
revert OnlyOwner();
|
||||
|
||||
_;
|
||||
}
|
||||
event OwnershipTransferred(Owner indexed previousOwner, Owner indexed newOwner);
|
||||
function setOwner(Owner newOwner) onlyOwner external {
|
||||
emit OwnershipTransferred({previousOwner: owner, newOwner: newOwner});
|
||||
owner = newOwner;
|
||||
}
|
||||
function renounceOwnership() onlyOwner external {
|
||||
owner = Owner.wrap(address(0));
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// owner() -> 0x1212121212121212121212121212120000000012
|
||||
// setOwner(address): 0x1212121212121212121212121212120000000012 ->
|
||||
// ~ emit OwnershipTransferred(address,address): #0x1212121212121212121212121212120000000012, #0x1212121212121212121212121212120000000012
|
||||
// renounceOwnership() ->
|
||||
// owner() -> 0
|
||||
// setOwner(address): 0x1212121212121212121212121212120000000012 -> FAILURE, hex"5fc483c5"
|
@ -0,0 +1,42 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
type MyAddress is address;
|
||||
contract C {
|
||||
function id(MyAddress a) external returns (MyAddress b) {
|
||||
b = a;
|
||||
}
|
||||
|
||||
function unwrap_assembly(MyAddress a) external returns (address b) {
|
||||
assembly { b := a }
|
||||
}
|
||||
|
||||
function wrap_assembly(address a) external returns (MyAddress b) {
|
||||
assembly { b := a }
|
||||
}
|
||||
|
||||
function unwrap(MyAddress a) external returns (address b) {
|
||||
b = MyAddress.unwrap(a);
|
||||
}
|
||||
function wrap(address a) external returns (MyAddress b) {
|
||||
b = MyAddress.wrap(a);
|
||||
}
|
||||
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// id(address): 5 -> 5
|
||||
// id(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// id(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
||||
// unwrap(address): 5 -> 5
|
||||
// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
||||
// wrap(address): 5 -> 5
|
||||
// wrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// wrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
||||
// unwrap_assembly(address): 5 -> 5
|
||||
// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
||||
// wrap_assembly(address): 5 -> 5
|
||||
// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
@ -0,0 +1,14 @@
|
||||
type MyInt is int;
|
||||
contract C {
|
||||
function f() external pure returns (MyInt a) {
|
||||
}
|
||||
function g() external pure returns (MyInt b, MyInt c) {
|
||||
b = MyInt.wrap(int(1));
|
||||
c = MyInt.wrap(1);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> 0
|
||||
// g() -> 1, 1
|
@ -0,0 +1,11 @@
|
||||
type MyAddress is address;
|
||||
contract C {
|
||||
function f() pure public {
|
||||
MyAddress.wrap;
|
||||
MyAddress.unwrap;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() ->
|
@ -0,0 +1,5 @@
|
||||
type MyInt is uint;
|
||||
|
||||
contract C {
|
||||
type MyAddress is address;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
type(MyInt) is uint256;
|
||||
// ----
|
||||
// ParserError 2314: (4-5): Expected identifier but got '('
|
@ -0,0 +1,6 @@
|
||||
function f() {
|
||||
type(uint).max;
|
||||
type MyInt is int;
|
||||
}
|
||||
// ----
|
||||
// ParserError 2314: (44-49): Expected ';' but got identifier
|
@ -0,0 +1,12 @@
|
||||
type MyAddress is address;
|
||||
type MyAddressPayable is address payable;
|
||||
type MyInt is int;
|
||||
type MyUInt is uint;
|
||||
type MyInt128 is int128;
|
||||
type MyUInt128 is uint128;
|
||||
// TODO add fixed point type, when it's implemented
|
||||
type MyFixedBytes32 is bytes32;
|
||||
type MyFixedBytes1 is bytes1;
|
||||
type MyBool is bool;
|
||||
/// test to see if having NatSpec causes issues
|
||||
type redundantNatSpec is bytes2;
|
@ -0,0 +1,6 @@
|
||||
type MyAddress is address;
|
||||
function f() {
|
||||
MyAddress a = MyAddress(5, 2);
|
||||
}
|
||||
// ----
|
||||
// TypeError 2558: (60-75): Exactly one argument expected for explicit type conversion.
|
@ -0,0 +1,4 @@
|
||||
type MyInt is int;
|
||||
function f(MyInt a) pure returns (MyInt b) {
|
||||
b = MyInt(a);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
type MyUInt is uint;
|
||||
type MyAddress is address;
|
||||
type AnotherUInt is uint;
|
||||
|
||||
function f() pure {
|
||||
MyUInt(-1);
|
||||
MyAddress(-1);
|
||||
MyUInt(5);
|
||||
MyAddress(address(5));
|
||||
|
||||
AnotherUInt(MyUInt.wrap(5));
|
||||
MyUInt(AnotherUInt.wrap(10));
|
||||
AnotherUInt.unwrap(MyUInt.wrap(5));
|
||||
MyUInt.unwrap(AnotherUInt.wrap(10));
|
||||
}
|
||||
// ----
|
||||
// TypeError 9640: (99-109): Explicit type conversion not allowed from "int_const -1" to "user defined type MyUInt".
|
||||
// TypeError 9640: (115-128): Explicit type conversion not allowed from "int_const -1" to "user defined type MyAddress".
|
||||
// TypeError 9640: (134-143): Explicit type conversion not allowed from "int_const 5" to "user defined type MyUInt".
|
||||
// TypeError 9640: (149-170): Explicit type conversion not allowed from "address" to "user defined type MyAddress".
|
||||
// TypeError 9640: (177-204): Explicit type conversion not allowed from "user defined type MyUInt" to "user defined type AnotherUInt".
|
||||
// TypeError 9640: (210-238): Explicit type conversion not allowed from "user defined type AnotherUInt" to "user defined type MyUInt".
|
||||
// TypeError 9553: (263-277): Invalid type for argument in function call. Invalid implicit conversion from user defined type MyUInt to user defined type AnotherUInt requested.
|
||||
// TypeError 9553: (298-318): Invalid type for argument in function call. Invalid implicit conversion from user defined type AnotherUInt to user defined type MyUInt requested.
|
@ -0,0 +1,8 @@
|
||||
type MyUint is uint;
|
||||
type MyAddress is address;
|
||||
|
||||
function f() pure {
|
||||
MyUint.wrap(5);
|
||||
MyAddress.wrap(address(5));
|
||||
}
|
||||
// ----
|
@ -0,0 +1,9 @@
|
||||
type MyAddress is address;
|
||||
type MyUInt8 is uint8;
|
||||
|
||||
function f() pure {
|
||||
MyAddress.wrap(address(5));
|
||||
MyUInt8.wrap(5);
|
||||
MyUInt8.wrap(50);
|
||||
}
|
||||
// ----
|
@ -0,0 +1,4 @@
|
||||
function f(MyIntB x) pure {}
|
||||
type MyIntB is MyIntB;
|
||||
// ----
|
||||
// TypeError 8657: (44-50): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,14 @@
|
||||
type MyInt is uint;
|
||||
type MyAddress is address;
|
||||
function f() pure {
|
||||
MyInt a;
|
||||
MyInt b = a;
|
||||
MyAddress c;
|
||||
MyAddress d = c;
|
||||
b;
|
||||
d;
|
||||
}
|
||||
|
||||
function g(MyInt a) pure returns (MyInt) {
|
||||
return a;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
type MyInt is int;
|
||||
function f(int a) pure returns (int) {
|
||||
MyInt b = a;
|
||||
|
||||
int c = b;
|
||||
|
||||
address d = b;
|
||||
|
||||
MyInt e = d;
|
||||
|
||||
uint x = 0;
|
||||
MyInt y = MyInt(x);
|
||||
|
||||
return e;
|
||||
}
|
||||
// ----
|
||||
// TypeError 9574: (62-73): Type int256 is not implicitly convertible to expected type user defined type MyInt.
|
||||
// TypeError 9574: (80-89): Type user defined type MyInt is not implicitly convertible to expected type int256.
|
||||
// TypeError 9574: (96-109): Type user defined type MyInt is not implicitly convertible to expected type address.
|
||||
// TypeError 9574: (116-127): Type address is not implicitly convertible to expected type user defined type MyInt.
|
||||
// TypeError 9640: (160-168): Explicit type conversion not allowed from "uint256" to "user defined type MyInt".
|
||||
// TypeError 6359: (182-183): Return argument type user defined type MyInt is not implicitly convertible to expected type (type of first return variable) int256.
|
@ -0,0 +1,7 @@
|
||||
type MyInt is int;
|
||||
function f() pure {
|
||||
(MyInt).wrap;
|
||||
(MyInt).wrap(5);
|
||||
(MyInt).unwrap;
|
||||
(MyInt).unwrap(MyInt.wrap(5));
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
type MyAddress is address;
|
||||
contract C {
|
||||
type MyAddress is address;
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (44-70): This declaration shadows an existing declaration.
|
@ -0,0 +1,9 @@
|
||||
type MyInt is int;
|
||||
type MyInt is address;
|
||||
contract C {
|
||||
type MyAddress is address;
|
||||
type MyAddress is address;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 2333: (19-41): Identifier already declared.
|
||||
// DeclarationError 2333: (90-116): Identifier already declared.
|
@ -0,0 +1,3 @@
|
||||
type MyBytes is bytes;
|
||||
// ----
|
||||
// TypeError 8129: (0-22): The underlying type of the user defined value type "MyBytes" is not a value type.
|
@ -0,0 +1,4 @@
|
||||
contract C {}
|
||||
type MyContract is C;
|
||||
// ----
|
||||
// TypeError 8657: (33-34): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,3 @@
|
||||
type MyFunction is function(uint) returns (uint);
|
||||
// ----
|
||||
// TypeError 8657: (19-49): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,3 @@
|
||||
type MyInt is mapping(uint => uint);
|
||||
// ----
|
||||
// TypeError 8657: (14-35): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,3 @@
|
||||
type MyString is string;
|
||||
// ----
|
||||
// TypeError 8129: (0-24): The underlying type of the user defined value type "MyString" is not a value type.
|
@ -0,0 +1,7 @@
|
||||
struct S {uint x;}
|
||||
|
||||
contract C {
|
||||
type MyType is S;
|
||||
}
|
||||
// ----
|
||||
// TypeError 8657: (52-53): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,19 @@
|
||||
type MyAddress is address;
|
||||
interface I {}
|
||||
contract C {
|
||||
function f(MyAddress a) external {
|
||||
}
|
||||
function f(address a) external {
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
function g(MyAddress a) external {
|
||||
}
|
||||
}
|
||||
contract E is D {
|
||||
function g(I a) external {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9914: (104-142): Function overload clash during conversion to external types for arguments.
|
||||
// TypeError 9914: (162-202): Function overload clash during conversion to external types for arguments.
|
@ -0,0 +1,4 @@
|
||||
type MyInt1 is MyInt2;
|
||||
type MyInt2 is MyInt1;
|
||||
// ----
|
||||
// TypeError 8657: (15-21): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,3 @@
|
||||
type MyFunction is function(MyFunction) external returns(MyFunction);
|
||||
// ----
|
||||
// TypeError 8657: (19-69): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,13 @@
|
||||
library L {
|
||||
type MyInt is int;
|
||||
}
|
||||
|
||||
contract C {
|
||||
L.MyInt a;
|
||||
type MyInt is int8;
|
||||
}
|
||||
|
||||
contract D is C {
|
||||
C.MyInt b;
|
||||
L.MyInt c;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
type MyInt is int;
|
||||
type MyInt is int;
|
||||
type MyAddress is address;
|
||||
type MyAddress is uint;
|
||||
// ----
|
||||
// DeclarationError 2333: (19-37): Identifier already declared.
|
||||
// DeclarationError 2333: (65-88): Identifier already declared.
|
@ -0,0 +1,3 @@
|
||||
type MyInt is MyInt;
|
||||
// ----
|
||||
// TypeError 8657: (14-19): The underlying type for a user defined value type has to be an elementary value type.
|
@ -0,0 +1,8 @@
|
||||
type MyInt is int;
|
||||
function test() pure {
|
||||
function (MyInt) returns (int) f = MyInt.unwrap;
|
||||
function (int) returns (MyInt) g = MyInt.wrap;
|
||||
}
|
||||
// ----
|
||||
// TypeError 9574: (46-93): Type function (user defined type MyInt) pure returns (int256) is not implicitly convertible to expected type function (user defined type MyInt) returns (int256). Special functions can not be converted to function types.
|
||||
// TypeError 9574: (99-144): Type function (int256) pure returns (user defined type MyInt) is not implicitly convertible to expected type function (int256) returns (user defined type MyInt). Special functions can not be converted to function types.
|
@ -0,0 +1,16 @@
|
||||
type MyInt is int;
|
||||
function f() {
|
||||
MyInt.wrap(5, 6, 7);
|
||||
MyInt.wrap({test: 5});
|
||||
MyInt.wrap();
|
||||
MyInt.unwrap(5);
|
||||
MyInt.unwrap({test: 5});
|
||||
MyInt.unwrap(MyInt.wrap(1), MyInt.wrap(2));
|
||||
}
|
||||
// ----
|
||||
// TypeError 6160: (38-57): Wrong argument count for function call: 3 arguments given but expected 1.
|
||||
// TypeError 4974: (63-84): Named argument "test" does not match function declaration.
|
||||
// TypeError 6160: (90-102): Wrong argument count for function call: 0 arguments given but expected 1.
|
||||
// TypeError 9553: (121-122): Invalid type for argument in function call. Invalid implicit conversion from int_const 5 to user defined type MyInt requested.
|
||||
// TypeError 4974: (129-152): Named argument "test" does not match function declaration.
|
||||
// TypeError 6160: (158-200): Wrong argument count for function call: 2 arguments given but expected 1.
|
Loading…
Reference in New Issue
Block a user