Skip to content

Commit 6847d0c

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 6847d0c

4 files changed

Lines changed: 59 additions & 1 deletion

File tree

libsolidity/analysis/TypeChecker.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3175,13 +3175,19 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
31753175
size_t const initialMemberCount = possibleMembers.size();
31763176
if (initialMemberCount > 1 && arguments)
31773177
{
3178-
// do overload resolution
3178+
// Do overload resolution, and filter out non-functions when called with parentheses.
3179+
// This allows library functions like OpenZeppelin's Address.isContract() to work
3180+
// alongside Tron's native address.isContract property.
3181+
// 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();)
31803184
if (
31813185
it->type->category() == Type::Category::Function &&
31823186
!dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*arguments, exprType)
31833187
)
31843188
it = possibleMembers.erase(it);
3189+
else if (it->type->category() != Type::Category::Function)
3190+
it = possibleMembers.erase(it);
31853191
else
31863192
++it;
31873193
}
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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity >=0.8.0;
3+
4+
// Test that accessing a member without parentheses when both a native property
5+
// and a library function exist with the same name results in an ambiguity error.
6+
// Users should use the native property syntax directly without "using for" to avoid this.
7+
library Address {
8+
function isContract(address account) internal view returns (bool) {
9+
return account.code.length > 0;
10+
}
11+
}
12+
13+
contract C {
14+
using Address for address;
15+
16+
function check(address a) public view returns (bool) {
17+
// Without parentheses - ambiguous between native property and library function
18+
return a.isContract;
19+
}
20+
}
21+
// ----
22+
// TypeError 6675: (519-531): Member "isContract" not unique after argument-dependent lookup in address.
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)