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:
@@ -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()
|
||||
|
||||
+50
-1
@@ -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 = {},
|
||||
|
||||
Reference in New Issue
Block a user