mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Allow mapping arguments for public and external library functions.
This commit is contained in:
		
							parent
							
								
									f6d8810103
								
							
						
					
					
						commit
						30e6f8d3fb
					
				| @ -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. | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -695,20 +695,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)) | ||||
| 		) | ||||
|  | ||||
| @ -1873,6 +1873,8 @@ void ExpressionCompiler::appendExternalFunctionCall( | ||||
| 				retSize = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 			else if (retType->decodingType()) | ||||
| 				retSize += retType->decodingType()->calldataEncodedSize(); | ||||
| 			else | ||||
| 				retSize += retType->calldataEncodedSize(); | ||||
| 	} | ||||
|  | ||||
| @ -8658,6 +8658,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"( | ||||
|  | ||||
| @ -3,4 +3,3 @@ library L { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (27-56): Type is required to live outside storage. | ||||
|  | ||||
| @ -3,4 +3,3 @@ library L { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (27-56): Type is required to live outside storage. | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user