mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
e154d43176
@ -3,6 +3,7 @@
|
||||
Language Features:
|
||||
* General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model.
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -42,6 +42,13 @@ scope (either the contract or the current module/source unit),
|
||||
including within all of its functions, and has no effect
|
||||
outside of the contract or module in which it is used.
|
||||
|
||||
When the directive is used at file level and applied to a
|
||||
user-defined type which was defined at file level in the same file,
|
||||
the word ``global`` can be added at the end. This will have the
|
||||
effect that the functions are attached to the type everywhere
|
||||
the type is available (including other files), not only in the
|
||||
scope of the using statement.
|
||||
|
||||
Let us rewrite the set example from the
|
||||
:ref:`libraries` section in this way, using file-level functions
|
||||
instead of library functions.
|
||||
|
@ -47,6 +47,7 @@ FixedBytes:
|
||||
'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32';
|
||||
For: 'for';
|
||||
Function: 'function';
|
||||
Global: 'global'; // not a real keyword
|
||||
Hex: 'hex';
|
||||
If: 'if';
|
||||
Immutable: 'immutable';
|
||||
|
@ -315,7 +315,7 @@ errorDefinition:
|
||||
* Using directive to bind 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) Semicolon;
|
||||
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
|
||||
/**
|
||||
* A type name can be an elementary type, a function type, a mapping type, a user-defined type
|
||||
* (e.g. a contract or struct) or an array type.
|
||||
@ -389,7 +389,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
|
||||
/**
|
||||
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
|
||||
*/
|
||||
identifier: Identifier | From | Error | Revert;
|
||||
identifier: Identifier | From | Error | Revert | Global;
|
||||
|
||||
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
|
||||
booleanLiteral: True | False;
|
||||
|
@ -417,6 +417,18 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
|
||||
_usingFor.location(),
|
||||
"The type has to be specified explicitly when attaching specific functions."
|
||||
);
|
||||
if (_usingFor.global() && !_usingFor.typeName())
|
||||
m_errorReporter.syntaxError(
|
||||
2854_error,
|
||||
_usingFor.location(),
|
||||
"Can only globally bind functions to specific types."
|
||||
);
|
||||
if (_usingFor.global() && m_currentContractKind)
|
||||
m_errorReporter.syntaxError(
|
||||
3367_error,
|
||||
_usingFor.location(),
|
||||
"\"global\" can only be used at file level."
|
||||
);
|
||||
if (m_currentContractKind == ContractKind::Interface)
|
||||
m_errorReporter.syntaxError(
|
||||
9088_error,
|
||||
|
@ -3656,6 +3656,28 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
);
|
||||
solAssert(normalizedType);
|
||||
|
||||
if (_usingFor.global())
|
||||
{
|
||||
if (m_currentContract)
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
if (Declaration const* typeDefinition = _usingFor.typeName()->annotation().type->typeDefinition())
|
||||
{
|
||||
if (typeDefinition->scope() != m_currentSourceUnit)
|
||||
m_errorReporter.typeError(
|
||||
4117_error,
|
||||
_usingFor.location(),
|
||||
"Can only use \"global\" with types defined in the same source unit at file level."
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
8841_error,
|
||||
_usingFor.location(),
|
||||
"Can only use \"global\" with user-defined types."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
|
||||
{
|
||||
solAssert(path->annotation().referencedDeclaration);
|
||||
|
@ -641,6 +641,10 @@ private:
|
||||
* 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
|
||||
* 2, this check is only done when a function is called.
|
||||
*
|
||||
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
|
||||
* a user-defined type defined in the same file at file level. In this case, the methods are
|
||||
* attached to all objects of that type regardless of scope.
|
||||
*/
|
||||
class UsingForDirective: public ASTNode
|
||||
{
|
||||
@ -650,9 +654,14 @@ public:
|
||||
SourceLocation const& _location,
|
||||
std::vector<ASTPointer<IdentifierPath>> _functions,
|
||||
bool _usesBraces,
|
||||
ASTPointer<TypeName> _typeName
|
||||
ASTPointer<TypeName> _typeName,
|
||||
bool _global
|
||||
):
|
||||
ASTNode(_id, _location), m_functions(_functions), m_usesBraces(_usesBraces), m_typeName(std::move(_typeName))
|
||||
ASTNode(_id, _location),
|
||||
m_functions(_functions),
|
||||
m_usesBraces(_usesBraces),
|
||||
m_typeName(std::move(_typeName)),
|
||||
m_global{_global}
|
||||
{
|
||||
}
|
||||
|
||||
@ -665,12 +674,14 @@ public:
|
||||
/// @returns a list of functions or the single library.
|
||||
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
|
||||
bool usesBraces() const { return m_usesBraces; }
|
||||
bool global() const { return m_global; }
|
||||
|
||||
private:
|
||||
/// Either the single library or a list of functions.
|
||||
std::vector<ASTPointer<IdentifierPath>> m_functions;
|
||||
bool m_usesBraces;
|
||||
ASTPointer<TypeName> m_typeName;
|
||||
bool m_global = false;
|
||||
};
|
||||
|
||||
class StructDefinition: public Declaration, public ScopeOpener
|
||||
|
@ -328,6 +328,7 @@ bool ASTJsonConverter::visit(UsingForDirective const& _node)
|
||||
}
|
||||
else
|
||||
attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front()));
|
||||
attributes.emplace_back("global", _node.global());
|
||||
|
||||
setJsonNode(_node, "UsingForDirective", move(attributes));
|
||||
|
||||
|
@ -359,7 +359,8 @@ ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Val
|
||||
_node,
|
||||
move(functions),
|
||||
!_node.isMember("libraryName"),
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
|
||||
memberAsBool(_node, "global")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -342,6 +342,13 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
solAssert(sourceUnit, "");
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
|
||||
if (Declaration const* typeDefinition = _type.typeDefinition())
|
||||
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(typeDefinition->scope()))
|
||||
for (auto usingFor: ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()))
|
||||
// We do not yet compare the type name because of normalization.
|
||||
if (usingFor->global() && usingFor->typeName())
|
||||
usingForDirectives.emplace_back(usingFor);
|
||||
|
||||
// Normalise data location of type.
|
||||
DataLocation typeLocation = DataLocation::Storage;
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||
|
@ -987,9 +987,15 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
|
||||
advance();
|
||||
else
|
||||
typeName = parseTypeName();
|
||||
bool global = false;
|
||||
if (m_scanner->currentToken() == Token::Identifier && currentLiteral() == "global")
|
||||
{
|
||||
global = true;
|
||||
advance();
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<UsingForDirective>(move(functions), usesBraces, typeName);
|
||||
return nodeFactory.createNode<UsingForDirective>(move(functions), usesBraces, typeName, global);
|
||||
}
|
||||
|
||||
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
||||
|
@ -33,6 +33,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"global": false,
|
||||
"id": 3,
|
||||
"nodeType": "UsingForDirective",
|
||||
"src": "0:19:1",
|
||||
@ -154,6 +155,7 @@
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"global": false,
|
||||
"id": 12,
|
||||
"libraryName":
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"global": false,
|
||||
"id": 3,
|
||||
"nodeType": "UsingForDirective",
|
||||
"src": "0:19:1",
|
||||
@ -111,6 +112,7 @@
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"global": false,
|
||||
"id": 12,
|
||||
"libraryName":
|
||||
{
|
||||
|
25
test/libsolidity/semanticTests/using/recursive_import.sol
Normal file
25
test/libsolidity/semanticTests/using/recursive_import.sol
Normal file
@ -0,0 +1,25 @@
|
||||
==== Source: A ====
|
||||
import {T as U} from "A";
|
||||
import "A" as X;
|
||||
|
||||
type T is uint;
|
||||
function f(T x) pure returns (T) { return T.wrap(T.unwrap(x) + 1); }
|
||||
function g(T x) pure returns (uint) { return T.unwrap(x) + 10; }
|
||||
|
||||
using { f } for X.X.U global;
|
||||
using { g } for T global;
|
||||
|
||||
function cr() pure returns (T) {}
|
||||
|
||||
==== Source: B ====
|
||||
import { cr } from "A";
|
||||
|
||||
contract C {
|
||||
function f() public returns (uint) {
|
||||
return cr().f().g();
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> 11
|
@ -0,0 +1,20 @@
|
||||
==== Source: A ====
|
||||
type global is uint;
|
||||
using { f } for global global;
|
||||
function f(global x) pure returns (global) { return global.wrap(global.unwrap(x) + 1); }
|
||||
==== Source: B ====
|
||||
import { global } from "A";
|
||||
|
||||
function g(global x) pure returns (global) { return global.wrap(global.unwrap(x) + 10); }
|
||||
|
||||
contract C {
|
||||
using { g } for global;
|
||||
function f(global r) public pure returns (global) {
|
||||
return r.f().g();
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256): 100 -> 111
|
@ -0,0 +1,45 @@
|
||||
==== Source: A ====
|
||||
type T is uint;
|
||||
using L for T global;
|
||||
library L {
|
||||
function inc(T x) internal pure returns (T) {
|
||||
return T.wrap(T.unwrap(x) + 1);
|
||||
}
|
||||
function dec(T x) external pure returns (T) {
|
||||
return T.wrap(T.unwrap(x) - 1);
|
||||
}
|
||||
}
|
||||
using {unwrap} for T global;
|
||||
function unwrap(T x) pure returns (uint) {
|
||||
return T.unwrap(x);
|
||||
}
|
||||
|
||||
==== Source: B ====
|
||||
contract C {
|
||||
function f() public pure returns (T r1) {
|
||||
r1 = r1.inc().inc();
|
||||
}
|
||||
}
|
||||
|
||||
import {T} from "A";
|
||||
|
||||
==== Source: C ====
|
||||
import {C} from "B";
|
||||
|
||||
contract D {
|
||||
function test() public returns (uint) {
|
||||
C c = new C();
|
||||
// This tests that bound 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
|
||||
// and a free function call.
|
||||
return c.f().inc().inc().dec().unwrap();
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// library: "A":L
|
||||
// test() -> 3
|
||||
// gas legacy: 130369
|
@ -0,0 +1,27 @@
|
||||
==== Source: A ====
|
||||
type T is uint;
|
||||
using L for T global;
|
||||
library L {
|
||||
function inc(T x) internal pure returns (T) {
|
||||
return T.wrap(T.unwrap(x) + 1);
|
||||
}
|
||||
function dec(T x) external pure returns (T) {
|
||||
return T.wrap(T.unwrap(x) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
==== Source: B ====
|
||||
contract C {
|
||||
function f() public pure returns (T r1, T r2) {
|
||||
r1 = r1.inc().inc();
|
||||
r2 = r1.dec();
|
||||
}
|
||||
}
|
||||
|
||||
import {T} from "A";
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// library: "A":L
|
||||
// f() -> 2, 1
|
21
test/libsolidity/syntaxTests/using/global_and_local.sol
Normal file
21
test/libsolidity/syntaxTests/using/global_and_local.sol
Normal file
@ -0,0 +1,21 @@
|
||||
==== Source: A ====
|
||||
using {f} for S global;
|
||||
struct S { uint x; }
|
||||
function gen() pure returns (S memory) {}
|
||||
function f(S memory _x) pure returns (uint) { return _x.x; }
|
||||
==== Source: B ====
|
||||
contract C {
|
||||
using {fun} for S;
|
||||
// Adds the same function again with the same name,
|
||||
// so it's fine.
|
||||
using {A.f} for S;
|
||||
|
||||
function test() pure public
|
||||
{
|
||||
uint p = g().f();
|
||||
p = g().fun();
|
||||
}
|
||||
}
|
||||
import {gen as g, f as fun, S} from "A";
|
||||
import "A" as A;
|
||||
// ----
|
@ -0,0 +1,5 @@
|
||||
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.
|
@ -0,0 +1,4 @@
|
||||
using {f} for uint global;
|
||||
function f(uint) pure{}
|
||||
// ----
|
||||
// TypeError 8841: (0-26): Can only use "global" with user-defined types.
|
@ -0,0 +1,7 @@
|
||||
using {f} for L.S global;
|
||||
function f(L.S memory) pure{}
|
||||
library L {
|
||||
struct S { uint x; }
|
||||
}
|
||||
// ----
|
||||
// TypeError 4117: (0-25): Can only use "global" with types defined in the same source unit at file level.
|
@ -0,0 +1,14 @@
|
||||
==== Source: A ====
|
||||
struct S { uint x; }
|
||||
==== Source: B ====
|
||||
|
||||
using {f} for S global;
|
||||
using {f} for A.S global;
|
||||
|
||||
function f(S memory) pure{}
|
||||
|
||||
import {S} from "A";
|
||||
import "A" as A;
|
||||
// ----
|
||||
// TypeError 4117: (B:1-24): Can only use "global" with types defined in the same source unit at file level.
|
||||
// TypeError 4117: (B:25-50): Can only use "global" with types defined in the same source unit at file level.
|
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
using {f} for uint global;
|
||||
}
|
||||
function f(uint) pure{}
|
||||
// ----
|
||||
// SyntaxError 3367: (17-43): "global" can only be used at file level.
|
||||
// TypeError 8841: (17-43): Can only use "global" with user-defined types.
|
21
test/libsolidity/syntaxTests/using/global_local_clash.sol
Normal file
21
test/libsolidity/syntaxTests/using/global_local_clash.sol
Normal file
@ -0,0 +1,21 @@
|
||||
==== Source: A ====
|
||||
using {f} for S global;
|
||||
struct S { uint x; }
|
||||
function gen() pure returns (S memory) {}
|
||||
function f(S memory _x) pure returns (uint) { return _x.x; }
|
||||
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
|
||||
using {f} for S;
|
||||
|
||||
function test() pure public
|
||||
{
|
||||
uint p = g().f();
|
||||
}
|
||||
}
|
||||
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.
|
17
test/libsolidity/syntaxTests/using/global_nonglobal.sol
Normal file
17
test/libsolidity/syntaxTests/using/global_nonglobal.sol
Normal file
@ -0,0 +1,17 @@
|
||||
==== Source: A ====
|
||||
using {f} for S global;
|
||||
using {g} for S;
|
||||
struct S { uint x; }
|
||||
function gen() pure returns (S memory) {}
|
||||
function f(S memory _x) pure { _x.g(); }
|
||||
function g(S memory _x) pure { }
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
function test() pure
|
||||
{
|
||||
gen().f();
|
||||
gen().g();
|
||||
}
|
||||
|
||||
// ----
|
||||
// TypeError 9582: (B:54-61): Member "g" not found or not visible after argument-dependent lookup in struct S memory.
|
15
test/libsolidity/syntaxTests/using/global_working.sol
Normal file
15
test/libsolidity/syntaxTests/using/global_working.sol
Normal file
@ -0,0 +1,15 @@
|
||||
==== Source: A ====
|
||||
using {f} for S global;
|
||||
// this should not conflict
|
||||
using {f} for S;
|
||||
struct S { uint x; }
|
||||
function gen() pure returns (S memory) {}
|
||||
function f(S memory _x) pure returns (uint) { return _x.x; }
|
||||
==== Source: B ====
|
||||
function test() pure
|
||||
{
|
||||
uint p = g().f();
|
||||
p++;
|
||||
}
|
||||
import {gen as g} from "A";
|
||||
// ----
|
Loading…
Reference in New Issue
Block a user