Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overload resolution for .value()-modified functions #526

Closed
axic opened this issue Apr 29, 2016 · 14 comments · Fixed by #9334
Closed

Overload resolution for .value()-modified functions #526

axic opened this issue Apr 29, 2016 · 14 comments · Fixed by #9334

Comments

@axic
Copy link
Member

axic commented Apr 29, 2016

I have a method, which gets called with value transfer using the <method>.value(<value>)(<args>) semantic. This fails when overloading.

Works:

contract ShapeshiftBot {
  function transfer(string coin, string recipient, bool acceptReturn, address returnAddress) returns (bytes32 myid);
  function transfer(string coin, string recipient, bool acceptReturn) returns (bytes32 myid);
  function transfer(string coin, string recipient) returns (bytes32 myid);
}

contract ShapeshiftBotLookup {
  function getAddress() returns (address _addr);
}

contract usingShapeshift {
  function shapeshiftInstance() returns (ShapeshiftBot) {
    ShapeshiftBotLookup lookup = ShapeshiftBotLookup(0x0);
    return ShapeshiftBot(lookup.getAddress());
  }

  function shapeshiftTransfer(uint value, string coin, string recipient, bool acceptReturn, address returnAddress) internal returns (bytes32 myid) {
    return shapeshiftInstance().transfer(coin, recipient, acceptReturn, returnAddress);
  }

  function shapeshiftTransfer(uint value, string coin, string recipient, bool acceptReturn) internal returns (bytes32 myid) {
    return shapeshiftInstance().transfer(coin, recipient, acceptReturn);
  }

  function shapeshiftTransfer(uint value, string coin, string recipient) internal returns (bytes32 myid) {
    return shapeshiftInstance().transfer(coin, recipient);
  }
}

Fails when adding .value() in usingShapeshift:

Error: Member "transfer" not unique after argument-dependent lookup in contract ShapeshiftBot
    return shapeshiftInstance().transfer.value(value)(coin, recipient, acceptReturn, returnAddress);
           ^---------------------------^
@axic
Copy link
Member Author

axic commented Apr 29, 2016

A reduced example for clarity.

Working:

contract ShapeshiftBot {
  function transfer(string a, string b);
  function transfer(string a);
}

contract usingShapeshift {
  ShapeshiftBot bot = ShapeshiftBot(0x0);

  function transfer(string a, string b) {
    bot.transfer(a, b);
  }

  function transfer(string a) {
    bot.transfer(a);
  }
}

Failing:

contract ShapeshiftBot {
  function transfer(string a, string b);
  function transfer(string a);
}

contract usingShapeshift {
  ShapeshiftBot bot = ShapeshiftBot(0x0);

  function transfer(string a, string b) {
    bot.transfer.value(1)(a, b);
  }

  function transfer(string a) {
    bot.transfer.value(1)(a);
  }
}

Working with .value(), but no overloading:

contract ShapeshiftBot {
  function transfer(string a);
}

contract usingShapeshift {
  ShapeshiftBot bot = ShapeshiftBot(0x0);

  function transfer(string a) {
    bot.transfer.value(1)(a);
  }
}

The problem appears with .gas() too.

@chriseth
Copy link
Contributor

chriseth commented May 2, 2016

Yes, we have a pending story for that. Sorry for the inconvenience. It is a bit hard to fix because you can only do overload resolution when you know the arguments but you need to know the type of the function so that you can tell whether it has a .vaule function or not.

@axic
Copy link
Member Author

axic commented May 2, 2016

