This commit is contained in:
Anton Bukov 2023-10-02 13:12:00 -06:00 committed by GitHub
commit 4ffb7a9458
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 523 additions and 22 deletions

View File

@ -119,6 +119,7 @@ Type Information
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`. - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`. - ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
- ``type(S).typehash`` (``bytes32``): the typehash of the given struct type ``S``, see :ref:`Type Information<meta-type>`.
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`. - ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`. - ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.

View File

@ -385,6 +385,13 @@ for an interface type ``I``:
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
function selectors defined within the interface itself - excluding all inherited functions. function selectors defined within the interface itself - excluding all inherited functions.
The following properties are available for an struct type ``S``:
``type(S).typehash``:
A ``bytes32`` value containing the `EIP-712 <https://eips.ethereum.org/EIPS/eip-712>`_
typehash of the given structure ``S``. This identifier is defined as ``keccak256`` of
structure name and all the fields with their types, wrapped in braces and separated by commas.
The following properties are available for an integer type ``T``: The following properties are available for an integer type ``T``:
``type(T).min`` ``type(T).min``

View File

@ -315,6 +315,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
wrongType = contractType->isSuper(); wrongType = contractType->isSuper();
else if ( else if (
typeCategory != Type::Category::Integer && typeCategory != Type::Category::Integer &&
typeCategory != Type::Category::Struct &&
typeCategory != Type::Category::Enum typeCategory != Type::Category::Enum
) )
wrongType = true; wrongType = true;
@ -327,7 +328,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
4259_error, 4259_error,
arguments.front()->location(), arguments.front()->location(),
"Invalid type for argument in the function call. " "Invalid type for argument in the function call. "
"An enum type, contract type or an integer type is required, but " + "An enum type, contract type, struct type or an integer type is required, but " +
type(*arguments.front())->humanReadableName() + " provided." type(*arguments.front())->humanReadableName() + " provided."
); );
@ -3390,6 +3391,33 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isPure = true; annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId") else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
annotation.isPure = true; annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "typehash")
{
annotation.isPure = true;
auto accessedStructType = dynamic_cast<StructType const*>(magicType->typeArgument());
solAssert(accessedStructType, "typehash requested on a non struct type.");
if (accessedStructType->recursive())
{
m_errorReporter.typeError(
9298_error,
_memberAccess.location(),
"\"typehash\" cannot be used for recursive structs."
);
}
for (auto const& member: accessedStructType->members(currentDefinitionScope()))
{
if (!member.type->isEIP712AllowedStructMemberType())
{
m_errorReporter.typeError(
9518_error,
_memberAccess.location(),
"\"typehash\" cannot be used for structs with members of \"" + member.type->humanReadableName() + "\" type."
);
}
}
}
else if ( else if (
magicType->kind() == MagicType::Kind::MetaType && magicType->kind() == MagicType::Kind::MetaType &&
(memberName == "min" || memberName == "max") (memberName == "min" || memberName == "max")

View File

@ -397,6 +397,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::MetaType, "runtimeCode"}, {MagicType::Kind::MetaType, "runtimeCode"},
{MagicType::Kind::MetaType, "name"}, {MagicType::Kind::MetaType, "name"},
{MagicType::Kind::MetaType, "interfaceId"}, {MagicType::Kind::MetaType, "interfaceId"},
{MagicType::Kind::MetaType, "typehash"},
{MagicType::Kind::MetaType, "min"}, {MagicType::Kind::MetaType, "min"},
{MagicType::Kind::MetaType, "max"}, {MagicType::Kind::MetaType, "max"},
}; };

View File

