Skip to content

Commit e154d43

Browse files
authored
Merge pull request #12288 from ethereum/exportUsing
Using global
2 parents e19c366 + 9188519 commit e154d43

26 files changed

+307
-6
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Language Features:
44
* General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model.
55
* General: ``using M for Type;`` is allowed at file level and ``M`` can now also be a brace-enclosed list of free functions or library functions.
6+
* General: ``using ... for T global;`` is allowed at file level where the user-defined type ``T`` has been defined, resulting in the effect of the statement being available everywhere ``T`` is available.
67

78

89
Compiler Features:

docs/contracts/using-for.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ scope (either the contract or the current module/source unit),
4242
including within all of its functions, and has no effect
4343
outside of the contract or module in which it is used.
4444

45+
When the directive is used at file level and applied to a
46+
user-defined type which was defined at file level in the same file,
47+
the word ``global`` can be added at the end. This will have the
48+
effect that the functions are attached to the type everywhere
49+
the type is available (including other files), not only in the
50+
scope of the using statement.
51+
4552
Let us rewrite the set example from the
4653
:ref:`libraries` section in this way, using file-level functions
4754
instead of library functions.

docs/grammar/SolidityLexer.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ FixedBytes:
4747
'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32';
4848
For: 'for';
4949
Function: 'function';
50+
Global: 'global'; // not a real keyword
5051
Hex: 'hex';
5152
If: 'if';
5253
Immutable: 'immutable';

docs/grammar/SolidityParser.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ errorDefinition:
315315
* Using directive to bind library functions and free functions to types.
316316
* Can occur within contracts and libraries and at the file level.
317317
*/
318-
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Semicolon;
318+
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
319319
/**
320320
* A type name can be an elementary type, a function type, a mapping type, a user-defined type
321321
* (e.g. a contract or struct) or an array type.
@@ -389,7 +389,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
389389
/**
390390
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
391391
*/
392-
identifier: Identifier | From | Error | Revert;
392+
identifier: Identifier | From | Error | Revert | Global;
393393

394394
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
395395
booleanLiteral: True | False;

libsolidity/analysis/SyntaxChecker.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,18 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
417417
_usingFor.location(),
418418
"The type has to be specified explicitly when attaching specific functions."
419419
);
420+
if (_usingFor.global() && !_usingFor.typeName())
421+
m_errorReporter.syntaxError(
422+
2854_error,
423+
_usingFor.location(),
424+
"Can only globally bind functions to specific types."
425+
);
426+
if (_usingFor.global() && m_currentContractKind)
427+
m_errorReporter.syntaxError(
428+
3367_error,
429+
_usingFor.location(),
430+
"\"global\" can only be used at file level."
431+
);
420432
if (m_currentContractKind == ContractKind::Interface)
421433
m_errorReporter.syntaxError(
422434
9088_error,

libsolidity/analysis/TypeChecker.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,6 +3656,28 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
36563656
);
36573657
solAssert(normalizedType);
36583658

3659+
if (_usingFor.global())
3660+
{
3661+
if (m_currentContract)
3662+
solAssert(m_errorReporter.hasErrors());
3663+
if (Declaration const* typeDefinition = _usingFor.typeName()->annotation().type->typeDefinition())
3664+
{
3665+
if (typeDefinition->scope() != m_currentSourceUnit)
3666+
m_errorReporter.typeError(
3667+
4117_error,
3668+
_usingFor.location(),
3669+
"Can only use \"global\" with types defined in the same source unit at file level."
3670+
);
3671+
}
3672+
else
3673+
m_errorReporter.typeError(
3674+
8841_error,
3675+
_usingFor.location(),
3676+
"Can only use \"global\" with user-defined types."
3677+
);
3678+
}
3679+
3680+
36593681
for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
36603682
{
36613683
solAssert(path->annotation().referencedDeclaration);

libsolidity/ast/AST.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,10 @@ class InheritanceSpecifier: public ASTNode
641641
* For version 3, T has to be implicitly convertible to the first parameter type of
642642
* all functions, and this is checked at the point of the using statement. For versions 1 and
643643
* 2, this check is only done when a function is called.
644+
*
645+
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
646+
* a user-defined type defined in the same file at file level. In this case, the methods are
647+
* attached to all objects of that type regardless of scope.
644648
*/
645649
class UsingForDirective: public ASTNode
646650
{
@@ -650,9 +654,14 @@ class UsingForDirective: public ASTNode
650654
SourceLocation const& _location,
651655
std::vector<ASTPointer<IdentifierPath>> _functions,
652656
bool _usesBraces,
653-
ASTPointer<TypeName> _typeName
657+
ASTPointer<TypeName> _typeName,
658+
bool _global
654659
):
655-
ASTNode(_id, _location), m_functions(_functions), m_usesBraces(_usesBraces), m_typeName(std::move(_typeName))
660+
ASTNode(_id, _location),
661+
m_functions(_functions),
662+
m_usesBraces(_usesBraces),
663+
m_typeName(std::move(_typeName)),
664+
m_global{_global}
656665
{
657666
}
658667

@@ -665,12 +674,14 @@ class UsingForDirective: public ASTNode
665674
/// @returns a list of functions or the single library.
666675
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
667676
bool usesBraces() const { return m_usesBraces; }
677+
bool global() const { return m_global; }
668678

669679
private:
670680
/// Either the single library or a list of functions.
671681
std::vector<ASTPointer<IdentifierPath>> m_functions;
672682
bool m_usesBraces;
673683
ASTPointer<TypeName> m_typeName;
684+
bool m_global = false;
674685
};
675686

676687
class StructDefinition: public Declaration, public ScopeOpener

libsolidity/ast/ASTJsonConverter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ bool ASTJsonConverter::visit(UsingForDirective const& _node)
328328
}
329329
else
330330
attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front()));
331+
attributes.emplace_back("global", _node.global());
331332

332333
setJsonNode(_node, "UsingForDirective", move(attributes));
333334

libsolidity/ast/ASTJsonImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@ ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Val
359359
_node,
360360
move(functions),
361361
!_node.isMember("libraryName"),
362-
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
362+
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
363+
memberAsBool(_node, "global")
363364
);
364365
}
365366

libsolidity/ast/Types.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
342342
solAssert(sourceUnit, "");
343343
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
344344

345+
if (Declaration const* typeDefinition = _type.typeDefinition())
346+
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(typeDefinition->scope()))
347+
for (auto usingFor: ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()))
348+
// We do not yet compare the type name because of normalization.
349+
if (usingFor->global() && usingFor->typeName())
350+
usingForDirectives.emplace_back(usingFor);
351+
345352
// Normalise data location of type.
346353
DataLocation typeLocation = DataLocation::Storage;
347354
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))

0 commit comments

Comments
 (0)