mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Consistent terminology for attached/bound functions
This commit is contained in:
parent
1c8745c54a
commit
64a4f32bc2
@ -143,7 +143,7 @@ The following points are all covered by the coding style but come up so often th
|
|||||||
Values of types that cannot be named: literals, tuples, array slices, storage references?
|
Values of types that cannot be named: literals, tuples, array slices, storage references?
|
||||||
- [ ] If it accepts a function, does it also accept an event or an error? These have function types but are not functions.
|
- [ ] If it accepts a function, does it also accept an event or an error? These have function types but are not functions.
|
||||||
- [ ] If it affects free functions, what about internal library functions?
|
- [ ] If it affects free functions, what about internal library functions?
|
||||||
- [ ] Bound library functions? Functions bound with `using for`?
|
- [ ] Attached library functions? Functions attached with `using for`?
|
||||||
- [ ] Possible combinations of `storage`, `memory`, `calldata`, `immutable`, `constant`?
|
- [ ] Possible combinations of `storage`, `memory`, `calldata`, `immutable`, `constant`?
|
||||||
Remember that internal functions can take `storage` arguments.
|
Remember that internal functions can take `storage` arguments.
|
||||||
- [ ] Does it work at construction time as well? What if you store it at construction time and read after deployment?
|
- [ ] Does it work at construction time as well? What if you store it at construction time and read after deployment?
|
||||||
|
@ -17,16 +17,16 @@ at contract level.
|
|||||||
The first part, ``A``, can be one of:
|
The first part, ``A``, can be one of:
|
||||||
|
|
||||||
- a list of file-level or library functions (``using {f, g, h, L.t} for uint;``) -
|
- a list of file-level or library functions (``using {f, g, h, L.t} for uint;``) -
|
||||||
only those functions will be attached to the type.
|
only those functions will be attached to the type as member functions
|
||||||
- the name of a library (``using L for uint;``) -
|
- the name of a library (``using L for uint;``) -
|
||||||
all functions (both public and internal ones) of the library are attached to the type
|
all functions (both public and internal ones) of the library are attached to the type.
|
||||||
|
|
||||||
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
|
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
|
||||||
Inside contracts, you can also use ``using L for *;``,
|
Inside contracts, you can also use ``using L for *;``,
|
||||||
which has the effect that all functions of the library ``L``
|
which has the effect that all functions of the library ``L``
|
||||||
are attached to *all* types.
|
are attached to *all* types.
|
||||||
|
|
||||||
If you specify a library, *all* functions in the library are attached,
|
If you specify a library, *all* functions in the library get attached,
|
||||||
even those where the type of the first parameter does not
|
even those where the type of the first parameter does not
|
||||||
match the type of the object. The type is checked at the
|
match the type of the object. The type is checked at the
|
||||||
point the function is called and function overload
|
point the function is called and function overload
|
||||||
|
@ -312,7 +312,7 @@ errorDefinition:
|
|||||||
Semicolon;
|
Semicolon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using directive to bind library functions and free functions to types.
|
* Using directive to attach library functions and free functions to types.
|
||||||
* Can occur within contracts and libraries and at the file level.
|
* Can occur within contracts and libraries and at the file level.
|
||||||
*/
|
*/
|
||||||
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
|
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
|
||||||
|
@ -664,7 +664,7 @@ introduced type and ``V`` has to be a built-in value type (the "underlying type"
|
|||||||
``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the
|
``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the
|
||||||
function ``C.unwrap`` is used to convert from the custom type to the underlying type.
|
function ``C.unwrap`` is used to convert from the custom type to the underlying type.
|
||||||
|
|
||||||
The type ``C`` does not have any operators or bound member functions. In particular, even the
|
The type ``C`` does not have any operators or attached member functions. In particular, even the
|
||||||
operator ``==`` is not defined. Explicit and implicit conversions to and from other types are
|
operator ``==`` is not defined. Explicit and implicit conversions to and from other types are
|
||||||
disallowed.
|
disallowed.
|
||||||
|
|
||||||
|
@ -470,7 +470,7 @@ bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor)
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4167_error,
|
4167_error,
|
||||||
function->location(),
|
function->location(),
|
||||||
"Only file-level functions and library functions can be bound to a type in a \"using\" statement"
|
"Only file-level functions and library functions can be attached to a type in a \"using\" statement"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -427,7 +427,7 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
|
|||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
2854_error,
|
2854_error,
|
||||||
_usingFor.location(),
|
_usingFor.location(),
|
||||||
"Can only globally bind functions to specific types."
|
"Can only globally attach functions to specific types."
|
||||||
);
|
);
|
||||||
if (_usingFor.global() && m_currentContractKind)
|
if (_usingFor.global() && m_currentContractKind)
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
@ -3167,7 +3167,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type))
|
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type))
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
!funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
|
!funType->hasBoundFirstArgument() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
|
||||||
"Function \"" + memberName + "\" cannot be called on an object of type " +
|
"Function \"" + memberName + "\" cannot be called on an object of type " +
|
||||||
exprType->humanReadableName() + " (expected " + funType->selfType()->humanReadableName() + ")."
|
exprType->humanReadableName() + " (expected " + funType->selfType()->humanReadableName() + ")."
|
||||||
);
|
);
|
||||||
@ -3194,7 +3194,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
"Storage arrays with nested mappings do not support .push(<arg>)."
|
"Storage arrays with nested mappings do not support .push(<arg>)."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!funType->bound())
|
if (!funType->hasBoundFirstArgument())
|
||||||
if (auto typeType = dynamic_cast<TypeType const*>(exprType))
|
if (auto typeType = dynamic_cast<TypeType const*>(exprType))
|
||||||
{
|
{
|
||||||
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
||||||
@ -3810,13 +3810,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
4731_error,
|
4731_error,
|
||||||
path->location(),
|
path->location(),
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"The function \"{}\" does not have any parameters, and therefore cannot be bound to the type \"{}\".",
|
"The function \"{}\" does not have any parameters, and therefore cannot be attached to the type \"{}\".",
|
||||||
joinHumanReadable(path->path(), "."),
|
joinHumanReadable(path->path(), "."),
|
||||||
normalizedType ? normalizedType->toString(true /* withoutDataLocation */) : "*"
|
normalizedType ? normalizedType->toString(true /* withoutDataLocation */) : "*"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
FunctionType const* functionType = dynamic_cast<FunctionType const&>(*functionDefinition.type()).asBoundFunction();
|
FunctionType const* functionType = dynamic_cast<FunctionType const&>(*functionDefinition.type()).withBoundFirstArgument();
|
||||||
solAssert(functionType && functionType->selfType(), "");
|
solAssert(functionType && functionType->selfType(), "");
|
||||||
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
||||||
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
|
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
|
||||||
@ -3826,7 +3826,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|||||||
3100_error,
|
3100_error,
|
||||||
path->location(),
|
path->location(),
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"The function \"{}\" cannot be bound 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 */),
|
||||||
|
@ -644,10 +644,9 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Using for directive:
|
* Using for directive:
|
||||||
*
|
*
|
||||||
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`
|
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`.
|
||||||
* 2. `using LibraryName for *` attaches to all types.
|
* 2. `using LibraryName for *` attaches to all types.
|
||||||
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ...,
|
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., `fn`, respectively to `T`.
|
||||||
* `fn`, respectively to `T`.
|
|
||||||
*
|
*
|
||||||
* For version 3, T has to be implicitly convertible to the first parameter type of
|
* For version 3, T has to be implicitly convertible to the first parameter type of
|
||||||
* all functions, and this is checked at the point of the using statement. For versions 1 and
|
* all functions, and this is checked at the point of the using statement. For versions 1 and
|
||||||
|
@ -450,7 +450,7 @@ FunctionType const* TypeProvider::function(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Can only use this constructor for "arbitraryParameters".
|
// Can only use this constructor for "arbitraryParameters".
|
||||||
solAssert(!_options.valueSet && !_options.gasSet && !_options.saltSet && !_options.bound);
|
solAssert(!_options.valueSet && !_options.gasSet && !_options.saltSet && !_options.hasBoundFirstArgument);
|
||||||
return createAndGet<FunctionType>(
|
return createAndGet<FunctionType>(
|
||||||
_parameterTypes,
|
_parameterTypes,
|
||||||
_returnParameterTypes,
|
_returnParameterTypes,
|
||||||
|
@ -305,7 +305,7 @@ MemberList const& Type::members(ASTNode const* _currentScope) const
|
|||||||
"");
|
"");
|
||||||
MemberList::MemberMap members = nativeMembers(_currentScope);
|
MemberList::MemberMap members = nativeMembers(_currentScope);
|
||||||
if (_currentScope)
|
if (_currentScope)
|
||||||
members += boundFunctions(*this, *_currentScope);
|
members += attachedFunctions(*this, *_currentScope);
|
||||||
m_members[_currentScope] = make_unique<MemberList>(std::move(members));
|
m_members[_currentScope] = make_unique<MemberList>(std::move(members));
|
||||||
}
|
}
|
||||||
return *m_members[_currentScope];
|
return *m_members[_currentScope];
|
||||||
@ -383,7 +383,7 @@ vector<UsingForDirective const*> usingForDirectivesForType(Type const& _type, AS
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
|
MemberList::MemberMap Type::attachedFunctions(Type const& _type, ASTNode const& _scope)
|
||||||
{
|
{
|
||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
|
|
||||||
@ -395,13 +395,13 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
|||||||
Type const* functionType =
|
Type const* functionType =
|
||||||
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
|
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
|
||||||
solAssert(functionType, "");
|
solAssert(functionType, "");
|
||||||
FunctionType const* asBoundFunction =
|
FunctionType const* withBoundFirstArgument =
|
||||||
dynamic_cast<FunctionType const&>(*functionType).asBoundFunction();
|
dynamic_cast<FunctionType const&>(*functionType).withBoundFirstArgument();
|
||||||
solAssert(asBoundFunction, "");
|
solAssert(withBoundFirstArgument, "");
|
||||||
|
|
||||||
if (_type.isImplicitlyConvertibleTo(*asBoundFunction->selfType()))
|
if (_type.isImplicitlyConvertibleTo(*withBoundFirstArgument->selfType()))
|
||||||
if (seenFunctions.insert(make_pair(*_name, &_function)).second)
|
if (seenFunctions.insert(make_pair(*_name, &_function)).second)
|
||||||
members.emplace_back(&_function, asBoundFunction, *_name);
|
members.emplace_back(&_function, withBoundFirstArgument, *_name);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (UsingForDirective const* ufd: usingForDirectivesForType(_type, _scope))
|
for (UsingForDirective const* ufd: usingForDirectivesForType(_type, _scope))
|
||||||
@ -1879,21 +1879,21 @@ MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const
|
|||||||
strings{string()},
|
strings{string()},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
FunctionType::Kind::ArrayPush
|
FunctionType::Kind::ArrayPush
|
||||||
)->asBoundFunction());
|
)->withBoundFirstArgument());
|
||||||
members.emplace_back("push", TypeProvider::function(
|
members.emplace_back("push", TypeProvider::function(
|
||||||
TypePointers{thisAsPointer, baseType()},
|
TypePointers{thisAsPointer, baseType()},
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
strings{string(),string()},
|
strings{string(),string()},
|
||||||
strings{},
|
strings{},
|
||||||
FunctionType::Kind::ArrayPush
|
FunctionType::Kind::ArrayPush
|
||||||
)->asBoundFunction());
|
)->withBoundFirstArgument());
|
||||||
members.emplace_back("pop", TypeProvider::function(
|
members.emplace_back("pop", TypeProvider::function(
|
||||||
TypePointers{thisAsPointer},
|
TypePointers{thisAsPointer},
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
strings{},
|
strings{},
|
||||||
FunctionType::Kind::ArrayPop
|
FunctionType::Kind::ArrayPop
|
||||||
)->asBoundFunction());
|
)->withBoundFirstArgument());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
@ -2952,7 +2952,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
|
|||||||
|
|
||||||
vector<string> FunctionType::parameterNames() const
|
vector<string> FunctionType::parameterNames() const
|
||||||
{
|
{
|
||||||
if (!bound())
|
if (!hasBoundFirstArgument())
|
||||||
return m_parameterNames;
|
return m_parameterNames;
|
||||||
return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
|
return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
|
||||||
}
|
}
|
||||||
@ -2981,7 +2981,7 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const
|
|||||||
|
|
||||||
TypePointers FunctionType::parameterTypes() const
|
TypePointers FunctionType::parameterTypes() const
|
||||||
{
|
{
|
||||||
if (!bound())
|
if (!hasBoundFirstArgument())
|
||||||
return m_parameterTypes;
|
return m_parameterTypes;
|
||||||
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
||||||
}
|
}
|
||||||
@ -3046,8 +3046,8 @@ string FunctionType::richIdentifier() const
|
|||||||
id += "value";
|
id += "value";
|
||||||
if (saltSet())
|
if (saltSet())
|
||||||
id += "salt";
|
id += "salt";
|
||||||
if (bound())
|
if (hasBoundFirstArgument())
|
||||||
id += "bound_to" + identifierList(selfType());
|
id += "attached_to" + identifierList(selfType());
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3081,11 +3081,11 @@ BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
|
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
|
||||||
|
|
||||||
// These two checks are duplicated in equalExcludingStateMutability, but are added here for error reporting.
|
// These two checks are duplicated in equalExcludingStateMutability, but are added here for error reporting.
|
||||||
if (convertTo.bound() != bound())
|
if (convertTo.hasBoundFirstArgument() != hasBoundFirstArgument())
|
||||||
return BoolResult::err("Bound functions can not be converted to non-bound functions.");
|
return BoolResult::err("Attached functions cannot be converted into unattached functions.");
|
||||||
|
|
||||||
if (convertTo.kind() != kind())
|
if (convertTo.kind() != kind())
|
||||||
return BoolResult::err("Special functions can not be converted to function types.");
|
return BoolResult::err("Special functions cannot be converted to function types.");
|
||||||
|
|
||||||
if (!equalExcludingStateMutability(convertTo))
|
if (!equalExcludingStateMutability(convertTo))
|
||||||
return false;
|
return false;
|
||||||
@ -3122,10 +3122,10 @@ TypeResult FunctionType::binaryOperatorResult(Token _operator, Type const* _othe
|
|||||||
else if (
|
else if (
|
||||||
kind() == Kind::External &&
|
kind() == Kind::External &&
|
||||||
sizeOnStack() == 2 &&
|
sizeOnStack() == 2 &&
|
||||||
!bound() &&
|
!hasBoundFirstArgument() &&
|
||||||
other.kind() == Kind::External &&
|
other.kind() == Kind::External &&
|
||||||
other.sizeOnStack() == 2 &&
|
other.sizeOnStack() == 2 &&
|
||||||
!other.bound()
|
!other.hasBoundFirstArgument()
|
||||||
)
|
)
|
||||||
return commonType(this, _other);
|
return commonType(this, _other);
|
||||||
|
|
||||||
@ -3210,7 +3210,7 @@ bool FunctionType::nameable() const
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
||||||
!bound() &&
|
!hasBoundFirstArgument() &&
|
||||||
!takesArbitraryParameters() &&
|
!takesArbitraryParameters() &&
|
||||||
!gasSet() &&
|
!gasSet() &&
|
||||||
!valueSet() &&
|
!valueSet() &&
|
||||||
@ -3249,7 +3249,7 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
|||||||
break;
|
break;
|
||||||
case Kind::ArrayPush:
|
case Kind::ArrayPush:
|
||||||
case Kind::ArrayPop:
|
case Kind::ArrayPop:
|
||||||
solAssert(bound(), "");
|
solAssert(hasBoundFirstArgument(), "");
|
||||||
slots = {};
|
slots = {};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -3262,7 +3262,7 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
|||||||
slots.emplace_back("value", TypeProvider::uint256());
|
slots.emplace_back("value", TypeProvider::uint256());
|
||||||
if (saltSet())
|
if (saltSet())
|
||||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||||
if (bound())
|
if (hasBoundFirstArgument())
|
||||||
slots.emplace_back("self", m_parameterTypes.front());
|
slots.emplace_back("self", m_parameterTypes.front());
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
@ -3423,7 +3423,7 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const
|
|||||||
|
|
||||||
Type const* FunctionType::mobileType() const
|
Type const* FunctionType::mobileType() const
|
||||||
{
|
{
|
||||||
if (valueSet() || gasSet() || saltSet() || bound())
|
if (valueSet() || gasSet() || saltSet() || hasBoundFirstArgument())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// return function without parameter names
|
// return function without parameter names
|
||||||
@ -3444,8 +3444,8 @@ bool FunctionType::canTakeArguments(
|
|||||||
Type const* _selfType
|
Type const* _selfType
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
solAssert(!bound() || _selfType, "");
|
solAssert(!hasBoundFirstArgument() || _selfType, "");
|
||||||
if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
|
if (hasBoundFirstArgument() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
|
||||||
return false;
|
return false;
|
||||||
TypePointers paramTypes = parameterTypes();
|
TypePointers paramTypes = parameterTypes();
|
||||||
std::vector<std::string> const paramNames = parameterNames();
|
std::vector<std::string> const paramNames = parameterNames();
|
||||||
@ -3524,10 +3524,10 @@ bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) con
|
|||||||
if (gasSet() != _other.gasSet() || valueSet() != _other.valueSet() || saltSet() != _other.saltSet())
|
if (gasSet() != _other.gasSet() || valueSet() != _other.valueSet() || saltSet() != _other.saltSet())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bound() != _other.bound())
|
if (hasBoundFirstArgument() != _other.hasBoundFirstArgument())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
solAssert(!bound() || *selfType() == *_other.selfType(), "");
|
solAssert(!hasBoundFirstArgument() || *selfType() == *_other.selfType(), "");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3648,14 +3648,14 @@ Type const* FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer FunctionType::asBoundFunction() const
|
FunctionTypePointer FunctionType::withBoundFirstArgument() const
|
||||||
{
|
{
|
||||||
solAssert(!m_parameterTypes.empty(), "");
|
solAssert(!m_parameterTypes.empty(), "");
|
||||||
solAssert(!gasSet(), "");
|
solAssert(!gasSet(), "");
|
||||||
solAssert(!valueSet(), "");
|
solAssert(!valueSet(), "");
|
||||||
solAssert(!saltSet(), "");
|
solAssert(!saltSet(), "");
|
||||||
Options options = Options::fromFunctionType(*this);
|
Options options = Options::fromFunctionType(*this);
|
||||||
options.bound = true;
|
options.hasBoundFirstArgument = true;
|
||||||
return TypeProvider::function(
|
return TypeProvider::function(
|
||||||
m_parameterTypes,
|
m_parameterTypes,
|
||||||
m_returnParameterTypes,
|
m_returnParameterTypes,
|
||||||
@ -3710,7 +3710,7 @@ FunctionTypePointer FunctionType::asExternallyCallableFunction(bool _inLibrary)
|
|||||||
|
|
||||||
Type const* FunctionType::selfType() const
|
Type const* FunctionType::selfType() const
|
||||||
{
|
{
|
||||||
solAssert(bound(), "Function is not bound.");
|
solAssert(hasBoundFirstArgument(), "Function is not attached to a type.");
|
||||||
solAssert(m_parameterTypes.size() > 0, "Function has no self type.");
|
solAssert(m_parameterTypes.size() > 0, "Function has no self type.");
|
||||||
return m_parameterTypes.at(0);
|
return m_parameterTypes.at(0);
|
||||||
}
|
}
|
||||||
|
@ -326,7 +326,7 @@ public:
|
|||||||
/// given location.
|
/// given location.
|
||||||
virtual bool dataStoredIn(DataLocation) const { return false; }
|
virtual bool dataStoredIn(DataLocation) const { return false; }
|
||||||
|
|
||||||
/// Returns the list of all members of this type. Default implementation: no members apart from bound.
|
/// Returns the list of all members of this type. Default implementation: no members apart from attached functions.
|
||||||
/// @param _currentScope scope in which the members are accessed.
|
/// @param _currentScope scope in which the members are accessed.
|
||||||
MemberList const& members(ASTNode const* _currentScope) const;
|
MemberList const& members(ASTNode const* _currentScope) const;
|
||||||
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
|
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
|
||||||
@ -379,11 +379,11 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns a member list containing all members added to this type by `using for` directives.
|
/// @returns a member list containing all members added to this type by `using for` directives.
|
||||||
static MemberList::MemberMap boundFunctions(Type const& _type, ASTNode const& _scope);
|
static MemberList::MemberMap attachedFunctions(Type const& _type, ASTNode const& _scope);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// @returns the members native to this type depending on the given context. This function
|
/// @returns the members native to this type depending on the given context. This function
|
||||||
/// is used (in conjunction with boundFunctions to fill m_members below.
|
/// is used (in conjunction with attachedFunctions to fill m_members below.
|
||||||
virtual MemberList::MemberMap nativeMembers(ASTNode const* /*_currentScope*/) const
|
virtual MemberList::MemberMap nativeMembers(ASTNode const* /*_currentScope*/) const
|
||||||
{
|
{
|
||||||
return MemberList::MemberMap();
|
return MemberList::MemberMap();
|
||||||
@ -1272,7 +1272,7 @@ public:
|
|||||||
bool saltSet = false;
|
bool saltSet = false;
|
||||||
/// true iff the function is called as arg1.fun(arg2, ..., argn).
|
/// true iff the function is called as arg1.fun(arg2, ..., argn).
|
||||||
/// This is achieved through the "using for" directive.
|
/// This is achieved through the "using for" directive.
|
||||||
bool bound = false;
|
bool hasBoundFirstArgument = false;
|
||||||
|
|
||||||
static Options withArbitraryParameters()
|
static Options withArbitraryParameters()
|
||||||
{
|
{
|
||||||
@ -1287,7 +1287,7 @@ public:
|
|||||||
result.gasSet = _type.gasSet();
|
result.gasSet = _type.gasSet();
|
||||||
result.valueSet = _type.valueSet();
|
result.valueSet = _type.valueSet();
|
||||||
result.saltSet = _type.saltSet();
|
result.saltSet = _type.saltSet();
|
||||||
result.bound = _type.bound();
|
result.hasBoundFirstArgument = _type.hasBoundFirstArgument();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1322,7 +1322,7 @@ public:
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// In this constructor, only the "arbitrary Parameters" option should be used.
|
// In this constructor, only the "arbitrary Parameters" option should be used.
|
||||||
solAssert(!bound() && !gasSet() && !valueSet() && !saltSet());
|
solAssert(!hasBoundFirstArgument() && !gasSet() && !valueSet() && !saltSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detailed constructor, use with care.
|
/// Detailed constructor, use with care.
|
||||||
@ -1354,8 +1354,8 @@ public:
|
|||||||
"Return parameter names list must match return parameter types list!"
|
"Return parameter names list must match return parameter types list!"
|
||||||
);
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
!bound() || !m_parameterTypes.empty(),
|
!hasBoundFirstArgument() || !m_parameterTypes.empty(),
|
||||||
"Attempted construction of bound function without self type"
|
"Attempted construction of attached function without self type"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1372,7 +1372,7 @@ public:
|
|||||||
/// storage pointers) are replaced by InaccessibleDynamicType instances.
|
/// storage pointers) are replaced by InaccessibleDynamicType instances.
|
||||||
TypePointers returnParameterTypesWithoutDynamicTypes() const;
|
TypePointers returnParameterTypesWithoutDynamicTypes() const;
|
||||||
std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; }
|
std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; }
|
||||||
/// @returns the "self" parameter type for a bound function
|
/// @returns the "self" parameter type for an attached function
|
||||||
Type const* selfType() const;
|
Type const* selfType() const;
|
||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
@ -1406,8 +1406,8 @@ public:
|
|||||||
|
|
||||||
/// @returns true if this function can take the given arguments (possibly
|
/// @returns true if this function can take the given arguments (possibly
|
||||||
/// after implicit conversion).
|
/// after implicit conversion).
|
||||||
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
|
/// @param _selfType if the function is attached as a member function, this has to be supplied
|
||||||
/// expression the function is called on.
|
/// and is the type of the expression the function is called on.
|
||||||
bool canTakeArguments(
|
bool canTakeArguments(
|
||||||
FuncCallArguments const& _arguments,
|
FuncCallArguments const& _arguments,
|
||||||
Type const* _selfType = nullptr
|
Type const* _selfType = nullptr
|
||||||
@ -1471,20 +1471,20 @@ public:
|
|||||||
bool gasSet() const { return m_options.gasSet; }
|
bool gasSet() const { return m_options.gasSet; }
|
||||||
bool valueSet() const { return m_options.valueSet; }
|
bool valueSet() const { return m_options.valueSet; }
|
||||||
bool saltSet() const { return m_options.saltSet; }
|
bool saltSet() const { return m_options.saltSet; }
|
||||||
bool bound() const { return m_options.bound; }
|
bool hasBoundFirstArgument() const { return m_options.hasBoundFirstArgument; }
|
||||||
|
|
||||||
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
||||||
/// of the parameters to false.
|
/// of the parameters to false.
|
||||||
Type const* copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const;
|
Type const* copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const;
|
||||||
|
|
||||||
/// @returns a copy of this function type with the `bound` flag set to true.
|
/// @returns a copy of this function type with the `hasBoundFirstArgument` flag set to true.
|
||||||
/// Should only be called on library functions.
|
/// Should only be called on library functions.
|
||||||
FunctionTypePointer asBoundFunction() const;
|
FunctionTypePointer withBoundFirstArgument() const;
|
||||||
|
|
||||||
/// @returns a copy of this function type where the location of reference types is changed
|
/// @returns a copy of this function type where the location of reference types is changed
|
||||||
/// from CallData to Memory. This is the type that would be used when the function is
|
/// from CallData to Memory. This is the type that would be used when the function is
|
||||||
/// called externally, as opposed to the parameter types that are available inside the function body.
|
/// called externally, as opposed to the parameter types that are available inside the function body.
|
||||||
/// Also supports variants to be used for library or bound calls.
|
/// Also supports variants to be used for library or attached function calls.
|
||||||
/// @param _inLibrary if true, uses DelegateCall as location.
|
/// @param _inLibrary if true, uses DelegateCall as location.
|
||||||
FunctionTypePointer asExternallyCallableFunction(bool _inLibrary) const;
|
FunctionTypePointer asExternallyCallableFunction(bool _inLibrary) const;
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
FunctionType const& function = *functionType;
|
FunctionType const& function = *functionType;
|
||||||
if (function.bound())
|
if (function.hasBoundFirstArgument())
|
||||||
solAssert(
|
solAssert(
|
||||||
function.kind() == FunctionType::Kind::DelegateCall ||
|
function.kind() == FunctionType::Kind::DelegateCall ||
|
||||||
function.kind() == FunctionType::Kind::Internal ||
|
function.kind() == FunctionType::Kind::Internal ||
|
||||||
@ -637,7 +637,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
bool shortcutTaken = false;
|
bool shortcutTaken = false;
|
||||||
if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||||
{
|
{
|
||||||
solAssert(!function.bound(), "");
|
solAssert(!function.hasBoundFirstArgument(), "");
|
||||||
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
||||||
{
|
{
|
||||||
// Do not directly visit the identifier, because this way, we can avoid
|
// Do not directly visit the identifier, because this way, we can avoid
|
||||||
@ -657,7 +657,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes());
|
unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes());
|
||||||
if (function.bound())
|
if (function.hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
// stack: arg2, ..., argn, label, arg1
|
// stack: arg2, ..., argn, label, arg1
|
||||||
unsigned depth = parameterSize + 1;
|
unsigned depth = parameterSize + 1;
|
||||||
@ -906,7 +906,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
argumentType &&
|
argumentType &&
|
||||||
functionType->kind() == FunctionType::Kind::External &&
|
functionType->kind() == FunctionType::Kind::External &&
|
||||||
argumentType->kind() == FunctionType::Kind::External &&
|
argumentType->kind() == FunctionType::Kind::External &&
|
||||||
!argumentType->bound(),
|
!argumentType->hasBoundFirstArgument(),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1029,7 +1029,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::ArrayPush:
|
case FunctionType::Kind::ArrayPush:
|
||||||
{
|
{
|
||||||
solAssert(function.bound(), "");
|
solAssert(function.hasBoundFirstArgument(), "");
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
|
|
||||||
if (function.parameterTypes().size() == 0)
|
if (function.parameterTypes().size() == 0)
|
||||||
@ -1095,7 +1095,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::ArrayPop:
|
case FunctionType::Kind::ArrayPop:
|
||||||
{
|
{
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
solAssert(function.bound(), "");
|
solAssert(function.hasBoundFirstArgument(), "");
|
||||||
solAssert(function.parameterTypes().empty(), "");
|
solAssert(function.parameterTypes().empty(), "");
|
||||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType());
|
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType());
|
||||||
solAssert(arrayType && arrayType->dataStoredIn(DataLocation::Storage), "");
|
solAssert(arrayType && arrayType->dataStoredIn(DataLocation::Storage), "");
|
||||||
@ -1476,10 +1476,10 @@ bool ExpressionCompiler::visit(NewExpression const&)
|
|||||||
bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
|
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
|
||||||
// Check whether the member is a bound function.
|
// Check whether the member is an attached function.
|
||||||
ASTString const& member = _memberAccess.memberName();
|
ASTString const& member = _memberAccess.memberName();
|
||||||
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||||
if (funType->bound())
|
if (funType->hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
acceptAndConvert(_memberAccess.expression(), *funType->selfType(), true);
|
acceptAndConvert(_memberAccess.expression(), *funType->selfType(), true);
|
||||||
if (funType->kind() == FunctionType::Kind::Internal)
|
if (funType->kind() == FunctionType::Kind::Internal)
|
||||||
@ -2570,14 +2570,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// function identifier [unless bare]
|
// function identifier [unless bare]
|
||||||
// contract address
|
// contract address
|
||||||
|
|
||||||
unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0;
|
unsigned selfSize = _functionType.hasBoundFirstArgument() ? _functionType.selfType()->sizeOnStack() : 0;
|
||||||
unsigned gasValueSize = (_functionType.gasSet() ? 1u : 0u) + (_functionType.valueSet() ? 1u : 0u);
|
unsigned gasValueSize = (_functionType.gasSet() ? 1u : 0u) + (_functionType.valueSet() ? 1u : 0u);
|
||||||
unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
|
unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
|
||||||
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
|
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
|
||||||
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
|
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
|
||||||
|
|
||||||
// move self object to top
|
// move self object to top
|
||||||
if (_functionType.bound())
|
if (_functionType.hasBoundFirstArgument())
|
||||||
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
||||||
|
|
||||||
auto funKind = _functionType.kind();
|
auto funKind = _functionType.kind();
|
||||||
@ -2605,7 +2605,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// Evaluate arguments.
|
// Evaluate arguments.
|
||||||
TypePointers argumentTypes;
|
TypePointers argumentTypes;
|
||||||
TypePointers parameterTypes = _functionType.parameterTypes();
|
TypePointers parameterTypes = _functionType.parameterTypes();
|
||||||
if (_functionType.bound())
|
if (_functionType.hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
argumentTypes.push_back(_functionType.selfType());
|
argumentTypes.push_back(_functionType.selfType());
|
||||||
parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
|
parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
|
||||||
|
@ -321,7 +321,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
_sourceType.isImplicitlyConvertibleTo(*m_dataType),
|
_sourceType.isImplicitlyConvertibleTo(*m_dataType),
|
||||||
"function item stored but target is not implicitly convertible to source"
|
"function item stored but target is not implicitly convertible to source"
|
||||||
);
|
);
|
||||||
solAssert(!fun->bound(), "");
|
solAssert(!fun->hasBoundFirstArgument(), "");
|
||||||
if (fun->kind() == FunctionType::Kind::External)
|
if (fun->kind() == FunctionType::Kind::External)
|
||||||
{
|
{
|
||||||
solAssert(fun->sizeOnStack() == 2, "");
|
solAssert(fun->sizeOnStack() == 2, "");
|
||||||
|
@ -953,7 +953,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
solAssert(!functionType->takesArbitraryParameters());
|
solAssert(!functionType->takesArbitraryParameters());
|
||||||
|
|
||||||
vector<string> args;
|
vector<string> args;
|
||||||
if (functionType->bound())
|
if (functionType->hasBoundFirstArgument())
|
||||||
args += IRVariable(_functionCall.expression()).part("self").stackSlots();
|
args += IRVariable(_functionCall.expression()).part("self").stackSlots();
|
||||||
|
|
||||||
for (size_t i = 0; i < arguments.size(); ++i)
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
@ -1024,7 +1024,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
solAssert(
|
solAssert(
|
||||||
IRVariable(arg).type() == *functionType &&
|
IRVariable(arg).type() == *functionType &&
|
||||||
functionType->kind() == FunctionType::Kind::External &&
|
functionType->kind() == FunctionType::Kind::External &&
|
||||||
!functionType->bound(),
|
!functionType->hasBoundFirstArgument(),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) <<
|
define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) <<
|
||||||
@ -1353,7 +1353,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::ArrayPop:
|
case FunctionType::Kind::ArrayPop:
|
||||||
{
|
{
|
||||||
solAssert(functionType->bound());
|
solAssert(functionType->hasBoundFirstArgument());
|
||||||
solAssert(functionType->parameterTypes().empty());
|
solAssert(functionType->parameterTypes().empty());
|
||||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||||
solAssert(arrayType);
|
solAssert(arrayType);
|
||||||
@ -1557,7 +1557,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
solAssert(!_functionCall.annotation().tryCall);
|
solAssert(!_functionCall.annotation().tryCall);
|
||||||
solAssert(!functionType->valueSet());
|
solAssert(!functionType->valueSet());
|
||||||
solAssert(!functionType->gasSet());
|
solAssert(!functionType->gasSet());
|
||||||
solAssert(!functionType->bound());
|
solAssert(!functionType->hasBoundFirstArgument());
|
||||||
|
|
||||||
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
||||||
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
||||||
@ -1623,7 +1623,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
|||||||
setLocation(_options);
|
setLocation(_options);
|
||||||
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
|
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
|
||||||
|
|
||||||
solUnimplementedAssert(!previousType.bound());
|
solUnimplementedAssert(!previousType.hasBoundFirstArgument());
|
||||||
|
|
||||||
// Copy over existing values.
|
// Copy over existing values.
|
||||||
for (auto const& item: previousType.stackItems())
|
for (auto const& item: previousType.stackItems())
|
||||||
@ -1668,7 +1668,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||||
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
|
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
|
||||||
|
|
||||||
if (memberFunctionType && memberFunctionType->bound())
|
if (memberFunctionType && memberFunctionType->hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
||||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
||||||
@ -2506,7 +2506,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
TypePointers parameterTypes = funType.parameterTypes();
|
TypePointers parameterTypes = funType.parameterTypes();
|
||||||
TypePointers argumentTypes;
|
TypePointers argumentTypes;
|
||||||
vector<string> argumentStrings;
|
vector<string> argumentStrings;
|
||||||
if (funType.bound())
|
if (funType.hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
parameterTypes.insert(parameterTypes.begin(), funType.selfType());
|
parameterTypes.insert(parameterTypes.begin(), funType.selfType());
|
||||||
argumentTypes.emplace_back(funType.selfType());
|
argumentTypes.emplace_back(funType.selfType());
|
||||||
@ -2655,7 +2655,7 @@ void IRGeneratorForStatements::appendBareCall(
|
|||||||
{
|
{
|
||||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||||
solAssert(
|
solAssert(
|
||||||
!funType.bound() &&
|
!funType.hasBoundFirstArgument() &&
|
||||||
!funType.takesArbitraryParameters() &&
|
!funType.takesArbitraryParameters() &&
|
||||||
_arguments.size() == 1 &&
|
_arguments.size() == 1 &&
|
||||||
funType.parameterTypes().size() == 1, ""
|
funType.parameterTypes().size() == 1, ""
|
||||||
|
@ -3120,12 +3120,12 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
|
|||||||
vector<ASTPointer<Expression const>> arguments = _funCall.sortedArguments();
|
vector<ASTPointer<Expression const>> arguments = _funCall.sortedArguments();
|
||||||
auto functionParams = funDef->parameters();
|
auto functionParams = funDef->parameters();
|
||||||
unsigned firstParam = 0;
|
unsigned firstParam = 0;
|
||||||
if (funType->bound())
|
if (funType->hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
calledExpr = innermostTuple(*calledExpr);
|
calledExpr = innermostTuple(*calledExpr);
|
||||||
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
auto const& attachedFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
||||||
solAssert(boundFunction, "");
|
solAssert(attachedFunction, "");
|
||||||
args.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
|
args.push_back(expr(attachedFunction->expression(), functionParams.front()->type()));
|
||||||
firstParam = 1;
|
firstParam = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ protected:
|
|||||||
void createReturnedExpressions(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
|
void createReturnedExpressions(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
|
||||||
|
|
||||||
/// @returns the symbolic arguments for a function call,
|
/// @returns the symbolic arguments for a function call,
|
||||||
/// taking into account bound functions and
|
/// taking into account attached functions and
|
||||||
/// type conversion.
|
/// type conversion.
|
||||||
std::vector<smtutil::Expression> symbolicArguments(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
|
std::vector<smtutil::Expression> symbolicArguments(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
|
||||||
|
|
||||||
|
@ -2811,7 +2811,7 @@ BOOST_AUTO_TEST_CASE(return_external_function_type)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: store bound internal library functions
|
// TODO: store attached internal library functions
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(shift_bytes)
|
BOOST_AUTO_TEST_CASE(shift_bytes)
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ import {C} from "B";
|
|||||||
contract D {
|
contract D {
|
||||||
function test() public returns (uint) {
|
function test() public returns (uint) {
|
||||||
C c = new C();
|
C c = new C();
|
||||||
// This tests that bound functions are available
|
// This tests that attached functions are available
|
||||||
// even if the type is not available by name.
|
// even if the type is not available by name.
|
||||||
// This is a regular function call, a
|
// This is a regular function call, a
|
||||||
// public and an internal library call
|
// public and an internal library call
|
||||||
|
@ -22,4 +22,4 @@ contract Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (B:269-313): Type function (struct L.Item memory) is not implicitly convertible to expected type function (struct L.Item memory) external. Special functions can not be converted to function types.
|
// TypeError 9574: (B:269-313): Type function (struct L.Item memory) is not implicitly convertible to expected type function (struct L.Item memory) external. Special functions cannot be converted to function types.
|
||||||
|
@ -53,7 +53,7 @@ contract C {
|
|||||||
// TypeError 9553: (415-428): Invalid type for argument in function call. Invalid implicit conversion from function () view external returns (uint256) to function () pure external returns (uint256) requested.
|
// TypeError 9553: (415-428): Invalid type for argument in function call. Invalid implicit conversion from function () view external returns (uint256) to function () pure external returns (uint256) requested.
|
||||||
// TypeError 9553: (465-481): Invalid type for argument in function call. Invalid implicit conversion from function () external returns (uint256) to function () pure external returns (uint256) requested.
|
// TypeError 9553: (465-481): Invalid type for argument in function call. Invalid implicit conversion from function () external returns (uint256) to function () pure external returns (uint256) requested.
|
||||||
// TypeError 9553: (518-545): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) pure external returns (uint256) to function () pure external returns (uint256) requested.
|
// TypeError 9553: (518-545): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) pure external returns (uint256) to function () pure external returns (uint256) requested.
|
||||||
// TypeError 9553: (582-589): Invalid type for argument in function call. Invalid implicit conversion from function () view returns (uint256) to function () pure external returns (uint256) requested. Special functions can not be converted to function types.
|
// TypeError 9553: (582-589): Invalid type for argument in function call. Invalid implicit conversion from function () view returns (uint256) to function () pure external returns (uint256) requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9553: (626-629): Invalid type for argument in function call. Invalid implicit conversion from function () pure returns (uint256) to function () pure external returns (uint256) requested. Special functions can not be converted to function types.
|
// TypeError 9553: (626-629): Invalid type for argument in function call. Invalid implicit conversion from function () pure returns (uint256) to function () pure external returns (uint256) requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9553: (666-686): Invalid type for argument in function call. Invalid implicit conversion from function () pure returns (uint256) to function () pure external returns (uint256) requested. Special functions can not be converted to function types.
|
// TypeError 9553: (666-686): Invalid type for argument in function call. Invalid implicit conversion from function () pure returns (uint256) to function () pure external returns (uint256) requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9582: (723-748): Member "testInternalFunction" not found or not visible after argument-dependent lookup in contract C.
|
// TypeError 9582: (723-748): Member "testInternalFunction" not found or not visible after argument-dependent lookup in contract C.
|
||||||
|
@ -4,4 +4,4 @@ contract C {
|
|||||||
function() internal pure x = E;
|
function() internal pure x = E;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 7407: (58-59): Type error E() is not implicitly convertible to expected type function () pure. Special functions can not be converted to function types.
|
// TypeError 7407: (58-59): Type error E() is not implicitly convertible to expected type function () pure. Special functions cannot be converted to function types.
|
||||||
|
@ -30,6 +30,6 @@ contract E {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9553: (140-143): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions can not be converted to function types.
|
// TypeError 9553: (140-143): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions can not be converted to function types.
|
// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9553: (345-348): Invalid type for argument in function call. Invalid implicit conversion from function D.f() to function () external requested. Special functions can not be converted to function types.
|
// TypeError 9553: (345-348): Invalid type for argument in function call. Invalid implicit conversion from function D.f() to function () external requested. Special functions cannot be converted to function types.
|
||||||
|
@ -4,4 +4,4 @@ contract C {
|
|||||||
function() internal pure x = E;
|
function() internal pure x = E;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 7407: (66-67): Type event E(uint256) is not implicitly convertible to expected type function () pure. Special functions can not be converted to function types.
|
// TypeError 7407: (66-67): Type event E(uint256) is not implicitly convertible to expected type function () pure. Special functions cannot be converted to function types.
|
||||||
|
@ -12,4 +12,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (209-280): Type function (uint256,uint256) pure returns (uint256) is not implicitly convertible to expected type function (uint256,uint256) pure returns (uint256). Bound functions can not be converted to non-bound functions.
|
// TypeError 9574: (209-280): Type function (uint256,uint256) pure returns (uint256) is not implicitly convertible to expected type function (uint256,uint256) pure returns (uint256). Attached functions cannot be converted into unattached functions.
|
||||||
|
@ -4,4 +4,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (42-103): Type function (uint256) view returns (bytes32) is not implicitly convertible to expected type function (uint256) view returns (bytes32). Special functions can not be converted to function types.
|
// TypeError 9574: (42-103): Type function (uint256) view returns (bytes32) is not implicitly convertible to expected type function (uint256) view returns (bytes32). Special functions cannot be converted to function types.
|
||||||
|
@ -10,5 +10,5 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) external returns (uint256) requested. Special functions can not be converted to function types.
|
// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) external returns (uint256) requested. Special functions cannot be converted to function types.
|
||||||
// TypeError 9574: (244-305): Type function (uint256) returns (uint256) is not implicitly convertible to expected type function (uint256) external returns (uint256). Special functions can not be converted to function types.
|
// TypeError 9574: (244-305): Type function (uint256) returns (uint256) is not implicitly convertible to expected type function (uint256) external returns (uint256). Special functions cannot be converted to function types.
|
||||||
|
@ -3,4 +3,4 @@ contract test {
|
|||||||
function(bytes memory) external internal a = fa;
|
function(bytes memory) external internal a = fa;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 7407: (106-108): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external. Special functions can not be converted to function types.
|
// TypeError 7407: (106-108): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external. Special functions cannot be converted to function types.
|
||||||
|
@ -2,10 +2,10 @@ library D { function double(uint self) public returns (uint) { return 2; } }
|
|||||||
contract C {
|
contract C {
|
||||||
using D for uint;
|
using D for uint;
|
||||||
function f(uint16 a) public returns (uint) {
|
function f(uint16 a) public returns (uint) {
|
||||||
// This is an error because the function is only bound to uint.
|
// This is an error because the function is only attached to uint.
|
||||||
// Had it been bound to *, it would have worked.
|
// Had it been attached to *, it would have worked.
|
||||||
return a.double();
|
return a.double();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9582: (305-313): Member "double" not found or not visible after argument-dependent lookup in uint16.
|
// TypeError 9582: (311-319): Member "double" not found or not visible after argument-dependent lookup in uint16.
|
||||||
|
@ -2,9 +2,9 @@ library D { function double(bytes32 self) public returns (uint) { return 2; } }
|
|||||||
contract C {
|
contract C {
|
||||||
using D for *;
|
using D for *;
|
||||||
function f(uint a) public returns (uint) {
|
function f(uint a) public returns (uint) {
|
||||||
// Bound to a, but self type does not match.
|
// Attached to a, but self type does not match.
|
||||||
return a.double();
|
return a.double();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9582: (227-235): Member "double" not found or not visible after argument-dependent lookup in uint256.
|
// TypeError 9582: (230-238): Member "double" not found or not visible after argument-dependent lookup in uint256.
|
||||||
|
@ -9,5 +9,5 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (218-271): Type function (struct D.s storage pointer,uint256) returns (uint256) is not implicitly convertible to expected type function (struct D.s storage pointer,uint256) returns (uint256). Bound functions can not be converted to non-bound functions.
|
// TypeError 9574: (218-271): Type function (struct D.s storage pointer,uint256) returns (uint256) is not implicitly convertible to expected type function (struct D.s storage pointer,uint256) returns (uint256). Attached functions cannot be converted into unattached functions.
|
||||||
// TypeError 6160: (298-302): Wrong argument count for function call: 1 arguments given but expected 2.
|
// TypeError 6160: (298-302): Wrong argument count for function call: 1 arguments given but expected 2.
|
||||||
|
@ -43,7 +43,7 @@ contract C is Base {
|
|||||||
abi.encodeCall(L.fInternal, (1, "123"));
|
abi.encodeCall(L.fInternal, (1, "123"));
|
||||||
abi.encodeCall(L.fExternal, (1, "123"));
|
abi.encodeCall(L.fExternal, (1, "123"));
|
||||||
}
|
}
|
||||||
function failBoundLibraryPointerCall() public returns (bytes memory) {
|
function failAttachedLibraryPointerCall() public returns (bytes memory) {
|
||||||
uint256 x = 1;
|
uint256 x = 1;
|
||||||
return abi.encodeCall(x.fExternal, (1, "123"));
|
return abi.encodeCall(x.fExternal, (1, "123"));
|
||||||
}
|
}
|
||||||
@ -70,9 +70,9 @@ contract C is Base {
|
|||||||
// TypeError 3509: (1187-1207): Expected regular external function type, or external view on public function. Provided internal function.
|
// TypeError 3509: (1187-1207): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
// TypeError 3509: (1286-1297): Expected regular external function type, or external view on public function. Provided internal function.
|
// TypeError 3509: (1286-1297): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
// TypeError 3509: (1329-1340): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
// TypeError 3509: (1329-1340): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
||||||
// TypeError 3509: (1471-1482): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
// TypeError 3509: (1474-1485): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
||||||
// TypeError 3509: (1592-1601): Expected regular external function type, or external view on public function. Provided internal function. Did you forget to prefix "this."?
|
// TypeError 3509: (1595-1604): Expected regular external function type, or external view on public function. Provided internal function. Did you forget to prefix "this."?
|
||||||
// TypeError 3509: (1722-1745): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
// TypeError 3509: (1725-1748): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
||||||
// TypeError 3509: (1771-1796): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
// TypeError 3509: (1774-1799): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
||||||
// TypeError 3509: (1902-1911): Expected regular external function type, or external view on public function. Provided internal function.
|
// TypeError 3509: (1905-1914): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
// TypeError 3509: (2010-2019): Expected regular external function type, or external view on public function. Provided creation function.
|
// TypeError 3509: (2013-2022): Expected regular external function type, or external view on public function. Provided creation function.
|
||||||
|
@ -10,5 +10,5 @@ contract B is A {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (133-160): Type function A.f() is not implicitly convertible to expected type function () external. Special functions can not be converted to function types.
|
// TypeError 9574: (133-160): Type function A.f() is not implicitly convertible to expected type function () external. Special functions cannot be converted to function types.
|
||||||
// TypeError 9574: (170-202): Type function A.g() pure is not implicitly convertible to expected type function () pure external. Special functions can not be converted to function types.
|
// TypeError 9574: (170-202): Type function A.g() pure is not implicitly convertible to expected type function () pure external. Special functions cannot be converted to function types.
|
||||||
|
@ -10,5 +10,5 @@ contract B {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (128-155): Type function A.f() is not implicitly convertible to expected type function () external. Special functions can not be converted to function types.
|
// TypeError 9574: (128-155): Type function A.f() is not implicitly convertible to expected type function () external. Special functions cannot be converted to function types.
|
||||||
// TypeError 9574: (165-197): Type function A.g() pure is not implicitly convertible to expected type function () pure external. Special functions can not be converted to function types.
|
// TypeError 9574: (165-197): Type function A.g() pure is not implicitly convertible to expected type function () pure external. Special functions cannot be converted to function types.
|
||||||
|
@ -4,5 +4,5 @@ function test() pure {
|
|||||||
function (int) returns (MyInt) g = MyInt.wrap;
|
function (int) returns (MyInt) g = MyInt.wrap;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 9574: (46-93): Type function (MyInt) pure returns (int256) is not implicitly convertible to expected type function (MyInt) returns (int256). Special functions can not be converted to function types.
|
// TypeError 9574: (46-93): Type function (MyInt) pure returns (int256) is not implicitly convertible to expected type function (MyInt) returns (int256). Special functions cannot be converted to function types.
|
||||||
// TypeError 9574: (99-144): Type function (int256) pure returns (MyInt) is not implicitly convertible to expected type function (int256) returns (MyInt). Special functions can not be converted to function types.
|
// TypeError 9574: (99-144): Type function (int256) pure returns (MyInt) is not implicitly convertible to expected type function (int256) returns (MyInt). Special functions cannot be converted to function types.
|
||||||
|
@ -10,5 +10,5 @@ contract C {
|
|||||||
using {id} for S;
|
using {id} for S;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 3100: (112-114): The function "id" cannot be bound to the type "uint256" because the type cannot be implicitly converted to the first argument of the function ("uint16").
|
// TypeError 3100: (112-114): The function "id" cannot be attached to the type "uint256" because the type cannot be implicitly converted to the first argument of the function ("uint16").
|
||||||
// TypeError 3100: (140-142): The function "id" cannot be bound to the type "struct S" because the type cannot be implicitly converted to the first argument of the function ("uint16").
|
// TypeError 3100: (140-142): The function "id" cannot be attached to the type "struct S" because the type cannot be implicitly converted to the first argument of the function ("uint16").
|
||||||
|
@ -2,4 +2,4 @@ using {f} for * global;
|
|||||||
function f(uint) pure{}
|
function f(uint) pure{}
|
||||||
// ----
|
// ----
|
||||||
// SyntaxError 8118: (0-23): The type has to be specified explicitly at file level (cannot use '*').
|
// SyntaxError 8118: (0-23): The type has to be specified explicitly at file level (cannot use '*').
|
||||||
// SyntaxError 2854: (0-23): Can only globally bind functions to specific types.
|
// SyntaxError 2854: (0-23): Can only globally attach functions to specific types.
|
||||||
|
@ -2,4 +2,4 @@ using L for * global;
|
|||||||
library L {}
|
library L {}
|
||||||
// ----
|
// ----
|
||||||
// SyntaxError 8118: (0-21): The type has to be specified explicitly at file level (cannot use '*').
|
// SyntaxError 8118: (0-21): The type has to be specified explicitly at file level (cannot use '*').
|
||||||
// SyntaxError 2854: (0-21): Can only globally bind functions to specific types.
|
// SyntaxError 2854: (0-21): Can only globally attach functions to specific types.
|
||||||
|
@ -7,7 +7,7 @@ function f1(S memory _x) pure returns (uint) { return _x.x + 1; }
|
|||||||
==== Source: B ====
|
==== Source: B ====
|
||||||
contract C {
|
contract C {
|
||||||
// Here, f points to f1, so we end up with two different functions
|
// Here, f points to f1, so we end up with two different functions
|
||||||
// bound as S.f
|
// attached as S.f
|
||||||
using {f} for S;
|
using {f} for S;
|
||||||
|
|
||||||
function test() pure public
|
function test() pure public
|
||||||
@ -18,4 +18,4 @@ contract C {
|
|||||||
import {gen as g, f1 as f, S} from "A";
|
import {gen as g, f1 as f, S} from "A";
|
||||||
import "A" as A;
|
import "A" as A;
|
||||||
// ----
|
// ----
|
||||||
// TypeError 6675: (B:181-186): Member "f" not unique after argument-dependent lookup in struct S memory.
|
// TypeError 6675: (B:184-189): Member "f" not unique after argument-dependent lookup in struct S memory.
|
||||||
|
@ -2,4 +2,4 @@ using {f} for * global;
|
|||||||
function f(uint) pure{}
|
function f(uint) pure{}
|
||||||
// ----
|
// ----
|
||||||
// SyntaxError 8118: (0-23): The type has to be specified explicitly at file level (cannot use '*').
|
// SyntaxError 8118: (0-23): The type has to be specified explicitly at file level (cannot use '*').
|
||||||
// SyntaxError 2854: (0-23): Can only globally bind functions to specific types.
|
// SyntaxError 2854: (0-23): Can only globally attach functions to specific types.
|
||||||
|
@ -4,4 +4,4 @@ function one() pure returns(uint) {
|
|||||||
|
|
||||||
using {one} for uint;
|
using {one} for uint;
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4731: (60-63): The function "one" does not have any parameters, and therefore cannot be bound to the type "uint256".
|
// TypeError 4731: (60-63): The function "one" does not have any parameters, and therefore cannot be attached to the type "uint256".
|
||||||
|
@ -2,4 +2,4 @@ struct S { uint8 x; }
|
|||||||
function f() {}
|
function f() {}
|
||||||
using {f} for S;
|
using {f} for S;
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4731: (45-46): The function "f" does not have any parameters, and therefore cannot be bound to the type "struct S".
|
// TypeError 4731: (45-46): The function "f" does not have any parameters, and therefore cannot be attached to the type "struct S".
|
||||||
|
@ -5,5 +5,5 @@ contract C {
|
|||||||
function g(uint) public { }
|
function g(uint) public { }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 4167: (24-25): Only file-level functions and library functions can be bound to a type in a "using" statement
|
// TypeError 4167: (24-25): Only file-level functions and library functions can be attached to a type in a "using" statement
|
||||||
// TypeError 4167: (27-28): Only file-level functions and library functions can be bound to a type in a "using" statement
|
// TypeError 4167: (27-28): Only file-level functions and library functions can be attached to a type in a "using" statement
|
||||||
|
Loading…
Reference in New Issue
Block a user