Fehler beim Kompilieren: Stapel zu tief

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!

Antworten (6)

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.");

ContractCompiler.cpp :

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 .

Danke für den Vorschlag! Würde ein Array als 1 lokale Variable zählen?
Ich glaube schon, ja. Andernfalls wären die Array-Größen auch auf 16 begrenzt, was kein besonders großes Array ist.
Ich versuche nicht, das wiederzubeleben, aber ich musste fragen, da ich diesen Fehler auch bekomme. Ist die maximale Anzahl lokaler Variablen jetzt weniger als 10? Ich habe diesen Fehler mit 15 Variablen
Hallo @MedMansour - habe deine andere Frage dazu entdeckt. Wie viele müssen Sie entfernen, bevor der Fehler verschwindet?
Ich habe keinen Fehler erhalten, als ich etwa 8 oder 9 Variablen aus meiner Struktur verlassen habe, aber ich habe bereits auf die Übergabe eines Arrays mit fester Größe umgestellt oder stattdessen Argumente getrennt. aber ich weiß nicht, was lokale Variable bedeutet. sind es nur die Variablen in Argumenten? oder die in der returns()Anweisung oder die Summe derjenigen mit den lokalen Variablen innerhalb der Funktion.
Ich gebe nur 8 Variablen über eine Struktur zurück und erhalte bereits diesen Fehler.return ( myStruct[id].var1, myStruct[id].var2, ...);
Bei der harten Grenze geht es wirklich um die Anzahl der Stack-Slots, nicht um Variablen - der EVM-Stack hat 1024 Slots, aber Sie können jederzeit nur auf die obersten 16/17 zugreifen. In den meisten Fällen benötigt 1 Variable 1 Steckplatz, aber nicht immer. Zum Beispiel benötigen dynamische Calldata-Arrays und externe Funktionszeiger 2 (dies wird durch 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.

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.

Was ändert das, meinst du?
Nicht sicher .. Ich habe es im Produktionscode von Uniswap v2 gesehen, also dachte ich, dass es gut sein muss.
Das Stack-Problem tritt am häufigsten auf, wenn zu viele lokale Variablen/Parameter im aktuellen Bereich vorhanden sind. Wenn Sie einige Variablendeklarationen in einen Block einfügen, gehen sie am Ende des Blocks aus dem Geltungsbereich, wodurch sich die Anzahl der Variablen verringert, die danach auf dem Stack gehalten werden müssen. Letztendlich sollte das Verfolgen der Lebensdauer lokaler Variablen in einer Funktion jedoch etwas sein, das der Compiler/Optimierer ohne solche "Tricks" herausfinden kann. Auch wenn dies beim aktuellen Codegenerator noch hilft, sollte das kommende Yul-basierte Codegen es sein viel schlauer damit.

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.

Scheint jetzt fertig zu sein.
@updogliu oder wer auch immer die neuesten Nachrichten kennt, bitte poste eine Antwort.
Ich neige dazu, @eth zuzustimmen. Dies scheint am 0.4.18 nicht behoben zu sein.

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 publicauf geändert externalund 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 publicbehebt 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 externalvs publicvorgenommen, 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.

Die eigentliche Ursache ist, dass es um Stack-Slots geht und Variablen nicht unbedingt 1:1 auf Slots abgebildet werden. Ich denke, der Grund, warum Sie den Fehler erhalten haben, ist, dass Calldata-String-Parameter zwei Slots benötigen, während Memory-Strings nur einen nehmen. externalDie Wahl der Parameter, anstatt sie publicwahrscheinlich 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.