Merge pull request #5382 from ethereum/libraryMappingPublic

Allow mapping arguments for public and external library functions.
This commit is contained in:
chriseth 2018-11-26 19:47:32 +01:00 committed by GitHub
commit 240ad0e34e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 106 additions and 27 deletions

View File

@ -1,6 +1,7 @@
### 0.5.1 (unreleased)
Language Features:
* Allow mapping type for parameters and return variables of public and external library functions.
* Allow public functions to override external functions.
Compiler Features:

View File

@ -697,20 +697,22 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (type(*var)->category() == Type::Category::Mapping)
{
if (!type(*var)->dataStoredIn(DataLocation::Storage))
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." );
else if (!isLibraryFunction && _function.isPublic())
m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
else
{
if (!type(*var)->canLiveOutsideStorage() && _function.isPublic())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
}
if (
type(*var)->category() == Type::Category::Mapping &&
!type(*var)->dataStoredIn(DataLocation::Storage)
)
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\".");
else if (
!type(*var)->canLiveOutsideStorage() &&
_function.visibility() > FunctionDefinition::Visibility::Internal
)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
if (
_function.visibility() > FunctionDefinition::Visibility::Internal &&
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(*var))
)

View File

@ -1873,6 +1873,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
retSize = 0;
break;
}
else if (retType->decodingType())
retSize += retType->decodingType()->calldataEncodedSize();
else
retSize += retType->calldataEncodedSize();
}

View File

@ -8710,6 +8710,90 @@ BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named)
ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17)));
}
BOOST_AUTO_TEST_CASE(using_library_mappings_public)
{
char const* sourceCode = R"(
library Lib {
function set(mapping(uint => uint) storage m, uint key, uint value) public
{
m[key] = value;
}
}
contract Test {
mapping(uint => uint) m1;
mapping(uint => uint) m2;
function f() public returns (uint, uint, uint, uint, uint, uint)
{
Lib.set(m1, 0, 1);
Lib.set(m1, 2, 42);
Lib.set(m2, 0, 23);
Lib.set(m2, 2, 99);
return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]);
}
}
)";
compileAndRun(sourceCode, 0, "Lib");
compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99)));
}
BOOST_AUTO_TEST_CASE(using_library_mappings_external)
{
char const* libSourceCode = R"(
library Lib {
function set(mapping(uint => uint) storage m, uint key, uint value) external
{
m[key] = value * 2;
}
}
)";
char const* sourceCode = R"(
library Lib {
function set(mapping(uint => uint) storage m, uint key, uint value) external;
}
contract Test {
mapping(uint => uint) m1;
mapping(uint => uint) m2;
function f() public returns (uint, uint, uint, uint, uint, uint)
{
Lib.set(m1, 0, 1);
Lib.set(m1, 2, 42);
Lib.set(m2, 0, 23);
Lib.set(m2, 2, 99);
return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]);
}
}
)";
compileAndRun(libSourceCode, 0, "Lib");
compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2), u256(0), u256(84), u256(46), u256(0), u256(198)));
}
BOOST_AUTO_TEST_CASE(using_library_mappings_return)
{
char const* sourceCode = R"(
library Lib {
function choose(mapping(uint => mapping(uint => uint)) storage m, uint key) external returns (mapping(uint => uint) storage) {
return m[key];
}
}
contract Test {
mapping(uint => mapping(uint => uint)) m;
function f() public returns (uint, uint, uint, uint, uint, uint)
{
Lib.choose(m, 0)[0] = 1;
Lib.choose(m, 0)[2] = 42;
Lib.choose(m, 1)[0] = 23;
Lib.choose(m, 1)[2] = 99;
return (m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]);
}
}
)";
compileAndRun(sourceCode, 0, "Lib");
compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99)));
}
BOOST_AUTO_TEST_CASE(using_library_structs)
{
char const* sourceCode = R"(

View File

@ -3,4 +3,3 @@ library L {
}
}
// ----
// TypeError: (27-56): Type is required to live outside storage.

View File

@ -3,4 +3,3 @@ library L {
}
}
// ----
// TypeError: (27-56): Type is required to live outside storage.

View File

@ -5,6 +5,3 @@ library L
}
}
// ----
// TypeError: (27-58): Type is required to live outside storage.
// TypeError: (60-91): Type is required to live outside storage.
// TypeError: (123-152): Type is required to live outside storage.

View File

@ -5,6 +5,3 @@ library L
}
}
// ----
// TypeError: (27-58): Type is required to live outside storage.
// TypeError: (60-91): Type is required to live outside storage.
// TypeError: (121-150): Type is required to live outside storage.

View File

@ -2,5 +2,5 @@ contract c {
function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {}
}
// ----
// TypeError: (29-59): Type is required to live outside storage.
// TypeError: (29-59): Internal or recursive type is not allowed for public or external functions.
// TypeError: (29-59): Mapping types for parameters or return variables can only be used in internal or library functions.
// TypeError: (84-112): Mapping types for parameters or return variables can only be used in internal or library functions.

View File

@ -2,5 +2,4 @@ contract c {
function f3(mapping(uint => uint) memory) view public {}
}
// ----
// TypeError: (29-57): Type is required to live outside storage.
// TypeError: (29-57): Internal or recursive type is not allowed for public or external functions.
// TypeError: (29-57): Mapping types for parameters or return variables can only be used in internal or library functions.

View File

@ -3,5 +3,4 @@ contract C {
}
}
// ----
// TypeError: (51-79): Type is required to live outside storage.
// TypeError: (51-79): Internal or recursive type is not allowed for public or external functions.
// TypeError: (51-79): Mapping types for parameters or return variables can only be used in internal or library functions.