Skip to content

Commit 0b189a4

Browse files
committed
fix: disambiguate library functions vs native properties in member access
When both a native property (e.g., address.isContract) and a library function with the same name are available via 'using for', disambiguate based on call syntax: - With parentheses: prefer library function (e.g., a.isContract()) - Without parentheses: prefer native property (e.g., a.isContract) This allows OpenZeppelin's Address.isContract() library function to work alongside Tron's native address.isContract property without conflict. The fix mirrors the identifier resolution pattern in visit(Identifier) where VariableDeclarations are preferred without parentheses (lines 3693-3745 in TypeChecker.cpp). Fixes the error: 'Member "isContract" not unique after argument-dependent lookup in address' when using OpenZeppelin contracts on Tron.
1 parent 3e2818d commit 0b189a4

4 files changed

Lines changed: 74 additions & 8 deletions

File tree

libsolidity/analysis/TypeChecker.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3173,17 +3173,33 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
31733173
auto const& arguments = annotation.arguments;
31743174
MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName);
31753175
size_t const initialMemberCount = possibleMembers.size();
3176-
if (initialMemberCount > 1 && arguments)
3176+
if (initialMemberCount > 1)
31773177
{
3178-
// do overload resolution
3178+
// Disambiguate when both native properties and library functions share the same name.
3179+
// With parentheses: prefer functions (do overload resolution, filter non-functions).
3180+
// Without parentheses: prefer non-functions (native properties).
3181+
// This mirrors the identifier resolution pattern at lines 3693-3745 where
3182+
// VariableDeclarations are preferred without parentheses.
31793183
for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
3180-
if (
3181-
it->type->category() == Type::Category::Function &&
3182-
!dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*arguments, exprType)
3183-
)
3184-
it = possibleMembers.erase(it);
3184+
{
3185+
bool const isFunction = it->type->category() == Type::Category::Function;
3186+
if (arguments)
3187+
{
3188+
// With parentheses: remove non-functions, and functions that can't take the arguments
3189+
if (!isFunction || !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*arguments, exprType))
3190+
it = possibleMembers.erase(it);
3191+
else
3192+
++it;
3193+
}
31853194
else
3186-
++it;
3195+
{
3196+
// Without parentheses: remove functions
3197+
if (isFunction)
3198+
it = possibleMembers.erase(it);
3199+
else
3200+
++it;
3201+
}
3202+
}
31873203
}
31883204

31893205
annotation.isConstant = false;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity >=0.8.0;
3+
4+
// Test that library function is preferred over native property when called with parentheses.
5+
// This allows OpenZeppelin's Address.isContract() to work alongside Tron's native address.isContract property.
6+
library Address {
7+
function isContract(address account) internal view returns (bool) {
8+
return account.code.length > 0;
9+
}
10+
}
11+
12+
contract C {
13+
using Address for address;
14+
15+
function check(address a) public view returns (bool) {
16+
// With parentheses - should use library function
17+
return a.isContract();
18+
}
19+
}
20+
// ----
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity >=0.8.0;
3+
4+
// Test that native property is preferred over library function when accessed without parentheses.
5+
// This allows Tron's native address.isContract property to work alongside library functions.
6+
library Address {
7+
function isContract(address account) internal view returns (bool) {
8+
return account.code.length > 0;
9+
}
10+
}
11+
12+
contract C {
13+
using Address for address;
14+
15+
function check(address a) public view returns (bool) {
16+
// Without parentheses - should use native property
17+
return a.isContract;
18+
}
19+
}
20+
// ----
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity >=0.8.0;
3+
4+
// Test Tron's native address.isContract property works without library.
5+
contract C {
6+
function check(address a) public view returns (bool) {
7+
return a.isContract;
8+
}
9+
}
10+
// ----

0 commit comments

Comments
 (0)