mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8105 from ethereum/functionTypeRefactor
Add a new Function Type referring to FunctionDefinition's without calling context and use it to allow selector lookup.
This commit is contained in:
commit
a4e34b378a
@ -1,6 +1,7 @@
|
|||||||
### 0.6.2 (unreleased)
|
### 0.6.2 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Allow accessing external functions via contract and interface names to obtain their selector.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -1691,6 +1691,16 @@ void TypeChecker::typeCheckFunctionCall(
|
|||||||
solAssert(!!_functionType, "");
|
solAssert(!!_functionType, "");
|
||||||
solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, "");
|
solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, "");
|
||||||
|
|
||||||
|
if (_functionType->kind() == FunctionType::Kind::Declaration)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_functionCall.location(),
|
||||||
|
"Cannot call function via contract name."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check for unsupported use of bare static call
|
// Check for unsupported use of bare static call
|
||||||
if (
|
if (
|
||||||
_functionType->kind() == FunctionType::Kind::BareStaticCall &&
|
_functionType->kind() == FunctionType::Kind::BareStaticCall &&
|
||||||
|
@ -199,7 +199,7 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
|
|||||||
vector<FunctionTypePointer> functions;
|
vector<FunctionTypePointer> functions;
|
||||||
for (FunctionDefinition const* f: contract->definedFunctions())
|
for (FunctionDefinition const* f: contract->definedFunctions())
|
||||||
if (f->isPartOfExternalInterface())
|
if (f->isPartOfExternalInterface())
|
||||||
functions.push_back(TypeProvider::function(*f, false));
|
functions.push_back(TypeProvider::function(*f, FunctionType::Kind::External));
|
||||||
for (VariableDeclaration const* v: contract->stateVariables())
|
for (VariableDeclaration const* v: contract->stateVariables())
|
||||||
if (v->isPartOfExternalInterface())
|
if (v->isPartOfExternalInterface())
|
||||||
functions.push_back(TypeProvider::function(*v));
|
functions.push_back(TypeProvider::function(*v));
|
||||||
@ -311,7 +311,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
|||||||
case Visibility::Private:
|
case Visibility::Private:
|
||||||
case Visibility::Internal:
|
case Visibility::Internal:
|
||||||
case Visibility::Public:
|
case Visibility::Public:
|
||||||
return TypeProvider::function(*this, _internal);
|
return TypeProvider::function(*this, FunctionType::Kind::Internal);
|
||||||
case Visibility::External:
|
case Visibility::External:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -327,7 +327,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
|||||||
return {};
|
return {};
|
||||||
case Visibility::Public:
|
case Visibility::Public:
|
||||||
case Visibility::External:
|
case Visibility::External:
|
||||||
return TypeProvider::function(*this, _internal);
|
return TypeProvider::function(*this, FunctionType::Kind::External);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
|||||||
TypePointer FunctionDefinition::type() const
|
TypePointer FunctionDefinition::type() const
|
||||||
{
|
{
|
||||||
solAssert(visibility() != Visibility::External, "");
|
solAssert(visibility() != Visibility::External, "");
|
||||||
return TypeProvider::function(*this);
|
return TypeProvider::function(*this, FunctionType::Kind::Internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
string FunctionDefinition::externalSignature() const
|
string FunctionDefinition::externalSignature() const
|
||||||
|
@ -414,9 +414,9 @@ ReferenceType const* TypeProvider::withLocation(ReferenceType const* _type, Data
|
|||||||
return static_cast<ReferenceType const*>(instance().m_generalTypes.back().get());
|
return static_cast<ReferenceType const*>(instance().m_generalTypes.back().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType const* TypeProvider::function(FunctionDefinition const& _function, bool _isInternal)
|
FunctionType const* TypeProvider::function(FunctionDefinition const& _function, FunctionType::Kind _kind)
|
||||||
{
|
{
|
||||||
return createAndGet<FunctionType>(_function, _isInternal);
|
return createAndGet<FunctionType>(_function, _kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType const* TypeProvider::function(VariableDeclaration const& _varDecl)
|
FunctionType const* TypeProvider::function(VariableDeclaration const& _varDecl)
|
||||||
|
@ -120,8 +120,8 @@ public:
|
|||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns the internally-facing or externally-facing type of a function.
|
/// @returns the internally-facing or externally-facing type of a function or the type of a function declaration.
|
||||||
static FunctionType const* function(FunctionDefinition const& _function, bool _isInternal = true);
|
static FunctionType const* function(FunctionDefinition const& _function, FunctionType::Kind _kind = FunctionType::Kind::Declaration);
|
||||||
|
|
||||||
/// @returns the accessor function type of a state variable.
|
/// @returns the accessor function type of a state variable.
|
||||||
static FunctionType const* function(VariableDeclaration const& _varDecl);
|
static FunctionType const* function(VariableDeclaration const& _varDecl);
|
||||||
|
@ -362,7 +362,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
|
|||||||
seenFunctions.insert(function);
|
seenFunctions.insert(function);
|
||||||
if (function->parameters().empty())
|
if (function->parameters().empty())
|
||||||
continue;
|
continue;
|
||||||
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
|
FunctionTypePointer fun = FunctionType(*function, FunctionType::Kind::External).asCallableFunction(true, true);
|
||||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||||
members.emplace_back(function->name(), fun, function);
|
members.emplace_back(function->name(), fun, function);
|
||||||
}
|
}
|
||||||
@ -1933,7 +1933,7 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
|
|||||||
if (!function->isVisibleInDerivedContracts() || !function->isImplemented())
|
if (!function->isVisibleInDerivedContracts() || !function->isImplemented())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto functionType = TypeProvider::function(*function, true);
|
auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal);
|
||||||
bool functionWithEqualArgumentsFound = false;
|
bool functionWithEqualArgumentsFound = false;
|
||||||
for (auto const& member: members)
|
for (auto const& member: members)
|
||||||
{
|
{
|
||||||
@ -2465,12 +2465,16 @@ TypePointer TupleType::closestTemporaryType(Type const* _targetType) const
|
|||||||
return TypeProvider::tuple(move(tempComponents));
|
return TypeProvider::tuple(move(tempComponents));
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
|
FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind):
|
||||||
m_kind(_isInternal ? Kind::Internal : Kind::External),
|
m_kind(_kind),
|
||||||
m_stateMutability(_function.stateMutability()),
|
m_stateMutability(_function.stateMutability()),
|
||||||
m_declaration(&_function)
|
m_declaration(&_function)
|
||||||
{
|
{
|
||||||
if (_isInternal && m_stateMutability == StateMutability::Payable)
|
solAssert(
|
||||||
|
_kind == Kind::Internal || _kind == Kind::External || _kind == Kind::Declaration,
|
||||||
|
"Only internal or external function types or function declaration types can be created from function definitions."
|
||||||
|
);
|
||||||
|
if (_kind == Kind::Internal && m_stateMutability == StateMutability::Payable)
|
||||||
m_stateMutability = StateMutability::NonPayable;
|
m_stateMutability = StateMutability::NonPayable;
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
|
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
|
||||||
@ -2689,6 +2693,7 @@ string FunctionType::richIdentifier() const
|
|||||||
string id = "t_function_";
|
string id = "t_function_";
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
{
|
{
|
||||||
|
case Kind::Declaration: id += "declaration"; break;
|
||||||
case Kind::Internal: id += "internal"; break;
|
case Kind::Internal: id += "internal"; break;
|
||||||
case Kind::External: id += "external"; break;
|
case Kind::External: id += "external"; break;
|
||||||
case Kind::DelegateCall: id += "delegatecall"; break;
|
case Kind::DelegateCall: id += "delegatecall"; break;
|
||||||
@ -2755,7 +2760,12 @@ bool FunctionType::operator==(Type const& _other) const
|
|||||||
|
|
||||||
BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return _convertTo.category() == category();
|
if (_convertTo.category() == category())
|
||||||
|
{
|
||||||
|
auto const& convertToType = dynamic_cast<FunctionType const&>(_convertTo);
|
||||||
|
return (m_kind == FunctionType::Kind::Declaration) == (convertToType.kind() == FunctionType::Kind::Declaration);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
@ -2808,7 +2818,18 @@ string FunctionType::canonicalName() const
|
|||||||
|
|
||||||
string FunctionType::toString(bool _short) const
|
string FunctionType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
string name = "function (";
|
string name = "function ";
|
||||||
|
if (m_kind == Kind::Declaration)
|
||||||
|
{
|
||||||
|
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
|
||||||
|
solAssert(functionDefinition, "");
|
||||||
|
auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope());
|
||||||
|
solAssert(contract, "");
|
||||||
|
name += contract->annotation().canonicalName;
|
||||||
|
name += '.';
|
||||||
|
name += functionDefinition->name();
|
||||||
|
}
|
||||||
|
name += '(';
|
||||||
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
|
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
|
||||||
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
|
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
|
||||||
name += ")";
|
name += ")";
|
||||||
@ -2940,6 +2961,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
{
|
{
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
{
|
{
|
||||||
|
case Kind::Declaration:
|
||||||
|
return {{"selector", TypeProvider::fixedBytes(4)}};
|
||||||
case Kind::External:
|
case Kind::External:
|
||||||
case Kind::Creation:
|
case Kind::Creation:
|
||||||
case Kind::BareCall:
|
case Kind::BareCall:
|
||||||
@ -3146,6 +3169,7 @@ string FunctionType::externalSignature() const
|
|||||||
case Kind::External:
|
case Kind::External:
|
||||||
case Kind::DelegateCall:
|
case Kind::DelegateCall:
|
||||||
case Kind::Event:
|
case Kind::Event:
|
||||||
|
case Kind::Declaration:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
solAssert(false, "Invalid function type for requesting external signature.");
|
solAssert(false, "Invalid function type for requesting external signature.");
|
||||||
@ -3210,6 +3234,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
|||||||
|
|
||||||
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
|
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
|
||||||
{
|
{
|
||||||
|
solAssert(m_kind != Kind::Declaration, "");
|
||||||
return TypeProvider::function(
|
return TypeProvider::function(
|
||||||
m_parameterTypes,
|
m_parameterTypes,
|
||||||
m_returnParameterTypes,
|
m_returnParameterTypes,
|
||||||
@ -3387,14 +3412,6 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
|||||||
auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
|
auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
|
||||||
isBase = (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end());
|
isBase = (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end());
|
||||||
}
|
}
|
||||||
if (contract.isLibrary())
|
|
||||||
for (FunctionDefinition const* function: contract.definedFunctions())
|
|
||||||
if (function->isVisibleAsLibraryMember())
|
|
||||||
members.emplace_back(
|
|
||||||
function->name(),
|
|
||||||
FunctionType(*function).asCallableFunction(true),
|
|
||||||
function
|
|
||||||
);
|
|
||||||
if (isBase)
|
if (isBase)
|
||||||
{
|
{
|
||||||
// We are accessing the type of a base contract, so add all public and protected
|
// We are accessing the type of a base contract, so add all public and protected
|
||||||
@ -3404,6 +3421,17 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool inLibrary = contract.isLibrary();
|
||||||
|
for (FunctionDefinition const* function: contract.definedFunctions())
|
||||||
|
if (
|
||||||
|
(inLibrary && function->isVisibleAsLibraryMember()) ||
|
||||||
|
(!inLibrary && function->isPartOfExternalInterface())
|
||||||
|
)
|
||||||
|
members.emplace_back(
|
||||||
|
function->name(),
|
||||||
|
FunctionType(*function).asCallableFunction(inLibrary),
|
||||||
|
function
|
||||||
|
);
|
||||||
for (auto const& stru: contract.definedStructs())
|
for (auto const& stru: contract.definedStructs())
|
||||||
members.emplace_back(stru->name(), stru->type(), stru);
|
members.emplace_back(stru->name(), stru->type(), stru);
|
||||||
for (auto const& enu: contract.definedEnums())
|
for (auto const& enu: contract.definedEnums())
|
||||||
|
@ -1051,11 +1051,16 @@ public:
|
|||||||
ABIEncodeWithSignature,
|
ABIEncodeWithSignature,
|
||||||
ABIDecode,
|
ABIDecode,
|
||||||
GasLeft, ///< gasleft()
|
GasLeft, ///< gasleft()
|
||||||
MetaType ///< type(...)
|
MetaType, ///< type(...)
|
||||||
|
/// Refers to a function declaration without calling context
|
||||||
|
/// (i.e. when accessed directly via the name of the containing contract).
|
||||||
|
/// Cannot be called.
|
||||||
|
Declaration
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Creates the type of a function.
|
/// Creates the type of a function.
|
||||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
/// @arg _kind must be Kind::Internal, Kind::External or Kind::Declaration.
|
||||||
|
explicit FunctionType(FunctionDefinition const& _function, Kind _kind = Kind::Declaration);
|
||||||
/// Creates the accessor function type of a state variable.
|
/// Creates the accessor function type of a state variable.
|
||||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
explicit FunctionType(VariableDeclaration const& _varDecl);
|
||||||
/// Creates the function type of an event.
|
/// Creates the function type of an event.
|
||||||
@ -1066,7 +1071,7 @@ public:
|
|||||||
FunctionType(
|
FunctionType(
|
||||||
strings const& _parameterTypes,
|
strings const& _parameterTypes,
|
||||||
strings const& _returnParameterTypes,
|
strings const& _returnParameterTypes,
|
||||||
Kind _kind = Kind::Internal,
|
Kind _kind,
|
||||||
bool _arbitraryParameters = false,
|
bool _arbitraryParameters = false,
|
||||||
StateMutability _stateMutability = StateMutability::NonPayable
|
StateMutability _stateMutability = StateMutability::NonPayable
|
||||||
): FunctionType(
|
): FunctionType(
|
||||||
|
@ -550,6 +550,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
solAssert(function.kind() == FunctionType::Kind::DelegateCall || function.kind() == FunctionType::Kind::Internal, "");
|
solAssert(function.kind() == FunctionType::Kind::DelegateCall || function.kind() == FunctionType::Kind::Internal, "");
|
||||||
switch (function.kind())
|
switch (function.kind())
|
||||||
{
|
{
|
||||||
|
case FunctionType::Kind::Declaration:
|
||||||
|
solAssert(false, "Attempted to generate code for calling a function definition.");
|
||||||
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
{
|
{
|
||||||
// Calling convention: Caller pushes return address and arguments
|
// Calling convention: Caller pushes return address and arguments
|
||||||
@ -1289,14 +1292,22 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
_memberAccess.expression().accept(*this);
|
_memberAccess.expression().accept(*this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Another special case for `this.f.selector` which does not need the address.
|
// Another special case for `this.f.selector` and for ``C.f.selector`` which do not need the address.
|
||||||
// There are other uses of `.selector` which do need the address, but we want this
|
// There are other uses of `.selector` which do need the address, but we want these
|
||||||
// specific use to be a pure expression.
|
// specific uses to be pure expressions.
|
||||||
if (
|
if (
|
||||||
_memberAccess.expression().annotation().type->category() == Type::Category::Function &&
|
auto const* functionType = dynamic_cast<FunctionType const*>(_memberAccess.expression().annotation().type);
|
||||||
member == "selector"
|
functionType && member == "selector"
|
||||||
)
|
)
|
||||||
if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
|
{
|
||||||
|
if (functionType->kind() == FunctionType::Kind::Declaration)
|
||||||
|
{
|
||||||
|
m_context << functionType->externalIdentifier();
|
||||||
|
/// need to store it as bytes4
|
||||||
|
utils().leftShiftNumberOnStack(224);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
|
||||||
if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
|
if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
|
||||||
if (exprInt->name() == "this")
|
if (exprInt->name() == "this")
|
||||||
if (Declaration const* declaration = expr->annotation().referencedDeclaration)
|
if (Declaration const* declaration = expr->annotation().referencedDeclaration)
|
||||||
@ -1313,6 +1324,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
utils().leftShiftNumberOnStack(224);
|
utils().leftShiftNumberOnStack(224);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance
|
// Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance
|
||||||
// opcode.
|
// opcode.
|
||||||
if (
|
if (
|
||||||
|
@ -75,7 +75,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
|||||||
}
|
}
|
||||||
if (_contractDef.constructor())
|
if (_contractDef.constructor())
|
||||||
{
|
{
|
||||||
FunctionType constrType(*_contractDef.constructor(), false);
|
FunctionType constrType(*_contractDef.constructor());
|
||||||
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
||||||
solAssert(!!externalFunctionType, "");
|
solAssert(!!externalFunctionType, "");
|
||||||
Json::Value method;
|
Json::Value method;
|
||||||
@ -92,7 +92,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
|||||||
for (auto const* fallbackOrReceive: {_contractDef.fallbackFunction(), _contractDef.receiveFunction()})
|
for (auto const* fallbackOrReceive: {_contractDef.fallbackFunction(), _contractDef.receiveFunction()})
|
||||||
if (fallbackOrReceive)
|
if (fallbackOrReceive)
|
||||||
{
|
{
|
||||||
FunctionType const* externalFunctionType = FunctionType(*fallbackOrReceive, false).interfaceFunctionType();
|
auto const* externalFunctionType = FunctionType(*fallbackOrReceive).interfaceFunctionType();
|
||||||
solAssert(!!externalFunctionType, "");
|
solAssert(!!externalFunctionType, "");
|
||||||
Json::Value method;
|
Json::Value method;
|
||||||
method["type"] = TokenTraits::toString(fallbackOrReceive->kind());
|
method["type"] = TokenTraits::toString(fallbackOrReceive->kind());
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
contract A {
|
||||||
|
function f() external {}
|
||||||
|
function g(uint256) external {}
|
||||||
|
}
|
||||||
|
contract B {
|
||||||
|
function f() external returns (uint256) {}
|
||||||
|
function g(uint256) external returns (uint256) {}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function test1() external returns(bytes4, bytes4, bytes4, bytes4) {
|
||||||
|
return (A.f.selector, A.g.selector, B.f.selector, B.g.selector);
|
||||||
|
}
|
||||||
|
function test2() external returns(bytes4, bytes4, bytes4, bytes4) {
|
||||||
|
A a; B b;
|
||||||
|
return (a.f.selector, a.g.selector, b.f.selector, b.g.selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// test1() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a)
|
||||||
|
// test2() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a)
|
@ -0,0 +1,14 @@
|
|||||||
|
contract A {
|
||||||
|
function f() external {}
|
||||||
|
function g() external pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function h() external {
|
||||||
|
function() external f = A.f;
|
||||||
|
function() external pure g = A.g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (128-155): Type function A.f() is not implicitly convertible to expected type function () external.
|
||||||
|
// TypeError: (165-197): Type function A.g() pure is not implicitly convertible to expected type function () pure external.
|
@ -0,0 +1,17 @@
|
|||||||
|
contract A {
|
||||||
|
function f() external {}
|
||||||
|
function g() external pure {}
|
||||||
|
function h() public pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function i() external {
|
||||||
|
A.f();
|
||||||
|
A.g();
|
||||||
|
A.h(); // might be allowed in the future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (160-165): Cannot call function via contract name.
|
||||||
|
// TypeError: (175-180): Cannot call function via contract name.
|
||||||
|
// TypeError: (190-195): Cannot call function via contract name.
|
@ -0,0 +1,9 @@
|
|||||||
|
contract A {
|
||||||
|
function f() external {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external pure {
|
||||||
|
A.f.selector;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
interface I {
|
||||||
|
function f() external;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external pure {
|
||||||
|
I.f.selector;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
contract A {
|
||||||
|
function f() internal {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external {
|
||||||
|
A.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (94-97): Member "f" not found or not visible after argument-dependent lookup in type(contract A).
|
@ -0,0 +1,12 @@
|
|||||||
|
contract A {
|
||||||
|
function f() external {}
|
||||||
|
function f(uint256) external {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external {
|
||||||
|
A.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (130-133): Member "f" not unique after argument-dependent lookup in type(contract A).
|
@ -0,0 +1,11 @@
|
|||||||
|
contract A {
|
||||||
|
function f() private {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external {
|
||||||
|
A.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (93-96): Member "f" not found or not visible after argument-dependent lookup in type(contract A).
|
@ -0,0 +1,9 @@
|
|||||||
|
contract A {
|
||||||
|
function f() public {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B {
|
||||||
|
function g() external pure {
|
||||||
|
A.f.selector;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user