@ -38,6 +38,8 @@
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <numeric>
#include <set>
using namespace solidity; using namespace solidity;
using namespace solidity::frontend; using namespace solidity::frontend;
@ -388,6 +390,67 @@ std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> UsingFo
return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to<std::vector>; return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to<std::vector>;
} }
void StructDefinition::insertEip712EncodedSubtypes(std::set<std::string>& subtypes) const
{
for (size_t i = 0; i < m_members.size(); i++)
{
Declaration const* declaration = nullptr;
switch (m_members[i]->type()->category())
{
case Type::Category::Struct:
declaration = m_members[i]->type()->typeDefinition();
break;
case Type::Category::Array:
if (auto const* arrayType = dynamic_cast<ArrayType const*>(m_members[i]->type()))
{
if (auto finalBaseType = dynamic_cast<StructType const*>(arrayType->finalBaseType(false)))
{
declaration = finalBaseType->typeDefinition();
}
}
break;
default:
continue;
}
if (!declaration)
{
continue;
}
if (auto const* structDef = dynamic_cast<StructDefinition const*>(declaration))
{
subtypes.insert(structDef->eip712EncodeTypeWithoutSubtypes());
structDef->insertEip712EncodedSubtypes(subtypes);
}
}
}
std::string StructDefinition::eip712EncodeTypeWithoutSubtypes() const
{
std::string str = name() + "(";
for (size_t i = 0; i < m_members.size(); i++)
{
str += i == 0 ? "" : ",";
str += m_members[i]->type()->eip712TypeName() + " " + m_members[i]->name();
}
return str + ")";
}
std::string StructDefinition::eip712EncodeType() const
{
// std::set enables duplicates elimination and ordered enumeration
std::set<std::string> subtypes;
insertEip712EncodedSubtypes(subtypes);
return std::accumulate(subtypes.begin(), subtypes.end(), eip712EncodeTypeWithoutSubtypes());
}
util::h256 StructDefinition::typehash() const
{
return util::keccak256(eip712EncodeType());
}
Type const* StructDefinition::type() const Type const* StructDefinition::type() const
{ {
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker."); solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");

View File

@ -745,6 +745,18 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; } std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
/// Fills set with the EIP-712 compatible struct encodings without subtypes concatenated.
void insertEip712EncodedSubtypes(std::set<std::string>& subtypes) const;
/// @returns the EIP-712 compatible struct encoding but without subtypes concatenated.
std::string eip712EncodeTypeWithoutSubtypes() const;
/// @returns the EIP-712 compatible struct encoding with subtypes sorted and concatenated.
std::string eip712EncodeType() const;
/// @returns the EIP-712 compatible typehash of this struct.
util::h256 typehash() const;
Type const* type() const override; Type const* type() const override;
bool isVisibleInDerivedContracts() const override { return true; } bool isVisibleInDerivedContracts() const override { return true; }

View File

@ -562,10 +562,11 @@ MagicType const* TypeProvider::meta(Type const* _type)
solAssert( solAssert(
_type && ( _type && (
_type->category() == Type::Category::Contract || _type->category() == Type::Category::Contract ||
_type->category() == Type::Category::Struct ||
_type->category() == Type::Category::Integer || _type->category() == Type::Category::Integer ||
_type->category() == Type::Category::Enum _type->category() == Type::Category::Enum
), ),
"Only enum, contracts or integer types supported for now." "Only enum, contract, struct or integer types supported for now."
); );
return createAndGet<MagicType>(_type); return createAndGet<MagicType>(_type);
} }

View File

