Beim Kompilieren bekomme ich folgende Fehlermeldung:
"Interner Compiler-Fehler: Stapel zu tief, versuchen Sie, lokale Variablen zu entfernen."
Gibt es eine Möglichkeit, dies zu umgehen? Ich bin mir nicht sicher, ob ich genug Variablen entfernen kann, um dies zu beheben.
Vielen Dank!
Sie treffen auf eine StackTooDeepException .
Der Solidity-Code scheint in der Anzahl der Variablen, die er als Problem ansieht, nicht konsistent zu sein, aber Sie haben eine Grenze von etwa 16 oder 17. (Obwohl eindeutig die untere Grenze von 16 diejenige sein wird, die einsetzt. ..)
CommonSubexpressionEliminator.cpp und CompilerUtils.cpp :
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables.");
Ohne Ihren Code zu sehen, ist es schwierig, weitere mögliche Lösungen zu kommentieren, aber Sie sollten versuchen, größere Funktionen in kleinere aufzuteilen.
Bearbeiten 2019:
Eine sehr ausführliche Erklärung dieses Fehlers und wie er vermieden werden kann, finden Sie im Artikel „Stack Too Deep“ – Error in Solidity .
Uniswap scheint eine nette Lösung für dieses Problem gefunden zu haben. Schließen Sie einen Teil Ihrer Funktion mit Klammern ein:
{ // 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));
}
Werfen Sie einen Blick auf UniswapV2.sol für den vollständigen Kontext.
https://github.com/ethereum/solidity/issues/267
Es hängt davon ab, wie komplex die Ausdrücke innerhalb der Funktion sind, aber mehr als 16 lokale Variablen funktionieren nicht. Diese Geschichte sollte es jedoch beheben: https://www.pivotaltracker.com/n/projects/1189488/stories/99085498
Die Geschichte wurde nicht begonnen.
Eine Problemumgehung hierfür besteht darin, lokale Variablen in ein Array des gleichen Typs wie auf den EVM-Arrays zu legen, die nur einen Stack-Slot belegen. Dadurch können Funktionen viel größer werden, bevor sie die Grenze von 16 Slots erreichen.
Zum Beispiel:
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;
}
}
Beachten Sie jedoch, dass die Speicherkapazität jedes Steckplatzes immer noch begrenzt ist. Auf diese Weise wird der bereitgestellte Speicher einfach effizienter genutzt. Bei sehr großen Zahlen dürfte auch dies schnell an seine Grenzen stoßen.
Hier ist eine lustige Sache. Ich habe einfach eine Methode von public
auf geändert external
und die Meldung erhalten:
/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));
^-----^
Zurücksetzen auf public
behebt den Fehler! Anscheinend ist es etwas nuancierter als nur die Anzahl der lokalen Variablen. Hier ist die Funktion:
/**
* @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;
}
Ich habe die Änderung als Reaktion auf diese Diskussion über Best Practices für external
vs public
vorgenommen, in der (wenn auch etwas unklar) erklärt wird, wie Funktionen in diesen Fällen unterschiedlich gehandhabt werden. Ich vermute, das ist der Grund dafür, warum es keine bestimmte Anzahl lokaler Variablen gibt, die diesen Fehler auslösen.
external
Die Wahl der Parameter, anstatt sie public
wahrscheinlich zu erzwingen, ist in calldata
.Packen Sie einfach Ihre Variablen in ein Speicherarray. Angenommen, Sie haben 30 lokale Adressvariablen. Anstatt von:
address addr1 = 0x0000000000000000000000000000000000000001;
address addr2 = 0x0000000000000000000000000000000000000002;
address addr3 = 0x0000000000000000000000000000000000000003;
// etc
Du kannst tun:
address[] memory joinedAddresses = new address[](30);
joinedAddresses[0] = 0x0000000000000000000000000000000000000001;
joinedAddresses[1] = 0x0000000000000000000000000000000000000002;
joinedAddresses[2] = 0x0000000000000000000000000000000000000003;
// etc
Hinweis: Ich habe diese Methode in Solidity 0.8.4 getestet.
Amer Amin
Richard Horrocks
Kaki Meister der Zeit
Richard Horrocks
Kaki Meister der Zeit
returns()
Anweisung oder die Summe derjenigen mit den lokalen Variablen innerhalb der Funktion.sunwarr10r
return ( myStruct[id].var1, myStruct[id].var2, ...);
Kamel
makeStackItems()
in der Compiler-Quelle bestimmt). Außerdem könnten einige zusätzliche Slots intern verwendet werden - die Sprache verbirgt den Stack absichtlich und das genaue Stack-Layout sollte als Implementierungsdetail betrachtet werden.