mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #13855 from ethereum/allow_external_library_functions_attached
Allow library external functions to be attached with using for
This commit is contained in:
commit
558c39dad6
@ -7,6 +7,7 @@ Compiler Features:
|
|||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* TypeChecker: Also allow external library functions in ``using for``.
|
||||||
|
|
||||||
|
|
||||||
### 0.8.18 (2023-02-01)
|
### 0.8.18 (2023-02-01)
|
||||||
|
@ -195,16 +195,23 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Declaration const*> NameAndTypeResolver::pathFromCurrentScopeWithAllDeclarations(std::vector<ASTString> const& _path) const
|
std::vector<Declaration const*> NameAndTypeResolver::pathFromCurrentScopeWithAllDeclarations(
|
||||||
|
std::vector<ASTString> const& _path,
|
||||||
|
bool _includeInvisibles
|
||||||
|
) const
|
||||||
{
|
{
|
||||||
solAssert(!_path.empty(), "");
|
solAssert(!_path.empty(), "");
|
||||||
vector<Declaration const*> pathDeclarations;
|
vector<Declaration const*> pathDeclarations;
|
||||||
|
|
||||||
ResolvingSettings settings;
|
ResolvingSettings settings;
|
||||||
settings.recursive = true;
|
settings.recursive = true;
|
||||||
settings.alsoInvisible = false;
|
settings.alsoInvisible = _includeInvisibles;
|
||||||
settings.onlyVisibleAsUnqualifiedNames = true;
|
settings.onlyVisibleAsUnqualifiedNames = true;
|
||||||
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), std::move(settings));
|
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), settings);
|
||||||
|
|
||||||
|
// inside the loop, use default settings, except for alsoInvisible
|
||||||
|
settings.recursive = false;
|
||||||
|
settings.onlyVisibleAsUnqualifiedNames = false;
|
||||||
|
|
||||||
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
|
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
|
||||||
{
|
{
|
||||||
@ -213,7 +220,7 @@ std::vector<Declaration const*> NameAndTypeResolver::pathFromCurrentScopeWithAll
|
|||||||
|
|
||||||
pathDeclarations.push_back(candidates.front());
|
pathDeclarations.push_back(candidates.front());
|
||||||
|
|
||||||
candidates = m_scopes.at(candidates.front())->resolveName(_path[i]);
|
candidates = m_scopes.at(candidates.front())->resolveName(_path[i], settings);
|
||||||
}
|
}
|
||||||
if (candidates.size() == 1)
|
if (candidates.size() == 1)
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@ public:
|
|||||||
/// Resolves a path starting from the "current" scope, but also searches parent scopes.
|
/// Resolves a path starting from the "current" scope, but also searches parent scopes.
|
||||||
/// Should only be called during the initial resolving phase.
|
/// Should only be called during the initial resolving phase.
|
||||||
/// @note Returns an empty vector if any component in the path was non-unique or not found. Otherwise, all declarations along the path are returned.
|
/// @note Returns an empty vector if any component in the path was non-unique or not found. Otherwise, all declarations along the path are returned.
|
||||||
std::vector<Declaration const*> pathFromCurrentScopeWithAllDeclarations(std::vector<ASTString> const& _path) const;
|
std::vector<Declaration const*> pathFromCurrentScopeWithAllDeclarations(std::vector<ASTString> const& _path, bool _includeInvisibles = false) const;
|
||||||
|
|
||||||
/// Generate and store warnings about declarations with the same name.
|
/// Generate and store warnings about declarations with the same name.
|
||||||
void warnHomonymDeclarations() const;
|
void warnHomonymDeclarations() const;
|
||||||
|
@ -173,6 +173,7 @@ void ReferencesResolver::endVisit(ModifierDefinition const&)
|
|||||||
|
|
||||||
void ReferencesResolver::endVisit(IdentifierPath const& _path)
|
void ReferencesResolver::endVisit(IdentifierPath const& _path)
|
||||||
{
|
{
|
||||||
|
// Note that library/functions names in "using {} for" directive are resolved separately in visit(UsingForDirective)
|
||||||
std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
|
std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
|
||||||
if (declarations.empty())
|
if (declarations.empty())
|
||||||
{
|
{
|
||||||
@ -184,6 +185,38 @@ void ReferencesResolver::endVisit(IdentifierPath const& _path)
|
|||||||
_path.annotation().pathDeclarations = std::move(declarations);
|
_path.annotation().pathDeclarations = std::move(declarations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReferencesResolver::visit(UsingForDirective const& _usingFor)
|
||||||
|
{
|
||||||
|
for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
|
||||||
|
{
|
||||||
|
// _includeInvisibles is enabled here because external library functions are marked invisible.
|
||||||
|
// As unintended side-effects other invisible names (eg.: super, this) may be returned as well.
|
||||||
|
// DeclarationTypeChecker should detect and report such situations.
|
||||||
|
vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(path->path(), true /* _includeInvisibles */);
|
||||||
|
if (declarations.empty())
|
||||||
|
{
|
||||||
|
string libraryOrFunctionNameErrorMessage =
|
||||||
|
_usingFor.usesBraces() ?
|
||||||
|
"Identifier is not a function name or not unique." :
|
||||||
|
"Identifier is not a library name.";
|
||||||
|
m_errorReporter.fatalDeclarationError(
|
||||||
|
9589_error,
|
||||||
|
path->location(),
|
||||||
|
libraryOrFunctionNameErrorMessage
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
path->annotation().referencedDeclaration = declarations.back();
|
||||||
|
path->annotation().pathDeclarations = std::move(declarations);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_usingFor.typeName())
|
||||||
|
_usingFor.typeName()->accept(*this);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||||
{
|
{
|
||||||
m_yulAnnotation = &_inlineAssembly.annotation();
|
m_yulAnnotation = &_inlineAssembly.annotation();
|
||||||
|
@ -84,6 +84,7 @@ private:
|
|||||||
void endVisit(IdentifierPath const& _path) override;
|
void endVisit(IdentifierPath const& _path) override;
|
||||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
bool visit(Return const& _return) override;
|
bool visit(Return const& _return) override;
|
||||||
|
bool visit(UsingForDirective const& _usingFor) override;
|
||||||
|
|
||||||
void operator()(yul::FunctionDefinition const& _function) override;
|
void operator()(yul::FunctionDefinition const& _function) override;
|
||||||
void operator()(yul::Identifier const& _identifier) override;
|
void operator()(yul::Identifier const& _identifier) override;
|
||||||
|
@ -3826,7 +3826,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
FunctionDefinition const& functionDefinition =
|
FunctionDefinition const& functionDefinition =
|
||||||
dynamic_cast<FunctionDefinition const&>(*path->annotation().referencedDeclaration);
|
dynamic_cast<FunctionDefinition const&>(*path->annotation().referencedDeclaration);
|
||||||
|
|
||||||
solAssert(functionDefinition.type());
|
FunctionType const* functionType = dynamic_cast<FunctionType const*>(
|
||||||
|
functionDefinition.libraryFunction() ?
|
||||||
|
functionDefinition.typeViaContractName() :
|
||||||
|
functionDefinition.type()
|
||||||
|
);
|
||||||
|
|
||||||
|
solAssert(functionType);
|
||||||
|
|
||||||
if (functionDefinition.parameters().empty())
|
if (functionDefinition.parameters().empty())
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
@ -3864,21 +3870,25 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType const* functionType = dynamic_cast<FunctionType const&>(*functionDefinition.type()).withBoundFirstArgument();
|
FunctionType const* functionTypeWithBoundFirstArgument = functionType->withBoundFirstArgument();
|
||||||
solAssert(functionType && functionType->selfType(), "");
|
solAssert(functionTypeWithBoundFirstArgument && functionTypeWithBoundFirstArgument->selfType(), "");
|
||||||
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
||||||
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
|
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionTypeWithBoundFirstArgument->selfType())
|
||||||
);
|
);
|
||||||
if (!result)
|
if (!result)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
3100_error,
|
3100_error,
|
||||||
path->location(),
|
path->location(),
|
||||||
|
SecondarySourceLocation().append(
|
||||||
|
"Function defined here:",
|
||||||
|
functionDefinition.location()
|
||||||
|
),
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"The function \"{}\" cannot be attached to the type \"{}\" because the type cannot "
|
"The function \"{}\" cannot be attached to the type \"{}\" because the type cannot "
|
||||||
"be implicitly converted to the first argument of the function (\"{}\"){}",
|
"be implicitly converted to the first argument of the function (\"{}\"){}",
|
||||||
joinHumanReadable(path->path(), "."),
|
joinHumanReadable(path->path(), "."),
|
||||||
usingForType->toString(true /* withoutDataLocation */),
|
usingForType->toString(true /* withoutDataLocation */),
|
||||||
functionType->selfType()->humanReadableName(),
|
functionTypeWithBoundFirstArgument->selfType()->humanReadableName(),
|
||||||
result.message().empty() ? "." : ": " + result.message()
|
result.message().empty() ? "." : ": " + result.message()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint a) external pure returns (uint) { return a * 1; }
|
||||||
|
function publicFunction(uint b) public pure returns (uint) { return b * 2; }
|
||||||
|
function internalFunction(uint c) internal pure returns (uint) { return c * 3; }
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using {L.externalFunction} for uint;
|
||||||
|
using {L.publicFunction} for uint;
|
||||||
|
using {L.internalFunction} for uint;
|
||||||
|
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
uint x = 1;
|
||||||
|
return x.externalFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public pure returns (uint) {
|
||||||
|
uint x = 1;
|
||||||
|
return x.publicFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public pure returns (uint) {
|
||||||
|
uint x = 1;
|
||||||
|
return x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// library: L
|
||||||
|
// f() -> 1
|
||||||
|
// g() -> 2
|
||||||
|
// h() -> 3
|
@ -0,0 +1,9 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
externalFunction(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 7576: (120-136): Undeclared identifier. "externalFunction" is not (or not yet) visible at this point.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
using {this.contractFunction} for uint;
|
||||||
|
|
||||||
|
function contractFunction(uint) external view {}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 9589: (24-45): Identifier is not a function name or not unique.
|
@ -9,4 +9,4 @@ contract C {
|
|||||||
using {id} for uint256;
|
using {id} for uint256;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 7920: (145-147): Identifier not found or not unique.
|
// DeclarationError 9589: (145-147): Identifier is not a function name or not unique.
|
||||||
|
@ -7,4 +7,4 @@ function f(int8 storage x) pure returns (int) {
|
|||||||
using {f} for uint8;
|
using {f} for uint8;
|
||||||
using {f} for int;
|
using {f} for int;
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 7920: (132-133): Identifier not found or not unique.
|
// DeclarationError 9589: (132-133): Identifier is not a function name or not unique.
|
||||||
|
@ -6,4 +6,4 @@ function f(uint x, uint y) pure returns (int) {
|
|||||||
}
|
}
|
||||||
using {f} for uint;
|
using {f} for uint;
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 7920: (138-139): Identifier not found or not unique.
|
// DeclarationError 9589: (138-139): Identifier is not a function name or not unique.
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
function baseFunction(uint) public pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract D is C {
|
||||||
|
using {super.baseFunction} for uint;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 9589: (92-110): Identifier is not a function name or not unique.
|
@ -0,0 +1,11 @@
|
|||||||
|
function f(uint x) pure { }
|
||||||
|
|
||||||
|
using f for uint;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function g(uint x) public pure {
|
||||||
|
x.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 4357: (35-36): Library name expected. If you want to attach a function, use '{...}'.
|
@ -0,0 +1,10 @@
|
|||||||
|
function f(uint x) pure { }
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using f for uint;
|
||||||
|
function g(uint x) public pure {
|
||||||
|
x.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 4357: (52-53): Library name expected. If you want to attach a function, use '{...}'.
|
@ -0,0 +1,7 @@
|
|||||||
|
interface I {
|
||||||
|
function g() external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
using {I.g} for uint;
|
||||||
|
// ----
|
||||||
|
// TypeError 4167: (56-59): Only file-level functions and library functions can be attached to a type in a "using" statement
|
@ -0,0 +1,9 @@
|
|||||||
|
interface I {
|
||||||
|
function g() external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using {I.g} for uint;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 4167: (73-76): Only file-level functions and library functions can be attached to a type in a "using" statement
|
17
test/libsolidity/syntaxTests/using/library_at_file_level.sol
Normal file
17
test/libsolidity/syntaxTests/using/library_at_file_level.sol
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
using L for uint;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,19 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
using {L.externalFunction} for uint;
|
||||||
|
using {L.publicFunction} for uint;
|
||||||
|
using {L.internalFunction} for uint;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,17 @@
|
|||||||
|
using {L.externalFunction, L.publicFunction, L.internalFunction} for uint;
|
||||||
|
|
||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6700: (299-319): Libraries cannot call their own functions externally.
|
||||||
|
// TypeError 6700: (329-347): Libraries cannot call their own functions externally.
|
@ -0,0 +1,17 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
using {L.externalFunction, L.publicFunction, L.internalFunction} for uint;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,17 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using {L.externalFunction, L.publicFunction, L.internalFunction} for uint;
|
||||||
|
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,19 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using {L.externalFunction} for uint;
|
||||||
|
using {L.publicFunction} for uint;
|
||||||
|
using {L.internalFunction} for uint;
|
||||||
|
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,17 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
using L for uint;
|
||||||
|
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -11,4 +11,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 7920: (115-123): Identifier not found or not unique.
|
// TypeError 4357: (115-123): Library name expected. If you want to attach a function, use '{...}'.
|
||||||
|
@ -10,4 +10,4 @@ contract C {
|
|||||||
using { id } for uint;
|
using { id } for uint;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 7920: (B:43-45): Identifier not found or not unique.
|
// DeclarationError 9589: (B:43-45): Identifier is not a function name or not unique.
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
contract A {
|
||||||
|
uint public data;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
A a = new A();
|
||||||
|
|
||||||
|
using {a.data} for uint;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 9589: (82-88): Identifier is not a function name or not unique.
|
@ -0,0 +1,3 @@
|
|||||||
|
using L for uint;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 9589: (6-7): Identifier is not a library name.
|
@ -0,0 +1,19 @@
|
|||||||
|
library L {
|
||||||
|
function externalFunction(uint) external pure {}
|
||||||
|
function publicFunction(uint) public pure {}
|
||||||
|
function internalFunction(uint) internal pure {}
|
||||||
|
function privateFunction(uint) private pure {}
|
||||||
|
|
||||||
|
using {externalFunction, publicFunction, internalFunction, privateFunction} for uint;
|
||||||
|
|
||||||
|
function f() public pure {
|
||||||
|
uint x;
|
||||||
|
x.externalFunction();
|
||||||
|
x.publicFunction();
|
||||||
|
x.internalFunction();
|
||||||
|
x.privateFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6700: (365-385): Libraries cannot call their own functions externally.
|
||||||
|
// TypeError 6700: (395-413): Libraries cannot call their own functions externally.
|
Loading…
Reference in New Issue
Block a user