@ -1886,6 +1886,23 @@ std::string ArrayType::canonicalName() const
return ret; return ret;
} }
std::string ArrayType::eip712TypeName() const
{
std::string ret;
if (isString())
ret = "string";
else if (isByteArrayOrString())
ret = "bytes";
else
{
ret = baseType()->eip712TypeName() + "[";
if (!isDynamicallySized())
ret += length().str();
ret += "]";
}
return ret;
}
std::string ArrayType::signatureInExternalFunction(bool _structsByName) const std::string ArrayType::signatureInExternalFunction(bool _structsByName) const
{ {
if (isByteArrayOrString()) if (isByteArrayOrString())
@ -2510,6 +2527,11 @@ std::string StructType::canonicalName() const
return *m_struct.annotation().canonicalName; return *m_struct.annotation().canonicalName;
} }
std::string StructType::eip712TypeName() const
{
return this->typeDefinition()->name();
}
FunctionTypePointer StructType::constructorType() const FunctionTypePointer StructType::constructorType() const
{ {
TypePointers paramTypes; TypePointers paramTypes;
@ -4196,15 +4218,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
}); });
case Kind::MetaType: case Kind::MetaType:
{ {
solAssert( solAssert(m_typeArgument, "");
m_typeArgument && (
m_typeArgument->category() == Type::Category::Contract ||
m_typeArgument->category() == Type::Category::Integer ||
m_typeArgument->category() == Type::Category::Enum
),
"Only enums, contracts or integer types supported for now"
);
if (m_typeArgument->category() == Type::Category::Contract) if (m_typeArgument->category() == Type::Category::Contract)
{ {
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition(); ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
@ -4220,6 +4234,12 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
{"name", TypeProvider::stringMemory()}, {"name", TypeProvider::stringMemory()},
}); });
} }
else if (m_typeArgument->category() == Type::Category::Struct)
{
return MemberList::MemberMap({
{"typehash", TypeProvider::fixedBytes(32)},
});
}
else if (m_typeArgument->category() == Type::Category::Integer) else if (m_typeArgument->category() == Type::Category::Integer)
{ {
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument); IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
@ -4236,6 +4256,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
{"max", enumTypePointer}, {"max", enumTypePointer},
}); });
} }
else
{
solAssert(false, "Only enums, contracts, structs or integer types supported for now");
}
} }
} }
solAssert(false, "Unknown kind of magic."); solAssert(false, "Unknown kind of magic.");

View File

