Open Amxx opened 3 years ago
The error message is of course bad because you cannot override the functions.
I'm fine with allowing it, but then without a warning.
I also think that such functions should be allowed and without a warning. If private
functions cannot even be referenced from inherited contracts, they should not clash.
We should be careful if non-private can be changed to private in inheritance.
Looks like it can't:
contract C {
function f() internal virtual {}
}
contract D is C {
function f() private override {}
}
Error: Overriding function visibility differs.
--> test.sol:6:5:
|
6 | function f() private override {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Overridden function is here:
--> test.sol:2:5:
|
2 | function f() internal virtual {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Also:
contract C {
function x() external virtual returns (uint) {}
function y() public virtual returns (uint) {}
function z() internal virtual returns (uint) {}
}
contract D is C {
uint private override x;
uint private override y;
uint private override z;
}
Error: Identifier already declared.
--> test.sol:9:5:
|
9 | uint private override y;
| ^^^^^^^^^^^^^^^^^^^^^^^
Note: The previous declaration is here:
--> test.sol:3:5:
|
3 | function y() public virtual returns (uint) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Identifier already declared.
--> test.sol:10:5:
|
10 | uint private override z;
| ^^^^^^^^^^^^^^^^^^^^^^^
Note: The previous declaration is here:
--> test.sol:4:5:
|
4 | function z() internal virtual returns (uint) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Override can only be used with public state variables.
--> test.sol:8:5:
|
8 | uint private override x;
| ^^^^^^^^^^^^^^^^^^^^^^^
Error: Override can only be used with public state variables.
--> test.sol:9:5:
|
9 | uint private override y;
| ^^^^^^^^^^^^^^^^^^^^^^^
Error: Override can only be used with public state variables.
--> test.sol:10:5:
|
10 | uint private override z;
| ^^^^^^^^^^^^^^^^^^^^^^^
Shadowing makes no sense considering inaccessible functions (i.e. private), so in essence allowing this makes sense.
However it definitely opens up the possibility for crafting/hiding confusing code, given contracts can have many internal and private functions, combining that with inheritance sometimes it is really hard to decipher what is the actual code used for the contract. The virtual/override specifiers help with that.
While I do not think it is a good idea to try "saving people from mistakes" with weird rules like this, but I would put the question on the table: should this change be made in a breaking release, just in order to trigger a more thorough review from people using the language? Users tend to review their contracts more carefully when they bump a breaking release.
@Amxx can you provide more cases that would benefit from this feature? I actually think @axic 's point is very valid here.
My use-case is a bit complex, and one might argue that it comes from bad design on our end.
OpenZeppelin provides a lot of functionalities in its different contracts and library. We also offer a framework for upgradeable contracts. This framework includes solidity code, and javascript tooling to help use deploy and update their contracts.
This tooling, in particular, checks dangerous behaviour, such a selfdestruct (see parity bug) and delegate calls (which can lead to self destruct). We want to protect users from using these in upgradeable contracts.
We have an Address
library, which includes functions such as isContract
, sendValue
, functionStaticCall
and functionDelegateCall
. While most are safe, this last one can be dangerous. In our plugin we don't have the ability to filter that effectively, and whenever a user import the library, we do check the entire library for potential vulnerabilities.
To limit false positives, we decided to:
functionDelegateCall
for the Address library (in the upgradeable repo)The result is that we have this private function in some of our upgradeable contracts:
Now if I want to build a UUPSUpgradeable contract (inherits from ERC1967UpgradeUpgradeable) with the multicall feature ... I get my private function defined twice.
I'm sure we can rework our transpilling workflow, and the way our checks are performed ... but you asked me for my usecase so here it is.
I don't think the concern that @axic shared is a significant one. Private functions are the easiest to trace back to their definition because it just requires a search in the contract where it is used.
Had the same issue in the mid 2020: https://github.com/ethereum/solidity/issues/9610
Now private
works as hypothetical final
/nonoverride
, but I wish private
methods produce no side effects outside of the contracts/libraries.
We discussed this today on the call. Overall there were some concerns about safety of allowing this but no one was strongly against so we're fine with changing the behavior. This is however very low priority right now because we have other important tasks on the roadmap. It may take some time before we get to implementing this ourselves, but we'd accept a PR if someone badly wanted to have it right now.
We can use GitCoin to boost this change :) Moreover, this could attract more contributors to the repo.
Sure, why not.
Hello @Amxx, I thought of an approach but not sure if this solves the exact problem. Please have a look:
contract A {
uint x;
function __myPrivateFunction(uint _num) private { // some state change
x=_num;
}
function callA() public {
__myPrivateFunction(1);
}
}
contract B {
uint x;
function __myPrivateFunction(uint _num) private { // some state change
x=_num;
}
function callB() public {
__myPrivateFunction(2);
}
}
Now since we are not able to inherit A and B together, we creat contracts that delegate calls to A and B:
contract A_caller{
uint public x_A;
A contractA;
function set_contractA(address _A) external {
contractA = A(_A);
}
function call_contractA() public {
(bool success,) = address(contractA).delegatecall(abi.encodeWithSignature("callA()"));
require(success);
}
}
contract B_caller{
uint public x_B;
B contractB;
function set_contractB(address _B) external {
contractB = B(_B);
}
function call_contractB() public {
(bool success,) = address(contractB).delegatecall(abi.encodeWithSignature("callB()"));
require(success);
}
}
And inherit these contracts together
contract AB is A_caller, B_caller{ }
This will allow us to access all the functions of A and B using the state of A_caller and B_caller.
Best regards!
In that you have to worry about storage compatibility of A and B, you can't call internal functions, you possibly have to do many checks...
Basically, you are arguing that doing something like a diamond proxy is a solution to the private function conflict.
While it is technically true, I really don't think it's a reasonable/acceptable solution.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
Hi everyone! This issue has been automatically closed due to inactivity. If you think this issue is still relevant in the latest Solidity version and you have something to contribute, feel free to reopen. However, unless the issue is a concrete proposal that can be implemented, we recommend starting a language discussion on the forum instead.
This should not have been close. The issue remains present and IMO a fix would be welcome.
@cameel can you reopen it ?
Sure. We're in the process of closing off stale issues that we're unlikely to ever to implement ourselves but since this one is pretty much waiting for an external contribution (and even has a bounty on it), I guess keeping it open is fine.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
This issue is still relevant and is addressed by a pending PR. It should bot because for stale.
when you inherit contracts, their functions and state variables are available to the derived contract. This includes functions marked as private. While you might expect that private functions in contract A and contract B would not be accessible from contract AB, Solidity allows access to these functions when they are inherited.
This behavior is by design in Solidity. When you inherit from contracts A and B in contract AB, all the functions and state variables from A and B, including the private ones, are part of contract AB. This allows the derived contract to access and use those functions as if they were its own.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function __myPrivateFunction() private {
// Does something useful
}
function callA() public {
__myPrivateFunction();
}
}
contract B {
function __myPrivateFunction() private {
// Does something useful
}
function callB() public {
__myPrivateFunction();
}
}
contract AB is A, B {
function callAB() public {
__myPrivateFunction(); // This works because it's inherited
callA(); // This works too
callB(); // This works too
}
}
In this code, AB inherits from both A and B, and it can access and call the private functions from both of them. This behavior follows Solidity's inheritance model, where all functions from parent contracts are included in the derived contract.
If you want to prevent access to the private functions from the derived contract, you should consider changing the access level of those functions to internal or external, depending on your intended use case and visibility requirements.
This allows the derived contract to access and use those functions as if they were its own
Private functions (and variable) are clearly not accessible from the derived contract like public or internal ones are. Sure, they make it into the final bytecode, but there is no reason to not "namespace" them.
A.__myPrivateFunction
access really is limited to A
. It is part of AB's bytecode, and it can be called indirectly through callA
... but as far as AB
is concerned, it could have any name, that would not matter. Therefore, I strongly believe that it should be considered as something in A that should not clash with anything in B or AB.
function callAB() public {
__myPrivateFunction(); // This works because it's inherited
callA(); // This works too
callB(); // This works too
}
This does NOT work. Unlike internal functions, private functions are NOT accessible in child contract.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
Again, this issue is still relevant and is addressed by a pending PR. It should not be marked as stale.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
Was it resolved or not?
no
@Amxx I think the problem is that you are trying to inherit both the contract in AB which means bringing functions of contract A and B in context of AB which found similar name style and visibility causing name conflict and error
which means bringing functions of contract A and B in context of AB
Functions are private within A and B. Private functions are not available in child contract, so I would argue they are not brought in the context of AB
@Amxx Okay yeah but i think is what is happening here , is that child contract cannot call the private function but still able to see the private functions as they are visible everywhere but can be called only inside that contract scope.
Imagine inheriting your smart contract from two different libraries, which are coincidentally using same name for different private functions. But why this parent’s private implementation should be an issue for you? Private members should have 0 exposure to the inherited contracts.
When AB tries to resolve the private function calls within the context of its parents, the compiler needs to distinguish between the implementations in A and B. Since the private function is not inherited, it’s not straightforward for the compiler to know which private function from the parent contracts should be considered when calls to these functions are made.
@HiteshMittal07 you should not be able to refer to private members of your parents. That’s what internal
is made for. I am really struggling why this concept is so hard to understand.
This issue has been marked as stale due to inactivity for the last 90 days. It will be automatically closed in 7 days.
Still relevant
Let consider the following code:
What I am expecting
implementation of
__myPrivateFunction
are private, and they only make sense in the context of the contract that they are part of (A
andB
). They are not accessible fromAB
, so it is not like if a call from a function withinAB
wouldn't be resolvable.IMO, this should compile, possibly with a warning, but not with an error.
What happens
I get an error:
TypeError: Derived contract must override function "__myPrivateFunction". Two or more base classes define function with same name and parameter types.
Question/Request
Is that behaviour wanted? needed? Whould it be possible to accept this kind of ghost-conflicts?