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?
|
||||
- [ ] 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?
|
||||
- [ ] Bound library functions? Functions bound with `using for`?
|
||||
- [ ] Attached library functions? Functions attached with `using for`?
|
||||
- [ ] Possible combinations of `storage`, `memory`, `calldata`, `immutable`, `constant`?
|
||||
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?
|
||||
|
@ -17,16 +17,16 @@ at contract level.
|
||||
The first part, ``A``, can be one of:
|
||||
|
||||
- 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;``) -
|
||||
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).
|
||||
Inside contracts, you can also use ``using L for *;``,
|
||||
which has the effect that all functions of the library ``L``
|
||||
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
|
||||
match the type of the object. The type is checked at the
|
||||
point the function is called and function overload
|
||||
|
@ -312,7 +312,7 @@ errorDefinition:
|
||||
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.
|
||||
*/
|
||||
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
|
||||
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
|
||||
disallowed.
|
||||
|
||||
|
@ -470,7 +470,7 @@ bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor)
|
||||
m_errorReporter.typeError(
|
||||
4167_error,
|
||||
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
|
||||
|
@ -427,7 +427,7 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
|
||||
m_errorReporter.syntaxError(
|
||||
2854_error,
|
||||
_usingFor.location(),
|
||||
"Can only globally bind functions to specific types."
|
||||
"Can only globally attach functions to specific types."
|
||||
);
|
||||
if (_usingFor.global() && m_currentContractKind)
|
||||
m_errorReporter.syntaxError(
|
||||
|
@ -3167,7 +3167,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type))
|
||||
{
|
||||
solAssert(
|
||||
!funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
|
||||
!funType->hasBoundFirstArgument() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
|
||||
"Function \"" + memberName + "\" cannot be called on an object of type " +
|
||||
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>)."
|
||||
);
|
||||
|
||||
if (!funType->bound())
|
||||
if (!funType->hasBoundFirstArgument())
|
||||
if (auto typeType = dynamic_cast<TypeType const*>(exprType))
|
||||
{
|
||||
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
||||
@ -3810,13 +3810,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
4731_error,
|
||||
path->location(),
|
||||
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(), "."),
|
||||
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(), "");
|
||||
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
||||
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
|
||||
@ -3826,7 +3826,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
3100_error,
|
||||
path->location(),
|
||||
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 (\"{}\"){}",
|
||||
joinHumanReadable(path->path(), "."),
|
||||
usingForType->toString(true /* withoutDataLocation */),
|
||||
|
@ -644,10 +644,9 @@ private:
|
||||
/**
|
||||
* 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.
|
||||
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ...,
|
||||
* `fn`, respectively to `T`.
|
||||
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., `fn`, respectively to `T`.
|
||||
*
|
||||
* 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
|
||||
|
@ -450,7 +450,7 @@ FunctionType const* TypeProvider::function(
|
||||
)
|
||||
{
|
||||
// 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>(
|
||||
_parameterTypes,
|
||||
_returnParameterTypes,
|
||||
|
@ -305,7 +305,7 @@ MemberList const& Type::members(ASTNode const* _currentScope) const
|
||||
"");
|
||||
MemberList::MemberMap members = nativeMembers(_currentScope);
|
||||
if (_currentScope)
|
||||
members += boundFunctions(*this, *_currentScope);
|
||||
members += attachedFunctions(*this, *_currentScope);
|
||||
m_members[_currentScope] = make_unique<MemberList>(std::move(members));
|
||||
}
|
||||
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;
|
||||
|
||||
@ -395,13 +395,13 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
Type const* functionType =
|
||||
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
|
||||
solAssert(functionType, "");
|
||||
FunctionType const* asBoundFunction =
|
||||
dynamic_cast<FunctionType const&>(*functionType).asBoundFunction();
|
||||
solAssert(asBoundFunction, "");
|
||||
FunctionType const* withBoundFirstArgument =
|
||||
dynamic_cast<FunctionType const&>(*functionType).withBoundFirstArgument();
|
||||
solAssert(withBoundFirstArgument, "");
|
||||
|
||||
if (_type.isImplicitlyConvertibleTo(*asBoundFunction->selfType()))
|
||||
if (_type.isImplicitlyConvertibleTo(*withBoundFirstArgument->selfType()))
|
||||
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))
|
||||
@ -1879,21 +1879,21 @@ MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const
|
||||
strings{string()},
|
||||
strings{string()},
|
||||
FunctionType::Kind::ArrayPush
|
||||
)->asBoundFunction());
|
||||
)->withBoundFirstArgument());
|
||||
members.emplace_back("push", TypeProvider::function(
|
||||
TypePointers{thisAsPointer, baseType()},
|
||||
TypePointers{},
|
||||
strings{string(),string()},
|
||||
strings{},
|
||||
FunctionType::Kind::ArrayPush
|
||||
)->asBoundFunction());
|
||||
)->withBoundFirstArgument());
|
||||
members.emplace_back("pop", TypeProvider::function(
|
||||
TypePointers{thisAsPointer},
|
||||
TypePointers{},
|
||||
strings{string()},
|
||||
strings{},
|
||||
FunctionType::Kind::ArrayPop
|
||||
)->asBoundFunction());
|
||||
)->withBoundFirstArgument());
|
||||
}
|
||||
}
|
||||
return members;
|
||||
@ -2952,7 +2952,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
|
||||
|
||||
vector<string> FunctionType::parameterNames() const
|
||||
{
|
||||
if (!bound())
|
||||
if (!hasBoundFirstArgument())
|
||||
return m_parameterNames;
|
||||
return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
|
||||
}
|
||||
@ -2981,7 +2981,7 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const
|
||||
|
||||
TypePointers FunctionType::parameterTypes() const
|
||||
{
|
||||
if (!bound())
|
||||
if (!hasBoundFirstArgument())
|
||||
return m_parameterTypes;
|
||||
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
||||
}
|
||||
@ -3046,8 +3046,8 @@ string FunctionType::richIdentifier() const
|
||||
id += "value";
|
||||
if (saltSet())
|
||||
id += "salt";
|
||||
if (bound())
|
||||
id += "bound_to" + identifierList(selfType());
|
||||
if (hasBoundFirstArgument())
|
||||
id += "attached_to" + identifierList(selfType());
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -3081,8 +3081,8 @@ BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
|
||||
|
||||
// These two checks are duplicated in equalExcludingStateMutability, but are added here for error reporting.
|
||||
if (convertTo.bound() != bound())
|
||||
return BoolResult::err("Bound functions can not be converted to non-bound functions.");
|
||||
if (convertTo.hasBoundFirstArgument() != hasBoundFirstArgument())
|
||||
return BoolResult::err("Attached functions cannot be converted into unattached functions.");
|
||||
|
||||
if (convertTo.kind() != kind())
|
||||
return BoolResult::err("Special functions cannot be converted to function types.");
|
||||
@ -3122,10 +3122,10 @@ TypeResult FunctionType::binaryOperatorResult(Token _operator, Type const* _othe
|
||||
else if (
|
||||
kind() == Kind::External &&
|
||||
sizeOnStack() == 2 &&
|
||||
!bound() &&
|
||||
!hasBoundFirstArgument() &&
|
||||
other.kind() == Kind::External &&
|
||||
other.sizeOnStack() == 2 &&
|
||||
!other.bound()
|
||||
!other.hasBoundFirstArgument()
|
||||
)
|
||||
return commonType(this, _other);
|
||||
|
||||
@ -3210,7 +3210,7 @@ bool FunctionType::nameable() const
|
||||
{
|
||||
return
|
||||
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
||||
!bound() &&
|
||||
!hasBoundFirstArgument() &&
|
||||
!takesArbitraryParameters() &&
|
||||
!gasSet() &&
|
||||
!valueSet() &&
|
||||
@ -3249,7 +3249,7 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
||||
break;
|
||||
case Kind::ArrayPush:
|
||||
case Kind::ArrayPop:
|
||||
solAssert(bound(), "");
|
||||
solAssert(hasBoundFirstArgument(), "");
|
||||
slots = {};
|
||||
break;
|
||||
default:
|
||||
@ -3262,7 +3262,7 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
||||
slots.emplace_back("value", TypeProvider::uint256());
|
||||
if (saltSet())
|
||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||
if (bound())
|
||||
if (hasBoundFirstArgument())
|
||||
slots.emplace_back("self", m_parameterTypes.front());
|
||||
return slots;
|
||||
}
|
||||
@ -3423,7 +3423,7 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const
|
||||
|
||||
Type const* FunctionType::mobileType() const
|
||||
{
|
||||
if (valueSet() || gasSet() || saltSet() || bound())
|
||||
if (valueSet() || gasSet() || saltSet() || hasBoundFirstArgument())
|
||||
return nullptr;
|
||||
|
||||
// return function without parameter names
|
||||
@ -3444,8 +3444,8 @@ bool FunctionType::canTakeArguments(
|
||||
Type const* _selfType
|
||||
) const
|
||||
{
|
||||
solAssert(!bound() || _selfType, "");
|
||||
if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
|
||||
solAssert(!hasBoundFirstArgument() || _selfType, "");
|
||||
if (hasBoundFirstArgument() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
|
||||
return false;
|
||||
TypePointers paramTypes = parameterTypes();
|
||||
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())
|
||||
return false;
|
||||
|
||||
if (bound() != _other.bound())
|
||||
if (hasBoundFirstArgument() != _other.hasBoundFirstArgument())
|
||||
return false;
|
||||
|
||||
solAssert(!bound() || *selfType() == *_other.selfType(), "");
|
||||
solAssert(!hasBoundFirstArgument() || *selfType() == *_other.selfType(), "");
|
||||
|
||||
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(!gasSet(), "");
|
||||
solAssert(!valueSet(), "");
|
||||
solAssert(!saltSet(), "");
|
||||
Options options = Options::fromFunctionType(*this);
|
||||
options.bound = true;
|
||||
options.hasBoundFirstArgument = true;
|
||||
return TypeProvider::function(
|
||||
m_parameterTypes,
|
||||
m_returnParameterTypes,
|
||||
@ -3710,7 +3710,7 @@ FunctionTypePointer FunctionType::asExternallyCallableFunction(bool _inLibrary)
|
||||
|
||||
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.");
|
||||
return m_parameterTypes.at(0);
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ public:
|
||||
/// given location.
|
||||
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.
|
||||
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.
|
||||
@ -379,11 +379,11 @@ public:
|
||||
|
||||
private:
|
||||
/// @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:
|
||||
/// @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
|
||||
{
|
||||
return MemberList::MemberMap();
|
||||
@ -1272,7 +1272,7 @@ public:
|
||||
bool saltSet = false;
|
||||
/// true iff the function is called as arg1.fun(arg2, ..., argn).
|
||||
/// This is achieved through the "using for" directive.
|
||||
bool bound = false;
|
||||
bool hasBoundFirstArgument = false;
|
||||
|
||||
static Options withArbitraryParameters()
|
||||
{
|
||||
@ -1287,7 +1287,7 @@ public:
|
||||
result.gasSet = _type.gasSet();
|
||||
result.valueSet = _type.valueSet();
|
||||
result.saltSet = _type.saltSet();
|
||||
result.bound = _type.bound();
|
||||
result.hasBoundFirstArgument = _type.hasBoundFirstArgument();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@ -1322,7 +1322,7 @@ public:
|
||||
)
|
||||
{
|
||||
// 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.
|
||||
@ -1354,8 +1354,8 @@ public:
|
||||
"Return parameter names list must match return parameter types list!"
|
||||
);
|
||||
solAssert(
|
||||
!bound() || !m_parameterTypes.empty(),
|
||||
"Attempted construction of bound function without self type"
|
||||
!hasBoundFirstArgument() || !m_parameterTypes.empty(),
|
||||
"Attempted construction of attached function without self type"
|
||||
);
|
||||
}
|
||||
|
||||
@ -1372,7 +1372,7 @@ public:
|
||||
/// storage pointers) are replaced by InaccessibleDynamicType instances.
|
||||
TypePointers returnParameterTypesWithoutDynamicTypes() const;
|
||||
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;
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
@ -1406,8 +1406,8 @@ public:
|
||||
|
||||
/// @returns true if this function can take the given arguments (possibly
|
||||
/// after implicit conversion).
|
||||
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
|
||||
/// expression the function is called on.
|
||||
/// @param _selfType if the function is attached as a member function, this has to be supplied
|
||||
/// and is the type of the expression the function is called on.
|
||||
bool canTakeArguments(
|
||||
FuncCallArguments const& _arguments,
|
||||
Type const* _selfType = nullptr
|
||||
@ -1471,20 +1471,20 @@ public:
|
||||
bool gasSet() const { return m_options.gasSet; }
|
||||
bool valueSet() const { return m_options.valueSet; }
|
||||
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
|
||||
/// of the parameters to false.
|
||||
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.
|
||||
FunctionTypePointer asBoundFunction() const;
|
||||
FunctionTypePointer withBoundFirstArgument() const;
|
||||
|
||||
/// @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
|
||||
/// 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.
|
||||
FunctionTypePointer asExternallyCallableFunction(bool _inLibrary) const;
|
||||
|
||||
|
@ -612,7 +612,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
else
|
||||
{
|
||||
FunctionType const& function = *functionType;
|
||||
if (function.bound())
|
||||
if (function.hasBoundFirstArgument())
|
||||
solAssert(
|
||||
function.kind() == FunctionType::Kind::DelegateCall ||
|
||||
function.kind() == FunctionType::Kind::Internal ||
|
||||
@ -637,7 +637,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
bool shortcutTaken = false;
|
||||
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))
|
||||
{
|
||||
// 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());
|
||||
if (function.bound())
|
||||
if (function.hasBoundFirstArgument())
|
||||
{
|
||||
// stack: arg2, ..., argn, label, arg1
|
||||
unsigned depth = parameterSize + 1;
|
||||
@ -906,7 +906,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
argumentType &&
|
||||
functionType->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:
|
||||
{
|
||||
solAssert(function.bound(), "");
|
||||
solAssert(function.hasBoundFirstArgument(), "");
|
||||
_functionCall.expression().accept(*this);
|
||||
|
||||
if (function.parameterTypes().size() == 0)
|
||||
@ -1095,7 +1095,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ArrayPop:
|
||||
{
|
||||
_functionCall.expression().accept(*this);
|
||||
solAssert(function.bound(), "");
|
||||
solAssert(function.hasBoundFirstArgument(), "");
|
||||
solAssert(function.parameterTypes().empty(), "");
|
||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType());
|
||||
solAssert(arrayType && arrayType->dataStoredIn(DataLocation::Storage), "");
|
||||
@ -1476,10 +1476,10 @@ bool ExpressionCompiler::visit(NewExpression const&)
|
||||
bool ExpressionCompiler::visit(MemberAccess const& _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();
|
||||
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
if (funType->bound())
|
||||
if (funType->hasBoundFirstArgument())
|
||||
{
|
||||
acceptAndConvert(_memberAccess.expression(), *funType->selfType(), true);
|
||||
if (funType->kind() == FunctionType::Kind::Internal)
|
||||
@ -2570,14 +2570,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
// function identifier [unless bare]
|
||||
// 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 contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
|
||||
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
|
||||
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
|
||||
|
||||
// move self object to top
|
||||
if (_functionType.bound())
|
||||
if (_functionType.hasBoundFirstArgument())
|
||||
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
||||
|
||||
auto funKind = _functionType.kind();
|
||||
@ -2605,7 +2605,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
// Evaluate arguments.
|
||||
TypePointers argumentTypes;
|
||||
TypePointers parameterTypes = _functionType.parameterTypes();
|
||||
if (_functionType.bound())
|
||||
if (_functionType.hasBoundFirstArgument())
|
||||
{
|
||||
argumentTypes.push_back(_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),
|
||||
"function item stored but target is not implicitly convertible to source"
|
||||
);
|
||||
solAssert(!fun->bound(), "");
|
||||
solAssert(!fun->hasBoundFirstArgument(), "");
|
||||
if (fun->kind() == FunctionType::Kind::External)
|
||||
{
|
||||
solAssert(fun->sizeOnStack() == 2, "");
|
||||
|
@ -953,7 +953,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(!functionType->takesArbitraryParameters());
|
||||
|
||||
vector<string> args;
|
||||
if (functionType->bound())
|
||||
if (functionType->hasBoundFirstArgument())
|
||||
args += IRVariable(_functionCall.expression()).part("self").stackSlots();
|
||||
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
@ -1024,7 +1024,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(
|
||||
IRVariable(arg).type() == *functionType &&
|
||||
functionType->kind() == FunctionType::Kind::External &&
|
||||
!functionType->bound(),
|
||||
!functionType->hasBoundFirstArgument(),
|
||||
""
|
||||
);
|
||||
define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) <<
|
||||
@ -1353,7 +1353,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::ArrayPop:
|
||||
{
|
||||
solAssert(functionType->bound());
|
||||
solAssert(functionType->hasBoundFirstArgument());
|
||||
solAssert(functionType->parameterTypes().empty());
|
||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||
solAssert(arrayType);
|
||||
@ -1557,7 +1557,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(!_functionCall.annotation().tryCall);
|
||||
solAssert(!functionType->valueSet());
|
||||
solAssert(!functionType->gasSet());
|
||||
solAssert(!functionType->bound());
|
||||
solAssert(!functionType->hasBoundFirstArgument());
|
||||
|
||||
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
||||
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
||||
@ -1623,7 +1623,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
||||
setLocation(_options);
|
||||
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
|
||||
|
||||
solUnimplementedAssert(!previousType.bound());
|
||||
solUnimplementedAssert(!previousType.hasBoundFirstArgument());
|
||||
|
||||
// Copy over existing values.
|
||||
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);
|
||||
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
|
||||
|
||||
if (memberFunctionType && memberFunctionType->bound())
|
||||
if (memberFunctionType && memberFunctionType->hasBoundFirstArgument())
|
||||
{
|
||||
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
||||
@ -2506,7 +2506,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
TypePointers parameterTypes = funType.parameterTypes();
|
||||
TypePointers argumentTypes;
|
||||
vector<string> argumentStrings;
|
||||
if (funType.bound())
|
||||
if (funType.hasBoundFirstArgument())
|
||||
{
|
||||
parameterTypes.insert(parameterTypes.begin(), funType.selfType());
|
||||
argumentTypes.emplace_back(funType.selfType());
|
||||
@ -2655,7 +2655,7 @@ void IRGeneratorForStatements::appendBareCall(
|
||||
{
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||
solAssert(
|
||||
!funType.bound() &&
|
||||
!funType.hasBoundFirstArgument() &&
|
||||
!funType.takesArbitraryParameters() &&
|
||||
_arguments.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();
|
||||
auto functionParams = funDef->parameters();
|
||||
unsigned firstParam = 0;
|
||||
if (funType->bound())
|
||||
if (funType->hasBoundFirstArgument())
|
||||
{
|
||||
calledExpr = innermostTuple(*calledExpr);
|
||||
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
||||
solAssert(boundFunction, "");
|
||||
args.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
|
||||
auto const& attachedFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
||||
solAssert(attachedFunction, "");
|
||||
args.push_back(expr(attachedFunction->expression(), functionParams.front()->type()));
|
||||
firstParam = 1;
|
||||
}
|
||||
|
||||
|
@ -401,7 +401,7 @@ protected:
|
||||
void createReturnedExpressions(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
|
||||
|
||||
/// @returns the symbolic arguments for a function call,
|
||||
/// taking into account bound functions and
|
||||
/// taking into account attached functions and
|
||||
/// type conversion.
|
||||
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)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ import {C} from "B";
|
||||
contract D {
|
||||
function test() public returns (uint) {
|
||||
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.
|
||||
// This is a regular function call, a
|
||||
// public and an internal library call
|
||||
|
@ -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.
|
||||
|
@ -2,10 +2,10 @@ library D { function double(uint self) public returns (uint) { return 2; } }
|
||||
contract C {
|
||||
using D for uint;
|
||||
function f(uint16 a) public returns (uint) {
|
||||
// This is an error because the function is only bound to uint.
|
||||
// Had it been bound to *, it would have worked.
|
||||
// This is an error because the function is only attached to uint.
|
||||
// Had it been attached to *, it would have worked.
|
||||
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 {
|
||||
using D for *;
|
||||
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();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// 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.
|
||||
|
@ -43,7 +43,7 @@ contract C is Base {
|
||||
abi.encodeCall(L.fInternal, (1, "123"));
|
||||
abi.encodeCall(L.fExternal, (1, "123"));
|
||||
}
|
||||
function failBoundLibraryPointerCall() public returns (bytes memory) {
|
||||
function failAttachedLibraryPointerCall() public returns (bytes memory) {
|
||||
uint256 x = 1;
|
||||
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: (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: (1471-1482): 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: (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: (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: (1902-1911): 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: (1474-1485): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
||||
// 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: (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: (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: (1905-1914): Expected regular external function type, or external view on public function. Provided internal function.
|
||||
// TypeError 3509: (2013-2022): Expected regular external function type, or external view on public function. Provided creation function.
|
||||
|
@ -10,5 +10,5 @@ contract C {
|
||||
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: (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: (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 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{}
|
||||
// ----
|
||||
// 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 {}
|
||||
// ----
|
||||
// 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 ====
|
||||
contract C {
|
||||
// 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;
|
||||
|
||||
function test() pure public
|
||||
@ -18,4 +18,4 @@ contract C {
|
||||
import {gen as g, f1 as f, S} from "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{}
|
||||
// ----
|
||||
// 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;
|
||||
// ----
|
||||
// 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() {}
|
||||
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 { }
|
||||
}
|
||||
// ----
|
||||
// TypeError 4167: (24-25): 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 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 attached to a type in a "using" statement
|
||||
|
Loading…
Reference in New Issue
Block a user