@ -357,6 +357,12 @@ public:
/// @returns the canonical name of this type for use in library function signatures. /// @returns the canonical name of this type for use in library function signatures.
virtual std::string canonicalName() const { return toString(true); } virtual std::string canonicalName() const { return toString(true); }
virtual std::string humanReadableName() const { return toString(); } virtual std::string humanReadableName() const { return toString(); }
virtual std::string eip712TypeName() const
{
solAssert(isEIP712AllowedStructMemberType(), "Invalid type ...");
return encodingType()->toString(true);
}
virtual bool isEIP712AllowedStructMemberType() const { return false; }
/// @returns the signature of this type in external functions, i.e. `uint256` for integers /// @returns the signature of this type in external functions, i.e. `uint256` for integers
/// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName, /// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName,
/// structs are given by canonical name like `ContractName.StructName[2]`. /// structs are given by canonical name like `ContractName.StructName[2]`.
@ -457,6 +463,7 @@ public:
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -502,6 +509,7 @@ public:
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
@ -551,6 +559,7 @@ public:
bool nameable() const override { return true; } bool nameable() const override { return true; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string eip712TypeName() const override{ solAssert(false, "EIP-712 is not supported for struct members of fixed point type"); }
Type const* encodingType() const override { return this; } Type const* encodingType() const override { return this; }
TypeResult interfaceType(bool) const override { return this; } TypeResult interfaceType(bool) const override { return this; }
@ -660,6 +669,8 @@ public:
return nullptr; return nullptr;
} }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
@ -698,6 +709,7 @@ public:
bool leftAligned() const override { return true; } bool leftAligned() const override { return true; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool) const override { return "bytes" + util::toString(m_bytes); } std::string toString(bool) const override { return "bytes" + util::toString(m_bytes); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -726,6 +738,7 @@ public:
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool) const override { return "bool"; } std::string toString(bool) const override { return "bool"; }
u256 literalValue(Literal const* _literal) const override; u256 literalValue(Literal const* _literal) const override;
@ -863,10 +876,12 @@ public:
u256 storageSize() const override; u256 storageSize() const override;
bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); } bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); }
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string humanReadableName() const override; std::string humanReadableName() const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string eip712TypeName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override; std::string signatureInExternalFunction(bool _structsByName) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
Type const* encodingType() const override; Type const* encodingType() const override;
@ -924,6 +939,7 @@ public:
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override; std::string richIdentifier() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of array slice type"); }
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool) const override { solAssert(false, ""); } unsigned calldataEncodedSize(bool) const override { solAssert(false, ""); }
unsigned calldataEncodedTailSize() const override { return 32; } unsigned calldataEncodedTailSize() const override { return 32; }
@ -974,6 +990,7 @@ public:
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool isValueType() const override { return !isSuper(); } bool isValueType() const override { return !isSuper(); }
bool nameable() const override { return !isSuper(); } bool nameable() const override { return !isSuper(); }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
@ -1036,6 +1053,7 @@ public:
u256 storageSize() const override; u256 storageSize() const override;
bool containsNestedMapping() const override; bool containsNestedMapping() const override;
bool nameable() const override { return true; } bool nameable() const override { return true; }
bool isEIP712AllowedStructMemberType() const override { return true; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1052,6 +1070,7 @@ public:
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override; std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string eip712TypeName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override; std::string signatureInExternalFunction(bool _structsByName) const override;
/// @returns a function that performs the type conversion between a list of struct members /// @returns a function that performs the type conversion between a list of struct members
@ -1100,6 +1119,7 @@ public:
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of Enum type"); }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override { return true; } bool nameable() const override { return true; }
@ -1187,6 +1207,7 @@ public:
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of user defined value type"); }
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); } std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
protected: protected:
@ -1218,6 +1239,8 @@ public:
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
Type const* mobileType() const override; Type const* mobileType() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of tuple type"); }
std::vector<Type const*> const& components() const { return m_components; } std::vector<Type const*> const& components() const { return m_components; }
protected: protected:
@ -1415,6 +1438,7 @@ public:
TypeResult binaryOperatorResult(Token, Type const*) const override; TypeResult binaryOperatorResult(Token, Type const*) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string humanReadableName() const override; std::string humanReadableName() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of function type"); }
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
unsigned calldataEncodedSize(bool _padded) const override; unsigned calldataEncodedSize(bool _padded) const override;
bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
@ -1551,6 +1575,7 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of mapping type"); }
bool containsNestedMapping() const override { return true; } bool containsNestedMapping() const override { return true; }
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
Type const* encodingType() const override; Type const* encodingType() const override;
@ -1595,6 +1620,7 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of type type"); }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _withoutDataLocation) const override { return "type(" + m_actualType->toString(_withoutDataLocation) + ")"; } std::string toString(bool _withoutDataLocation) const override { return "type(" + m_actualType->toString(_withoutDataLocation) + ")"; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1623,6 +1649,7 @@ public:
u256 storageSize() const override; u256 storageSize() const override;
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override; std::string richIdentifier() const override;
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of modifier type"); }
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
std::string toString(bool _withoutDataLocation) const override; std::string toString(bool _withoutDataLocation) const override;
protected: protected:
@ -1647,6 +1674,7 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of module type"); }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1686,6 +1714,7 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of magic type"); }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1721,6 +1750,7 @@ public:
unsigned calldataEncodedSize(bool) const override { return 32; } unsigned calldataEncodedSize(bool) const override { return 32; }
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
std::string eip712TypeName() const override { solAssert(false, "EIP-712 is not supported for struct members of inaccessible dynamic type"); }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool) const override { return "inaccessible dynamic type"; } std::string toString(bool) const override { return "inaccessible dynamic type"; }
Type const* decodingType() const override; Type const* decodingType() const override;

View File

@ -1934,6 +1934,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition(); ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
m_context << (u256{contract.interfaceId()} << (256 - 32)); m_context << (u256{contract.interfaceId()} << (256 - 32));
} }
else if (member == "typehash")
{
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
solAssert(dynamic_cast<StructType const*>(arg), "typehash called on a non-struct type");
StructDefinition const& struct_ = dynamic_cast<StructType const&>(*arg).structDefinition();
m_context << struct_.typehash();
}
else if (member == "min" || member == "max") else if (member == "min" || member == "max")
{ {
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type); MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);

View File

