mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add implicit convertibility to function pointer with higher state mutability
This commit is contained in:
parent
414559bd07
commit
c059119145
@ -426,8 +426,26 @@ function type should not return anything, the whole ``returns (<return types>)``
|
|||||||
part has to be omitted.
|
part has to be omitted.
|
||||||
|
|
||||||
By default, function types are internal, so the ``internal`` keyword can be
|
By default, function types are internal, so the ``internal`` keyword can be
|
||||||
omitted. In contrast, contract functions themselves are public by default,
|
omitted. Note that this only applies to function types. Visibility has
|
||||||
only when used as the name of a type, the default is internal.
|
to be specified explicitly for functions defined in contracts, they
|
||||||
|
do not have a default.
|
||||||
|
|
||||||
|
A function type ``A`` is implicitly convertible to a function type ``B`` if and only if
|
||||||
|
their parameter types are identical, their return types are identical,
|
||||||
|
their internal/external property is identical and the state mutability of ``A``
|
||||||
|
is not more restrictive than the state mutability of ``B``. In particular:
|
||||||
|
|
||||||
|
- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions
|
||||||
|
- ``view`` functions can be converted to ``non-payable`` functions
|
||||||
|
- ``payable`` functions can be converted to ``non-payable`` functions
|
||||||
|
|
||||||
|
No other conversions are possible.
|
||||||
|
|
||||||
|
The rule about ``payable`` and ``non-payable`` might be a little
|
||||||
|
confusing, but in essence, if a function is ``payable``, this means that it
|
||||||
|
also accepts a payment of zero Ether, so it also is ``non-payable``.
|
||||||
|
On the other hand, a ``non-payable`` function will reject Ether sent to it,
|
||||||
|
so ``non-payable`` functions cannot be converted to ``payable`` functions.
|
||||||
|
|
||||||
If a function type variable is not initialized, calling it will result
|
If a function type variable is not initialized, calling it will result
|
||||||
in an exception. The same happens if you call a function after using ``delete``
|
in an exception. The same happens if you call a function after using ``delete``
|
||||||
|
@ -2569,28 +2569,10 @@ bool FunctionType::operator==(Type const& _other) const
|
|||||||
{
|
{
|
||||||
if (_other.category() != category())
|
if (_other.category() != category())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
|
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
|
||||||
if (
|
if (!equalExcludingStateMutability(other))
|
||||||
m_kind != other.m_kind ||
|
|
||||||
m_stateMutability != other.stateMutability() ||
|
|
||||||
m_parameterTypes.size() != other.m_parameterTypes.size() ||
|
|
||||||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
|
|
||||||
)
|
|
||||||
return false;
|
return false;
|
||||||
|
if (m_stateMutability != other.stateMutability())
|
||||||
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
|
|
||||||
if (
|
|
||||||
!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
|
|
||||||
!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
//@todo this is ugly, but cannot be prevented right now
|
|
||||||
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
|
|
||||||
return false;
|
|
||||||
if (bound() != other.bound())
|
|
||||||
return false;
|
|
||||||
if (bound() && *selfType() != *other.selfType())
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2606,6 +2588,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return _convertTo.category() == category();
|
return _convertTo.category() == category();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
|
{
|
||||||
|
if (_convertTo.category() != category())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
|
||||||
|
|
||||||
|
if (!equalExcludingStateMutability(convertTo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// non-payable should not be convertible to payable
|
||||||
|
if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// payable should be convertible to non-payable, because you are free to pay 0 ether
|
||||||
|
if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// e.g. pure should be convertible to view, but not the other way around.
|
||||||
|
if (m_stateMutability > convertTo.stateMutability())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
|
||||||
{
|
{
|
||||||
if (_operator == Token::Value::Delete)
|
if (_operator == Token::Value::Delete)
|
||||||
@ -2863,6 +2870,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
|
||||||
|
{
|
||||||
|
if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
|
||||||
|
return false;
|
||||||
|
return equal(
|
||||||
|
m_returnParameterTypes.cbegin(),
|
||||||
|
m_returnParameterTypes.cend(),
|
||||||
|
_other.m_returnParameterTypes.cbegin(),
|
||||||
|
[](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
|
||||||
|
{
|
||||||
|
if (m_kind != _other.m_kind)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//@todo this is ugly, but cannot be prevented right now
|
||||||
|
if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bound() != _other.bound())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
solAssert(!bound() || *selfType() == *_other.selfType(), "");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool FunctionType::isBareCall() const
|
bool FunctionType::isBareCall() const
|
||||||
{
|
{
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
|
@ -1012,6 +1012,7 @@ public:
|
|||||||
|
|
||||||
virtual std::string richIdentifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
|
||||||
@ -1041,8 +1042,12 @@ public:
|
|||||||
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
|
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
|
||||||
/// expression the function is called on.
|
/// expression the function is called on.
|
||||||
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
|
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
|
||||||
/// @returns true if the types of parameters are equal (doesn't check return parameter types)
|
/// @returns true if the types of parameters are equal (does not check return parameter types)
|
||||||
bool hasEqualParameterTypes(FunctionType const& _other) const;
|
bool hasEqualParameterTypes(FunctionType const& _other) const;
|
||||||
|
/// @returns true iff the return types are equal (does not check parameter types)
|
||||||
|
bool hasEqualReturnTypes(FunctionType const& _other) const;
|
||||||
|
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
|
||||||
|
bool equalExcludingStateMutability(FunctionType const& _other) const;
|
||||||
|
|
||||||
/// @returns true if the ABI is used for this call (only meaningful for external calls)
|
/// @returns true if the ABI is used for this call (only meaningful for external calls)
|
||||||
bool isBareCall() const;
|
bool isBareCall() const;
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
contract Test
|
||||||
|
{
|
||||||
|
function uint256_to_uint256(uint256 x) internal pure returns (uint256) { return x; }
|
||||||
|
function uint256_to_string(uint256 x) internal pure returns (string memory) { return x == 0 ? "a" : "b"; }
|
||||||
|
function uint256_to_string_storage(uint256) internal pure returns (string storage);
|
||||||
|
function string_to_uint256(string memory x) internal pure returns (uint256) { return bytes(x).length; }
|
||||||
|
function string_to_string(string memory x) internal pure returns (string memory) { return x; }
|
||||||
|
|
||||||
|
function uint256_uint256_to_uint256(uint256 x, uint256 y) internal pure returns (uint256) { return x + y; }
|
||||||
|
function uint256_uint256_to_string(uint256 x, uint256 y) internal pure returns (string memory) { return x == y ? "a" : "b"; }
|
||||||
|
function string_uint256_to_string(string memory x, uint256 y) internal pure returns (string memory) { return y == 0 ? "a" : x; }
|
||||||
|
function string_string_to_string(string memory x, string memory y) internal pure returns (string memory) { return bytes(x).length == 0 ? y : x; }
|
||||||
|
function uint256_string_to_string(uint256 x, string memory y) internal pure returns (string memory) { return x == 0 ? "a" : y; }
|
||||||
|
|
||||||
|
function tests() internal pure
|
||||||
|
{
|
||||||
|
function (uint256) internal pure returns (uint256) var_uint256_to_uint256 = uint256_to_string;
|
||||||
|
function (uint256) internal pure returns (string memory) var_uint256_to_string = uint256_to_string_storage;
|
||||||
|
function (string memory) internal pure returns (uint256) var_string_to_uint256 = uint256_to_string;
|
||||||
|
function (string memory) internal pure returns (string memory) var_string_to_string = var_uint256_to_string;
|
||||||
|
|
||||||
|
function (uint256, uint256) internal pure returns (uint256) var_uint256_uint256_to_uint256 = uint256_to_uint256;
|
||||||
|
function (string memory, uint256) internal pure returns (string memory) var_string_uint256_to_string = string_to_string;
|
||||||
|
function (string memory, string memory) internal pure returns (string memory) var_string_string_to_string = string_to_string;
|
||||||
|
|
||||||
|
var_uint256_to_uint256(1);
|
||||||
|
var_uint256_to_string(2);
|
||||||
|
var_string_to_uint256("a");
|
||||||
|
var_string_to_string("b");
|
||||||
|
var_uint256_uint256_to_uint256(3, 4);
|
||||||
|
var_string_uint256_to_string("c", 7);
|
||||||
|
var_string_string_to_string("d", "e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (1218-1311): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (uint256) pure returns (uint256).
|
||||||
|
// TypeError: (1319-1425): Type function (uint256) pure returns (string storage pointer) is not implicitly convertible to expected type function (uint256) pure returns (string memory).
|
||||||
|
// TypeError: (1433-1531): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (string memory) pure returns (uint256).
|
||||||
|
// TypeError: (1539-1646): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (string memory) pure returns (string memory).
|
||||||
|
// TypeError: (1655-1766): Type function (uint256) pure returns (uint256) is not implicitly convertible to expected type function (uint256,uint256) pure returns (uint256).
|
||||||
|
// TypeError: (1774-1893): Type function (string memory) pure returns (string memory) is not implicitly convertible to expected type function (string memory,uint256) pure returns (string memory).
|
||||||
|
// TypeError: (1901-2025): Type function (string memory) pure returns (string memory) is not implicitly convertible to expected type function (string memory,string memory) pure returns (string memory).
|
@ -0,0 +1,39 @@
|
|||||||
|
contract Test
|
||||||
|
{
|
||||||
|
function uint256_to_uint256(uint256 x) internal pure returns (uint256) { return x; }
|
||||||
|
function uint256_to_string(uint256 x) internal pure returns (string memory) { return x == 0 ? "a" : "b"; }
|
||||||
|
function string_to_uint256(string memory x) internal pure returns (uint256) { return bytes(x).length; }
|
||||||
|
function string_to_string(string memory x) internal pure returns (string memory) { return x; }
|
||||||
|
|
||||||
|
function uint256_uint256_to_uint256(uint256 x, uint256 y) internal pure returns (uint256) { return x + y; }
|
||||||
|
function uint256_uint256_to_string(uint256 x, uint256 y) internal pure returns (string memory) { return x == y ? "a" : "b"; }
|
||||||
|
function string_uint256_to_string(string memory x, uint256 y) internal pure returns (string memory) { return y == 0 ? "a" : x; }
|
||||||
|
function string_string_to_string(string memory x, string memory y) internal pure returns (string memory) { return bytes(x).length == 0 ? y : x; }
|
||||||
|
function uint256_string_to_string(uint256 x, string memory y) internal pure returns (string memory) { return x == 0 ? "a" : y; }
|
||||||
|
|
||||||
|
function tests() internal pure
|
||||||
|
{
|
||||||
|
function (uint256) internal pure returns (uint256) var_uint256_to_uint256 = uint256_to_uint256;
|
||||||
|
function (uint256) internal pure returns (string memory) var_uint256_to_string = uint256_to_string;
|
||||||
|
function (string memory) internal pure returns (uint256) var_string_to_uint256 = string_to_uint256;
|
||||||
|
function (string memory) internal pure returns (string memory) var_string_to_string = string_to_string;
|
||||||
|
|
||||||
|
function (uint256, uint256) internal pure returns (uint256) var_uint256_uint256_to_uint256 = uint256_uint256_to_uint256;
|
||||||
|
function (uint256, uint256) internal pure returns (string memory) var_uint256_uint256_to_string = uint256_uint256_to_string;
|
||||||
|
function (string memory, uint256) internal pure returns (string memory) var_string_uint256_to_string = string_uint256_to_string;
|
||||||
|
function (string memory, string memory) internal pure returns (string memory) var_string_string_to_string = string_string_to_string;
|
||||||
|
function (uint256, string memory) internal pure returns (string memory) var_uint256_string_to_string = uint256_string_to_string;
|
||||||
|
|
||||||
|
// Avoid unused variable warnings:
|
||||||
|
var_uint256_to_uint256(1);
|
||||||
|
var_uint256_to_string(2);
|
||||||
|
var_string_to_uint256("a");
|
||||||
|
var_string_to_string("b");
|
||||||
|
var_uint256_uint256_to_uint256(3, 4);
|
||||||
|
var_uint256_uint256_to_string(5, 6);
|
||||||
|
var_string_uint256_to_string("c", 7);
|
||||||
|
var_string_string_to_string("d", "e");
|
||||||
|
var_uint256_string_to_string(8, "f");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,51 @@
|
|||||||
|
contract Test
|
||||||
|
{
|
||||||
|
function internalPureFunc(uint256 x) internal pure returns (uint256) { return x; }
|
||||||
|
function internalViewFunc(uint256 x) internal view returns (uint256) { return x; }
|
||||||
|
function internalMutableFunc(uint256 x) internal returns (uint256) { return x; }
|
||||||
|
|
||||||
|
function externalPureFunc(uint256 x) external pure returns (uint256) { return x; }
|
||||||
|
function externalViewFunc(uint256 x) external view returns (uint256) { return x; }
|
||||||
|
function externalPayableFunc(uint256 x) external payable returns (uint256) { return x; }
|
||||||
|
function externalMutableFunc(uint256 x) external returns (uint256) { return x; }
|
||||||
|
|
||||||
|
function funcTakesInternalPure(function(uint256) internal pure returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesInternalView(function(uint256) internal view returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesInternalMutable(function(uint256) internal returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
|
||||||
|
function funcTakesExternalPure(function(uint256) external pure returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalView(function(uint256) external view returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalPayable(function(uint256) external payable returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalMutable(function(uint256) external returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
|
||||||
|
function tests() internal
|
||||||
|
{
|
||||||
|
funcTakesInternalPure(internalViewFunc); // view -> pure should fail
|
||||||
|
funcTakesInternalPure(internalMutableFunc); // mutable -> pure should fail
|
||||||
|
|
||||||
|
funcTakesInternalView(internalMutableFunc); // mutable -> view should fail
|
||||||
|
|
||||||
|
funcTakesExternalPure(this.externalViewFunc); // view -> pure should fail
|
||||||
|
funcTakesExternalPure(this.externalPayableFunc); // payable -> pure should fail
|
||||||
|
funcTakesExternalPure(this.externalMutableFunc); // mutable -> pure should fail
|
||||||
|
|
||||||
|
funcTakesExternalView(this.externalPayableFunc); // payable -> view should fail
|
||||||
|
funcTakesExternalView(this.externalMutableFunc); // mutable -> view should fail
|
||||||
|
|
||||||
|
funcTakesExternalPayable(this.externalPureFunc); // pure -> payable should fail
|
||||||
|
funcTakesExternalPayable(this.externalViewFunc); // view -> payable should fail
|
||||||
|
funcTakesExternalPayable(this.externalMutableFunc); // mutable -> payable should fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (1580-1596): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view returns (uint256) to function (uint256) pure returns (uint256) requested.
|
||||||
|
// TypeError: (1653-1672): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) pure returns (uint256) requested.
|
||||||
|
// TypeError: (1733-1752): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) view returns (uint256) requested.
|
||||||
|
// TypeError: (1813-1834): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view external returns (uint256) to function (uint256) pure external returns (uint256) requested.
|
||||||
|
// TypeError: (1891-1915): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) payable external returns (uint256) to function (uint256) pure external returns (uint256) requested.
|
||||||
|
// TypeError: (1975-1999): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) pure external returns (uint256) requested.
|
||||||
|
// TypeError: (2060-2084): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) payable external returns (uint256) to function (uint256) view external returns (uint256) requested.
|
||||||
|
// TypeError: (2144-2168): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) view external returns (uint256) requested.
|
||||||
|
// TypeError: (2232-2253): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) pure external returns (uint256) to function (uint256) payable external returns (uint256) requested.
|
||||||
|
// TypeError: (2316-2337): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view external returns (uint256) to function (uint256) payable external returns (uint256) requested.
|
||||||
|
// TypeError: (2400-2424): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) payable external returns (uint256) requested.
|
@ -0,0 +1,46 @@
|
|||||||
|
contract Test
|
||||||
|
{
|
||||||
|
uint y;
|
||||||
|
function internalPureFunc(uint256 x) internal pure returns (uint256) { return x; }
|
||||||
|
function internalViewFunc(uint256 x) internal view returns (uint256) { return x + y; }
|
||||||
|
function internalMutableFunc(uint256 x) internal returns (uint256) { y = x; return x; }
|
||||||
|
|
||||||
|
function externalPureFunc(uint256 x) external pure returns (uint256) { return x; }
|
||||||
|
function externalViewFunc(uint256 x) external view returns (uint256) { return x + y; }
|
||||||
|
function externalPayableFunc(uint256 x) external payable returns (uint256) { return x + y; }
|
||||||
|
function externalMutableFunc(uint256 x) external returns (uint256) { y = x; return x; }
|
||||||
|
|
||||||
|
function funcTakesInternalPure(function(uint256) internal pure returns(uint256) a) internal pure returns (uint256) { return a(4); }
|
||||||
|
function funcTakesInternalView(function(uint256) internal view returns(uint256) a) internal view returns (uint256) { return a(4); }
|
||||||
|
function funcTakesInternalMutable(function(uint256) internal returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
|
||||||
|
function funcTakesExternalPure(function(uint256) external pure returns(uint256) a) internal pure returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalView(function(uint256) external view returns(uint256) a) internal view returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalPayable(function(uint256) external payable returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
function funcTakesExternalMutable(function(uint256) external returns(uint256) a) internal returns (uint256) { return a(4); }
|
||||||
|
|
||||||
|
function tests() internal
|
||||||
|
{
|
||||||
|
funcTakesInternalPure(internalPureFunc);
|
||||||
|
|
||||||
|
funcTakesInternalView(internalPureFunc);
|
||||||
|
funcTakesInternalView(internalViewFunc);
|
||||||
|
|
||||||
|
funcTakesInternalMutable(internalPureFunc);
|
||||||
|
funcTakesInternalMutable(internalViewFunc);
|
||||||
|
funcTakesInternalMutable(internalMutableFunc);
|
||||||
|
|
||||||
|
funcTakesExternalPure(this.externalPureFunc);
|
||||||
|
|
||||||
|
funcTakesExternalView(this.externalPureFunc);
|
||||||
|
funcTakesExternalView(this.externalViewFunc);
|
||||||
|
|
||||||
|
funcTakesExternalPayable(this.externalPayableFunc);
|
||||||
|
|
||||||
|
funcTakesExternalMutable(this.externalPureFunc);
|
||||||
|
funcTakesExternalMutable(this.externalViewFunc);
|
||||||
|
funcTakesExternalMutable(this.externalPayableFunc);
|
||||||
|
funcTakesExternalMutable(this.externalMutableFunc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
Loading…
Reference in New Issue
Block a user