Skip to content

Commit

Permalink
Merge pull request ethereum#13741 from ethereum/consistent-bound-func…
Browse files Browse the repository at this point in the history
…tion-terminology

Consistent terminology for attached/bound functions
  • Loading branch information
cameel authored Dec 14, 2022
2 parents a9fe05e + abbf2cb commit e769c79
Show file tree
Hide file tree
Showing 88 changed files with 137 additions and 137 deletions.
2 changes: 1 addition & 1 deletion ReviewChecklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
15 changes: 8 additions & 7 deletions docs/contracts/using-for.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@ 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.
- the name of a library (``using L for uint;``) -
all functions (both public and internal ones) of the library are attached to the type
- A list of file-level or library functions (e.g. ``using {f, g, h, L.t} for uint;``) -
only those functions will be attached to the type as member functions.
Note that private library functions can only be specified when ``using for`` is inside the library.
- The name of a library (e.g. ``using L for uint;``) -
all non-private functions 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 *;``,
Inside contracts, you can also use ``*`` in place of the type (e.g. ``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
resolution is performed.

If you use a list of functions (``using {f, g, h, L.t} for uint;``),
If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint;``),
then the type (``uint``) has to be implicitly convertible to the
first parameter of each of these functions. This check is
performed even if none of these functions are called.
Expand Down
2 changes: 1 addition & 1 deletion docs/grammar/SolidityParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion docs/types/value-types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion libsolidity/analysis/DeclarationTypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/analysis/SyntaxChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
10 changes: 5 additions & 5 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() + ")."
);
Expand All @@ -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());
Expand Down Expand Up @@ -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())
Expand All @@ -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 */),
Expand Down
5 changes: 2 additions & 3 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,10 +644,9 @@ class InheritanceSpecifier: public ASTNode
/**
* 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
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/TypeProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
60 changes: 30 additions & 30 deletions libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;

Expand All @@ -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))
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -3081,11 +3081,11 @@ 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 can not be converted to function types.");
return BoolResult::err("Special functions cannot be converted to function types.");

if (!equalExcludingStateMutability(convertTo))
return false;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -3210,7 +3210,7 @@ bool FunctionType::nameable() const
{
return
(m_kind == Kind::Internal || m_kind == Kind::External) &&
!bound() &&
!hasBoundFirstArgument() &&
!takesArbitraryParameters() &&
!gasSet() &&
!valueSet() &&
Expand Down Expand Up @@ -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:
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit e769c79

Please sign in to comment.