Al intentar compilar, me sale el siguiente error:
"Error interno del compilador: pila demasiado profunda, intente eliminar las variables locales".
¿Hay alguna forma de evitar esto? No estoy seguro de si podré eliminar suficientes variables para solucionar esto.
¡Gracias!
Estás golpeando una StackTooDeepException .
El código de Solidity no parece ser consistente en la cantidad de variables que ve como un problema, pero tiene un límite de alrededor de 16 o 17. (Aunque claramente el límite inferior de 16 será el que se active. ..)
CommonSubexpressionEliminator.cpp y CompilerUtils.cpp :
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables.");
Sin ver su código, es difícil comentar más sobre las posibles soluciones, pero una cosa que puede intentar sería dividir las funciones más grandes en otras más pequeñas.
Editar 2019:
Una explicación muy detallada de este error, y cómo se puede evitar, se analiza en el artículo "Stack Too Deep" - Error en Solidity .
Uniswap parece haber encontrado una buena solución para este problema. Rodea una parte de tu función con corchetes:
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
Eche un vistazo a UniswapV2.sol para ver el contexto completo.
https://github.com/ethereum/solidity/issues/267
Depende de cuán complejas sean las expresiones dentro de la función, pero más de 16 variables locales no funcionarán. Sin embargo, esta historia debería solucionarlo: https://www.pivotaltracker.com/n/projects/1189488/stories/99085498
La historia no se ha iniciado.
Una solución para esto es colocar las variables locales en una matriz del mismo tipo que en las matrices EVM que solo ocupan una ranura de pila. Por lo tanto, las funciones pueden hacerse mucho más grandes antes de alcanzar el límite de 16 ranuras.
Por ejemplo:
contract A {
// This will get the error: 'stack too deep, try...'
function deepStack
(
uint8 _a,
uint8 _b,
uint8 _c,
uint8 _d,
uint16 _e,
uint16 _f,
uint16 _g,
uint16 _h,
uint32 _i,
uint32 _j,
uint32 _k,
uint32 _l,
uint64 _m,
uint64 _n,
uint64 _o,
uint64 _p,
uint128 _q
)
public
returns (bool success)
{
return true;
}
// This function works
function deepStackSolution
(
uint8[] _aToD,
uint16[] _eToH,
uint32[] _iToL,
uint64[] _mToP,
uint128 _q
)
public
returns (bool success)
{
return true;
}
}
Sin embargo, tenga en cuenta que la capacidad de almacenamiento de cada ranura sigue siendo limitada. De esta manera, el almacenamiento proporcionado se utilizará de manera más eficiente. Si está utilizando números realmente grandes, esto también podría alcanzar sus límites pronto.
Aquí hay algo divertido. Simplemente cambié un método de public
a external
y recibí el mensaje:
/Users/cliff/Documents/in-app-pro-shop/contracts/SKUFactory.sol:42:23:
CompilerError: Stack too deep, try removing local variables.
skus.push(SKU(_shopId, skuId, _skuTypeId, _price, _name, _desc, _consumable, _limited, _limit));
^-----^
¡ Cambiarlo de nuevo a public
elimina el error! Aparentemente, es un poco más matizado que cuántas variables locales hay. Aquí está la función:
/**
* @notice Create a SKU (Shopkeeping Unit) for a Shop
* @dev Can only be run by shop owner
*/
function createSKU(
uint256 _shopId,
uint256 _skuTypeId,
uint256 _price,
string _name,
string _desc,
bool _consumable,
bool _limited,
uint256 _limit
)
public
onlyShopOwner(_shopId)
returns(uint256)
{
// SKUs must have a non-zero price
require(_price > 0);
// Get SKU ID
uint256 skuId = skus.length;
// Create and store SKU Type
skus.push(SKU(_shopId, skuId, _skuTypeId, _price, _name, _desc, _consumable, _limited, _limit));
// Add SKU to Shop's SKU list
shopSKUs[_shopId].push(skuId);
// Add SKU ID to SKU Type's SKU list
skuTypeSKUs[_skuTypeId].push(skuId);
// Emit Event with name of the new SKU
emit NewSKU(_shopId, skuId, _name);
// Return the new SKU ID
return skuId;
}
Estaba haciendo el cambio en respuesta a esta discusión sobre las mejores prácticas para external
vs public
, donde se explica (aunque un poco turbiamente) cómo las funciones se manejan de manera diferente en esos casos. Supongo que esa es la raíz de por qué no hay un número específico de variables locales que desencadenan este error.
external
en lugar de public
forzar probablemente que los parámetros estén en calldata.Simplemente empaque sus variables en una matriz de memoria. Por ejemplo, supongamos que tiene 30 variables de direcciones locales. En vez de:
address addr1 = 0x0000000000000000000000000000000000000001;
address addr2 = 0x0000000000000000000000000000000000000002;
address addr3 = 0x0000000000000000000000000000000000000003;
// etc
Tu puedes hacer:
address[] memory joinedAddresses = new address[](30);
joinedAddresses[0] = 0x0000000000000000000000000000000000000001;
joinedAddresses[1] = 0x0000000000000000000000000000000000000002;
joinedAddresses[2] = 0x0000000000000000000000000000000000000003;
// etc
Nota: probé este método en Solidity 0.8.4.
Amer Amén
Richard Horrocks
kaki maestro del tiempo
Richard Horrocks
kaki maestro del tiempo
returns()
declaración o la suma de aquellos con las variables locales dentro de la función.solwarr10r
return ( myStruct[id].var1, myStruct[id].var2, ...);
camello
makeStackItems()
la fuente del compilador). Además, algunas ranuras adicionales pueden usarse internamente: el lenguaje oculta la pila a propósito y el diseño exacto de la pila debe considerarse un detalle de implementación.