Solidity: Multi-file projects that do not all use ABIEncoderV2

Created on 24 Feb 2020  路  12Comments  路  Source: ethereum/solidity

Description

I tried truffle test, it returns pointless error. Then, I used remix to compile test contract, it returns : InternalCompilerError: Unknown dynamically sized type: struct Stack.Item memory. After that, I added pragma experimental ABIEncoderV2; into test contract. It compiled on remix perfectly but truffle test still does not work

Environment

  • Compiler version: 0.6.0
  • Target EVM version (as per compiler settings): byzantium
  • Framework/IDE (e.g. Truffle or Remix): Truffle
  • EVM execution environment / backend / blockchain client: GETH/Quorum
  • Operating system: Ubuntu 16.04 LTS

Steps to Reproduce

My main contract :

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;


contract Stack{

    struct Item{
         string  name;
         uint256 quantity;
         bool isSolid;
    }

    mapping(address => Item[]) userItemsMap;


    function addItem(string memory _name, uint256 _quantity, bool _isSolid) public {
        userItemsMap[msg.sender].push(Item({
        name:_name,
        quantity:_quantity,
        isSolid :_isSolid
            }));
    }

    function getItems() public view returns(Item[] memory){
        return userItemsMap[msg.sender];
    }

    function getLastItem() public view returns(Item memory){
        Item[] memory userItems = userItemsMap[msg.sender];
        require(userItems.length>0);
        return userItems[userItems.length - 1];
    }



}

And my test contract,

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Stack.sol";

contract  TestStackContract{

    function testAddingItem() public {

        Stack stackInstance =  Stack(DeployedAddresses.Stack());

        stackInstance.addItem("Apple",125,true);

        Stack.Item memory lastItem = stackInstance.getLastItem();

        Assert.equal(lastItem.name, "Apple", "Name should be Apple");

    }

}

When I run truffle test it returns this :

Compiling your contracts...
===========================
> Compiling ./contracts/Stack.sol
> Compiling ./test/TestBiddingContract.sol
> Compiling ./test/TestStack.sol

InternalCompilerError:

Compilation failed. See above.
Truffle v5.1.13 (core: 5.1.13)
Node v8.17.0

it is another issue on Truffle that I already asked. Message of its error on remix is that : InternalCompilerError: Unknown dynamically sized type: struct Stack.Item memory

bug should report better error

Most helpful comment

I suspect this actually is a bug in the compiler. Note that the old abi encoder is used in one file while the new abi encoder is used in the other file. The one that has the old encoder is using a new-encoder-feature during decoding. So to reproduce it, you need to use at least two source files.

All 12 comments

The above code compiles and works in Remix. Leaving for the truffle team to look into. Reopen if confirmed issue in Solidity compiler.
Screenshot 2020-02-25 at 10 50 53

I suspect this actually is a bug in the compiler. Note that the old abi encoder is used in one file while the new abi encoder is used in the other file. The one that has the old encoder is using a new-encoder-feature during decoding. So to reproduce it, you need to use at least two source files.

Without truffle and removing the Assert line it is not reproducible. Maybe it was fixed?

Can still reproduce it:

1_Storage.sol:

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
contract Stack{
    struct Item{ uint x; }
    function getLastItem() public view returns(Item memory){
    }
}

2_owner.sol:

pragma solidity ^0.6.0;
import "./1_Storage.sol";
contract  TestStackContract{
    function testAddingItem() public {
        Stack(0x00).getLastItem();
    }
}