Not fully aware of the internals of the compiler, but would an explicit flag (i.e. #500 and its appropriate keywords on functions) help?

Which cases value is not accepted today? constant, private, internal ?

@chriseth chriseth changed the title .value() breaks overloaded method lookup Overload resolution for .value()-modified functions Aug 5, 2016
@chriseth chriseth added this to the 99-nice-but-how milestone Aug 5, 2016
@axic
Copy link
Member Author

axic commented Sep 6, 2016

@chriseth with payable, is this now easier to fix?

@chriseth
Copy link
Contributor

chriseth commented Sep 7, 2016

Not really, the thing is that the argumentTypes AST annotation has to be pulled through several AST nodes, and this will not even solve cases like var x = f.value; x(2 ether)(1,2);

@3esmit
Copy link
Contributor

3esmit commented Dec 10, 2018

This affects me in 0.5.1.

browser/PaymentChannel.sol:35:9: TypeError: Member "newChannel" not unique after argument-dependent lookup in contract ChannelFactory.
        _factory.newChannel.value(_amount)(_recipient, _duration);   
        ^-----------------^
contract ChannelFactory {
    function newChannel(address payable _recipient, uint256 _duration) external payable returns(PaymentChannel _channel){
        _channel = (new PaymentChannelETH).value(msg.value)(_recipient, _duration);
    }
    function newChannel(Token _token, address _recipient, uint256 _duration) external returns(PaymentChannel _channel) {
        _channel = new PaymentChannelERC20(_token, _recipient, _duration);
        _token.transferFrom(msg.sender, address(_channel), _token.allowance(msg.sender, address(this)));
    }
}

contract Account {
    function openChannel(ChannelFactory _factory, Token _token, address payable _recipient, uint256 _duration, uint256 _amount) external {
        _token.approve(address(_factory), _amount);
        _factory.newChannel(_token, _recipient, _duration);   
    }
    function openChannel(ChannelFactory _factory,  address payable _recipient, uint256 _duration, uint256 _amount) external {
        _factory.newChannel.value(_amount)(_recipient, _duration);   
    }
}

See example failing to compile built in remix.ethereum.org: https://gist.github.com/3esmit/fce237c187d1f54eacb2c434553b2dd2

While this is not fixed, compilation should fail when one function being overloaded have payable modifier, otherwise would not be possible to use the payable modifier.

@3esmit
Copy link
Contributor

3esmit commented Dec 10, 2018

As this also affects .gas, overloading of functions should not even be supported.

I see that is a hard problem to solve.
I see some possible solutions that don't look nice, but might do it:

  • For payable with gas setting: _factory.newChannel(_recipient, _duration)(_amount, _gas);
  • For payable with only value: _factory.newChannel(_recipient, _duration)(_amount);
  • For non payable: _factory.newChannel(_token, _recipient, _duration)(_gas);

I think the best way would be rework how one contract call/send to other, and using the interface call to generate the abi that would be used in the call/send.

I imagine:
Non payable auto-gas: this.call(_factory.newChannel(_token, _recipient, _duration))
Non payable manual-gas: this.call(_factory.newChannel(_token, _recipient, _duration), _gas)
Payable with auto-gas: this.send(_factory.newChannel(_recipient, _duration), _value)
Payable with manual-gas: this.send(_factory.newChannel(_recipient, _duration), _value, _gas)

@derekchiang
Copy link

Is there currently no way around this issue? So basically there's no way to call an overloaded payable function with some value?

@3esmit
Copy link
Contributor

3esmit commented Mar 3, 2019

You can use address(otherContract).call.value(amount)(abi.encodeWithSignature("method(uint256,bytes)", param1, param2));

@chriseth
Copy link
Contributor

chriseth commented Mar 4, 2019

Or you perform a type conversion to a contract that only has one of the two overloads.

gskapka added a commit to provable-things/ethereum-api that referenced this issue Apr 16, 2019
...and has to switch to lower level calls w/ a sprinkling of assembly to
get at the return values in order to deal with overloaded interfaces.

Pertinent issue here:
ethereum/solidity#526 (comment)
gskapka added a commit to provable-things/ethereum-api that referenced this issue Apr 17, 2019
...and has to switch to lower level calls w/ a sprinkling of assembly to
get at the return values in order to deal with overloaded interfaces.

Pertinent issue here:
ethereum/solidity#526 (comment)
@JonahGroendal
Copy link

Could the syntax be changed? IMO .value()() seems out of place in Solidity anyway since functions aren't first-class objects.

@chriseth
Copy link
Contributor

chriseth commented May 7, 2019

If you have a good idea about how to change the syntax, we are all ears! Note that functions actually are first-class objects in Solidity: https://solidity.readthedocs.io/en/v0.5.8/types.html#function-types

gskapka added a commit to provable-things/ethereum-api that referenced this issue Jun 10, 2019
...and has to switch to lower level calls w/ a sprinkling of assembly to
get at the return values in order to deal with overloaded interfaces.

Pertinent issue here:
ethereum/solidity#526 (comment)
D-Nice pushed a commit to provable-things/ethereum-api that referenced this issue Jul 2, 2019
...and has to switch to lower level calls w/ a sprinkling of assembly to
get at the return values in order to deal with overloaded interfaces.

Pertinent issue here:
ethereum/solidity#526 (comment)
D-Nice pushed a commit to provable-things/ethereum-api that referenced this issue Jul 3, 2019
...and has to switch to lower level calls w/ a sprinkling of assembly to
get at the return values in order to deal with overloaded interfaces.

Pertinent issue here:
ethereum/solidity#526 (comment)
@Marenz
Copy link
Contributor

Marenz commented Jan 23, 2020

This issue can be re-visited now that #8177 is almost merged

@chriseth
Copy link
Contributor

It will be almost the same problem. But we should add a test, actually!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants