From ccf658b0e8a1c2042b61e8dbd4b1d9b995b15764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 2 Jun 2021 22:49:32 +0200 Subject: [PATCH] Fix assertion preventing assignment of arrays of implicitly convertible function types --- Changelog.md | 1 + libsolidity/codegen/LValue.cpp | 14 +++++- .../function_type_array_to_storage.sol | 44 +++++++++++++++++ .../function_type_array_to_memory.sol | 49 +++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/semanticTests/conversions/function_type_array_to_storage.sol create mode 100644 test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol diff --git a/Changelog.md b/Changelog.md index 26c958076..24c0c0c58 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ Compiler Features: Bugfixes: * AST: Do not output value of Yul literal if it is not a valid UTF-8 string. + * Code Generator: Fix internal error when function arrays are assigned to storage variables and the function types can be implicitly converted but are not identical. * Code Generator: Fix internal error when super would have to skip an unimplemented function in the virtual resolution order. * Control Flow Graph: Take internal calls to functions that always revert into account for reporting unused or unassigned variables. * SMTChecker: Fix internal error on struct constructor with fixed bytes member initialized with string literal. diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 0b999e0c4..e7e069352 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -300,16 +300,26 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: value storage_ref cleared_value multiplier utils.copyToStackTop(3 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack()); // stack: value storage_ref cleared_value multiplier value - if (FunctionType const* fun = dynamic_cast(m_dataType)) + if (auto const* fun = dynamic_cast(m_dataType)) { - solAssert(_sourceType == *m_dataType, "function item stored but target is not equal to source"); + solAssert( + _sourceType.isImplicitlyConvertibleTo(*m_dataType), + "function item stored but target is not implicitly convertible to source" + ); + solAssert(!fun->bound(), ""); if (fun->kind() == FunctionType::Kind::External) + { + solAssert(fun->sizeOnStack() == 2, ""); // Combine the two-item function type into a single stack slot. utils.combineExternalFunctionType(false); + } else + { + solAssert(fun->sizeOnStack() == 1, ""); m_context << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND; + } } else if (m_dataType->category() == Type::Category::FixedBytes) { diff --git a/test/libsolidity/semanticTests/conversions/function_type_array_to_storage.sol b/test/libsolidity/semanticTests/conversions/function_type_array_to_storage.sol new file mode 100644 index 000000000..4e0a8a561 --- /dev/null +++ b/test/libsolidity/semanticTests/conversions/function_type_array_to_storage.sol @@ -0,0 +1,44 @@ +contract C { + function () external returns(uint)[1] externalDefaultArray; + function () external view returns(uint)[1] externalViewArray; + function () external pure returns(uint)[1] externalPureArray; + + function () internal returns(uint)[1] internalDefaultArray; + function () internal view returns(uint)[1] internalViewArray; + function () internal pure returns(uint)[1] internalPureArray; + + function externalDefault() external returns(uint) { return 11; } + function externalView() external view returns(uint) { return 12; } + function externalPure() external pure returns(uint) { return 13; } + + function internalDefault() internal returns(uint) { return 21; } + function internalView() internal view returns(uint) { return 22; } + function internalPure() internal pure returns(uint) { return 23; } + + function testViewToDefault() public returns (uint, uint) { + externalDefaultArray = [this.externalView]; + internalDefaultArray = [internalView]; + + return (externalDefaultArray[0](), internalDefaultArray[0]()); + } + + function testPureToDefault() public returns (uint, uint) { + externalDefaultArray = [this.externalPure]; + internalDefaultArray = [internalPure]; + + return (externalDefaultArray[0](), internalDefaultArray[0]()); + } + + function testPureToView() public returns (uint, uint) { + externalViewArray = [this.externalPure]; + internalViewArray = [internalPure]; + + return (externalViewArray[0](), internalViewArray[0]()); + } +} +// ==== +// compileViaYul: also +// ---- +// testViewToDefault() -> 12, 22 +// testPureToDefault() -> 13, 23 +// testPureToView() -> 13, 23 diff --git a/test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol b/test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol new file mode 100644 index 000000000..179eafdc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol @@ -0,0 +1,49 @@ +contract C { + function externalDefault() external returns(uint) { return 11; } + function externalView() external view returns(uint) { return 12; } + function externalPure() external pure returns(uint) { return 13; } + + function internalDefault() internal returns(uint) { return 21; } + function internalView() internal view returns(uint) { return 22; } + function internalPure() internal pure returns(uint) { return 23; } + + function testViewToDefault() public returns (uint, uint) { + function () external returns(uint)[1] memory externalDefaultArray; + function () internal returns(uint)[1] memory internalDefaultArray; + + // This would work if we were assigning to storage rather than memory + externalDefaultArray = [this.externalView]; + internalDefaultArray = [internalView]; + + return (externalDefaultArray[0](), internalDefaultArray[0]()); + } + + function testPureToDefault() public returns (uint, uint) { + function () external returns(uint)[1] memory externalDefaultArray; + function () internal returns(uint)[1] memory internalDefaultArray; + + // This would work if we were assigning to storage rather than memory + externalDefaultArray = [this.externalPure]; + internalDefaultArray = [internalPure]; + + return (externalDefaultArray[0](), internalDefaultArray[0]()); + } + + function testPureToView() public returns (uint, uint) { + function () external returns(uint)[1] memory externalViewArray; + function () internal returns(uint)[1] memory internalViewArray; + + // This would work if we were assigning to storage rather than memory + externalViewArray = [this.externalPure]; + internalViewArray = [internalPure]; + + return (externalViewArray[0](), internalViewArray[0]()); + } +} +// ---- +// TypeError 7407: (760-779): Type function () view external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. +// TypeError 7407: (812-826): Type function () view returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory. +// TypeError 7407: (1230-1249): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. +// TypeError 7407: (1282-1296): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory. +// TypeError 7407: (1688-1707): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. +// TypeError 7407: (1737-1751): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.