It was not reproducible for me, maybe because I placed them into a single file? (Didn't use two files.)

Yes, I think the bug is that you are using a contract that uses ABIEncoderV2 from a file that does not have it activated.

Oh you've commented that earlier too.

Yes, I think the bug is that you are using a contract that uses ABIEncoderV2 from a file that does not have it activated.

I got to this issue helping one or our users debug an ICE, and that was the reason. I think this is pretty common, and really really hard to debug.

A compilation error explaining that importing a file that uses ABIEncoderV2 from one that doesn't is forbidden would be ideal.

@alcuadrado I completely agree. #9835 does exactly what you suggest. The fix itself is simple but there's a small complication that needs to be resolved first. There's one case that did compile fine before (even though it shouldn't have) and now results in a compilation error. It happens when you have a modifier defined in an ABIv1 context but only ever use it in context where ABIEncoderV2 is enabled. Seems far fetched but turns out that the Colony contract (which is one of the contracts we're using in CI to make sure we don't break external projects) does just that and now won't compile. I need to report it to them and also probably patch it in our clone of their repo (unless they fix it right away).

I've been busy working on the binaries and did not have time to deal with this so my fix unfortunately did not make it into 0.7.2 (released yesterday) but you can expect it in 0.7.3.

For reference, here's an ABI encoder sandwich, based on Colony code, that reproduces the problem with the modifier:

IColonyNetwork.sol

pragma experimental ABIEncoderV2;

contract ColonyNetworkDataTypes {
    struct Skill {
        bool globalSkill;
    }
}

contract IColonyNetwork is ColonyNetworkDataTypes {
    function getSkill() public view returns (Skill memory skill) {}
}

ColonyStorage.sol

import "./IColonyNetwork.sol";

contract ColonyStorage is ColonyNetworkDataTypes {
    modifier validGlobalSkill() {
        IColonyNetwork(0x00).getSkill();
        _;
    }
}

Colony.sol

pragma experimental ABIEncoderV2;

import "./ColonyStorage.sol";

contract Colony is ColonyStorage {
    function emitSkillReputationPenalty(uint256 _skillId, address _user, int256 _amount)
        public
        validGlobalSkill()
    {}
}

If you compile it, it does not fail with Internal Compiler Error.

Turns out this is not just a matter of ICE covering a compiler error. There are multiple cases where code that should compile doesn't and I keep finding new ones.

Using a modifier that calls a V2 function

a.sol:

pragma experimental ABIEncoderV2;

struct Data {
    bool flag;
}

contract A {
    function get() public view returns (Data memory) {}
}

b.sol:

pragma experimental ABIEncoderV2;

import "./a.sol";

contract B {
    modifier validate() {
        A(0x00).get();
        _;
    }
}

c.sol:

import "./b.sol";

contract C is B {
    function foo()
        public
        validate()
    {}
}
Internal compiler error during compilation:
/solidity/libsolidity/codegen/CompilerUtils.cpp(1440): Throw in function unsigned int solidity::frontend::CompilerUtils::loadFromMemoryHelper(const solidity::frontend::Type&, bool, bool)
Dynamic exception type: boost::wrapexcept<solidity::langutil::InternalCompilerError>
std::exception::what:
[solidity::util::tag_comment*] =

Simply inheriting from a V2 contract

a.sol:

pragma experimental ABIEncoderV2;

contract C {
    struct Item {
        uint x;
    }

    function get() external view returns(Item memory) {}
}

b.sol:

import "./a.sol";

contract D is C {}
Unimplemented feature:
/solidity/libsolidity/codegen/CompilerUtils.cpp(420): Throw in function void solidity::frontend::CompilerUtils::encodeToMemory(const TypePointers&, const TypePointers&, bool, bool, bool)
Dynamic exception type: boost::wrapexcept<solidity::langutil::UnimplementedFeatureError>
std::exception::what: Encoding type "struct C.Item memory" not yet implemented.
[solidity::util::tag_comment*] = Encoding type "struct C.Item memory" not yet implemented.

a.sol compiles just fine on its own.

In order to get my fix into the upcoming 0.7.3 I've split off the two cases above into separate issues: #9969, #9970. I wanted to fix them in #9835 but they have different underlying causes so there's no point in holding one back because of the other.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hiqua picture hiqua  路  4Comments

chriseth picture chriseth  路  4Comments

Dohtar1337 picture Dohtar1337  路  4Comments

chriseth picture chriseth  路  3Comments

madvas picture madvas  路  3Comments