mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #11930 from ethereum/calldataStructInlineAssembly
Fix inline assembly assignments to calldata structs and statically-sized calldata arrays.
This commit is contained in:
		
						commit
						8735d3fb6c
					
				| @ -15,6 +15,7 @@ Compiler Features: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  |  * Code Generator: Fix ICE on assigning to calldata structs and statically-sized calldata arrays in inline assembly. | ||||||
|  * Code Generator: Use stable source order for ABI functions. |  * Code Generator: Use stable source order for ABI functions. | ||||||
|  * Commandline Interface: Report optimizer options as invalid in Standard JSON and linker modes instead of ignoring them. |  * Commandline Interface: Report optimizer options as invalid in Standard JSON and linker modes instead of ignoring them. | ||||||
|  * Commandline Interface: Disallow the ``--experimental-via-ir`` option in Standard JSON, Assembler and Linker modes. |  * Commandline Interface: Disallow the ``--experimental-via-ir`` option in Standard JSON, Assembler and Linker modes. | ||||||
|  | |||||||
| @ -124,9 +124,22 @@ Access to External Variables, Functions and Libraries | |||||||
| You can access Solidity variables and other identifiers by using their name. | You can access Solidity variables and other identifiers by using their name. | ||||||
| 
 | 
 | ||||||
| Local variables of value type are directly usable in inline assembly. | Local variables of value type are directly usable in inline assembly. | ||||||
|  | They can both be read and assigned to. | ||||||
| 
 | 
 | ||||||
| Local variables that refer to memory or calldata evaluate to the | Local variables that refer to memory evaluate to the address of the variable in memory not the value itself. | ||||||
| address of the variable in memory, resp. calldata, not the value itself. | Such variables can also be assigned to, but note that an assignment will only change the pointer and not the data | ||||||
|  | and that it is your responsibility to respect Solidity's memory management. | ||||||
|  | See :ref:`Conventions in Solidity <conventions-in-solidity>`. | ||||||
|  | 
 | ||||||
|  | Similarly, local variables that refer to statically-sized calldata arrays or calldata structs | ||||||
|  | evaluate to the address of the variable in calldata, not the value itself. | ||||||
|  | The variable can also be assigned a new offset, but note that no validation to ensure that | ||||||
|  | the variable will not point beyond ``calldatasize()`` is performed. | ||||||
|  | 
 | ||||||
|  | For dynamic calldata arrays, you can access | ||||||
|  | their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``. | ||||||
|  | Both expressions can also be assigned to, but as for the static case, no validation will be performed | ||||||
|  | to ensure that the resulting data area is within the bounds of ``calldatasize()``. | ||||||
| 
 | 
 | ||||||
| For local storage variables or state variables, a single Yul identifier | For local storage variables or state variables, a single Yul identifier | ||||||
| is not sufficient, since they do not necessarily occupy a single full storage slot. | is not sufficient, since they do not necessarily occupy a single full storage slot. | ||||||
| @ -135,9 +148,10 @@ inside that slot. To retrieve the slot pointed to by the variable ``x``, you | |||||||
| use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``. | use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``. | ||||||
| Using ``x`` itself will result in an error. | Using ``x`` itself will result in an error. | ||||||
| 
 | 
 | ||||||
| For dynamic calldata arrays, you can access | You can also assign to the ``.slot`` part of a local storage variable pointer. | ||||||
| their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``. | For these (structs, arrays or mappings), the ``.offset`` part is always zero. | ||||||
| Both expressions can also be assigned to. | It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable, | ||||||
|  | though. | ||||||
| 
 | 
 | ||||||
| Local Solidity variables are available for assignments, for example: | Local Solidity variables are available for assignments, for example: | ||||||
| 
 | 
 | ||||||
| @ -178,17 +192,6 @@ Since Solidity 0.7.0, variables and functions declared inside the | |||||||
| inline assembly block may not contain ``.``, but using ``.`` is | inline assembly block may not contain ``.``, but using ``.`` is | ||||||
| valid to access Solidity variables from outside the inline assembly block. | valid to access Solidity variables from outside the inline assembly block. | ||||||
| 
 | 
 | ||||||
| Assignments are possible to assembly-local variables and to function-local |  | ||||||
| variables. Take care that when you assign to variables that point to |  | ||||||
| memory or storage, you will only change the pointer and not the data. |  | ||||||
| 
 |  | ||||||
| You can assign to the ``.slot`` part of a local storage variable pointer. |  | ||||||
| For these (structs, arrays or mappings), the ``.offset`` part is always zero. |  | ||||||
| It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable, |  | ||||||
| though. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Things to Avoid | Things to Avoid | ||||||
| --------------- | --------------- | ||||||
| 
 | 
 | ||||||
