Merge pull request #9781 from ethereum/smt-type-interfaceid

[SMTChecker] Support type(I).interfaceId
This commit is contained in:
Leonardo 2020-09-15 00:05:15 +02:00 committed by GitHub
commit 4d470cd285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 9 deletions

View File

@ -6,7 +6,7 @@ Language Features:
Compiler Features:
* SMTChecker: Support shifts.
* SMTChecker: Support structs.
* SMTChecker: Support ``type(T).min`` and ``type(T).max``.
* SMTChecker: Support ``type(T).min``, ``type(T).max``, and ``type(I).interfaceId``.
* Yul Optimizer: Prune unused parameters in functions.
* Yul Optimizer: Inline into functions further down in the call graph first.
* Yul Optimizer: Try to simplify function names.

View File

@ -208,6 +208,14 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
});
}
uint64_t ContractDefinition::interfaceId() const
{
uint64_t result{0};
for (auto const& function: interfaceFunctionList(false))
result ^= util::fromBigEndian<uint64_t>(function.first.ref());
return result;
}
TypePointer ContractDefinition::type() const
{
return TypeProvider::typeType(TypeProvider::contract(*this));

View File

@ -509,6 +509,8 @@ public:
/// as intended for use by the ABI.
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const;
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const;
/// @returns the EIP-165 compatible interface identifier. This will exclude inherited functions.
uint64_t interfaceId() const;
/// @returns a list of all declarations in this contract
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }

View File

@ -1598,10 +1598,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
uint64_t result{0};
for (auto const& function: contract.interfaceFunctionList(false))
result ^= fromBigEndian<uint64_t>(function.first.ref());
m_context << (u256{result} << (256 - 32));
m_context << (u256{contract.interfaceId()} << (256 - 32));
}
else if (member == "min" || member == "max")
{

View File

@ -1549,10 +1549,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
uint64_t result{0};
for (auto const& function: contract.interfaceFunctionList(false))
result ^= fromBigEndian<uint64_t>(function.first.ref());
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
}
else if (member == "min" || member == "max")
{

View File

@ -888,6 +888,11 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
IntegerType const& integerType = dynamic_cast<IntegerType const&>(*magicType->typeArgument());
defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue());
}
else if (memberName == "interfaceId")
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition();
defineExpr(_memberAccess, contract.interfaceId());
}
else
// NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not
// at all useable in the SMT checker currently

View File

@ -0,0 +1,32 @@
pragma experimental SMTChecker;
interface I1 {
}
interface I2 {
function f() external;
}
interface I3 {
function f() external;
function g(uint, address) external;
}
contract C {
function f() public pure {
assert(type(I1).interfaceId == 0);
assert(type(I2).interfaceId != 0);
assert(type(I2).interfaceId == 0x26121ff0);
assert(type(I2).interfaceId != 0);
assert(type(I3).interfaceId == 0x822b51c6);
}
function g() public pure {
assert(type(I1).interfaceId == type(I2).interfaceId);
}
function h() public pure {
assert(type(I2).interfaceId == type(I3).interfaceId);
}
}
// ----
// Warning 6328: (449-501): Assertion violation happens here.
// Warning 6328: (536-588): Assertion violation happens here.