diff --git a/Changelog.md b/Changelog.md index df45a558f41a..7457c79ab26c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.8.31 (unreleased) Language Features: + * Support for EIP-712 struct typehash via ``type(S).typehash`` for computing the keccak256 hash of the EIP-712 encoding of struct type ``S``. Compiler Features: * ethdebug: Experimental support for instructions and source locations under EOF. diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 79aa6e65d552..e52437fa6c32 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -130,6 +130,7 @@ Type Information - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. - ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information`. +- ``type(S).typehash`` (``bytes32``): the typehash of the given struct type ``S``, see :ref:`Type Information`. - ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information`. - ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information`. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index fce4cf96d994..50df1d42f7ff 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -404,6 +404,13 @@ for an interface type ``I``: 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. +The following properties are available for an struct type ``S``: + +``type(S).typehash``: + A ``bytes32`` value containing the `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``: ``type(T).min`` diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e56ee157d94d..f5a27dd5aa6a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -210,6 +210,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio wrongType = contractType->isSuper(); else if ( typeCategory != Type::Category::Integer && + typeCategory != Type::Category::Struct && typeCategory != Type::Category::Enum ) wrongType = true; @@ -222,7 +223,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio 4259_error, arguments.front()->location(), "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." ); @@ -3302,6 +3303,27 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.isPure = true; else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId") annotation.isPure = true; + else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "typehash") + { + annotation.isPure = true; + auto accessedStructType = dynamic_cast(magicType->typeArgument()); + solAssert(accessedStructType, "typehash requested on a non struct type."); + + // Direct recursion is already rejected by DeclarationTypeChecker, + // so only dynamic array based recursion can reach here. which is valid. + + 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 ( magicType->kind() == MagicType::Kind::MetaType && (memberName == "min" || memberName == "max") diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index cbd538551106..93595c955008 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -398,6 +398,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::MetaType, "runtimeCode"}, {MagicType::Kind::MetaType, "name"}, {MagicType::Kind::MetaType, "interfaceId"}, + {MagicType::Kind::MetaType, "typehash"}, {MagicType::Kind::MetaType, "min"}, {MagicType::Kind::MetaType, "max"}, }; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index b59273b18d5d..5fdfc07c7a07 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -38,6 +38,8 @@ #include #include +#include +#include using namespace solidity; using namespace solidity::frontend; @@ -409,6 +411,75 @@ std::vector, std::optional>> UsingFo return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to; } +void StructDefinition::insertEip712EncodedSubtypes(std::set& subtypes) const +{ + std::set processedStructs; + collectEip712SubtypesWithCycleTracking(subtypes, processedStructs, this); +} + +void StructDefinition::collectEip712SubtypesWithCycleTracking(std::set& subtypes, std::set& processedStructs, StructDefinition const* rootStruct) const +{ + if (processedStructs.count(this)) + return; + + processedStructs.insert(this); + + for (auto const& member: m_members) + { + Declaration const* declaration = nullptr; + + switch (member->type()->category()) + { + case Type::Category::Struct: + declaration = member->type()->typeDefinition(); + break; + case Type::Category::Array: + if (auto const* arrayType = dynamic_cast(member->type())) + if (auto finalBaseType = dynamic_cast(arrayType->finalBaseType(false))) + { + declaration = finalBaseType->typeDefinition(); + } + break; + default: + continue; + } + + if (!declaration) + continue; + + if (auto const* structDef = dynamic_cast(declaration)) + if (structDef != rootStruct) + { + subtypes.insert(structDef->eip712EncodeTypeWithoutSubtypes()); + structDef->collectEip712SubtypesWithCycleTracking(subtypes, processedStructs, rootStruct); + } + } +} + +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 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 { solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker."); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a4e19582b250..230a207c9699 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -777,6 +777,23 @@ class StructDefinition: public Declaration, public StructurallyDocumented, publi std::vector> const& members() const { return m_members; } + /// Fills set with the EIP-712 compatible struct encodings without subtypes concatenated. + void insertEip712EncodedSubtypes(std::set& subtypes) const; + +private: + void collectEip712SubtypesWithCycleTracking(std::set& subtypes, std::set& processedStructs, StructDefinition const* rootStruct) const; + +public: + + /// @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; bool isVisibleInDerivedContracts() const override { return true; } diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index 41e103edf94f..98f020234195 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -563,10 +563,11 @@ MagicType const* TypeProvider::meta(Type const* _type) solAssert( _type && ( _type->category() == Type::Category::Contract || + _type->category() == Type::Category::Struct || _type->category() == Type::Category::Integer || _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(_type); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f5c83b0cf991..c5c6c4825807 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1910,6 +1910,23 @@ std::string ArrayType::canonicalName() const 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 { if (isByteArrayOrString()) @@ -2548,6 +2565,11 @@ std::string StructType::canonicalName() const return *m_struct.annotation().canonicalName; } +std::string StructType::eip712TypeName() const +{ + return this->typeDefinition()->name(); +} + FunctionTypePointer StructType::constructorType() const { TypePointers paramTypes; @@ -2666,6 +2688,11 @@ std::string EnumType::canonicalName() const return *m_enum.annotation().canonicalName; } +std::string EnumType::eip712TypeName() const +{ + return "uint8"; +} + size_t EnumType::numberOfMembers() const { return m_enum.members().size(); @@ -4255,15 +4282,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const return {}; case Kind::MetaType: { - solAssert( - 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" - ); - + solAssert(m_typeArgument, ""); if (m_typeArgument->category() == Type::Category::Contract) { ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); @@ -4279,6 +4298,12 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"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) { IntegerType const* integerTypePointer = dynamic_cast(m_typeArgument); @@ -4295,6 +4320,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"max", enumTypePointer}, }); } + else + { + solAssert(false, "Only enums, contracts, structs or integer types supported for now"); + } } } solAssert(false, "Unknown kind of magic."); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 8d2453d8d503..3bd468a966d8 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -360,6 +360,12 @@ class Type /// @returns the canonical name of this type for use in library function signatures. virtual std::string canonicalName() const { return toString(true); } 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 /// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName, /// structs are given by canonical name like `ContractName.StructName[2]`. @@ -460,6 +466,7 @@ class AddressType: public Type bool leftAligned() const override { return false; } bool isValueType() const override { return true; } bool nameable() const override { return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } MemberList::MemberMap nativeMembers(ASTNode const*) const override; @@ -506,6 +513,7 @@ class IntegerType: public Type bool leftAligned() const override { return false; } bool isValueType() const override { return true; } bool nameable() const override { return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } std::string toString(bool _withoutDataLocation) const override; @@ -555,6 +563,7 @@ class FixedPointType: public Type bool nameable() const override { return true; } 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; } TypeResult interfaceType(bool) const override { return this; } @@ -664,6 +673,8 @@ class StringLiteralType: public Type return nullptr; } + bool isEIP712AllowedStructMemberType() const override { return true; } + std::string richIdentifier() const override; bool operator==(Type const& _other) const override; @@ -702,6 +713,7 @@ class FixedBytesType: public Type bool leftAligned() const override { return true; } bool isValueType() 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); } MemberList::MemberMap nativeMembers(ASTNode const*) const override; @@ -730,6 +742,7 @@ class BoolType: public Type bool leftAligned() const override { return false; } bool isValueType() const override { return true; } bool nameable() const override { return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } std::string toString(bool) const override { return "bool"; } u256 literalValue(Literal const* _literal) const override; @@ -868,10 +881,12 @@ class ArrayType: public ReferenceType u256 storageSize() const override; bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); } bool nameable() const override { return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } std::string toString(bool _withoutDataLocation) const override; std::string humanReadableName() const override; std::string canonicalName() const override; + std::string eip712TypeName() const override; std::string signatureInExternalFunction(bool _structsByName) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; Type const* encodingType() const override; @@ -929,6 +944,7 @@ class ArraySliceType: public ReferenceType BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) 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; unsigned calldataEncodedSize(bool) const override { solAssert(false, ""); } unsigned calldataEncodedTailSize() const override { return 32; } @@ -979,6 +995,7 @@ class ContractType: public Type bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool isValueType() 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 canonicalName() const override; @@ -1044,6 +1061,7 @@ class StructType: public ReferenceType u256 storageSize() const override; bool containsNestedMapping() const override; bool nameable() const override { return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } std::string toString(bool _withoutDataLocation) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; @@ -1060,6 +1078,7 @@ class StructType: public ReferenceType std::unique_ptr copyForLocation(DataLocation _location, bool _isPointer) const override; std::string canonicalName() const override; + std::string eip712TypeName() const override; std::string signatureInExternalFunction(bool _structsByName) const override; /// @returns a function that performs the type conversion between a list of struct members @@ -1106,8 +1125,10 @@ class EnumType: public Type } unsigned storageBytes() const override; bool leftAligned() const override { return false; } + bool isEIP712AllowedStructMemberType() const override { return true; } std::string toString(bool _withoutDataLocation) const override; std::string canonicalName() const override; + std::string eip712TypeName() const override; bool isValueType() const override { return true; } bool nameable() const override { return true; } @@ -1172,6 +1193,7 @@ class UserDefinedValueType: public Type solAssert(underlyingType().nameable(), ""); return true; } + bool isEIP712AllowedStructMemberType() const override { return true; } bool containsNestedMapping() const override { @@ -1227,6 +1249,8 @@ class TupleType: public CompositeType bool hasSimpleZeroValueInMemory() const override { return false; } 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 const& components() const { return m_components; } protected: @@ -1425,6 +1449,7 @@ class FunctionType: public Type TypeResult binaryOperatorResult(Token, Type const*) const override; std::string canonicalName() 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; unsigned calldataEncodedSize(bool _padded) const override; bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } @@ -1561,6 +1586,7 @@ class MappingType: public CompositeType bool operator==(Type const& _other) const override; std::string toString(bool _withoutDataLocation) 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; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } Type const* encodingType() const override; @@ -1605,6 +1631,7 @@ class TypeType: public Type bool operator==(Type const& _other) const override; bool canBeStored() const override { return false; } 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, ""); } std::string toString(bool _withoutDataLocation) const override { return "type(" + m_actualType->toString(_withoutDataLocation) + ")"; } MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; @@ -1633,6 +1660,7 @@ class ModifierType: public Type u256 storageSize() const override; bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } 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==(ModifierType const& _other) const; std::string toString(bool _withoutDataLocation) const override; @@ -1658,6 +1686,7 @@ class ModuleType: public Type std::string richIdentifier() const override; bool operator==(Type const& _other) const override; 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, ""); } MemberList::MemberMap nativeMembers(ASTNode const*) const override; @@ -1700,6 +1729,7 @@ class MagicType: public Type std::string richIdentifier() const override; bool operator==(Type const& _other) const override; 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, ""); } MemberList::MemberMap nativeMembers(ASTNode const*) const override; @@ -1735,6 +1765,7 @@ class InaccessibleDynamicType: public Type unsigned calldataEncodedSize(bool) const override { return 32; } bool canBeStored() const override { return false; } 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, ""); } std::string toString(bool) const override { return "inaccessible dynamic type"; } Type const* decodingType() const override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 85634906e01f..83ba6ff04ea2 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1994,6 +1994,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); m_context << (u256{contract.interfaceId()} << (256 - 32)); } + else if (member == "typehash") + { + Type const* arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + solAssert(dynamic_cast(arg), "typehash called on a non-struct type"); + StructDefinition const& struct_ = dynamic_cast(*arg).structDefinition(); + m_context << struct_.typehash(); + } else if (member == "min" || member == "max") { MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5902cfef68b6..7e796ece49ca 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2012,6 +2012,15 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) ContractDefinition const& contract = contractType.contractDefinition(); define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; } + else if (member == "typehash") + { + Type const* arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + solAssert(!!arg); + StructType const* structType = dynamic_cast(arg); + solAssert(!!structType); + StructDefinition const& struct_ = structType->structDefinition(); + define(_memberAccess) << "0x" << struct_.typehash() << "\n"; + } else if (member == "min" || member == "max") { MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 6cf6cea93285..66e652c67ddd 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1446,6 +1446,11 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) ContractDefinition const& contract = dynamic_cast(*magicType->typeArgument()).contractDefinition(); defineExpr(_memberAccess, contract.interfaceId()); } + else if (memberName == "typehash") + { + StructDefinition const& structDef = dynamic_cast(*magicType->typeArgument()).structDefinition(); + defineExpr(_memberAccess, u256(structDef.typehash())); + } else // 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 diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index d91932767b22..27aa32bab867 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -1916,6 +1916,9 @@ BOOST_AUTO_TEST_CASE(builtins) interface I {} contract C { + struct S { + uint x; + } function accessBuiltin() public payable { abi.decode; abi.encode; @@ -1956,6 +1959,7 @@ BOOST_AUTO_TEST_CASE(builtins) address(0).staticcall; type(C).name; type(I).interfaceId; + type(S).typehash; type(uint).min; type(uint).max; assert; diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_EIP712_example.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_EIP712_example.sol new file mode 100644 index 000000000000..886a1c286a77 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_EIP712_example.sol @@ -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 diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_different_scopes.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_different_scopes.sol new file mode 100644 index 000000000000..0126eab24656 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_different_scopes.sol @@ -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 diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_enum_conversion.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_enum_conversion.sol new file mode 100644 index 000000000000..38272961fc61 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_enum_conversion.sol @@ -0,0 +1,22 @@ +contract C { + + enum E { + VALUE, + VALUE2, + VALUE3, + VALUE4 + } + + struct Mail { + address from; + address to; + string contents; + E e; + } + + function f() public pure returns(bool) { + return type(Mail).typehash == keccak256("Mail(address from,address to,string contents,uint8 e)"); + } +} +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_enum_different_contracts.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_enum_different_contracts.sol new file mode 100644 index 000000000000..d276b9b2479e --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_enum_different_contracts.sol @@ -0,0 +1,25 @@ +==== Source: A.sol ==== +contract A { + enum EnumInA { + VALUE, + VALUE2, + VALUE3, + VALUE4 + } +} + +==== Source: B.sol ==== +import "A.sol"; + +contract B { + struct S { + uint256 y; + A.EnumInA enumValue; + } + + function t() public pure returns (bool) { + return type(S).typehash == keccak256("S(uint256 y,uint8 enumValue)"); + } +} +// ---- +// t() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_import_alias.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_import_alias.sol new file mode 100644 index 000000000000..413b57d13788 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_import_alias.sol @@ -0,0 +1,27 @@ +==== Source: A.sol ==== +struct S { + uint256 x; +} + +==== Source: B.sol ==== +import {S as T} from "A.sol"; + +struct S { + T t; + uint256 y; +} + +contract Test { + function testImportedStructHash() public pure returns (bool) { + // T (which is A.S) should hash as "S(uint256 x)" to use original definition + return type(T).typehash == keccak256("S(uint256 x)"); + } + + function testLocalStructHash() public pure returns (bool) { + // Local S contains T (alias for A.S), so it should hash as "S(S t,uint256 y)S(uint256 x)" + return type(S).typehash == keccak256("S(S t,uint256 y)S(uint256 x)"); + } +} +// ---- +// testImportedStructHash() -> true +// testLocalStructHash() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_nested.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_nested.sol new file mode 100644 index 000000000000..8de7b6166b84 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_nested.sol @@ -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 diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_permit_example.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_permit_example.sol new file mode 100644 index 000000000000..e57e89785780 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_permit_example.sol @@ -0,0 +1,15 @@ +contract C { + struct Permit { + address owner; + address spender; + uint256 value; + uint256 nonce; + uint256 deadline; + } + + function f() public pure returns(bool) { + return type(Permit).typehash == keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + } +} +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_array.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_array.sol new file mode 100644 index 000000000000..60bcc77d4c18 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_array.sol @@ -0,0 +1,12 @@ +contract C { + struct Node { + uint256 value; + Node[] children; + } + + function f() public pure returns (bool) { + return type(Node).typehash == keccak256("Node(uint256 value,Node[] children)"); + } +} +// ---- +// f() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_double_array.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_double_array.sol new file mode 100644 index 000000000000..1a599fb57075 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_double_array.sol @@ -0,0 +1,21 @@ +contract C { + struct Left { + Right[] right; + } + + + struct Right { + Left left; + } + + function g() public pure returns (bool) { + return type(Right).typehash == keccak256("Right(Left left)Left(Right[] right)"); + } + + function f() public pure returns (bool) { + return type(Left).typehash == keccak256("Left(Right[] right)Right(Left left)"); + } +} +// ---- +// g() -> true +// f() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_more_array.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_more_array.sol new file mode 100644 index 000000000000..9f00adfec6b7 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_recursive_more_array.sol @@ -0,0 +1,46 @@ +contract C { + + struct Left { + Down[] down; + Right[] right; + Up[] up; + } + + struct Right { + Down[] down; + Left[] left; + Up[] up; + } + + struct Up { + Down[] down; + Left[] left; + Right[] right; + } + struct Down { + Up[] up; + Left[] left; + Right[] right; + } + + // first element and alphabetical order of structs. Down -> Left -> Right -> Up. + function g() public pure returns (bool) { + return type(Right).typehash == keccak256("Right(Down[] down,Left[] left,Up[] up)Down(Up[] up,Left[] left,Right[] right)Left(Down[] down,Right[] right,Up[] up)Up(Down[] down,Left[] left,Right[] right)"); + } + + function f() public pure returns (bool) { + return type(Left).typehash == keccak256("Left(Down[] down,Right[] right,Up[] up)Down(Up[] up,Left[] left,Right[] right)Right(Down[] down,Left[] left,Up[] up)Up(Down[] down,Left[] left,Right[] right)"); + } + + function h() public pure returns (bool) { + return type(Up).typehash == keccak256("Up(Down[] down,Left[] left,Right[] right)Down(Up[] up,Left[] left,Right[] right)Left(Down[] down,Right[] right,Up[] up)Right(Down[] down,Left[] left,Up[] up)"); + } + function i() public pure returns (bool) { + return type(Down).typehash == keccak256("Down(Up[] up,Left[] left,Right[] right)Left(Down[] down,Right[] right,Up[] up)Right(Down[] down,Left[] left,Up[] up)Up(Down[] down,Left[] left,Right[] right)"); + } +} +// ---- +// g() -> true +// f() -> true +// h() -> true +// i() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_same_name_different_contracts.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_same_name_different_contracts.sol new file mode 100644 index 000000000000..c6070b0e5201 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_same_name_different_contracts.sol @@ -0,0 +1,30 @@ +==== Source: A.sol ==== +contract A { + struct S { + uint256 x; + } +} + +==== Source: B.sol ==== +import "A.sol"; + +contract B { + struct S { + A.S nested; + uint256 y; + } + + function testLocalHash() public pure returns (bool) { + // B.S should encode as "S(S nested,uint256 y)S(uint256 x)" + // This tests whether the nested struct name is properly handled + return type(S).typehash == keccak256("S(S nested,uint256 y)S(uint256 x)"); + } + + function testImportedHash() public pure returns (bool) { + // A.S should encode as "S(uint256 x)" + return type(A.S).typehash == keccak256("S(uint256 x)"); + } +} +// ---- +// testLocalHash() -> true +// testImportedHash() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_shadow.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_shadow.sol new file mode 100644 index 000000000000..fe549295fa77 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_shadow.sol @@ -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 diff --git a/test/libsolidity/semanticTests/types/struct/type_typehash_super.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_super.sol new file mode 100644 index 000000000000..b3b504607bd0 --- /dev/null +++ b/test/libsolidity/semanticTests/types/struct/type_typehash_super.sol @@ -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 diff --git a/test/libsolidity/smtCheckerTests/types/type_typehash.sol b/test/libsolidity/smtCheckerTests/types/type_typehash.sol new file mode 100644 index 000000000000..96ba5515ab54 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/type_typehash.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol b/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol index 8950217334d3..87208373bbb0 100644 --- a/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol +++ b/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/errors/bytes_concat_on_error_type_info.sol b/test/libsolidity/syntaxTests/errors/bytes_concat_on_error_type_info.sol index 4b8bbd4599f0..f1823cd86d96 100644 --- a/test/libsolidity/syntaxTests/errors/bytes_concat_on_error_type_info.sol +++ b/test/libsolidity/syntaxTests/errors/bytes_concat_on_error_type_info.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/events/bytes_concat_on_event_type_info.sol b/test/libsolidity/syntaxTests/events/bytes_concat_on_event_type_info.sol index 40323830e56b..7f0fcda8a587 100644 --- a/test/libsolidity/syntaxTests/events/bytes_concat_on_event_type_info.sol +++ b/test/libsolidity/syntaxTests/events/bytes_concat_on_event_type_info.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol index a8fef765eea1..70d629c66ada 100644 --- a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol @@ -12,4 +12,4 @@ contract SuperTest is Other { // ==== // bytecodeFormat: legacy // ---- -// 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. diff --git a/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol index 0740b31cfd1c..6635b10f9996 100644 --- a/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol +++ b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/metaTypes/super_name.sol b/test/libsolidity/syntaxTests/metaTypes/super_name.sol index 5795b81908c7..6a2f98e99f94 100644 --- a/test/libsolidity/syntaxTests/metaTypes/super_name.sol +++ b/test/libsolidity/syntaxTests/metaTypes/super_name.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol index 427caf4d01ce..c9ab5e6a1162 100644 --- a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupported_arg_for_type.sol b/test/libsolidity/syntaxTests/metaTypes/unsupported_arg_for_type.sol index aa7686ed8168..b4bcf4ab61fb 100644 --- a/test/libsolidity/syntaxTests/metaTypes/unsupported_arg_for_type.sol +++ b/test/libsolidity/syntaxTests/metaTypes/unsupported_arg_for_type.sol @@ -1,9 +1,8 @@ contract Test { - struct S { uint x; } function f() public pure { // 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. diff --git a/test/libsolidity/syntaxTests/tryCatch/no_special.sol b/test/libsolidity/syntaxTests/tryCatch/no_special.sol index d760e6ee35f5..2e572ea4574d 100644 --- a/test/libsolidity/syntaxTests/tryCatch/no_special.sol +++ b/test/libsolidity/syntaxTests/tryCatch/no_special.sol @@ -14,4 +14,4 @@ contract C { // ---- // 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 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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_const.sol b/test/libsolidity/syntaxTests/types/struct/typehash_const.sol new file mode 100644 index 000000000000..e9f33f1d18c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_const.sol @@ -0,0 +1,7 @@ +contract C { + struct S { + uint256[][10][] x; + } + + bytes32 constant h = type(S).typehash; +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_contract.sol b/test/libsolidity/syntaxTests/types/struct/typehash_contract.sol new file mode 100644 index 000000000000..22bad80aa847 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_contract.sol @@ -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). diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_enum.sol b/test/libsolidity/syntaxTests/types/struct/typehash_enum.sol new file mode 100644 index 000000000000..670ef1ccf851 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_enum.sol @@ -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). diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_immutable.sol b/test/libsolidity/syntaxTests/types/struct/typehash_immutable.sol new file mode 100644 index 000000000000..a2adbf737b31 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_immutable.sol @@ -0,0 +1,11 @@ +contract C { + struct S { + uint256[][10][] x; + } + + bytes32 immutable h; + + constructor() { + h = type(S).typehash; + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_enum.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_enum.sol new file mode 100644 index 000000000000..db384f894c6c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_enum.sol @@ -0,0 +1,13 @@ +contract C { + enum E { + VALUE + } + + struct S { + E e; + } + + bytes32 h = type(S).typehash; +} + +// ---- diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_fixed128x18.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_fixed128x18.sol new file mode 100644 index 000000000000..0b3c5b57bf91 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_fixed128x18.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_function.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_function.sol new file mode 100644 index 000000000000..7e2e5895c5b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_function.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_mapping.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_mapping.sol new file mode 100644 index 000000000000..b5edc62ab2f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_mapping.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_ufixed128x18.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_ufixed128x18.sol new file mode 100644 index 000000000000..5393a30e88ce --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_ufixed128x18.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_member_userdefined.sol b/test/libsolidity/syntaxTests/types/struct/typehash_member_userdefined.sol new file mode 100644 index 000000000000..77a2a308d4db --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_userdefined.sol @@ -0,0 +1,8 @@ +type T is uint256; +contract C { + struct S { + T t; + } + + bytes32 h = type(S).typehash; +} diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_pure.sol b/test/libsolidity/syntaxTests/types/struct/typehash_pure.sol new file mode 100644 index 000000000000..fdca58e2747b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_pure.sol @@ -0,0 +1,9 @@ +contract C { + struct S { + uint256 x; + } + + function f() public pure returns(bytes32) { + return type(S).typehash; + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_recursive_double.sol b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_double.sol new file mode 100644 index 000000000000..c56cbe98a81c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_double.sol @@ -0,0 +1,15 @@ + +contract C { + + struct Left { + Right[] right; + } + + struct Right { + Left left; + } + + bytes32 h = type(Left).typehash; + bytes32 h2 = type(Right).typehash; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct.sol b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct.sol new file mode 100644 index 000000000000..683118634ff8 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { + S[] s; + } + + bytes32 h = type(S).typehash; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct_2.sol b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct_2.sol new file mode 100644 index 000000000000..0fa3706bf6b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct_2.sol @@ -0,0 +1,13 @@ +contract A { + struct S { + C.S[] s; + } +} +contract C { + struct S { + A.S s; + } + + bytes32 h = type(S).typehash; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable.sol b/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable.sol new file mode 100644 index 000000000000..049bc46d52b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable_2.sol b/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable_2.sol new file mode 100644 index 000000000000..9ba0bc7612ad --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_struct_variable_2.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_uint256.sol b/test/libsolidity/syntaxTests/types/struct/typehash_uint256.sol new file mode 100644 index 000000000000..d6ff2d73d783 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_uint256.sol @@ -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). diff --git a/test/libsolidity/syntaxTests/types/struct/typehash_viewpure.sol b/test/libsolidity/syntaxTests/types/struct/typehash_viewpure.sol new file mode 100644 index 000000000000..85bad50b2bcd --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_viewpure.sol @@ -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