| @ -199,6 +202,8 @@ functional-style opcodes, counting stack height for | |||||||
| variable access and removing stack slots for assembly-local variables when the end | variable access and removing stack slots for assembly-local variables when the end | ||||||
| of their block is reached. | of their block is reached. | ||||||
| 
 | 
 | ||||||
|  | .. _conventions-in-solidity: | ||||||
|  | 
 | ||||||
| Conventions in Solidity | Conventions in Solidity | ||||||
| ----------------------- | ----------------------- | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -860,15 +860,28 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) | |||||||
| 			} | 			} | ||||||
| 			else if (variable->type()->dataStoredIn(DataLocation::CallData)) | 			else if (variable->type()->dataStoredIn(DataLocation::CallData)) | ||||||
| 			{ | 			{ | ||||||
| 				auto const* arrayType = dynamic_cast<ArrayType const*>(variable->type()); | 				if (auto const* arrayType = dynamic_cast<ArrayType const*>(variable->type())) | ||||||
| 				solAssert( | 				{ | ||||||
| 					arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData), | 					if (arrayType->isDynamicallySized()) | ||||||
| 					"" | 					{ | ||||||
| 				); | 						solAssert(suffix == "offset" || suffix == "length", ""); | ||||||
| 				solAssert(suffix == "offset" || suffix == "length", ""); | 						solAssert(variable->type()->sizeOnStack() == 2, ""); | ||||||
| 				solAssert(variable->type()->sizeOnStack() == 2, ""); | 						if (suffix == "length") | ||||||
| 				if (suffix == "length") | 							stackDiff--; | ||||||
| 					stackDiff--; | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						solAssert(variable->type()->sizeOnStack() == 1, ""); | ||||||
|  | 						solAssert(suffix.empty(), ""); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					auto const* structType = dynamic_cast<StructType const*>(variable->type()); | ||||||
|  | 					solAssert(structType, ""); | ||||||
|  | 					solAssert(variable->type()->sizeOnStack() == 1, ""); | ||||||
|  | 					solAssert(suffix.empty(), ""); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			else | 			else | ||||||
| 				solAssert(suffix.empty(), ""); | 				solAssert(suffix.empty(), ""); | ||||||
|  | |||||||
| @ -0,0 +1,10 @@ | |||||||
|  | contract C { | ||||||
|  |     function f(uint[2][2] calldata x) public returns (uint[2][2] memory r) { | ||||||
|  |         assembly { x := 0x24 } | ||||||
|  |         r = x; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // f(uint256[2][2]): 0x0, 8, 7, 6, 5 -> 8, 7, 6, 5 | ||||||
| @ -0,0 +1,18 @@ | |||||||
|  | pragma abicoder v2; | ||||||
|  | 
 | ||||||
|  | contract C { | ||||||
|  | 	struct S { uint256 x; } | ||||||
|  | 	struct S2 { uint256 x; uint256 y; } | ||||||
|  | 	function f(S calldata s, S2 calldata s2) public pure returns (uint256 r, uint256 r2) { | ||||||
|  | 		assembly { | ||||||
|  | 			s := s2 | ||||||
|  | 			s2 := 4 | ||||||
|  | 		} | ||||||
|  | 		r = s.x; | ||||||
|  | 		r2 = s2.x; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // f((uint256),(uint256,uint256)): 0x42, 0x07, 0x77 -> 0x07, 0x42 | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | pragma abicoder v2; | ||||||
|  | contract C { | ||||||
|  |   struct S { int8 x; int8 y; } | ||||||
|  |   function f() internal pure returns(S calldata s) { | ||||||
|  |     assembly { | ||||||
|  |       s := 0x24 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   function g() public pure returns(int8, int8) { | ||||||
|  |     S calldata s = f(); | ||||||
|  |     return (s.x, s.y); | ||||||
|  |   } | ||||||
|  |   function h() public pure returns(uint256) { f(); return 0x42; } | ||||||
|  |   function i() public pure returns(uint256) { abi.decode(msg.data[4:], (S)); return 0x42; } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // g(): 0xCAFFEE, 0x42, 0x21 -> 0x42, 0x21 | ||||||
|  | // g(): 0xCAFFEE, 0x4242, 0x2121 -> FAILURE | ||||||
|  | // g(): 0xCAFFEE, 0x42 -> 0x42, 0 | ||||||
|  | // h() -> 0x42 | ||||||
|  | // i() -> FAILURE | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | contract C { | ||||||
|  |     function f() internal returns (uint256[] calldata) {} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 3464: (48-66): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | contract C { | ||||||
|  |     function f() internal returns (uint256[1] calldata) {} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 3464: (48-67): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. | ||||||
| @ -0,0 +1,6 @@ | |||||||
|  | contract C { | ||||||
|  |     struct S { uint256 x; } | ||||||
|  |     function f() internal returns (S calldata) {} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 3464: (76-86): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user