diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index a60d3b50f..3b78da099 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -119,6 +119,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 d2256884e..a401c21d9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -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 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 5c28d3cd2..9c96237e1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -315,6 +315,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio wrongType = contractType->isSuper(); else if ( typeCategory != Type::Category::Integer && + typeCategory != Type::Category::Struct && typeCategory != Type::Category::Enum ) wrongType = true; @@ -327,7 +328,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." ); @@ -3390,6 +3391,33 @@ 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."); + + 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 ( magicType->kind() == MagicType::Kind::MetaType && (memberName == "min" || memberName == "max") diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 3f6b954e7..b2ff9d997 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -397,6 +397,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 c0bf90091..bf98277a3 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; @@ -388,6 +390,67 @@ std::vector, std::optional>> UsingFo return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to; } +void StructDefinition::insertEip712EncodedSubtypes(std::set& 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(m_members[i]->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)) + { + 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 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 ca23b0d61..a2028017f 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -745,6 +745,18 @@ public: 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; + + /// @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 da5087556..9b68ca3f8 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -562,10 +562,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 7b3e42143..1b057170a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1886,6 +1886,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()) @@ -2510,6 +2527,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; @@ -4196,15 +4218,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const }); 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(); @@ -4220,6 +4234,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); @@ -4236,6 +4256,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 fd1b6a7d3..c878e5c0a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -357,6 +357,12 @@ public: /// @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]`. @@ -457,6 +463,7 @@ public: 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; @@ -502,6 +509,7 @@ public: 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; @@ -551,6 +559,7 @@ public: 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; } @@ -660,6 +669,8 @@ public: return nullptr; } + bool isEIP712AllowedStructMemberType() const override { return true; } + std::string richIdentifier() const override; bool operator==(Type const& _other) const override; @@ -698,6 +709,7 @@ public: 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; @@ -726,6 +738,7 @@ public: 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; @@ -863,10 +876,12 @@ public: 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; @@ -924,6 +939,7 @@ public: 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; } @@ -974,6 +990,7 @@ public: 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; @@ -1036,6 +1053,7 @@ public: 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; @@ -1052,6 +1070,7 @@ public: 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 @@ -1100,6 +1119,7 @@ public: bool leftAligned() const override { return false; } 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 Enum type"); } bool isValueType() const override { return true; } bool nameable() const override { return true; } @@ -1187,6 +1207,7 @@ public: 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 user defined value type"); } std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); } protected: @@ -1218,6 +1239,8 @@ public: 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: @@ -1415,6 +1438,7 @@ public: 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; } @@ -1551,6 +1575,7 @@ public: 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; @@ -1595,6 +1620,7 @@ public: 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; @@ -1623,6 +1649,7 @@ public: 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; std::string toString(bool _withoutDataLocation) const override; protected: @@ -1647,6 +1674,7 @@ public: 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; @@ -1686,6 +1714,7 @@ public: 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; @@ -1721,6 +1750,7 @@ public: 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 517f49c35..99e7a3e9e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1934,6 +1934,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 c05fffbff..198649d77 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1940,6 +1940,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 d985e359e..764cc40ab 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1409,6 +1409,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 d91932767..27aa32bab 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 000000000..886a1c286 --- /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 000000000..0126eab24 --- /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_nested.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_nested.sol new file mode 100644 index 000000000..8de7b6166 --- /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_shadow.sol b/test/libsolidity/semanticTests/types/struct/type_typehash_shadow.sol new file mode 100644 index 000000000..fe549295f --- /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 000000000..b3b504607 --- /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 000000000..96ba5515a --- /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 895021733..87208373b 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 4b8bbd459..f1823cd86 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 40323830e..7f0fcda8a 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 7d057e132..788f081ea 100644 --- a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol index 0740b31cf..6635b10f9 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 5795b8190..6a2f98e99 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 427caf4d0..c9ab5e6a1 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 aa7686ed8..b4bcf4ab6 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 d760e6ee3..2e572ea45 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 000000000..e9f33f1d1 --- /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 000000000..22bad80aa --- /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 000000000..670ef1ccf --- /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 000000000..a2adbf737 --- /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 000000000..695ef07ac --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_enum.sol @@ -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. 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 000000000..0b3c5b57b --- /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 000000000..7e2e5895c --- /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 000000000..b5edc62ab --- /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 000000000..5393a30e8 --- /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 000000000..5f46378c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_member_userdefined.sol @@ -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. 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 000000000..fdca58e27 --- /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_struct.sol b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct.sol new file mode 100644 index 000000000..64d34716c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct.sol @@ -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. 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 000000000..11897755a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct/typehash_recursive_struct_2.sol @@ -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. 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 000000000..049bc46d5 --- /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 000000000..9ba0bc761 --- /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 000000000..d6ff2d73d --- /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 000000000..85bad50b2 --- /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