@ -1940,6 +1940,15 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
ContractDefinition const& contract = contractType.contractDefinition(); ContractDefinition const& contract = contractType.contractDefinition();
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
} }
else if (member == "typehash")
{
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
solAssert(!!arg);
StructType const* structType = dynamic_cast<StructType const*>(arg);
solAssert(!!structType);
StructDefinition const& struct_ = structType->structDefinition();
define(_memberAccess) << "0x" << struct_.typehash() << "\n";
}
else if (member == "min" || member == "max") else if (member == "min" || member == "max")
{ {
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type); MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);

View File

@ -1409,6 +1409,11 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition(); ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition();
defineExpr(_memberAccess, contract.interfaceId()); defineExpr(_memberAccess, contract.interfaceId());
} }
else if (memberName == "typehash")
{
StructDefinition const& structDef = dynamic_cast<StructType const&>(*magicType->typeArgument()).structDefinition();
defineExpr(_memberAccess, u256(structDef.typehash()));
}
else else
// NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not // NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not
// at all usable in the SMT checker currently // at all usable in the SMT checker currently

View File

@ -1916,6 +1916,9 @@ BOOST_AUTO_TEST_CASE(builtins)
interface I {} interface I {}
contract C { contract C {
struct S {
uint x;
}
function accessBuiltin() public payable { function accessBuiltin() public payable {
abi.decode; abi.decode;
abi.encode; abi.encode;
@ -1956,6 +1959,7 @@ BOOST_AUTO_TEST_CASE(builtins)
address(0).staticcall; address(0).staticcall;
type(C).name; type(C).name;
type(I).interfaceId; type(I).interfaceId;
type(S).typehash;
type(uint).min; type(uint).min;
type(uint).max; type(uint).max;
assert; assert;

View File

@ -0,0 +1,13 @@
contract C {
struct Mail {
address from;
address to;
string contents;
}
function f() public pure returns(bool) {
return type(Mail).typehash == keccak256("Mail(address from,address to,string contents)");
}
}
// ----
// f() -> true

View File

@ -0,0 +1,23 @@
library A {
struct S1 {
uint256 x;
}
bytes32 internal constant a = type(S1).typehash;
}
library B {
struct S1 {
uint256 x;
}
bytes32 internal constant b = type(S1).typehash;
}
contract C {
function f() public pure returns(bool) {
return A.a == B.b;
}
}
// ----
// f() -> true

View File

@ -0,0 +1,26 @@
contract C {
struct S2 {
uint256 x;
S1 y;
}
struct S1 {
uint256 z;
S3[] w;
}
struct S3 {
address a;
uint24 b;
}
function f() public pure returns(bool, bool, bool) {
return (
type(S3).typehash == keccak256("S3(address a,uint24 b)"),
type(S1).typehash == keccak256("S1(uint256 z,S3[] w)S3(address a,uint24 b)"),
type(S2).typehash == keccak256("S2(uint256 x,S1 y)S1(uint256 z,S3[] w)S3(address a,uint24 b)")
);
}
}
// ----
// f() -> true, true, true

View File

@ -0,0 +1,21 @@
struct S {
uint256 x;
}
bytes32 constant TYPE_HASH_FILE_LEVEL = type(S).typehash;
contract C {
struct S {
uint256 y;
}
function f() public pure returns(bool, bool, bool) {
return (
type(S).typehash == keccak256("S(uint256 y)"),
type(S).typehash == TYPE_HASH_FILE_LEVEL,
TYPE_HASH_FILE_LEVEL == keccak256("S(uint256 x)")
);
}
}
// ----
// f() -> true, false, true

View File

@ -0,0 +1,14 @@
contract A {
struct S {
string x;
bool[10][] y;
}
}
contract C is A {
function f() public pure returns(bool) {
return type(S).typehash == keccak256("S(string x,bool[10][] y)");
}
}
// ----
// f() -> true

View File

@ -0,0 +1,43 @@
interface I1 {
}
contract C {
struct S1 {
uint256 x;
}
struct S2 {
uint256 x;
address y;
}
struct S3 {
uint256 x;
I1 y;
S2 third;
}
struct S4 {
S3 one;
S2 two;
}
struct S5 {
S2 two;
S1 one;
S3 three;
S4[5] four;
}
function f() public pure {
assert(type(S1).typehash == 0x78a822935e38445215ba7404686b919cbbef5725cbf9231d92802f542d7456e0); // keccak256("S1(uint256 x)")
assert(type(S2).typehash == 0x6c397ebd50462a81423e44830702ee1214cb9ab734bf173eb55f04238c9d398f); // keccak256("S2(uint256 x,address y)")
assert(type(S3).typehash == 0xfa5685568fb2f15c09479ecbcfe9d0494743d804587d1966db67d5e62ea4344a); // keccak256("S3(uint256 x,address y,S2 third)S2(uint256 x,address y)")
assert(type(S4).typehash == 0x17ed8da37c0446eeffef8cd38d116505b399b5644fdc3a59f8a68a68dd5d4178); // keccak256("S4(S3 one,S2 two)S2(uint256 x,address y)S3(uint256 x,address y,S2 third)")
assert(type(S5).typehash == 0x5e52252fbbc0eda2d75f57c57d47fbec3bc6b215a9a3790c7f7ca44a36eb5185); // keccak256("S5(S2 two,S1 one,S3 three,S4[5] four)S1(uint256 x)S2(uint256 x,address y)S3(uint256 x,address y,S2 third)S4(S3 one,S2 two)")
}
}
// ====
// SMTEngine: all
// ----
// Info 1391: CHC: 5 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them.

View File

@ -5,4 +5,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError 4259: (93-98): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(bytes storage pointer) provided. // TypeError 4259: (93-98): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(bytes storage pointer) provided.

View File

@ -6,4 +6,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError 4259: (126-139): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but error MyCustomError(uint256,bool) provided. // TypeError 4259: (126-139): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but error MyCustomError(uint256,bool) provided.

View File

@ -6,4 +6,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError 4259: (124-137): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but event MyCustomEvent(uint256) provided. // TypeError 4259: (124-137): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but event MyCustomEvent(uint256) provided.

View File

@ -10,4 +10,4 @@ contract SuperTest is Other {
} }
} }
// ---- // ----
// TypeError 4259: (177-182): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super SuperTest) provided. // TypeError 4259: (177-182): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(contract super SuperTest) provided.

View File

@ -14,4 +14,4 @@ abstract contract Test is ERC165 {
} }
} }
// ---- // ----
// TypeError 4259: (592-597): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super Test) provided. // TypeError 4259: (592-597): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(contract super Test) provided.

View File

@ -50,4 +50,4 @@ contract D is B, C {
} }
} }
// ---- // ----
// TypeError 4259: (426-431): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super B) provided. // TypeError 4259: (426-431): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(contract super B) provided.

View File

@ -4,4 +4,4 @@ contract Test {
} }
} }
// ---- // ----
// TypeError 4259: (65-75): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract Test) provided. // TypeError 4259: (65-75): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(contract Test) provided.

View File

@ -1,9 +1,8 @@
contract Test { contract Test {
struct S { uint x; }
function f() public pure { function f() public pure {
// Unsupported for now, but might be supported in the future // Unsupported for now, but might be supported in the future
type(S); type(bytes32);
} }
} }
// ---- // ----
// TypeError 4259: (154-155): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(struct Test.S storage pointer) provided. // TypeError 4259: (129-136): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(bytes32) provided.

View File

@ -14,4 +14,4 @@ contract C {
// ---- // ----
// TypeError 5347: (72-76): Try can only be used with external function calls and contract creation calls. // TypeError 5347: (72-76): Try can only be used with external function calls and contract creation calls.
// TypeError 2536: (119-128): Try can only be used with external function calls and contract creation calls. // TypeError 2536: (119-128): Try can only be used with external function calls and contract creation calls.
// TypeError 4259: (176-183): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(address) provided. // TypeError 4259: (176-183): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but type(address) provided.

View File

@ -0,0 +1,7 @@
contract C {
struct S {
uint256[][10][] x;
}
bytes32 constant h = type(S).typehash;
}

View File

@ -0,0 +1,5 @@
contract C {
bytes32 x = type(C).typehash;
}
// ----
// TypeError 9582: (29-45): Member "typehash" not found or not visible after argument-dependent lookup in type(contract C).

View File

@ -0,0 +1,9 @@
contract C {
enum E {
VALUE
}
bytes32 h = type(E).typehash;
}
// ----
// TypeError 9582: (63-79): Member "typehash" not found or not visible after argument-dependent lookup in type(enum C.E).

View File

@ -0,0 +1,11 @@
contract C {
struct S {
uint256[][10][] x;
}
bytes32 immutable h;
constructor() {
h = type(S).typehash;
}
}

View File

@ -0,0 +1,14 @@
contract C {
enum E {
VALUE
}
struct S {
E e;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (98-114): "typehash" cannot be used for structs with members of "enum C.E" type.

View File

@ -0,0 +1,9 @@
contract C {
struct S {
fixed128x18 x;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (74-90): "typehash" cannot be used for structs with members of "fixed128x18" type.

View File

@ -0,0 +1,9 @@
contract C {
struct S {
function(uint256) internal returns(uint256) f;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (106-122): "typehash" cannot be used for structs with members of "function (uint256) returns (uint256)" type.

View File

@ -0,0 +1,9 @@
contract C {
struct S {
mapping (uint256 => uint256) x;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (91-107): "typehash" cannot be used for structs with members of "mapping(uint256 => uint256)" type.

View File

@ -0,0 +1,9 @@
contract C {
struct S {
ufixed128x18 x;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (75-91): "typehash" cannot be used for structs with members of "ufixed128x18" type.

View File

@ -0,0 +1,10 @@
type T is uint256;
contract C {
struct S {
T t;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9518: (83-99): "typehash" cannot be used for structs with members of "T" type.

View File

@ -0,0 +1,9 @@
contract C {
struct S {
uint256 x;
}
function f() public pure returns(bytes32) {
return type(S).typehash;
}
}

View File

@ -0,0 +1,9 @@
contract C {
struct S {
S[] s;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9298: (66-82): "typehash" cannot be used for recursive structs.

View File

@ -0,0 +1,14 @@
contract A {
struct S {
C.S[] s;
}
}
contract C {
struct S {
A.S s;
}
bytes32 h = type(S).typehash;
}
// ----
// TypeError 9298: (119-135): "typehash" cannot be used for recursive structs.

View File

@ -0,0 +1,15 @@
contract C {
struct S {
uint256 x;
}
function f1(S calldata s) public pure returns(bytes32 h) {
h = type(s).typehash; // should fail
}
function f3(S calldata s) public pure returns(bytes32 h) {
h = type(S).typehash;
}
}
// ----
// TypeError 4259: (134-135): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but struct C.S calldata provided.

View File

@ -0,0 +1,15 @@
contract C {
struct S {
uint256 x;
}
function f1(S memory s) public pure returns(bytes32 h) {
h = type(s).typehash; // should fail
}
function f3(S calldata s) public pure returns(bytes32 h) {
h = type(S).typehash;
}
}
// ----
// TypeError 4259: (132-133): Invalid type for argument in the function call. An enum type, contract type, struct type or an integer type is required, but struct C.S memory provided.

View File

@ -0,0 +1,5 @@
contract C {
bytes32 h = type(uint256).typehash;
}
// ----
// TypeError 9582: (29-51): Member "typehash" not found or not visible after argument-dependent lookup in type(uint256).

View File

@ -0,0 +1,11 @@
contract C {
struct S {
uint256 x;
}
function f() public view returns(bytes32) { // can be pure
return type(S).typehash;
}
}
// ----
// Warning 2018: (58-155): Function state mutability can be restricted to pure