Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * Component that translates Solidity code into Yul at statement level and below.
20
 */
21
22
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
23
24
#include <libsolidity/codegen/ABIFunctions.h>
25
#include <libsolidity/codegen/ir/IRGenerationContext.h>
26
#include <libsolidity/codegen/ir/IRLValue.h>
27
#include <libsolidity/codegen/ir/IRVariable.h>
28
#include <libsolidity/codegen/YulUtilFunctions.h>
29
#include <libsolidity/codegen/ABIFunctions.h>
30
#include <libsolidity/codegen/CompilerUtils.h>
31
#include <libsolidity/codegen/ReturnInfo.h>
32
#include <libsolidity/ast/TypeProvider.h>
33
#include <libsolidity/ast/ASTUtils.h>
34
#include <libsolidity/analysis/ConstantEvaluator.h>
35
36
#include <libevmasm/GasMeter.h>
37
38
#include <libyul/AsmPrinter.h>
39
#include <libyul/AST.h>
40
#include <libyul/Dialect.h>
41
#include <libyul/Utilities.h>
42
#include <libyul/optimiser/ASTCopier.h>
43
44
#include <liblangutil/Exceptions.h>
45
46
#include <libsolutil/Whiskers.h>
47
#include <libsolutil/StringUtils.h>
48
#include <libsolutil/Keccak256.h>
49
#include <libsolutil/FunctionSelector.h>
50
#include <libsolutil/Visitor.h>
51
52
#include <range/v3/algorithm/all_of.hpp>
53
#include <range/v3/view/transform.hpp>
54
55
using namespace solidity;
56
using namespace solidity::util;
57
using namespace solidity::frontend;
58
using namespace std::string_literals;
59
60
namespace
61
{
62
63
struct CopyTranslate: public yul::ASTCopier
64
{
65
  using ExternalRefsMap = std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>;
66
67
  CopyTranslate(IRGenerationContext& _context, ExternalRefsMap const& _references):
68
174
    m_context(_context), m_references(_references) {}
69
70
  using ASTCopier::operator();
71
72
  yul::Expression operator()(yul::Identifier const& _identifier) override
73
  {
74
    // The operator() function is only called in lvalue context. In rvalue context,
75
    // only translate(yul::Identifier) is called.
76
    if (m_references.count(&_identifier))
77
      return translateReference(_identifier);
78
    else
79
      return ASTCopier::operator()(_identifier);
80
  }
81
82
  yul::YulName translateIdentifier(yul::YulName _name) override
83
  {
84
    // Strictly, the dialect used by inline assembly could be different
85
    // from the Yul dialect we are compiling to. By only translating `YulName`s which correspond to Identifiers,
86
    // we are implicitly excluding builtins together with the assumption, that numerical builtin handles
87
    // stay identical. Special care has to be taken, that these numerical handles stay consistent.
88
    return yul::YulName{"usr$" + _name.str()};
89
  }
90
91
  yul::Identifier translate(yul::Identifier const& _identifier) override
92
  {
93
    if (!m_references.count(&_identifier))
94
      return ASTCopier::translate(_identifier);
95
96
    yul::Expression translated = translateReference(_identifier);
97
    solAssert(std::holds_alternative<yul::Identifier>(translated));
98
    return std::get<yul::Identifier>(std::move(translated));
99
  }
100
101
private:
102
103
  /// Translates a reference to a local variable, potentially including
104
  /// a suffix. Might return a literal, which causes this to be invalid in
105
  /// lvalue-context.
106
  yul::Expression translateReference(yul::Identifier const& _identifier)
107
  {
108
    auto const& reference = m_references.at(&_identifier);
109
    auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
110
    solUnimplementedAssert(varDecl);
111
    std::string const& suffix = reference.suffix;
112
113
    std::string value;
114
    if (suffix.empty() && varDecl->isLocalVariable())
115
    {
116
      auto const& var = m_context.localVariable(*varDecl);
117
      solAssert(var.type().sizeOnStack() == 1);
118
119
      value = var.commaSeparatedList();
120
    }
121
    else if (varDecl->isConstant())
122
    {
123
      VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl);
124
      solAssert(variable);
125
126
      if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
127
      {
128
        u256 intValue = dynamic_cast<RationalNumberType const&>(*variable->value()->annotation().type).literalValue(nullptr);
129
        if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type()))
130
          intValue <<= 256 - 8 * bytesType->numBytes();
131
        else
132
          solAssert(variable->type()->category() == Type::Category::Integer);
133
        value = intValue.str();
134
      }
135
      else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get()))
136
      {
137
        Type const* type = literal->annotation().type;
138
139
        switch (type->category())
140
        {
141
        case Type::Category::Bool:
142
        case Type::Category::Address:
143
          solAssert(type->category() == variable->annotation().type->category());
144
          value = toCompactHexWithPrefix(type->literalValue(literal));
145
          break;
146
        case Type::Category::StringLiteral:
147
        {
148
          auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type);
149
          solAssert(variable->type()->category() == Type::Category::FixedBytes);
150
          unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes();
151
          solAssert(stringLiteral.value().size() <= numBytes);
152
          value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft)));
153
          break;
154
        }
155
        default:
156
          solAssert(false);
157
        }
158
      }
159
      else
160
        solAssert(false, "Invalid constant in inline assembly.");
161
    }
162
    else if (varDecl->isStateVariable())
163
    {
164
      if (suffix == "slot")
165
        value = m_context.storageLocationOfStateVariable(*varDecl).first.str();
166
      else if (suffix == "offset")
167
        value = std::to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
168
      else
169
        solAssert(false);
170
    }
171
    else if (varDecl->type()->dataStoredIn(DataLocation::Storage))
172
    {
173
      solAssert(suffix == "slot" || suffix == "offset");
174
      solAssert(varDecl->isLocalVariable());
175
      solAssert(!varDecl->type()->isValueType());
176
      if (suffix == "slot")
177
        value = IRVariable{*varDecl}.part("slot").name();
178
      else
179
      {
180
        solAssert(!IRVariable{*varDecl}.hasPart("offset"));
181
        value = "0"s;
182
      }
183
    }
184
    else if (varDecl->type()->dataStoredIn(DataLocation::CallData))
185
    {
186
      solAssert(suffix == "offset" || suffix == "length");
187
      value = IRVariable{*varDecl}.part(suffix).name();
188
    }
189
    else if (
190
      auto const* functionType = dynamic_cast<FunctionType const*>(varDecl->type());
191
      functionType && functionType->kind() == FunctionType::Kind::External
192
    )
193
    {
194
      solAssert(suffix == "selector" || suffix == "address");
195
      solAssert(varDecl->type()->sizeOnStack() == 2);
196
      if (suffix == "selector")
197
        value = IRVariable{*varDecl}.part("functionSelector").name();
198
      else
199
        value = IRVariable{*varDecl}.part("address").name();
200
    }
201
    else
202
      solAssert(false);
203
204
    if (isDigit(value.front()))
205
      return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::valueOfNumberLiteral(value)};
206
    else
207
      return yul::Identifier{_identifier.debugData, yul::YulName{value}};
208
  }
209
210
  IRGenerationContext& m_context;
211
  ExternalRefsMap const& m_references;
212
};
213
214
}
215
216
std::string IRGeneratorForStatementsBase::code() const
217
12.3k
{
218
12.3k
  return m_code.str();
219
12.3k
}
220
221
std::ostringstream& IRGeneratorForStatementsBase::appendCode(bool _addLocationComment)
222
128k
{
223
128k
  if (
224
128k
    _addLocationComment &&
225
93.5k
    m_currentLocation.isValid() &&
226
93.5k
    m_lastLocation != m_currentLocation
227
128k
  )
228
53.8k
    m_code << dispenseLocationComment(m_currentLocation, m_context) << "\n";
229
230
128k
  m_lastLocation = m_currentLocation;
231
232
128k
  return m_code;
233
128k
}
234
235
void IRGeneratorForStatementsBase::setLocation(ASTNode const& _node)
236
71.7k
{
237
71.7k
  m_currentLocation = _node.location();
238
71.7k
}
239
240
std::string IRGeneratorForStatements::code() const
241
12.3k
{
242
12.3k
  solAssert(!m_currentLValue, "LValue not reset!");
243
12.3k
  return IRGeneratorForStatementsBase::code();
244
12.3k
}
245
246
void IRGeneratorForStatements::generate(Block const& _block)
247
3.46k
{
248
3.46k
  try
249
3.46k
  {
250
3.46k
    _block.accept(*this);
251
3.46k
  }
252
3.46k
  catch (langutil::UnimplementedFeatureError const& _error)
253
3.46k
  {
254
0
    if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
255
0
      _error << langutil::errinfo_sourceLocation(m_currentLocation);
256
0
    BOOST_THROW_EXCEPTION(_error);
257
0
  }
258
3.46k
}
259
260
void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl)
261
1.49k
{
262
1.49k
  try
263
1.49k
  {
264
1.49k
    setLocation(_varDecl);
265
266
1.49k
    solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
267
1.49k
    solAssert(!_varDecl.isConstant());
268
1.49k
    if (!_varDecl.value())
269
1.00k
      return;
270
485
    solAssert(_varDecl.referenceLocation() != VariableDeclaration::Location::Transient, "Transient storage state variables cannot be initialized in place.");
271
272
485
    _varDecl.value()->accept(*this);
273
274
485
    writeToLValue(
275
485
      _varDecl.immutable() ?
276
31
      IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} :
277
485
      IRLValue{*_varDecl.annotation().type, IRLValue::Storage{
278
454
        toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first),
279
454
        m_context.storageLocationOfStateVariable(_varDecl).second
280
454
      }},
281
485
      *_varDecl.value()
282
485
    );
283
485
  }
284
1.49k
  catch (langutil::UnimplementedFeatureError const& _error)
285
1.49k
  {
286
0
    if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
287
0
      _error << langutil::errinfo_sourceLocation(m_currentLocation);
288
0
    BOOST_THROW_EXCEPTION(_error);
289
0
  }
290
1.49k
}
291
292
void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl)
293
2.53k
{
294
2.53k
  try
295
2.53k
  {
296
2.53k
    setLocation(_varDecl);
297
298
2.53k
    solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable.");
299
300
2.53k
    auto const* type = _varDecl.type();
301
2.53k
    if (dynamic_cast<MappingType const*>(type))
302
0
      return;
303
2.53k
    else if (auto const* refType = dynamic_cast<ReferenceType const*>(type))
304
1.23k
      if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer())
305
0
        return;
306
307
2.53k
    IRVariable zero = zeroValue(*type);
308
2.53k
    assign(m_context.localVariable(_varDecl), zero);
309
2.53k
  }
310
2.53k
  catch (langutil::UnimplementedFeatureError const& _error)
311
2.53k
  {
312
0
    if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
313
0
      _error << langutil::errinfo_sourceLocation(m_currentLocation);
314
0
    BOOST_THROW_EXCEPTION(_error);
315
0
  }
316
2.53k
}
317
318
IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType)
319
190
{
320
190
  try
321
190
  {
322
190
    setLocation(_expression);
323
324
190
    _expression.accept(*this);
325
326
190
    setLocation(_expression);
327
190
    IRVariable variable{m_context.newYulVariable(), _targetType};
328
190
    define(variable, _expression);
329
190
    return variable;
330
190
  }
331
190
  catch (langutil::UnimplementedFeatureError const& _error)
332
190
  {
333
0
    if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
334
0
      _error << langutil::errinfo_sourceLocation(m_currentLocation);
335
0
    BOOST_THROW_EXCEPTION(_error);
336
0
  }
337
190
}
338
339
std::string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
340
67
{
341
67
  try
342
67
  {
343
67
    std::string functionName = IRNames::constantValueFunction(_constant);
344
67
    return m_context.functionCollector().createFunction(functionName, [&] {
345
55
      Whiskers templ(R"(
346
55
        <sourceLocationComment>
347
55
        function <functionName>() -> <ret> {
348
55
          <code>
349
55
          <ret> := <value>
350
55
        }
351
55
      )");
352
55
      templ("sourceLocationComment", dispenseLocationComment(_constant, m_context));
353
55
      templ("functionName", functionName);
354
55
      IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
355
55
      solAssert(_constant.value());
356
55
      Type const& constantType = *_constant.type();
357
55
      templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
358
55
      templ("code", generator.code());
359
55
      templ("ret", IRVariable("ret", constantType).commaSeparatedList());
360
361
55
      return templ.render();
362
55
    });
363
67
  }
364
67
  catch (langutil::UnimplementedFeatureError const& _error)
365
67
  {
366
0
    if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error))
367
0
      _error << langutil::errinfo_sourceLocation(m_currentLocation);
368
0
    BOOST_THROW_EXCEPTION(_error);
369
0
  }
370
67
}
371
372
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
373
1.18k
{
374
1.18k
  setLocation(_varDeclStatement);
375
376
1.18k
  if (Expression const* expression = _varDeclStatement.initialValue())
377
570
  {
378
570
    if (_varDeclStatement.declarations().size() > 1)
379
24
    {
380
24
      auto const* tupleType = dynamic_cast<TupleType const*>(expression->annotation().type);
381
24
      solAssert(tupleType, "Expected expression of tuple type.");
382
24
      solAssert(_varDeclStatement.declarations().size() == tupleType->components().size(), "Invalid number of tuple components.");
383
81
      for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i)
384
57
        if (auto const& decl = _varDeclStatement.declarations()[i])
385
46
        {
386
46
          solAssert(tupleType->components()[i]);
387
46
          define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i));
388
46
        }
389
24
    }
390
546
    else
391
546
    {
392
546
      VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front();
393
546
      define(m_context.addLocalVariable(varDecl), *expression);
394
546
    }
395
570
  }
396
615
  else
397
615
    for (auto const& decl: _varDeclStatement.declarations())
398
615
      if (decl)
399
615
      {
400
615
        declare(m_context.addLocalVariable(*decl));
401
615
        initializeLocalVar(*decl);
402
615
      }
403
1.18k
}
404
405
bool IRGeneratorForStatements::visit(Conditional const& _conditional)
406
342
{
407
342
  _conditional.condition().accept(*this);
408
409
342
  setLocation(_conditional);
410
411
342
  std::string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean());
412
342
  declare(_conditional);
413
414
342
  appendCode() << "switch " << condition << "\n" "case 0 {\n";
415
416
342
  _conditional.falseExpression().accept(*this);
417
342
  setLocation(_conditional);
418
419
342
  assign(_conditional, _conditional.falseExpression());
420
342
  appendCode() << "}\n" "default {\n";
421
422
342
  _conditional.trueExpression().accept(*this);
423
342
  setLocation(_conditional);
424
425
342
  assign(_conditional, _conditional.trueExpression());
426
342
  appendCode() << "}\n";
427
428
342
  return false;
429
342
}
430
431
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
432
2.05k
{
433
2.05k
  _assignment.rightHandSide().accept(*this);
434
2.05k
  setLocation(_assignment);
435
436
2.05k
  Token assignmentOperator = _assignment.assignmentOperator();
437
2.05k
  Token binaryOperator =
438
2.05k
    assignmentOperator == Token::Assign ?
439
1.91k
    assignmentOperator :
440
2.05k
    TokenTraits::AssignmentToBinaryOp(assignmentOperator);
441
442
2.05k
  if (TokenTraits::isShiftOp(binaryOperator))
443
2.05k
    solAssert(type(_assignment.rightHandSide()).mobileType());
444
2.05k
  IRVariable value =
445
2.05k
    type(_assignment.leftHandSide()).isValueType() ?
446
1.53k
    convert(
447
1.53k
      _assignment.rightHandSide(),
448
1.53k
      TokenTraits::isShiftOp(binaryOperator) ? *type(_assignment.rightHandSide()).mobileType() : type(_assignment)
449
1.53k
    ) :
450
2.05k
    _assignment.rightHandSide();
451
452
2.05k
  _assignment.leftHandSide().accept(*this);
453
454
2.05k
  solAssert(!!m_currentLValue, "LValue not retrieved.");
455
2.05k
  setLocation(_assignment);
456
457
2.05k
  if (assignmentOperator != Token::Assign)
458
135
  {
459
135
    solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
460
135
    solAssert(binaryOperator != Token::Exp);
461
135
    solAssert(type(_assignment) == type(_assignment.leftHandSide()));
462
463
135
    IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
464
135
    solAssert(type(_assignment) == leftIntermediate.type());
465
466
135
    define(_assignment) << (
467
135
      TokenTraits::isShiftOp(binaryOperator) ?
468
0
      shiftOperation(binaryOperator, leftIntermediate, value) :
469
135
      binaryOperation(binaryOperator, type(_assignment), leftIntermediate.name(), value.name())
470
135
    ) << "\n";
471
472
135
    writeToLValue(*m_currentLValue, IRVariable(_assignment));
473
135
  }
474
1.91k
  else
475
1.91k
  {
476
1.91k
    writeToLValue(*m_currentLValue, value);
477
478
1.91k
    if (dynamic_cast<ReferenceType const*>(&m_currentLValue->type))
479
510
      define(_assignment, readFromLValue(*m_currentLValue));
480
1.40k
    else if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
481
1.40k
      define(_assignment, value);
482
1.91k
  }
483
484
2.05k
  m_currentLValue.reset();
485
2.05k
  return false;
486
2.05k
}
487
488
bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
489
1.66k
{
490
1.66k
  setLocation(_tuple);
491
492
1.66k
  if (_tuple.isInlineArray())
493
301
  {
494
301
    auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
495
301
    solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
496
301
    define(_tuple) <<
497
301
      m_utils.allocateMemoryArrayFunction(arrayType) <<
498
301
      "(" <<
499
301
      _tuple.components().size() <<
500
301
      ")\n";
501
502
301
    std::string mpos = IRVariable(_tuple).part("mpos").name();
503
301
    Type const& baseType = *arrayType.baseType();
504
645
    for (size_t i = 0; i < _tuple.components().size(); i++)
505
344
    {
506
344
      Expression const& component = *_tuple.components()[i];
507
344
      component.accept(*this);
508
344
      setLocation(_tuple);
509
344
      IRVariable converted = convert(component, baseType);
510
344
      appendCode() <<
511
344
        m_utils.writeToMemoryFunction(baseType) <<
512
344
        "(" <<
513
344
        ("add(" + mpos + ", " + std::to_string(i * arrayType.memoryStride()) + ")") <<
514
344
        ", " <<
515
344
        converted.commaSeparatedList() <<
516
344
        ")\n";
517
344
    }
518
301
  }
519
1.36k
  else
520
1.36k
  {
521
1.36k
    bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
522
1.36k
    if (willBeWrittenTo)
523
1.36k
      solAssert(!m_currentLValue);
524
1.36k
    if (_tuple.components().size() == 1)
525
726
    {
526
726
      solAssert(_tuple.components().front());
527
726
      _tuple.components().front()->accept(*this);
528
726
      setLocation(_tuple);
529
726
      if (willBeWrittenTo)
530
726
        solAssert(!!m_currentLValue);
531
725
      else
532
725
        define(_tuple, *_tuple.components().front());
533
726
    }
534
640
    else
535
640
    {
536
640
      std::vector<std::optional<IRLValue>> lvalues;
537
1.97k
      for (size_t i = 0; i < _tuple.components().size(); ++i)
538
1.33k
        if (auto const& component = _tuple.components()[i])
539
1.32k
        {
540
1.32k
          component->accept(*this);
541
1.32k
          setLocation(_tuple);
542
1.32k
          if (willBeWrittenTo)
543
5
          {
544
5
            solAssert(!!m_currentLValue);
545
5
            lvalues.emplace_back(std::move(m_currentLValue));
546
5
            m_currentLValue.reset();
547
5
          }
548
1.32k
          else
549
1.32k
            define(IRVariable(_tuple).tupleComponent(i), *component);
550
1.32k
        }
551
5
        else if (willBeWrittenTo)
552
5
          lvalues.emplace_back();
553
554
640
      if (_tuple.annotation().willBeWrittenTo)
555
5
        m_currentLValue.emplace(IRLValue{
556
5
          *_tuple.annotation().type,
557
5
          IRLValue::Tuple{std::move(lvalues)}
558
5
        });
559
640
    }
560
1.36k
  }
561
1.66k
  return false;
562
1.66k
}
563
564
bool IRGeneratorForStatements::visit(Block const& _block)
565
3.65k
{
566
3.65k
  if (_block.unchecked())
567
10
  {
568
10
    solAssert(m_context.arithmetic() == Arithmetic::Checked);
569
10
    m_context.setArithmetic(Arithmetic::Wrapping);
570
10
  }
571
3.65k
  return true;
572
3.65k
}
573
574
void IRGeneratorForStatements::endVisit(Block const& _block)
575
3.65k
{
576
3.65k
  if (_block.unchecked())
577
10
  {
578
10
    solAssert(m_context.arithmetic() == Arithmetic::Wrapping);
579
10
    m_context.setArithmetic(Arithmetic::Checked);
580
10
  }
581
3.65k
}
582
583
bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
584
20
{
585
20
  _ifStatement.condition().accept(*this);
586
20
  setLocation(_ifStatement);
587
20
  std::string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean());
588
589
20
  if (_ifStatement.falseStatement())
590
0
  {
591
0
    appendCode() << "switch " << condition << "\n" "case 0 {\n";
592
0
    _ifStatement.falseStatement()->accept(*this);
593
0
    setLocation(_ifStatement);
594
0
    appendCode() << "}\n" "default {\n";
595
0
  }
596
20
  else
597
20
    appendCode() << "if " << condition << " {\n";
598
20
  _ifStatement.trueStatement().accept(*this);
599
20
  setLocation(_ifStatement);
600
20
  appendCode() << "}\n";
601
602
20
  return false;
603
20
}
604
605
void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder)
606
100
{
607
100
  solAssert(m_placeholderCallback);
608
100
  setLocation(_placeholder);
609
100
  appendCode() << m_placeholderCallback();
610
100
}
611
612
bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
613
90
{
614
90
  setLocation(_forStatement);
615
90
  generateLoop(
616
90
    _forStatement.body(),
617
90
    _forStatement.condition(),
618
90
    _forStatement.initializationExpression(),
619
90
    _forStatement.loopExpression(),
620
90
    false, // _isDoWhile
621
90
    *_forStatement.annotation().isSimpleCounterLoop
622
90
  );
623
624
90
  return false;
625
90
}
626
627
bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement)
628
16
{
629
16
  setLocation(_whileStatement);
630
16
  generateLoop(
631
16
    _whileStatement.body(),
632
16
    &_whileStatement.condition(),
633
16
    nullptr,
634
16
    nullptr,
635
16
    _whileStatement.isDoWhile()
636
16
  );
637
638
16
  return false;
639
16
}
640
641
bool IRGeneratorForStatements::visit(Continue const& _continue)
642
0
{
643
0
  setLocation(_continue);
644
0
  appendCode() << "continue\n";
645
0
  return false;
646
0
}
647
648
bool IRGeneratorForStatements::visit(Break const& _break)
649
0
{
650
0
  setLocation(_break);
651
0
  appendCode() << "break\n";
652
0
  return false;
653
0
}
654
655
void IRGeneratorForStatements::endVisit(Return const& _return)
656
1.33k
{
657
1.33k
  setLocation(_return);
658
1.33k
  if (Expression const* value = _return.expression())
659
1.31k
  {
660
1.31k
    solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer.");
661
1.31k
    std::vector<ASTPointer<VariableDeclaration>> const& returnParameters =
662
1.31k
      _return.annotation().functionReturnParameters->parameters();
663
1.31k
    if (returnParameters.size() > 1)
664
1.07k
      for (size_t i = 0; i < returnParameters.size(); ++i)
665
736
        assign(m_context.localVariable(*returnParameters[i]), IRVariable(*value).tupleComponent(i));
666
971
    else if (returnParameters.size() == 1)
667
971
      assign(m_context.localVariable(*returnParameters.front()), *value);
668
1.31k
  }
669
1.33k
  appendCode() << "leave\n";
670
1.33k
}
671
672
bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation)
673
728
{
674
728
  setLocation(_unaryOperation);
675
676
728
  FunctionDefinition const* function = *_unaryOperation.annotation().userDefinedFunction;
677
728
  if (function)
678
0
  {
679
0
    _unaryOperation.subExpression().accept(*this);
680
0
    setLocation(_unaryOperation);
681
682
0
    solAssert(function->isImplemented());
683
0
    solAssert(function->isFree());
684
0
    solAssert(function->parameters().size() == 1);
685
0
    solAssert(function->returnParameters().size() == 1);
686
0
    solAssert(*function->returnParameters()[0]->type() == *_unaryOperation.annotation().type);
687
688
0
    std::string argument = expressionAsType(_unaryOperation.subExpression(), *function->parameters()[0]->type());
689
0
    solAssert(!argument.empty());
690
691
0
    solAssert(_unaryOperation.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal);
692
0
    define(_unaryOperation) <<
693
0
      m_context.enqueueFunctionForCodeGeneration(*function) <<
694
0
      ("(" + argument + ")\n");
695
696
0
    return false;
697
0
  }
698
699
728
  Type const& resultType = type(_unaryOperation);
700
728
  Token const op = _unaryOperation.getOperator();
701
702
728
  if (resultType.category() == Type::Category::RationalNumber)
703
132
  {
704
132
    define(_unaryOperation) << formatNumber(resultType.literalValue(nullptr)) << "\n";
705
132
    return false;
706
132
  }
707
708
596
  _unaryOperation.subExpression().accept(*this);
709
596
  setLocation(_unaryOperation);
710
711
596
  if (op == Token::Delete)
712
12
  {
713
12
    solAssert(!!m_currentLValue, "LValue not retrieved.");
714
12
    std::visit(
715
12
      util::GenericVisitor{
716
12
        [&](IRLValue::Storage const& _storage) {
717
12
          appendCode() <<
718
12
            m_utils.storageSetToZeroFunction(m_currentLValue->type, VariableDeclaration::Location::Unspecified) <<
719
12
            "(" <<
720
12
            _storage.slot <<
721
12
            ", " <<
722
12
            _storage.offsetString() <<
723
12
            ")\n";
724
12
          m_currentLValue.reset();
725
12
        },
726
12
        [&](IRLValue::TransientStorage const& _transientStorage) {
727
0
          appendCode() <<
728
0
            m_utils.storageSetToZeroFunction(m_currentLValue->type, VariableDeclaration::Location::Transient) <<
729
0
            "(" <<
730
0
            _transientStorage.slot <<
731
0
            ", " <<
732
0
            _transientStorage.offsetString() <<
733
0
            ")\n";
734
0
          m_currentLValue.reset();
735
0
        },
736
12
        [&](auto const&) {
737
0
          IRVariable zeroValue(m_context.newYulVariable(), m_currentLValue->type);
738
0
          define(zeroValue) << m_utils.zeroValueFunction(m_currentLValue->type) << "()\n";
739
0
          writeToLValue(*m_currentLValue, zeroValue);
740
0
          m_currentLValue.reset();
741
0
        }
Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Stack>(solidity::frontend::IRLValue::Stack const&) const
Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Immutable>(solidity::frontend::IRLValue::Immutable const&) const
Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Memory>(solidity::frontend::IRLValue::Memory const&) const
Unexecuted instantiation: IRGeneratorForStatements.cpp:auto solidity::frontend::IRGeneratorForStatements::visit(solidity::frontend::UnaryOperation const&)::$_2::operator()<solidity::frontend::IRLValue::Tuple>(solidity::frontend::IRLValue::Tuple const&) const
742
12
      },
743
12
      m_currentLValue->kind
744
12
    );
745
12
  }
746
584
  else if (resultType.category() == Type::Category::Integer)
747
583
  {
748
583
    solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!");
749
750
583
    if (op == Token::Inc || op == Token::Dec)
751
386
    {
752
386
      solAssert(!!m_currentLValue, "LValue not retrieved.");
753
386
      IRVariable modifiedValue(m_context.newYulVariable(), resultType);
754
386
      IRVariable originalValue = readFromLValue(*m_currentLValue);
755
756
386
      bool checked = m_context.arithmetic() == Arithmetic::Checked;
757
386
      define(modifiedValue) <<
758
386
        (op == Token::Inc ?
759
304
          (checked ? m_utils.incrementCheckedFunction(resultType) : m_utils.incrementWrappingFunction(resultType)) :
760
386
          (checked ? m_utils.decrementCheckedFunction(resultType) : m_utils.decrementWrappingFunction(resultType))
761
386
        ) <<
762
386
        "(" <<
763
386
        originalValue.name() <<
764
386
        ")\n";
765
386
      writeToLValue(*m_currentLValue, modifiedValue);
766
386
      m_currentLValue.reset();
767
768
386
      define(_unaryOperation, _unaryOperation.isPrefixOperation() ? modifiedValue : originalValue);
769
386
    }
770
197
    else if (op == Token::BitNot)
771
129
      appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
772
68
    else if (op == Token::Add)
773
      // According to SyntaxChecker...
774
68
      solAssert(false, "Use of unary + is disallowed.");
775
68
    else if (op == Token::Sub)
776
68
    {
777
68
      IntegerType const& intType = *dynamic_cast<IntegerType const*>(&resultType);
778
68
      define(_unaryOperation) << (
779
68
        m_context.arithmetic() == Arithmetic::Checked ?
780
64
        m_utils.negateNumberCheckedFunction(intType) :
781
68
        m_utils.negateNumberWrappingFunction(intType)
782
68
      ) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n";
783
68
    }
784
0
    else
785
68
      solUnimplemented("Unary operator not yet implemented");
786
583
  }
787
1
  else if (resultType.category() == Type::Category::FixedBytes)
788
0
  {
789
0
    solAssert(op == Token::BitNot, "Only bitwise negation is allowed for FixedBytes");
790
0
    solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!");
791
0
    appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
792
0
  }
793
1
  else if (resultType.category() == Type::Category::Bool)
794
1
  {
795
1
    solAssert(
796
1
      op != Token::BitNot,
797
1
      "Bitwise Negation can't be done on bool!"
798
1
    );
799
800
1
    appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
801
1
  }
802
0
  else
803
1
    solUnimplemented("Unary operator not yet implemented");
804
805
596
  return false;
806
728
}
807
808
void IRGeneratorForStatements::endVisit(RevertStatement const& _revertStatement)
809
2
{
810
2
  ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_revertStatement.errorCall().expression()));
811
2
  solAssert(error);
812
2
  revertWithError(
813
2
    error->functionType(true)->externalSignature(),
814
2
    error->functionType(true)->parameterTypes(),
815
2
    _revertStatement.errorCall().sortedArguments()
816
2
  );
817
2
}
818
819
bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
820
6.92k
{
821
6.92k
  setLocation(_binOp);
822
823
6.92k
  FunctionDefinition const* function = *_binOp.annotation().userDefinedFunction;
824
6.92k
  if (function)
825
0
  {
826
0
    _binOp.leftExpression().accept(*this);
827
0
    _binOp.rightExpression().accept(*this);
828
0
    setLocation(_binOp);
829
830
0
    solAssert(function->isImplemented());
831
0
    solAssert(function->isFree());
832
0
    solAssert(function->parameters().size() == 2);
833
0
    solAssert(function->returnParameters().size() == 1);
834
0
    solAssert(*function->returnParameters()[0]->type() == *_binOp.annotation().type);
835
836
0
    std::string left = expressionAsType(_binOp.leftExpression(), *function->parameters()[0]->type());
837
0
    std::string right = expressionAsType(_binOp.rightExpression(), *function->parameters()[1]->type());
838
0
    solAssert(!left.empty() && !right.empty());
839
840
0
    solAssert(_binOp.userDefinedFunctionType()->kind() == FunctionType::Kind::Internal);
841
0
    define(_binOp) <<
842
0
      m_context.enqueueFunctionForCodeGeneration(*function) <<
843
0
      ("(" + left + ", " + right + ")\n");
844
845
0
    return false;
846
0
  }
847
848
6.92k
  solAssert(!!_binOp.annotation().commonType);
849
6.92k
  Type const* commonType = _binOp.annotation().commonType;
850
6.92k
  langutil::Token op = _binOp.getOperator();
851
852
6.92k
  if (op == Token::And || op == Token::Or)
853
11
  {
854
    // This can short-circuit!
855
11
    appendAndOrOperatorCode(_binOp);
856
11
    return false;
857
11
  }
858
859
6.91k
  if (commonType->category() == Type::Category::RationalNumber)
860
114
  {
861
114
    define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
862
114
    return false; // skip sub-expressions
863
114
  }
864
865
6.79k
  _binOp.leftExpression().accept(*this);
866
6.79k
  _binOp.rightExpression().accept(*this);
867
6.79k
  setLocation(_binOp);
868
869
6.79k
  if (TokenTraits::isCompareOp(op))
870
413
  {
871
413
    solAssert(commonType->isValueType());
872
873
413
    bool isSigned = false;
874
413
    if (auto type = dynamic_cast<IntegerType const*>(commonType))
875
384
      isSigned = type->isSigned();
876
877
413
    std::string args = expressionAsCleanedType(_binOp.leftExpression(), *commonType);
878
413
    args += ", " + expressionAsCleanedType(_binOp.rightExpression(), *commonType);
879
880
413
    auto functionType = dynamic_cast<FunctionType const*>(commonType);
881
413
    solAssert(functionType ? (op == Token::Equal || op == Token::NotEqual) : true, "Invalid function pointer comparison!");
882
883
413
    std::string expr;
884
885
413
    if (functionType && functionType->kind() ==  FunctionType::Kind::External)
886
0
    {
887
0
      solUnimplementedAssert(functionType->sizeOnStack() == 2, "");
888
0
      expr = m_utils.externalFunctionPointersEqualFunction() +
889
0
        "(" +
890
0
        IRVariable{_binOp.leftExpression()}.part("address").name() + "," +
891
0
        IRVariable{_binOp.leftExpression()}.part("functionSelector").name() + "," +
892
0
        IRVariable{_binOp.rightExpression()}.part("address").name() + "," +
893
0
        IRVariable{_binOp.rightExpression()}.part("functionSelector").name() +
894
0
        ")";
895
0
      if (op == Token::NotEqual)
896
0
        expr = "iszero(" + expr + ")";
897
0
    }
898
413
    else if (op == Token::Equal)
899
139
      expr = "eq(" + std::move(args) + ")";
900
274
    else if (op == Token::NotEqual)
901
55
      expr = "iszero(eq(" + std::move(args) + "))";
902
219
    else if (op == Token::GreaterThanOrEqual)
903
0
      expr = "iszero(" + std::string(isSigned ? "slt(" : "lt(") + std::move(args) + "))";
904
219
    else if (op == Token::LessThanOrEqual)
905
18
      expr = "iszero(" + std::string(isSigned ? "sgt(" : "gt(") + std::move(args) + "))";
906
201
    else if (op == Token::GreaterThan)
907
76
      expr = (isSigned ? "sgt(" : "gt(") + std::move(args) + ")";
908
125
    else if (op == Token::LessThan)
909
125
      expr = (isSigned ? "slt(" : "lt(") + std::move(args) + ")";
910
0
    else
911
125
      solAssert(false, "Unknown comparison operator.");
912
413
    define(_binOp) << expr << "\n";
913
413
  }
914
6.38k
  else if (op == Token::Exp)
915
1.61k
  {
916
1.61k
    IRVariable left = convert(_binOp.leftExpression(), *commonType);
917
1.61k
    IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
918
919
1.61k
    if (m_context.arithmetic() == Arithmetic::Wrapping)
920
0
      define(_binOp) << m_utils.wrappingIntExpFunction(
921
0
        dynamic_cast<IntegerType const&>(left.type()),
922
0
        dynamic_cast<IntegerType const&>(right.type())
923
0
      ) << "(" << left.name() << ", " << right.name() << ")\n";
924
1.61k
    else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type))
925
137
    {
926
137
      solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation.");
927
137
      solAssert(dynamic_cast<IntegerType const*>(commonType));
928
929
137
      define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction(
930
137
        *rationalNumberType,
931
137
        dynamic_cast<IntegerType const&>(right.type()),
932
137
        dynamic_cast<IntegerType const&>(*commonType)
933
137
      ) << "(" << right.name() << ")\n";
934
137
    }
935
1.48k
    else
936
1.48k
      define(_binOp) << m_utils.overflowCheckedIntExpFunction(
937
1.48k
        dynamic_cast<IntegerType const&>(left.type()),
938
1.48k
        dynamic_cast<IntegerType const&>(right.type())
939
1.48k
      ) << "(" << left.name() << ", " << right.name() << ")\n";
940
1.61k
  }
941
4.76k
  else if (TokenTraits::isShiftOp(op))
942
18
  {
943
18
    IRVariable left = convert(_binOp.leftExpression(), *commonType);
944
18
    IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
945
18
    define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n";
946
18
  }
947
4.74k
  else
948
4.74k
  {
949
4.74k
    std::string left = expressionAsType(_binOp.leftExpression(), *commonType);
950
4.74k
    std::string right = expressionAsType(_binOp.rightExpression(), *commonType);
951
4.74k
    define(_binOp) << binaryOperation(_binOp.getOperator(), *commonType, left, right) << "\n";
952
4.74k
  }
953
6.79k
  return false;
954
6.91k
}
955
956
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
957
3.01k
{
958
3.01k
  setLocation(_functionCall);
959
3.01k
  auto functionCallKind = *_functionCall.annotation().kind;
960
961
3.01k
  if (functionCallKind == FunctionCallKind::TypeConversion)
962
414
  {
963
414
    solAssert(
964
414
      _functionCall.expression().annotation().type->category() == Type::Category::TypeType,
965
414
      "Expected category to be TypeType"
966
414
    );
967
414
    solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion");
968
414
    define(_functionCall, *_functionCall.arguments().front());
969
414
    return;
970
414
  }
971
972
2.60k
  FunctionTypePointer functionType = nullptr;
973
2.60k
  if (functionCallKind == FunctionCallKind::StructConstructorCall)
974
0
  {
975
0
    auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
976
0
    auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
977
0
    functionType = structType.constructorType();
978
0
  }
979
2.60k
  else
980
2.60k
    functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
981
982
2.60k
  TypePointers parameterTypes = functionType->parameterTypes();
983
984
2.60k
  std::vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
985
986
2.60k
  if (functionCallKind == FunctionCallKind::StructConstructorCall)
987
0
  {
988
0
    TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
989
0
    auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
990
991
0
    define(_functionCall) << m_utils.allocateMemoryStructFunction(structType) << "()\n";
992
993
0
    MemberList::MemberMap members = structType.nativeMembers(nullptr);
994
995
0
    solAssert(members.size() == arguments.size(), "Struct parameter mismatch.");
996
997
0
    for (size_t i = 0; i < arguments.size(); i++)
998
0
    {
999
0
      IRVariable converted = convert(*arguments[i], *parameterTypes[i]);
1000
0
      appendCode() <<
1001
0
        m_utils.writeToMemoryFunction(*functionType->parameterTypes()[i]) <<
1002
0
        "(add(" <<
1003
0
        IRVariable(_functionCall).part("mpos").name() <<
1004
0
        ", " <<
1005
0
        structType.memoryOffsetOfMember(members[i].name) <<
1006
0
        "), " <<
1007
0
        converted.commaSeparatedList() <<
1008
0
        ")\n";
1009
0
    }
1010
1011
0
    return;
1012
0
  }
1013
1014
2.60k
  switch (functionType->kind())
1015
2.60k
  {
1016
0
  case FunctionType::Kind::Declaration:
1017
0
    solAssert(false, "Attempted to generate code for calling a function definition.");
1018
0
    break;
1019
198
  case FunctionType::Kind::Internal:
1020
198
  {
1021
198
    FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract());
1022
1023
198
    solAssert(!functionType->takesArbitraryParameters());
1024
1025
198
    std::vector<std::string> args;
1026
198
    if (functionType->hasBoundFirstArgument())
1027
3
      args += IRVariable(_functionCall.expression()).part("self").stackSlots();
1028
1029
339
    for (size_t i = 0; i < arguments.size(); ++i)
1030
141
      args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
1031
1032
198
    if (functionDef)
1033
176
    {
1034
176
      solAssert(functionDef->isImplemented());
1035
1036
176
      define(_functionCall) <<
1037
176
        m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
1038
176
        "(" <<
1039
176
        joinHumanReadable(args) <<
1040
176
        ")\n";
1041
176
    }
1042
22
    else
1043
22
    {
1044
22
      YulArity arity = YulArity::fromType(*functionType);
1045
22
      m_context.internalFunctionCalledThroughDispatch(arity);
1046
1047
22
      define(_functionCall) <<
1048
22
        IRNames::internalDispatch(arity) <<
1049
22
        "(" <<
1050
22
        IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
1051
22
        joinHumanReadablePrefixed(args) <<
1052
22
        ")\n";
1053
22
    }
1054
198
    break;
1055
0
  }
1056
102
  case FunctionType::Kind::External:
1057
149
  case FunctionType::Kind::DelegateCall:
1058
149
    appendExternalFunctionCall(_functionCall, arguments);
1059
149
    break;
1060
84
  case FunctionType::Kind::BareCall:
1061
84
  case FunctionType::Kind::BareDelegateCall:
1062
84
  case FunctionType::Kind::BareStaticCall:
1063
84
    appendBareCall(_functionCall, arguments);
1064
84
    break;
1065
0
  case FunctionType::Kind::BareCallCode:
1066
0
    solAssert(false, "Callcode has been removed.");
1067
796
  case FunctionType::Kind::Event:
1068
796
  {
1069
796
    auto const& event = dynamic_cast<EventDefinition const&>(functionType->declaration());
1070
796
    TypePointers paramTypes = functionType->parameterTypes();
1071
796
    ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
1072
1073
796
    std::vector<IRVariable> indexedArgs;
1074
796
    std::vector<std::string> nonIndexedArgs;
1075
796
    TypePointers nonIndexedArgTypes;
1076
796
    TypePointers nonIndexedParamTypes;
1077
796
    if (!event.isAnonymous())
1078
796
      define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) <<
1079
796
        formatNumber(u256(h256::Arith(keccak256(functionType->externalSignature())))) << "\n";
1080
1.59k
    for (size_t i = 0; i < event.parameters().size(); ++i)
1081
796
    {
1082
796
      Expression const& arg = *arguments[i];
1083
796
      if (event.parameters()[i]->isIndexed())
1084
0
      {
1085
0
        std::string value;
1086
0
        if (auto const& referenceType = dynamic_cast<ReferenceType const*>(paramTypes[i]))
1087
0
          define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) <<
1088
0
            m_utils.packedHashFunction({arg.annotation().type}, {referenceType}) <<
1089
0
            "(" <<
1090
0
            IRVariable(arg).commaSeparatedList() <<
1091
0
            ")\n";
1092
0
        else if (auto functionType = dynamic_cast<FunctionType const*>(paramTypes[i]))
1093
0
        {
1094
0
          solAssert(
1095
0
            IRVariable(arg).type() == *functionType &&
1096
0
            functionType->kind() == FunctionType::Kind::External &&
1097
0
            !functionType->hasBoundFirstArgument(),
1098
0
            ""
1099
0
          );
1100
0
          define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) <<
1101
0
            m_utils.combineExternalFunctionIdFunction() <<
1102
0
            "(" <<
1103
0
            IRVariable(arg).commaSeparatedList() <<
1104
0
            ")\n";
1105
0
        }
1106
0
        else
1107
0
        {
1108
0
          solAssert(parameterTypes[i]->sizeOnStack() == 1, "");
1109
0
          indexedArgs.emplace_back(convertAndCleanup(arg, *parameterTypes[i]));
1110
0
        }
1111
0
      }
1112
796
      else
1113
796
      {
1114
796
        nonIndexedArgs += IRVariable(arg).stackSlots();
1115
796
        nonIndexedArgTypes.push_back(arg.annotation().type);
1116
796
        nonIndexedParamTypes.push_back(paramTypes[i]);
1117
796
      }
1118
796
    }
1119
796
    solAssert(indexedArgs.size() <= 4, "Too many indexed arguments.");
1120
796
    Whiskers templ(R"({
1121
796
      let <pos> := <allocateUnbounded>()
1122
796
      let <end> := <encode>(<pos> <nonIndexedArgs>)
1123
796
      <log>(<pos>, sub(<end>, <pos>) <indexedArgs>)
1124
796
    })");
1125
796
    templ("pos", m_context.newYulVariable());
1126
796
    templ("end", m_context.newYulVariable());
1127
796
    templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
1128
796
    templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
1129
796
    templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
1130
796
    templ("log", "log" + std::to_string(indexedArgs.size()));
1131
796
    templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | ranges::views::transform([&](auto const& _arg) {
1132
796
      return _arg.commaSeparatedList();
1133
796
    })));
1134
796
    appendCode() << templ.render();
1135
796
    break;
1136
0
  }
1137
7
  case FunctionType::Kind::Wrap:
1138
8
  case FunctionType::Kind::Unwrap:
1139
8
  {
1140
8
    solAssert(arguments.size() == 1);
1141
8
    FunctionType::Kind kind = functionType->kind();
1142
8
    if (kind == FunctionType::Kind::Wrap)
1143
8
      solAssert(
1144
1
        type(*arguments.at(0)).isImplicitlyConvertibleTo(
1145
1
          dynamic_cast<UserDefinedValueType const&>(type(_functionCall)).underlyingType()
1146
1
        ),
1147
1
        ""
1148
1
      );
1149
1
    else
1150
8
      solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType);
1151
1152
8
    define(_functionCall, *arguments.at(0));
1153
8
    break;
1154
7
  }
1155
104
  case FunctionType::Kind::Assert:
1156
111
  case FunctionType::Kind::Require:
1157
111
  {
1158
111
    solAssert(arguments.size() > 0, "Expected at least one parameter for require/assert");
1159
111
    solAssert(arguments.size() <= 2, "Expected no more than two parameters for require/assert");
1160
1161
111
    Type const* messageArgumentType = arguments.size() == 2 ? arguments[1]->annotation().type : nullptr;
1162
1163
111
    auto const* magicType = dynamic_cast<MagicType const*>(messageArgumentType);
1164
111
    if (magicType && magicType->kind() == MagicType::Kind::Error)
1165
0
    {
1166
0
      auto const& errorConstructorCall = dynamic_cast<FunctionCall const&>(*arguments[1]);
1167
0
      appendCode() << m_utils.requireWithErrorFunction(errorConstructorCall) << "(" <<IRVariable(*arguments[0]).name();
1168
0
      for (auto argument: errorConstructorCall.arguments())
1169
0
        if (argument->annotation().type->sizeOnStack() > 0)
1170
0
          appendCode() << ", " << IRVariable(*argument).commaSeparatedList();
1171
0
      appendCode() << ")\n";
1172
0
    }
1173
111
    else
1174
111
    {
1175
      // This option only removes strings, not custom errors
1176
111
      if (m_context.revertStrings() == RevertStrings::Strip)
1177
0
        messageArgumentType = nullptr;
1178
111
      ASTPointer<Expression const> stringArgumentExpression = messageArgumentType ? arguments[1] : nullptr;
1179
111
      std::string requireOrAssertFunction = m_utils.requireOrAssertFunction(
1180
111
        functionType->kind() == FunctionType::Kind::Assert,
1181
111
        messageArgumentType,
1182
111
        stringArgumentExpression
1183
111
      );
1184
111
      appendCode() << std::move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name();
1185
111
      if (messageArgumentType && messageArgumentType->sizeOnStack() > 0)
1186
7
        appendCode() << ", " << IRVariable(*arguments[1]).commaSeparatedList();
1187
111
      appendCode() << ")\n";
1188
111
    }
1189
1190
111
    break;
1191
104
  }
1192
42
  case FunctionType::Kind::ABIEncode:
1193
42
  case FunctionType::Kind::ABIEncodePacked:
1194
44
  case FunctionType::Kind::ABIEncodeWithSelector:
1195
44
  case FunctionType::Kind::ABIEncodeCall:
1196
48
  case FunctionType::Kind::ABIEncodeWithSignature:
1197
48
  {
1198
48
    bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
1199
48
    solAssert(functionType->padArguments() != isPacked);
1200
48
    bool const hasSelectorOrSignature =
1201
48
      functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
1202
46
      functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
1203
46
      functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
1204
1205
48
    TypePointers argumentTypes;
1206
48
    TypePointers targetTypes;
1207
48
    std::vector<std::string> argumentVars;
1208
48
    std::string selector;
1209
48
    std::vector<ASTPointer<Expression const>> argumentsOfEncodeFunction;
1210
1211
48
    if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
1212
0
    {
1213
0
      solAssert(arguments.size() == 2);
1214
      // Account for tuples with one component which become that component
1215
0
      if (type(*arguments[1]).category() == Type::Category::Tuple)
1216
0
      {
1217
0
        auto const& tupleExpression = dynamic_cast<TupleExpression const&>(*arguments[1]);
1218
0
        for (auto component: tupleExpression.components())
1219
0
          argumentsOfEncodeFunction.push_back(component);
1220
0
      }
1221
0
      else
1222
0
        argumentsOfEncodeFunction.push_back(arguments[1]);
1223
0
    }
1224
48
    else
1225
87
      for (size_t i = 0; i < arguments.size(); ++i)
1226
39
      {
1227
        // ignore selector
1228
39
        if (hasSelectorOrSignature && i == 0)
1229
6
          continue;
1230
33
        argumentsOfEncodeFunction.push_back(arguments[i]);
1231
33
      }
1232
1233
48
    for (auto const& argument: argumentsOfEncodeFunction)
1234
33
    {
1235
33
      argumentTypes.emplace_back(&type(*argument));
1236
33
      argumentVars += IRVariable(*argument).stackSlots();
1237
33
    }
1238
1239
48
    if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
1240
0
    {
1241
0
      auto encodedFunctionType = dynamic_cast<FunctionType const*>(arguments.front()->annotation().type);
1242
0
      solAssert(encodedFunctionType);
1243
0
      encodedFunctionType = encodedFunctionType->asExternallyCallableFunction(false);
1244
0
      solAssert(encodedFunctionType);
1245
0
      targetTypes = encodedFunctionType->parameterTypes();
1246
0
    }
1247
48
    else
1248
48
      for (auto const& argument: argumentsOfEncodeFunction)
1249
33
        targetTypes.emplace_back(type(*argument).fullEncodingType(false, true, isPacked));
1250
1251
1252
48
    if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
1253
0
    {
1254
0
      auto const& selectorType = dynamic_cast<FunctionType const&>(type(*arguments.front()));
1255
0
      if (selectorType.kind() == FunctionType::Kind::Declaration)
1256
0
      {
1257
0
        solAssert(selectorType.hasDeclaration());
1258
0
        selector = formatNumber(selectorType.externalIdentifier() << (256 - 32));
1259
0
      }
1260
0
      else
1261
0
      {
1262
0
        selector = convert(
1263
0
          IRVariable(*arguments[0]).part("functionSelector"),
1264
0
          *TypeProvider::fixedBytes(4)
1265
0
        ).name();
1266
0
      }
1267
0
    }
1268
48
    else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
1269
4
    {
1270
      // hash the signature
1271
4
      Type const& selectorType = type(*arguments.front());
1272
4
      if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType))
1273
4
        selector = formatNumber(util::selectorFromSignatureU256(stringType->value()));
1274
0
      else
1275
0
      {
1276
        // Used to reset the free memory pointer later.
1277
        // TODO This is an abuse of the `allocateUnbounded` function.
1278
        // We might want to introduce a new set of memory handling functions here
1279
        // a la "setMemoryCheckPoint" and "freeUntilCheckPoint".
1280
0
        std::string freeMemoryPre = m_context.newYulVariable();
1281
0
        appendCode() << "let " << freeMemoryPre << " := " << m_utils.allocateUnboundedFunction() << "()\n";
1282
0
        IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
1283
0
        IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
1284
1285
0
        std::string dataAreaFunction = m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory());
1286
0
        std::string arrayLengthFunction = m_utils.arrayLengthFunction(*TypeProvider::bytesMemory());
1287
0
        define(hashVariable) <<
1288
0
          "keccak256(" <<
1289
0
          (dataAreaFunction + "(" + array.commaSeparatedList() + ")") <<
1290
0
          ", " <<
1291
0
          (arrayLengthFunction + "(" + array.commaSeparatedList() +")") <<
1292
0
          ")\n";
1293
0
        IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
1294
0
        define(selectorVariable, hashVariable);
1295
0
        selector = selectorVariable.name();
1296
0
        appendCode() << m_utils.finalizeAllocationFunction() << "(" << freeMemoryPre << ", 0)\n";
1297
0
      }
1298
4
    }
1299
44
    else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
1300
2
      selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name();
1301
1302
48
    Whiskers templ(R"(
1303
48
      let <data> := <allocateUnbounded>()
1304
48
      let <memPtr> := add(<data>, 0x20)
1305
48
      <?+selector>
1306
48
        mstore(<memPtr>, <selector>)
1307
48
        <memPtr> := add(<memPtr>, 4)
1308
48
      </+selector>
1309
48
      let <mend> := <encode>(<memPtr><arguments>)
1310
48
      mstore(<data>, sub(<mend>, add(<data>, 0x20)))
1311
48
      <finalizeAllocation>(<data>, sub(<mend>, <data>))
1312
48
    )");
1313
48
    templ("data", IRVariable(_functionCall).part("mpos").name());
1314
48
    templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
1315
48
    templ("memPtr", m_context.newYulVariable());
1316
48
    templ("mend", m_context.newYulVariable());
1317
48
    templ("selector", selector);
1318
48
    templ("encode",
1319
48
      isPacked ?
1320
0
      m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) :
1321
48
      m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false)
1322
48
    );
1323
48
    templ("arguments", joinHumanReadablePrefixed(argumentVars));
1324
48
    templ("finalizeAllocation", m_utils.finalizeAllocationFunction());
1325
1326
48
    appendCode() << templ.render();
1327
48
    break;
1328
44
  }
1329
14
  case FunctionType::Kind::ABIDecode:
1330
14
  {
1331
14
    Whiskers templ(R"(
1332
14
      <?+retVars>let <retVars> := </+retVars> <abiDecode>(<offset>, add(<offset>, <length>))
1333
14
    )");
1334
1335
14
    Type const* firstArgType = arguments.front()->annotation().type;
1336
14
    TypePointers targetTypes;
1337
1338
14
    if (TupleType const* targetTupleType = dynamic_cast<TupleType const*>(_functionCall.annotation().type))
1339
0
      targetTypes = targetTupleType->components();
1340
14
    else
1341
14
      targetTypes = TypePointers{_functionCall.annotation().type};
1342
1343
14
    if (
1344
14
      auto referenceType = dynamic_cast<ReferenceType const*>(firstArgType);
1345
14
      referenceType && referenceType->dataStoredIn(DataLocation::CallData)
1346
14
      )
1347
0
    {
1348
0
      solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()));
1349
0
      IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata());
1350
0
      templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false));
1351
0
      templ("offset", var.part("offset").name());
1352
0
      templ("length", var.part("length").name());
1353
0
    }
1354
14
    else
1355
14
    {
1356
14
      IRVariable var = convert(*arguments[0], *TypeProvider::bytesMemory());
1357
14
      templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, true));
1358
14
      templ("offset", "add(" + var.part("mpos").name() + ", 32)");
1359
14
      templ("length",
1360
14
        m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) + "(" + var.part("mpos").name() + ")"
1361
14
      );
1362
14
    }
1363
14
    templ("retVars", IRVariable(_functionCall).commaSeparatedList());
1364
1365
14
    appendCode() << templ.render();
1366
14
    break;
1367
44
  }
1368
11
  case FunctionType::Kind::Revert:
1369
11
  {
1370
11
    solAssert(arguments.size() == parameterTypes.size());
1371
11
    solAssert(arguments.size() <= 1);
1372
11
    solAssert(
1373
11
      arguments.empty() ||
1374
11
      arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
1375
11
    "");
1376
11
    if (m_context.revertStrings() == RevertStrings::Strip || arguments.empty())
1377
0
      appendCode() << "revert(0, 0)\n";
1378
11
    else
1379
11
      revertWithError(
1380
11
        "Error(string)",
1381
11
        {TypeProvider::stringMemory()},
1382
11
        {arguments.front()}
1383
11
      );
1384
11
    break;
1385
44
  }
1386
  // Array creation using new
1387
629
  case FunctionType::Kind::ObjectCreation:
1388
629
  {
1389
629
    ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
1390
629
    solAssert(arguments.size() == 1);
1391
1392
629
    IRVariable value = convert(*arguments[0], *TypeProvider::uint256());
1393
629
    define(_functionCall) <<
1394
629
      m_utils.allocateAndInitializeMemoryArrayFunction(arrayType) <<
1395
629
      "(" <<
1396
629
      value.commaSeparatedList() <<
1397
629
      ")\n";
1398
629
    break;
1399
44
  }
1400
4
  case FunctionType::Kind::KECCAK256:
1401
4
  {
1402
4
    solAssert(arguments.size() == 1);
1403
1404
4
    ArrayType const* arrayType = TypeProvider::bytesMemory();
1405
1406
4
    if (auto const* stringLiteral = dynamic_cast<StringLiteralType const*>(arguments.front()->annotation().type))
1407
2
    {
1408
      // Optimization: Compute keccak256 on string literals at compile-time.
1409
2
      define(_functionCall) <<
1410
2
        ("0x" + keccak256(stringLiteral->value()).hex()) <<
1411
2
        "\n";
1412
2
    }
1413
2
    else
1414
2
    {
1415
2
      auto array = convert(*arguments[0], *arrayType);
1416
1417
2
      std::string dataAreaFunction = m_utils.arrayDataAreaFunction(*arrayType);
1418
2
      std::string arrayLengthFunction = m_utils.arrayLengthFunction(*arrayType);
1419
2
      define(_functionCall) <<
1420
2
        "keccak256(" <<
1421
2
        (dataAreaFunction + "(" + array.commaSeparatedList() + ")") <<
1422
2
        ", " <<
1423
2
        (arrayLengthFunction + "(" + array.commaSeparatedList() +")") <<
1424
2
        ")\n";
1425
2
    }
1426
4
    break;
1427
44
  }
1428
0
  case FunctionType::Kind::ERC7201:
1429
0
  {
1430
0
    solAssert(arguments.size() == 1);
1431
0
    Type const* argType = arguments.front()->annotation().type;
1432
0
    solAssert(argType);
1433
0
    if (dynamic_cast<StringLiteralType const*>(argType))
1434
0
    {
1435
0
      std::optional<u256> slot = erc7201CompileTimeValue(_functionCall);
1436
0
      solAssert(slot.has_value());
1437
0
      define(_functionCall) << formatNumber(*slot) << "\n";
1438
0
    }
1439
0
    else
1440
0
    {
1441
0
      Whiskers templ(R"(
1442
0
        <erc7201Builtin>(<arrayDataArea>(<namespaceID>), <arrayLength>(<namespaceID>))
1443
0
      )");
1444
1445
0
      IRVariable stringArg = convert(*arguments[0], *TypeProvider::stringMemory());
1446
0
      solAssert(stringArg.stackSlots().size() == 1);
1447
0
      std::string namespaceID = stringArg.stackSlots().front();
1448
1449
0
      templ("arrayDataArea", m_utils.arrayDataAreaFunction(*TypeProvider::stringMemory()));
1450
0
      templ("arrayLength", m_utils.arrayLengthFunction(*TypeProvider::stringMemory()));
1451
0
      templ("namespaceID", namespaceID);
1452
0
      templ("erc7201Builtin", m_utils.erc7201());
1453
1454
0
      define(_functionCall) << templ.render();
1455
0
    }
1456
0
    break;
1457
44
  }
1458
30
  case FunctionType::Kind::ArrayPop:
1459
30
  {
1460
30
    solAssert(functionType->hasBoundFirstArgument());
1461
30
    solAssert(functionType->parameterTypes().empty());
1462
30
    ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
1463
30
    solAssert(arrayType);
1464
30
    define(_functionCall) <<
1465
30
      m_utils.storageArrayPopFunction(*arrayType) <<
1466
30
      "(" <<
1467
30
      IRVariable(_functionCall.expression()).commaSeparatedList() <<
1468
30
      ")\n";
1469
30
    break;
1470
44
  }
1471
271
  case FunctionType::Kind::ArrayPush:
1472
271
  {
1473
271
    ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
1474
271
    solAssert(arrayType);
1475
1476
271
    if (arguments.empty())
1477
3
    {
1478
3
      auto slotName = m_context.newYulVariable();
1479
3
      auto offsetName = m_context.newYulVariable();
1480
3
      appendCode() << "let " << slotName << ", " << offsetName << " := " <<
1481
3
        m_utils.storageArrayPushZeroFunction(*arrayType) <<
1482
3
        "(" << IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n";
1483
3
      setLValue(_functionCall, IRLValue{
1484
3
        *arrayType->baseType(),
1485
3
        IRLValue::Storage{
1486
3
          slotName,
1487
3
          offsetName,
1488
3
        }
1489
3
      });
1490
3
    }
1491
268
    else
1492
268
    {
1493
268
      IRVariable argument =
1494
268
        arrayType->baseType()->isValueType() ?
1495
95
        convert(*arguments.front(), *arrayType->baseType()) :
1496
268
        *arguments.front();
1497
1498
268
      appendCode() <<
1499
268
        m_utils.storageArrayPushFunction(*arrayType, &argument.type()) <<
1500
268
        "(" <<
1501
268
        IRVariable(_functionCall.expression()).commaSeparatedList() <<
1502
268
        (argument.stackSlots().empty() ? "" : (", " + argument.commaSeparatedList()))  <<
1503
268
        ")\n";
1504
268
    }
1505
271
    break;
1506
44
  }
1507
0
  case FunctionType::Kind::StringConcat:
1508
28
  case FunctionType::Kind::BytesConcat:
1509
28
  {
1510
28
    TypePointers argumentTypes;
1511
28
    std::vector<std::string> argumentVars;
1512
28
    for (ASTPointer<Expression const> const& argument: arguments)
1513
55
    {
1514
55
      argumentTypes.emplace_back(&type(*argument));
1515
55
      argumentVars += IRVariable(*argument).stackSlots();
1516
55
    }
1517
28
    define(IRVariable(_functionCall)) <<
1518
28
      m_utils.bytesOrStringConcatFunction(argumentTypes, functionType->kind()) <<
1519
28
      "(" <<
1520
28
      joinHumanReadable(argumentVars) <<
1521
28
      ")\n";
1522
28
    break;
1523
0
  }
1524
2
  case FunctionType::Kind::Error:
1525
53
  case FunctionType::Kind::MetaType:
1526
53
  {
1527
53
    break;
1528
2
  }
1529
8
  case FunctionType::Kind::AddMod:
1530
11
  case FunctionType::Kind::MulMod:
1531
11
  {
1532
11
    static std::map<FunctionType::Kind, std::string> functions = {
1533
11
      {FunctionType::Kind::AddMod, "addmod"},
1534
11
      {FunctionType::Kind::MulMod, "mulmod"},
1535
11
    };
1536
11
    solAssert(functions.find(functionType->kind()) != functions.end());
1537
11
    solAssert(arguments.size() == 3 && parameterTypes.size() == 3);
1538
1539
11
    IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2]));
1540
11
    define(modulus, *arguments[2]);
1541
11
    Whiskers templ("if iszero(<modulus>) { <panic>() }\n");
1542
11
    templ("modulus", modulus.name());
1543
11
    templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero));
1544
11
    appendCode() << templ.render();
1545
1546
11
    std::string args;
1547
33
    for (size_t i = 0; i < 2; ++i)
1548
22
      args += expressionAsType(*arguments[i], *(parameterTypes[i])) + ", ";
1549
11
    args += modulus.name();
1550
11
    define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n";
1551
11
    break;
1552
8
  }
1553
0
  case FunctionType::Kind::GasLeft:
1554
0
  case FunctionType::Kind::Selfdestruct:
1555
0
  case FunctionType::Kind::BlockHash:
1556
0
  case FunctionType::Kind::BlobHash:
1557
0
  {
1558
0
    static std::map<FunctionType::Kind, std::string> functions = {
1559
0
      {FunctionType::Kind::GasLeft, "gas"},
1560
0
      {FunctionType::Kind::Selfdestruct, "selfdestruct"},
1561
0
      {FunctionType::Kind::BlockHash, "blockhash"},
1562
0
      {FunctionType::Kind::BlobHash, "blobhash"},
1563
0
    };
1564
0
    solAssert(functions.find(functionType->kind()) != functions.end());
1565
1566
0
    std::string args;
1567
0
    for (size_t i = 0; i < arguments.size(); ++i)
1568
0
      args += (args.empty() ? "" : ", ") + expressionAsType(*arguments[i], *(parameterTypes[i]));
1569
0
    define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n";
1570
0
    break;
1571
0
  }
1572
90
  case FunctionType::Kind::Creation:
1573
90
  {
1574
90
    solAssert(!functionType->gasSet(), "Gas limit set for contract creation.");
1575
90
    solAssert(
1576
90
      functionType->returnParameterTypes().size() == 1,
1577
90
      "Constructor should return only one type"
1578
90
    );
1579
1580
90
    TypePointers argumentTypes;
1581
90
    std::vector<std::string> constructorParams;
1582
90
    for (ASTPointer<Expression const> const& arg: arguments)
1583
0
    {
1584
0
      argumentTypes.push_back(arg->annotation().type);
1585
0
      constructorParams += IRVariable{*arg}.stackSlots();
1586
0
    }
1587
1588
90
    ContractDefinition const* contract =
1589
90
      &dynamic_cast<ContractType const&>(*functionType->returnParameterTypes().front()).contractDefinition();
1590
90
    m_context.addSubObject(contract);
1591
1592
90
    Whiskers t(R"(
1593
90
      let <memPos> := <allocateUnbounded>()
1594
90
      let <memEnd> := add(<memPos>, datasize("<object>"))
1595
90
      if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { <panic>() }
1596
90
      datacopy(<memPos>, dataoffset("<object>"), datasize("<object>"))
1597
90
      <memEnd> := <abiEncode>(<memEnd><constructorParams>)
1598
90
      <?saltSet>
1599
90
        let <address> := create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>)
1600
90
      <!saltSet>
1601
90
        let <address> := create(<value>, <memPos>, sub(<memEnd>, <memPos>))
1602
90
      </saltSet>
1603
90
      <?isTryCall>
1604
90
        let <success> := iszero(iszero(<address>))
1605
90
      <!isTryCall>
1606
90
        if iszero(<address>) { <forwardingRevert>() }
1607
90
      </isTryCall>
1608
90
    )");
1609
90
    t("memPos", m_context.newYulVariable());
1610
90
    t("memEnd", m_context.newYulVariable());
1611
90
    t("allocateUnbounded", m_utils.allocateUnboundedFunction());
1612
90
    t("object", IRNames::creationObject(*contract));
1613
90
    t("panic", m_utils.panicFunction(PanicCode::ResourceError));
1614
90
    t("abiEncode",
1615
90
      m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
1616
90
    );
1617
90
    t("constructorParams", joinHumanReadablePrefixed(constructorParams));
1618
90
    t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
1619
90
    t("saltSet", functionType->saltSet());
1620
90
    if (functionType->saltSet())
1621
0
      t("salt", IRVariable(_functionCall.expression()).part("salt").name());
1622
90
    solAssert(IRVariable(_functionCall).stackSlots().size() == 1);
1623
90
    t("address", IRVariable(_functionCall).commaSeparatedList());
1624
90
    t("isTryCall", _functionCall.annotation().tryCall);
1625
90
    if (_functionCall.annotation().tryCall)
1626
0
      t("success", IRNames::trySuccessConditionVariable(_functionCall));
1627
90
    else
1628
90
      t("forwardingRevert", m_utils.forwardingRevertFunction());
1629
90
    appendCode() << t.render();
1630
1631
90
    break;
1632
0
  }
1633
0
  case FunctionType::Kind::Send:
1634
12
  case FunctionType::Kind::Transfer:
1635
12
  {
1636
12
    solAssert(arguments.size() == 1 && parameterTypes.size() == 1);
1637
12
    std::string address{IRVariable(_functionCall.expression()).part("address").name()};
1638
12
    std::string value{expressionAsType(*arguments[0], *(parameterTypes[0]))};
1639
12
    Whiskers templ(R"(
1640
12
      let <gas> := 0
1641
12
      if iszero(<value>) { <gas> := <callStipend> }
1642
12
        let <success> := call(<gas>, <address>, <value>, 0, 0, 0, 0)
1643
12
      <?isTransfer>
1644
12
        if iszero(<success>) { <forwardingRevert>() }
1645
12
      </isTransfer>
1646
12
    )");
1647
12
    templ("gas", m_context.newYulVariable());
1648
12
    templ("callStipend", toString(evmasm::GasCosts::callStipend));
1649
12
    templ("address", address);
1650
12
    templ("value", value);
1651
12
    if (functionType->kind() == FunctionType::Kind::Transfer)
1652
12
      templ("success", m_context.newYulVariable());
1653
0
    else
1654
0
      templ("success", IRVariable(_functionCall).commaSeparatedList());
1655
12
    templ("isTransfer", functionType->kind() == FunctionType::Kind::Transfer);
1656
12
    templ("forwardingRevert", m_utils.forwardingRevertFunction());
1657
12
    appendCode() << templ.render();
1658
1659
12
    break;
1660
0
  }
1661
4
  case FunctionType::Kind::ECRecover:
1662
57
  case FunctionType::Kind::RIPEMD160:
1663
57
  case FunctionType::Kind::SHA256:
1664
57
  {
1665
57
    solAssert(!_functionCall.annotation().tryCall);
1666
57
    solAssert(!functionType->valueSet());
1667
57
    solAssert(!functionType->gasSet());
1668
57
    solAssert(!functionType->hasBoundFirstArgument());
1669
1670
57
    static std::map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
1671
57
      {FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
1672
57
      {FunctionType::Kind::SHA256, std::make_tuple(2, 0)},
1673
57
      {FunctionType::Kind::RIPEMD160, std::make_tuple(3, 12)},
1674
57
    };
1675
57
    auto [ address, offset ] = precompiles[functionType->kind()];
1676
57
    TypePointers argumentTypes;
1677
57
    std::vector<std::string> argumentStrings;
1678
57
    for (auto const& arg: arguments)
1679
69
    {
1680
69
      argumentTypes.emplace_back(&type(*arg));
1681
69
      argumentStrings += IRVariable(*arg).stackSlots();
1682
69
    }
1683
57
    Whiskers templ(R"(
1684
57
      let <pos> := <allocateUnbounded>()
1685
57
      let <end> := <encodeArgs>(<pos> <argumentString>)
1686
57
      <?isECRecover>
1687
57
        mstore(0, 0)
1688
57
      </isECRecover>
1689
57
        let <success> := <call>(<gas>, <address> <?isCall>, 0</isCall>, <pos>, sub(<end>, <pos>), 0, 32)
1690
57
      if iszero(<success>) { <forwardingRevert>() }
1691
57
      let <retVars> := <shl>(mload(0))
1692
57
    )");
1693
57
    templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call");
1694
57
    templ("isCall", !m_context.evmVersion().hasStaticCall());
1695
57
    templ("shl", m_utils.shiftLeftFunction(offset * 8));
1696
57
    templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
1697
57
    templ("pos", m_context.newYulVariable());
1698
57
    templ("end", m_context.newYulVariable());
1699
57
    templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind());
1700
57
    if (FunctionType::Kind::ECRecover == functionType->kind())
1701
4
      templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes));
1702
53
    else
1703
53
      templ("encodeArgs", m_context.abiFunctions().tupleEncoderPacked(argumentTypes, parameterTypes));
1704
57
    templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
1705
57
    templ("address", toString(address));
1706
57
    templ("success", m_context.newYulVariable());
1707
57
    templ("retVars", IRVariable(_functionCall).commaSeparatedList());
1708
57
    templ("forwardingRevert", m_utils.forwardingRevertFunction());
1709
57
    if (m_context.evmVersion().canOverchargeGasForCall())
1710
      // Send all gas (requires tangerine whistle EVM)
1711
49
      templ("gas", "gas()");
1712
8
    else
1713
8
    {
1714
      // @todo The value 10 is not exact and this could be fine-tuned,
1715
      // but this has worked for years in the old code generator.
1716
8
      u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10 + evmasm::GasCosts::callNewAccountGas;
1717
8
      templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
1718
8
    }
1719
1720
57
    appendCode() << templ.render();
1721
1722
57
    break;
1723
57
  }
1724
0
  default:
1725
0
    solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
1726
2.60k
  }
1727
2.60k
}
1728
1729
void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
1730
20
{
1731
20
  setLocation(_options);
1732
20
  FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
1733
1734
20
  solUnimplementedAssert(!previousType.hasBoundFirstArgument());
1735
1736
  // Copy over existing values.
1737
20
  for (auto const& item: previousType.stackItems())
1738
40
    define(IRVariable(_options).part(std::get<0>(item)), IRVariable(_options.expression()).part(std::get<0>(item)));
1739
1740
40
  for (size_t i = 0; i < _options.names().size(); ++i)
1741
20
  {
1742
20
    std::string const& name = *_options.names()[i];
1743
20
    solAssert(name == "salt" || name == "gas" || name == "value");
1744
1745
20
    define(IRVariable(_options).part(name), *_options.options()[i]);
1746
20
  }
1747
20
}
1748
1749
bool IRGeneratorForStatements::visit(MemberAccess const& _memberAccess)
1750
1.10k
{
1751
  // A shortcut for <address>.code.length. We skip visiting <address>.code and directly visit
1752
  // <address>. The actual code is generated in endVisit.
1753
1.10k
  if (
1754
1.10k
    auto innerExpression = dynamic_cast<MemberAccess const*>(&_memberAccess.expression());
1755
1.10k
    _memberAccess.memberName() == "length" &&
1756
62
    innerExpression &&
1757
1
    innerExpression->memberName() == "code" &&
1758
0
    innerExpression->expression().annotation().type->category() == Type::Category::Address
1759
1.10k
  )
1760
0
  {
1761
0
    solAssert(innerExpression->annotation().type->category() == Type::Category::Array);
1762
    // Skip visiting <address>.code
1763
0
    innerExpression->expression().accept(*this);
1764
1765
0
    return false;
1766
0
  }
1767
1768
1.10k
  return true;
1769
1.10k
}
1770
1771
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
1772
1.10k
{
1773
1.10k
  setLocation(_memberAccess);
1774
1775
1.10k
  ASTString const& member = _memberAccess.memberName();
1776
1.10k
  auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
1777
1.10k
  Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
1778
1779
1.10k
  if (memberFunctionType && memberFunctionType->hasBoundFirstArgument())
1780
345
  {
1781
345
    define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
1782
345
    solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
1783
345
    if (memberFunctionType->kind() == FunctionType::Kind::Internal)
1784
3
      assignInternalFunctionIDIfNotCalledDirectly(
1785
3
        _memberAccess,
1786
3
        dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration())
1787
3
      );
1788
342
    else if (
1789
342
      memberFunctionType->kind() == FunctionType::Kind::ArrayPush ||
1790
71
      memberFunctionType->kind() == FunctionType::Kind::ArrayPop
1791
342
    )
1792
301
    {
1793
      // Nothing to do.
1794
301
    }
1795
41
    else
1796
41
    {
1797
41
      auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration());
1798
41
      solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall);
1799
41
      auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope());
1800
41
      solAssert(contract && contract->isLibrary());
1801
41
      define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
1802
41
      define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n";
1803
41
    }
1804
345
    return;
1805
345
  }
1806
1807
762
  switch (objectCategory)
1808
762
  {
1809
106
  case Type::Category::Contract:
1810
106
  {
1811
106
    ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
1812
106
    if (type.isSuper())
1813
106
      solAssert(false);
1814
1815
    // ordinary contract type
1816
106
    else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
1817
106
    {
1818
106
      u256 identifier;
1819
106
      if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
1820
10
        identifier = FunctionType(*variable).externalIdentifier();
1821
96
      else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration))
1822
96
        identifier = FunctionType(*function).externalIdentifier();
1823
0
      else
1824
96
        solAssert(false, "Contract member is neither variable nor function.");
1825
1826
106
      define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
1827
106
      define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(identifier) << "\n";
1828
106
    }
1829
0
    else
1830
106
      solAssert(false, "Invalid member access in contract");
1831
106
    break;
1832
0
  }
1833
0
  case Type::Category::Integer:
1834
0
  {
1835
0
    solAssert(false, "Invalid member access to integer");
1836
0
    break;
1837
0
  }
1838
109
  case Type::Category::Address:
1839
109
  {
1840
109
    if (member == "balance")
1841
6
      define(_memberAccess) <<
1842
6
        "balance(" <<
1843
6
        expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
1844
6
        ")\n";
1845
103
    else if (member == "code")
1846
7
    {
1847
7
      std::string externalCodeFunction = m_utils.externalCodeFunction();
1848
7
      define(_memberAccess) <<
1849
7
        externalCodeFunction <<
1850
7
        "(" <<
1851
7
        expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
1852
7
        ")\n";
1853
7
    }
1854
96
    else if (member == "codehash")
1855
0
    {
1856
0
      define(_memberAccess) <<
1857
0
        "extcodehash(" <<
1858
0
        expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
1859
0
        ")\n";
1860
0
    }
1861
96
    else if (std::set<std::string>{"send", "transfer"}.count(member))
1862
12
    {
1863
12
      solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable);
1864
12
      define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
1865
12
    }
1866
84
    else if (std::set<std::string>{"call", "callcode", "delegatecall", "staticcall"}.count(member))
1867
84
      define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
1868
0
    else
1869
84
      solAssert(false, "Invalid member access to address");
1870
109
    break;
1871
0
  }
1872
34
  case Type::Category::Function:
1873
34
    if (member == "selector")
1874
30
    {
1875
30
      FunctionType const& functionType = dynamic_cast<FunctionType const&>(
1876
30
        *_memberAccess.expression().annotation().type
1877
30
      );
1878
30
      if (
1879
30
        functionType.kind() == FunctionType::Kind::External ||
1880
25
        functionType.kind() == FunctionType::Kind::DelegateCall
1881
30
      )
1882
5
        define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector"));
1883
25
      else if (
1884
25
        functionType.kind() == FunctionType::Kind::Declaration ||
1885
6
        functionType.kind() == FunctionType::Kind::Error ||
1886
        // In some situations, internal function types also provide the "selector" member.
1887
        // See Types.cpp for details.
1888
6
        functionType.kind() == FunctionType::Kind::Internal
1889
25
      )
1890
25
      {
1891
25
        solAssert(functionType.hasDeclaration());
1892
25
        solAssert(
1893
25
          functionType.kind() == FunctionType::Kind::Error ||
1894
25
          functionType.declaration().isPartOfExternalInterface(),
1895
25
          ""
1896
25
        );
1897
25
        define(IRVariable{_memberAccess}) << formatNumber(
1898
25
          util::selectorFromSignatureU256(functionType.externalSignature())
1899
25
        ) << "\n";
1900
25
      }
1901
0
      else if (functionType.kind() == FunctionType::Kind::Event)
1902
0
      {
1903
0
        solAssert(functionType.hasDeclaration());
1904
0
        solAssert(functionType.kind() == FunctionType::Kind::Event);
1905
0
        solAssert(
1906
0
          !(dynamic_cast<EventDefinition const&>(functionType.declaration()).isAnonymous())
1907
0
        );
1908
0
        define(IRVariable{_memberAccess}) << formatNumber(
1909
0
          u256(h256::Arith(util::keccak256(functionType.externalSignature())))
1910
0
        ) << "\n";
1911
0
      }
1912
0
      else
1913
0
        solAssert(false, "Invalid use of .selector: " + functionType.toString(false));
1914
30
    }
1915
4
    else if (member == "address")
1916
4
    {
1917
4
      solUnimplementedAssert(
1918
4
        dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() ==
1919
4
        FunctionType::Kind::External
1920
4
      );
1921
4
      define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address"));
1922
4
    }
1923
0
    else
1924
4
      solAssert(
1925
34
        !!_memberAccess.expression().annotation().type->memberType(member),
1926
34
        "Invalid member access to function."
1927
34
      );
1928
34
    break;
1929
141
  case Type::Category::Magic:
1930
    // we can ignore the kind of magic and only look at the name of the member
1931
141
    if (member == "coinbase")
1932
0
      define(_memberAccess) << "coinbase()\n";
1933
141
    else if (member == "timestamp")
1934
0
      define(_memberAccess) << "timestamp()\n";
1935
141
    else if (member == "difficulty" || member == "prevrandao")
1936
0
    {
1937
0
      if (m_context.evmVersion().hasPrevRandao())
1938
0
        define(_memberAccess) << "prevrandao()\n";
1939
0
      else
1940
0
        define(_memberAccess) << "difficulty()\n";
1941
0
    }
1942
141
    else if (member == "number")
1943
0
      define(_memberAccess) << "number()\n";
1944
141
    else if (member == "gaslimit")
1945
0
      define(_memberAccess) << "gaslimit()\n";
1946
141
    else if (member == "sender")
1947
4
      define(_memberAccess) << "caller()\n";
1948
137
    else if (member == "value")
1949
0
      define(_memberAccess) << "callvalue()\n";
1950
137
    else if (member == "origin")
1951
0
      define(_memberAccess) << "origin()\n";
1952
137
    else if (member == "gasprice")
1953
0
      define(_memberAccess) << "gasprice()\n";
1954
137
    else if (member == "chainid")
1955
0
      define(_memberAccess) << "chainid()\n";
1956
137
    else if (member == "basefee")
1957
0
      define(_memberAccess) << "basefee()\n";
1958
137
    else if (member == "blobbasefee")
1959
0
      define(_memberAccess) << "blobbasefee()\n";
1960
137
    else if (member == "data")
1961
18
    {
1962
18
      IRVariable var(_memberAccess);
1963
18
      define(var.part("offset")) << "0\n";
1964
18
      define(var.part("length")) << "calldatasize()\n";
1965
18
    }
1966
119
    else if (member == "sig")
1967
5
      define(_memberAccess) <<
1968
5
        "and(calldataload(0), " <<
1969
5
        formatNumber(u256(0xffffffff) << (256 - 32)) <<
1970
5
        ")\n";
1971
114
    else if (member == "gas")
1972
114
      solAssert(false, "Gas has been removed.");
1973
114
    else if (member == "blockhash")
1974
114
      solAssert(false, "Blockhash has been removed.");
1975
114
    else if (member == "creationCode" || member == "runtimeCode")
1976
0
    {
1977
0
      Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
1978
0
      auto const& contractType = dynamic_cast<ContractType const&>(*arg);
1979
0
      solAssert(!contractType.isSuper());
1980
0
      ContractDefinition const& contract = contractType.contractDefinition();
1981
0
      m_context.addSubObject(&contract);
1982
0
      appendCode() << Whiskers(R"(
1983
0
        let <size> := datasize("<objectName>")
1984
0
        let <result> := <allocationFunction>(add(<size>, 32))
1985
0
        mstore(<result>, <size>)
1986
0
        datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>)
1987
0
      )")
1988
0
      ("allocationFunction", m_utils.allocationFunction())
1989
0
      ("size", m_context.newYulVariable())
1990
0
      ("objectName", IRNames::creationObject(contract) + (member == "runtimeCode" ? "." + IRNames::deployedObject(contract) : ""))
1991
0
      ("result", IRVariable(_memberAccess).commaSeparatedList()).render();
1992
0
    }
1993
114
    else if (member == "name")
1994
0
    {
1995
0
      Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
1996
0
      ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
1997
0
      define(IRVariable(_memberAccess)) << m_utils.copyLiteralToMemoryFunction(contract.name()) << "()\n";
1998
0
    }
1999
114
    else if (member == "interfaceId")
2000
0
    {
2001
0
      Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
2002
0
      auto const& contractType = dynamic_cast<ContractType const&>(*arg);
2003
0
      solAssert(!contractType.isSuper());
2004
0
      ContractDefinition const& contract = contractType.contractDefinition();
2005
0
      define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
2006
0
    }
2007
114
    else if (member == "min" || member == "max")
2008
51
    {
2009
51
      MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
2010
2011
51
      std::string requestedValue;
2012
51
      if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument()))
2013
51
      {
2014
51
        if (member == "min")
2015
35
          requestedValue = formatNumber(integerType->min());
2016
16
        else
2017
16
          requestedValue = formatNumber(integerType->max());
2018
51
      }
2019
0
      else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument()))
2020
0
      {
2021
0
        if (member == "min")
2022
0
          requestedValue = std::to_string(enumType->minValue());
2023
0
        else
2024
0
          requestedValue = std::to_string(enumType->maxValue());
2025
0
      }
2026
0
      else
2027
0
        solAssert(false, "min/max requested on unexpected type.");
2028
2029
51
      define(_memberAccess) << requestedValue << "\n";
2030
51
    }
2031
63
    else if (std::set<std::string>{"encode", "encodePacked", "encodeWithSelector", "encodeCall", "encodeWithSignature", "decode"}.count(member))
2032
63
    {
2033
      // no-op
2034
63
    }
2035
0
    else
2036
63
      solAssert(false, "Unknown magic member.");
2037
141
    break;
2038
240
  case Type::Category::Struct:
2039
240
  {
2040
240
    auto const& structType = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type);
2041
2042
240
    IRVariable expression(_memberAccess.expression());
2043
240
    switch (structType.location())
2044
240
    {
2045
158
    case DataLocation::Storage:
2046
158
    {
2047
158
      std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member);
2048
158
      std::string slot = m_context.newYulVariable();
2049
158
      appendCode() << "let " << slot << " := " <<
2050
158
        ("add(" + expression.part("slot").name() + ", " + offsets.first.str() + ")\n");
2051
158
      setLValue(_memberAccess, IRLValue{
2052
158
        type(_memberAccess),
2053
158
        IRLValue::Storage{slot, offsets.second}
2054
158
      });
2055
158
      break;
2056
0
    }
2057
78
    case DataLocation::Memory:
2058
78
    {
2059
78
      std::string pos = m_context.newYulVariable();
2060
78
      appendCode() << "let " << pos << " := " <<
2061
78
        ("add(" + expression.part("mpos").name() + ", " + structType.memoryOffsetOfMember(member).str() + ")\n");
2062
78
      setLValue(_memberAccess, IRLValue{
2063
78
        type(_memberAccess),
2064
78
        IRLValue::Memory{pos}
2065
78
      });
2066
78
      break;
2067
0
    }
2068
4
    case DataLocation::CallData:
2069
4
    {
2070
4
      std::string baseRef = expression.part("offset").name();
2071
4
      std::string offset = m_context.newYulVariable();
2072
4
      appendCode() << "let " << offset << " := " << "add(" << baseRef << ", " << std::to_string(structType.calldataOffsetOfMember(member)) << ")\n";
2073
4
      if (_memberAccess.annotation().type->isDynamicallyEncoded())
2074
3
        define(_memberAccess) <<
2075
3
          m_utils.accessCalldataTailFunction(*_memberAccess.annotation().type) <<
2076
3
          "(" <<
2077
3
          baseRef <<
2078
3
          ", " <<
2079
3
          offset <<
2080
3
          ")\n";
2081
1
      else if (
2082
1
        dynamic_cast<ArrayType const*>(_memberAccess.annotation().type) ||
2083
1
        dynamic_cast<StructType const*>(_memberAccess.annotation().type)
2084
1
      )
2085
0
        define(_memberAccess) << offset << "\n";
2086
1
      else
2087
1
        define(_memberAccess) <<
2088
1
          m_utils.readFromCalldata(*_memberAccess.annotation().type) <<
2089
1
          "(" <<
2090
1
          offset <<
2091
1
          ")\n";
2092
4
      break;
2093
0
    }
2094
0
    default:
2095
0
      solAssert(false, "Illegal data location for struct.");
2096
240
    }
2097
240
    break;
2098
240
  }
2099
240
  case Type::Category::Enum:
2100
0
  {
2101
0
    EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type);
2102
0
    define(_memberAccess) << std::to_string(type.memberValue(_memberAccess.memberName())) << "\n";
2103
0
    break;
2104
240
  }
2105
62
  case Type::Category::Array:
2106
62
  {
2107
62
    auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
2108
62
    if (member == "length")
2109
62
    {
2110
      // shortcut for <address>.code.length
2111
62
      if (
2112
62
        auto innerExpression = dynamic_cast<MemberAccess const*>(&_memberAccess.expression());
2113
62
        innerExpression &&
2114
1
        innerExpression->memberName() == "code" &&
2115
0
        innerExpression->expression().annotation().type->category() == Type::Category::Address
2116
62
      )
2117
0
        define(_memberAccess) <<
2118
0
          "extcodesize(" <<
2119
0
          expressionAsType(innerExpression->expression(), *TypeProvider::address()) <<
2120
0
          ")\n";
2121
62
      else
2122
62
        define(_memberAccess) <<
2123
62
          m_utils.arrayLengthFunction(type) <<
2124
62
          "(" <<
2125
62
          IRVariable(_memberAccess.expression()).commaSeparatedList() <<
2126
62
          ")\n";
2127
62
    }
2128
0
    else if (member == "pop" || member == "push")
2129
0
    {
2130
0
      solAssert(type.location() == DataLocation::Storage);
2131
0
      define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot"));
2132
0
    }
2133
0
    else
2134
0
      solAssert(false, "Invalid array member access.");
2135
2136
62
    break;
2137
240
  }
2138
0
  case Type::Category::FixedBytes:
2139
0
  {
2140
0
    auto const& type = dynamic_cast<FixedBytesType const&>(*_memberAccess.expression().annotation().type);
2141
0
    if (member == "length")
2142
0
      define(_memberAccess) << std::to_string(type.numBytes()) << "\n";
2143
0
    else
2144
0
      solAssert(false, "Illegal fixed bytes member.");
2145
0
    break;
2146
240
  }
2147
68
  case Type::Category::TypeType:
2148
68
  {
2149
68
    Type const& actualType = *dynamic_cast<TypeType const&>(
2150
68
      *_memberAccess.expression().annotation().type
2151
68
    ).actualType();
2152
2153
68
    if (actualType.category() == Type::Category::Contract)
2154
32
    {
2155
32
      ContractType const& contractType = dynamic_cast<ContractType const&>(actualType);
2156
32
      if (contractType.isSuper())
2157
0
      {
2158
0
        solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
2159
0
        ContractDefinition const* super = contractType.contractDefinition().superContract(m_context.mostDerivedContract());
2160
0
        solAssert(super, "Super contract not available.");
2161
0
        FunctionDefinition const& resolvedFunctionDef =
2162
0
          dynamic_cast<FunctionDefinition const&>(
2163
0
            *_memberAccess.annotation().referencedDeclaration
2164
0
          ).resolveVirtual(m_context.mostDerivedContract(), super);
2165
2166
0
        solAssert(resolvedFunctionDef.functionType(true));
2167
0
        solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
2168
0
        assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef);
2169
0
      }
2170
32
      else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
2171
0
        handleVariableReference(*variable, _memberAccess);
2172
32
      else if (memberFunctionType)
2173
30
      {
2174
30
        switch (memberFunctionType->kind())
2175
30
        {
2176
19
        case FunctionType::Kind::Declaration:
2177
19
          break;
2178
3
        case FunctionType::Kind::Internal:
2179
3
          if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
2180
3
            assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function);
2181
0
          else
2182
3
            solAssert(false, "Function not found in member access");
2183
3
          break;
2184
2
        case FunctionType::Kind::Event:
2185
2
          solAssert(
2186
2
            dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration),
2187
2
            "Event not found"
2188
2
          );
2189
            // the call will do the resolving
2190
2
          break;
2191
0
        case FunctionType::Kind::Error:
2192
0
          solAssert(
2193
0
            dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration),
2194
0
            "Error not found"
2195
0
          );
2196
          // The function call will resolve the selector.
2197
0
          break;
2198
6
        case FunctionType::Kind::DelegateCall:
2199
6
          define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
2200
6
          define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
2201
6
          break;
2202
0
        case FunctionType::Kind::External:
2203
0
        case FunctionType::Kind::Creation:
2204
0
        case FunctionType::Kind::Send:
2205
0
        case FunctionType::Kind::BareCall:
2206
0
        case FunctionType::Kind::BareCallCode:
2207
0
        case FunctionType::Kind::BareDelegateCall:
2208
0
        case FunctionType::Kind::BareStaticCall:
2209
0
        case FunctionType::Kind::Transfer:
2210
0
        case FunctionType::Kind::ECRecover:
2211
0
        case FunctionType::Kind::SHA256:
2212
0
        case FunctionType::Kind::RIPEMD160:
2213
0
        default:
2214
0
          solAssert(false, "unsupported member function");
2215
30
        }
2216
30
      }
2217
2
      else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type))
2218
2
      {
2219
      // no-op
2220
2
      }
2221
0
      else
2222
        // The old code generator had a generic "else" case here
2223
        // without any specific code being generated,
2224
        // but it would still be better to have an exhaustive list.
2225
2
        solAssert(false);
2226
32
    }
2227
36
    else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
2228
0
      define(_memberAccess) << std::to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
2229
36
    else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
2230
36
      solAssert(member == "wrap" || member == "unwrap");
2231
28
    else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
2232
28
      solAssert(arrayType->isByteArrayOrString() && member == "concat");
2233
0
    else
2234
      // The old code generator had a generic "else" case here
2235
      // without any specific code being generated,
2236
      // but it would still be better to have an exhaustive list.
2237
28
      solAssert(false);
2238
68
    break;
2239
68
  }
2240
68
  case Type::Category::Module:
2241
2
  {
2242
2
    Type::Category category = _memberAccess.annotation().type->category();
2243
2
    solAssert(
2244
2
      dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
2245
2
      dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
2246
2
      dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
2247
2
      dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
2248
2
      category == Type::Category::TypeType ||
2249
2
      category == Type::Category::Module,
2250
2
      ""
2251
2
    );
2252
2
    if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
2253
2
    {
2254
2
      solAssert(variable->isConstant());
2255
2
      handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
2256
2
    }
2257
0
    else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
2258
0
    {
2259
0
      auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
2260
0
      solAssert(function && function->isFree());
2261
0
      solAssert(function->functionType(true));
2262
0
      solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal);
2263
0
      solAssert(funType->kind() == FunctionType::Kind::Internal);
2264
0
      solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
2265
2266
0
      assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function);
2267
0
    }
2268
0
    else if (auto const* contract = dynamic_cast<ContractDefinition const*>(_memberAccess.annotation().referencedDeclaration))
2269
0
    {
2270
0
      if (contract->isLibrary())
2271
0
        define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
2272
0
    }
2273
2
    break;
2274
68
  }
2275
0
  default:
2276
0
    solAssert(false, "Member access to unknown type.");
2277
762
  }
2278
762
}
2279
2280
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
2281
174
{
2282
174
  setLocation(_inlineAsm);
2283
174
  if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe)
2284
76
    m_context.setMemoryUnsafeInlineAssemblySeen();
2285
174
  CopyTranslate bodyCopier{m_context, _inlineAsm.annotation().externalReferences};
2286
2287
174
  yul::Statement modified = bodyCopier(_inlineAsm.operations().root());
2288
2289
174
  solAssert(std::holds_alternative<yul::Block>(modified));
2290
2291
174
  appendCode() << yul::AsmPrinter(_inlineAsm.dialect())(std::get<yul::Block>(modified)) << "\n";
2292
174
  return false;
2293
174
}
2294
2295
2296
void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
2297
7.79k
{
2298
7.79k
  setLocation(_indexAccess);
2299
7.79k
  Type const& baseType = *_indexAccess.baseExpression().annotation().type;
2300
2301
7.79k
  if (baseType.category() == Type::Category::Mapping)
2302
399
  {
2303
399
    solAssert(_indexAccess.indexExpression(), "Index expression expected.");
2304
2305
399
    MappingType const& mappingType = dynamic_cast<MappingType const&>(baseType);
2306
399
    Type const& keyType = *_indexAccess.indexExpression()->annotation().type;
2307
2308
399
    std::string slot = m_context.newYulVariable();
2309
399
    Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n");
2310
399
    templ("slot", slot);
2311
399
    templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
2312
399
    templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
2313
399
    templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
2314
399
    appendCode() << templ.render();
2315
399
    setLValue(_indexAccess, IRLValue{
2316
399
      *_indexAccess.annotation().type,
2317
399
      IRLValue::Storage{
2318
399
        slot,
2319
399
        0u
2320
399
      }
2321
399
    });
2322
399
  }
2323
7.39k
  else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice)
2324
7.39k
  {
2325
7.39k
    ArrayType const& arrayType =
2326
7.39k
      baseType.category() == Type::Category::Array ?
2327
7.39k
      dynamic_cast<ArrayType const&>(baseType) :
2328
7.39k
      dynamic_cast<ArraySliceType const&>(baseType).arrayType();
2329
2330
7.39k
    if (baseType.category() == Type::Category::ArraySlice)
2331
7.39k
      solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized());
2332
2333
7.39k
    solAssert(_indexAccess.indexExpression(), "Index expression expected.");
2334
2335
7.39k
    switch (arrayType.location())
2336
7.39k
    {
2337
683
      case DataLocation::Storage:
2338
683
      {
2339
683
        std::string slot = m_context.newYulVariable();
2340
683
        std::string offset = m_context.newYulVariable();
2341
2342
683
        appendCode() << Whiskers(R"(
2343
683
          let <slot>, <offset> := <indexFunc>(<array>, <index>)
2344
683
        )")
2345
683
        ("slot", slot)
2346
683
        ("offset", offset)
2347
683
        ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType))
2348
683
        ("array", IRVariable(_indexAccess.baseExpression()).part("slot").name())
2349
683
        ("index", IRVariable(*_indexAccess.indexExpression()).name())
2350
683
        .render();
2351
2352
683
        setLValue(_indexAccess, IRLValue{
2353
683
          *_indexAccess.annotation().type,
2354
683
          IRLValue::Storage{slot, offset}
2355
683
        });
2356
2357
683
        break;
2358
0
      }
2359
0
      case DataLocation::Transient:
2360
0
        solUnimplemented("Transient data location is only supported for value types.");
2361
0
        break;
2362
6.70k
      case DataLocation::Memory:
2363
6.70k
      {
2364
6.70k
        std::string const indexAccessFunction = m_utils.memoryArrayIndexAccessFunction(arrayType);
2365
6.70k
        std::string const baseRef = IRVariable(_indexAccess.baseExpression()).part("mpos").name();
2366
6.70k
        std::string const indexExpression = expressionAsType(
2367
6.70k
          *_indexAccess.indexExpression(),
2368
6.70k
          *TypeProvider::uint256()
2369
6.70k
        );
2370
6.70k
        std::string const memAddress = indexAccessFunction + "(" + baseRef + ", " + indexExpression + ")";
2371
2372
6.70k
        setLValue(_indexAccess, IRLValue{
2373
6.70k
          *arrayType.baseType(),
2374
6.70k
          IRLValue::Memory{memAddress, arrayType.isByteArrayOrString()}
2375
6.70k
        });
2376
6.70k
        break;
2377
0
      }
2378
4
      case DataLocation::CallData:
2379
4
      {
2380
4
        std::string const indexAccessFunction = m_utils.calldataArrayIndexAccessFunction(arrayType);
2381
4
        std::string const baseRef = IRVariable(_indexAccess.baseExpression()).commaSeparatedList();
2382
4
        std::string const indexExpression = expressionAsType(
2383
4
          *_indexAccess.indexExpression(),
2384
4
          *TypeProvider::uint256()
2385
4
        );
2386
4
        std::string const calldataAddress = indexAccessFunction + "(" + baseRef + ", " + indexExpression + ")";
2387
2388
4
        if (arrayType.isByteArrayOrString())
2389
0
          define(_indexAccess) <<
2390
0
            m_utils.cleanupFunction(*arrayType.baseType()) <<
2391
0
            "(calldataload(" <<
2392
0
            calldataAddress <<
2393
0
            "))\n";
2394
4
        else if (arrayType.baseType()->isValueType())
2395
3
          define(_indexAccess) <<
2396
3
            m_utils.readFromCalldata(*arrayType.baseType()) <<
2397
3
            "(" <<
2398
3
            calldataAddress <<
2399
3
            ")\n";
2400
1
        else
2401
1
          define(_indexAccess) << calldataAddress << "\n";
2402
4
        break;
2403
0
      }
2404
7.39k
    }
2405
7.39k
  }
2406
2
  else if (baseType.category() == Type::Category::FixedBytes)
2407
0
  {
2408
0
    auto const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
2409
0
    solAssert(_indexAccess.indexExpression(), "Index expression expected.");
2410
2411
0
    IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()};
2412
0
    define(index, *_indexAccess.indexExpression());
2413
0
    appendCode() << Whiskers(R"(
2414
0
      if iszero(lt(<index>, <length>)) { <panic>() }
2415
0
      let <result> := <shl248>(byte(<index>, <array>))
2416
0
    )")
2417
0
    ("index", index.name())
2418
0
    ("length", std::to_string(fixedBytesType.numBytes()))
2419
0
    ("panic", m_utils.panicFunction(PanicCode::ArrayOutOfBounds))
2420
0
    ("array", IRVariable(_indexAccess.baseExpression()).name())
2421
0
    ("shl248", m_utils.shiftLeftFunction(256 - 8))
2422
0
    ("result", IRVariable(_indexAccess).name())
2423
0
    .render();
2424
0
  }
2425
2
  else if (baseType.category() == Type::Category::TypeType)
2426
2
  {
2427
2
    solAssert(baseType.sizeOnStack() == 0);
2428
2
    solAssert(_indexAccess.annotation().type->sizeOnStack() == 0);
2429
    // no-op - this seems to be a lone array type (`structType[];`)
2430
2
  }
2431
0
  else
2432
2
    solAssert(false, "Index access only allowed for mappings or arrays.");
2433
7.79k
}
2434
2435
void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess)
2436
15
{
2437
15
  setLocation(_indexRangeAccess);
2438
15
  Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type;
2439
15
  solAssert(
2440
15
    baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice,
2441
15
    "Index range accesses is available only on arrays and array slices."
2442
15
  );
2443
2444
15
  ArrayType const& arrayType =
2445
15
    baseType.category() == Type::Category::Array ?
2446
15
    dynamic_cast<ArrayType const &>(baseType) :
2447
15
    dynamic_cast<ArraySliceType const &>(baseType).arrayType();
2448
2449
15
  switch (arrayType.location())
2450
15
  {
2451
15
    case DataLocation::CallData:
2452
15
    {
2453
15
      solAssert(baseType.isDynamicallySized());
2454
15
      IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
2455
15
      if (_indexRangeAccess.startExpression())
2456
15
        define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
2457
0
      else
2458
0
        define(sliceStart) << u256(0) << "\n";
2459
2460
15
      IRVariable sliceEnd{
2461
15
        m_context.newYulVariable(),
2462
15
        *TypeProvider::uint256()
2463
15
      };
2464
15
      if (_indexRangeAccess.endExpression())
2465
15
        define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()});
2466
0
      else
2467
0
        define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length"));
2468
2469
15
      IRVariable range{_indexRangeAccess};
2470
15
      define(range) <<
2471
15
        m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" <<
2472
15
        IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " <<
2473
15
        sliceStart.name() << ", " <<
2474
15
        sliceEnd.name() << ")\n";
2475
15
      break;
2476
0
    }
2477
0
    default:
2478
0
      solUnimplemented("Index range accesses is implemented only on calldata arrays.");
2479
15
  }
2480
15
}
2481
2482
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
2483
13.0k
{
2484
13.0k
  setLocation(_identifier);
2485
13.0k
  Declaration const* declaration = _identifier.annotation().referencedDeclaration;
2486
13.0k
  if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
2487
518
  {
2488
518
    switch (magicVar->type()->category())
2489
518
    {
2490
181
    case Type::Category::Contract:
2491
181
      solAssert(_identifier.name() == "this");
2492
181
      define(_identifier) << "address()\n";
2493
181
      break;
2494
0
    case Type::Category::Integer:
2495
0
      solAssert(_identifier.name() == "now");
2496
0
      define(_identifier) << "timestamp()\n";
2497
0
      break;
2498
0
    case Type::Category::TypeType:
2499
0
    {
2500
0
      auto typeType = dynamic_cast<TypeType const*>(magicVar->type());
2501
0
      if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType()))
2502
0
        solAssert(!contractType->isSuper() || _identifier.name() == "super");
2503
0
      break;
2504
0
    }
2505
337
    default:
2506
337
      break;
2507
518
    }
2508
518
    return;
2509
518
  }
2510
12.5k
  else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
2511
188
  {
2512
188
    solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual);
2513
188
    FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract());
2514
2515
188
    solAssert(resolvedFunctionDef.functionType(true));
2516
188
    solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
2517
188
    assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef);
2518
188
  }
2519
12.3k
  else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
2520
11.5k
    handleVariableReference(*varDecl, _identifier);
2521
844
  else if (auto const* contract = dynamic_cast<ContractDefinition const*>(declaration))
2522
40
  {
2523
40
    if (contract->isLibrary())
2524
6
      define(IRVariable(_identifier).part("address")) << linkerSymbol(*contract) << "\n";
2525
40
  }
2526
804
  else if (dynamic_cast<EventDefinition const*>(declaration))
2527
794
  {
2528
    // no-op
2529
794
  }
2530
10
  else if (dynamic_cast<ErrorDefinition const*>(declaration))
2531
2
  {
2532
    // no-op
2533
2
  }
2534
8
  else if (dynamic_cast<EnumDefinition const*>(declaration))
2535
0
  {
2536
    // no-op
2537
0
  }
2538
8
  else if (dynamic_cast<StructDefinition const*>(declaration))
2539
0
  {
2540
    // no-op
2541
0
  }
2542
8
  else if (dynamic_cast<ImportDirective const*>(declaration))
2543
2
  {
2544
    // no-op
2545
2
  }
2546
6
  else if (dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration))
2547
6
  {
2548
    // no-op
2549
6
  }
2550
0
  else
2551
0
  {
2552
0
    solAssert(false, "Identifier type not expected in expression context.");
2553
0
  }
2554
13.0k
}
2555
2556
bool IRGeneratorForStatements::visit(Literal const& _literal)
2557
14.8k
{
2558
14.8k
  setLocation(_literal);
2559
14.8k
  Type const& literalType = type(_literal);
2560
2561
14.8k
  switch (literalType.category())
2562
14.8k
  {
2563
13.4k
  case Type::Category::RationalNumber:
2564
13.4k
  case Type::Category::Bool:
2565
13.4k
  case Type::Category::Address:
2566
13.4k
    define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
2567
13.4k
    break;
2568
1.38k
  case Type::Category::StringLiteral:
2569
1.38k
    break; // will be done during conversion
2570
0
  default:
2571
0
    solUnimplemented("Only integer, boolean and string literals implemented for now.");
2572
14.8k
  }
2573
14.8k
  return false;
2574
14.8k
}
2575
2576
void IRGeneratorForStatements::handleVariableReference(
2577
  VariableDeclaration const& _variable,
2578
  Expression const& _referencingExpression
2579
)
2580
11.5k
{
2581
11.5k
  if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant())
2582
67
    define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
2583
11.4k
  else if (_variable.isStateVariable() && _variable.immutable())
2584
66
    setLValue(_referencingExpression, IRLValue{
2585
66
      *_variable.annotation().type,
2586
66
      IRLValue::Immutable{&_variable}
2587
66
    });
2588
11.3k
  else if (m_context.isLocalVariable(_variable))
2589
9.19k
    setLValue(_referencingExpression, IRLValue{
2590
9.19k
      *_variable.annotation().type,
2591
9.19k
      IRLValue::Stack{m_context.localVariable(_variable)}
2592
9.19k
    });
2593
2.17k
  else if (m_context.isStateVariable(_variable) && _variable.referenceLocation() == VariableDeclaration::Location::Transient)
2594
0
    setLValue(_referencingExpression, IRLValue{
2595
0
      *_variable.annotation().type,
2596
0
      IRLValue::TransientStorage{
2597
0
        toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first),
2598
0
        m_context.storageLocationOfStateVariable(_variable).second
2599
0
      }
2600
0
    });
2601
2.17k
  else if (m_context.isStateVariable(_variable))
2602
2.17k
  {
2603
2.17k
    solAssert(_variable.referenceLocation() == VariableDeclaration::Location::Unspecified, "Must have storage location.");
2604
2.17k
    setLValue(_referencingExpression, IRLValue{
2605
2.17k
      *_variable.annotation().type,
2606
2.17k
      IRLValue::Storage{
2607
2.17k
        toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first),
2608
2.17k
        m_context.storageLocationOfStateVariable(_variable).second
2609
2.17k
      }
2610
2.17k
    });
2611
2.17k
  }
2612
0
  else
2613
2.17k
    solAssert(false, "Invalid variable kind.");
2614
11.5k
}
2615
2616
void IRGeneratorForStatements::appendExternalFunctionCall(
2617
  FunctionCall const& _functionCall,
2618
  std::vector<ASTPointer<Expression const>> const& _arguments
2619
)
2620
149
{
2621
149
  FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
2622
149
  solAssert(!funType.takesArbitraryParameters());
2623
149
  solAssert(_arguments.size() == funType.parameterTypes().size());
2624
149
  solAssert(!funType.isBareCall());
2625
149
  FunctionType::Kind const funKind = funType.kind();
2626
2627
149
  solAssert(
2628
149
    funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall,
2629
149
    "Can only be used for regular external calls."
2630
149
  );
2631
2632
149
  bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
2633
149
  bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
2634
2635
149
  ReturnInfo const returnInfo{m_context.evmVersion(), funType};
2636
2637
149
  TypePointers parameterTypes = funType.parameterTypes();
2638
149
  TypePointers argumentTypes;
2639
149
  std::vector<std::string> argumentStrings;
2640
149
  if (funType.hasBoundFirstArgument())
2641
41
  {
2642
41
    parameterTypes.insert(parameterTypes.begin(), funType.selfType());
2643
41
    argumentTypes.emplace_back(funType.selfType());
2644
41
    argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots();
2645
41
  }
2646
2647
149
  for (auto const& arg: _arguments)
2648
36
  {
2649
36
    argumentTypes.emplace_back(&type(*arg));
2650
36
    argumentStrings += IRVariable(*arg).stackSlots();
2651
36
  }
2652
2653
149
  if (!m_context.evmVersion().canOverchargeGasForCall())
2654
14
  {
2655
    // Touch the end of the output area so that we do not pay for memory resize during the call
2656
    // (which we would have to subtract from the gas left)
2657
    // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
2658
    // would remove that, so we use MSTORE here.
2659
14
    if (!funType.gasSet() && returnInfo.estimatedReturnSize > 0)
2660
12
      appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << std::to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
2661
14
  }
2662
2663
  // NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically.
2664
  // When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy().
2665
149
  Whiskers templ(R"(
2666
149
    <?checkExtcodesize>
2667
149
      if iszero(extcodesize(<address>)) { <revertNoCode>() }
2668
149
    </checkExtcodesize>
2669
149
    // storage for arguments and returned data
2670
149
    let <pos> := <allocateUnbounded>()
2671
149
    mstore(<pos>, <shl28>(<funSel>))
2672
149
    let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
2673
149
2674
149
    let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize>)
2675
149
    <?noTryCall>
2676
149
      if iszero(<success>) { <forwardingRevert>() }
2677
149
    </noTryCall>
2678
149
    <?+retVars> let <retVars> </+retVars>
2679
149
    if <success> {
2680
149
      <?isReturndataSizeDynamic>
2681
149
        let <returnDataSizeVar> := returndatasize()
2682
149
        returndatacopy(<pos>, 0, <returnDataSizeVar>)
2683
149
      <!isReturndataSizeDynamic>
2684
149
        let <returnDataSizeVar> := <staticReturndataSize>
2685
149
        <?supportsReturnData>
2686
149
          if gt(<returnDataSizeVar>, returndatasize()) {
2687
149
            <returnDataSizeVar> := returndatasize()
2688
149
          }
2689
149
        </supportsReturnData>
2690
149
      </isReturndataSizeDynamic>
2691
149
2692
149
      // update freeMemoryPointer according to dynamic return size
2693
149
      <finalizeAllocation>(<pos>, <returnDataSizeVar>)
2694
149
2695
149
      // decode return parameters from external try-call into retVars
2696
149
      <?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnDataSizeVar>))
2697
149
    }
2698
149
  )");
2699
149
  templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
2700
2701
  // We do not need to check extcodesize if we expect return data: If there is no
2702
  // code, the call will return empty data and the ABI decoder will revert.
2703
149
  size_t encodedHeadSize = 0;
2704
149
  for (auto const& t: returnInfo.returnTypes)
2705
153
    encodedHeadSize += t->decodingType()->calldataHeadSize();
2706
149
  bool const checkExtcodesize =
2707
149
    (
2708
149
      encodedHeadSize == 0 ||
2709
118
      !m_context.evmVersion().supportsReturndata() ||
2710
87
      m_context.revertStrings() >= RevertStrings::Debug
2711
149
    );
2712
149
  templ("checkExtcodesize", checkExtcodesize);
2713
2714
149
  templ("pos", m_context.newYulVariable());
2715
149
  templ("end", m_context.newYulVariable());
2716
149
  if (_functionCall.annotation().tryCall)
2717
53
    templ("success", IRNames::trySuccessConditionVariable(_functionCall));
2718
96
  else
2719
96
    templ("success", m_context.newYulVariable());
2720
149
  templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
2721
149
  templ("finalizeAllocation", m_utils.finalizeAllocationFunction());
2722
149
  templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
2723
2724
149
  templ("funSel", IRVariable(_functionCall.expression()).part("functionSelector").name());
2725
149
  templ("address", IRVariable(_functionCall.expression()).part("address").name());
2726
2727
149
  if (returnInfo.dynamicReturnSize)
2728
149
    solAssert(m_context.evmVersion().supportsReturndata());
2729
149
  templ("returnDataSizeVar", m_context.newYulVariable());
2730
149
  templ("staticReturndataSize", std::to_string(returnInfo.estimatedReturnSize));
2731
149
  templ("supportsReturnData", m_context.evmVersion().supportsReturndata());
2732
2733
149
  std::string const retVars = IRVariable(_functionCall).commaSeparatedList();
2734
149
  templ("retVars", retVars);
2735
149
  solAssert(retVars.empty() == returnInfo.returnTypes.empty());
2736
2737
149
  templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
2738
149
  templ("isReturndataSizeDynamic", returnInfo.dynamicReturnSize);
2739
2740
149
  templ("noTryCall", !_functionCall.annotation().tryCall);
2741
2742
149
  bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
2743
2744
149
  solAssert(funType.padArguments());
2745
149
  templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
2746
149
  templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
2747
2748
149
  solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
2749
149
  solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
2750
2751
149
  templ("hasValue", !isDelegateCall && !useStaticCall);
2752
149
  templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
2753
2754
149
  if (funType.gasSet())
2755
20
    templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
2756
129
  else if (m_context.evmVersion().canOverchargeGasForCall())
2757
    // Send all gas (requires tangerine whistle EVM)
2758
117
    templ("gas", "gas()");
2759
12
  else
2760
12
  {
2761
    // send all gas except the amount needed to execute "SUB" and "CALL"
2762
    // @todo this retains too much gas for now, needs to be fine-tuned.
2763
12
    u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
2764
12
    if (funType.valueSet())
2765
0
      gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
2766
12
    if (!checkExtcodesize)
2767
0
      gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
2768
12
    templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
2769
12
  }
2770
  // Order is important here, STATICCALL might overlap with DELEGATECALL.
2771
149
  if (isDelegateCall)
2772
47
    templ("call", "delegatecall");
2773
102
  else if (useStaticCall)
2774
26
    templ("call", "staticcall");
2775
76
  else
2776
76
    templ("call", "call");
2777
2778
149
  templ("forwardingRevert", m_utils.forwardingRevertFunction());
2779
2780
149
  appendCode() << templ.render();
2781
149
}
2782
2783
void IRGeneratorForStatements::appendBareCall(
2784
  FunctionCall const& _functionCall,
2785
  std::vector<ASTPointer<Expression const>> const& _arguments
2786
)
2787
84
{
2788
84
  FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
2789
84
  solAssert(
2790
84
    !funType.hasBoundFirstArgument() &&
2791
84
    !funType.takesArbitraryParameters() &&
2792
84
    _arguments.size() == 1 &&
2793
84
    funType.parameterTypes().size() == 1, ""
2794
84
  );
2795
84
  FunctionType::Kind const funKind = funType.kind();
2796
2797
84
  solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall());
2798
84
  solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
2799
84
  solAssert(
2800
84
    funKind == FunctionType::Kind::BareCall ||
2801
84
    funKind == FunctionType::Kind::BareDelegateCall ||
2802
84
    funKind == FunctionType::Kind::BareStaticCall, ""
2803
84
  );
2804
2805
84
  solAssert(!_functionCall.annotation().tryCall);
2806
84
  Whiskers templ(R"(
2807
84
    <?needsEncoding>
2808
84
      let <pos> := <allocateUnbounded>()
2809
84
      let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>)
2810
84
    <!needsEncoding>
2811
84
      let <pos> := add(<arg>, 0x20)
2812
84
      let <length> := mload(<arg>)
2813
84
    </needsEncoding>
2814
84
2815
84
    let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
2816
84
2817
84
    let <returndataVar> := <extractReturndataFunction>()
2818
84
  )");
2819
2820
84
  templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
2821
84
  templ("pos", m_context.newYulVariable());
2822
84
  templ("length", m_context.newYulVariable());
2823
2824
84
  templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
2825
84
  Type const& argType = type(*_arguments.front());
2826
84
  if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory())
2827
8
    templ("needsEncoding", false);
2828
76
  else
2829
76
  {
2830
76
    templ("needsEncoding", true);
2831
76
    ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
2832
76
    templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()}));
2833
76
  }
2834
2835
84
  templ("success", IRVariable(_functionCall).tupleComponent(0).name());
2836
84
  templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).commaSeparatedList());
2837
84
  templ("extractReturndataFunction", m_utils.extractReturndataFunction());
2838
2839
84
  templ("address", IRVariable(_functionCall.expression()).part("address").name());
2840
2841
84
  if (funKind == FunctionType::Kind::BareCall)
2842
84
  {
2843
84
    templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
2844
84
    templ("call", "call");
2845
84
  }
2846
0
  else
2847
0
  {
2848
0
    solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
2849
0
    templ("value", "");
2850
0
    if (funKind == FunctionType::Kind::BareStaticCall)
2851
0
      templ("call", "staticcall");
2852
0
    else
2853
0
      templ("call", "delegatecall");
2854
0
  }
2855
2856
84
  if (funType.gasSet())
2857
0
    templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
2858
84
  else if (m_context.evmVersion().canOverchargeGasForCall())
2859
    // Send all gas (requires tangerine whistle EVM)
2860
76
    templ("gas", "gas()");
2861
8
  else
2862
8
  {
2863
    // send all gas except the amount needed to execute "SUB" and "CALL"
2864
    // @todo this retains too much gas for now, needs to be fine-tuned.
2865
8
    u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
2866
8
    if (funType.valueSet())
2867
0
      gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
2868
8
    gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
2869
8
    templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
2870
8
  }
2871
2872
84
  appendCode() << templ.render();
2873
84
}
2874
2875
void IRGeneratorForStatements::assignInternalFunctionIDIfNotCalledDirectly(
2876
  Expression const& _expression,
2877
  FunctionDefinition const& _referencedFunction
2878
)
2879
194
{
2880
194
  solAssert(
2881
194
    dynamic_cast<MemberAccess const*>(&_expression) ||
2882
194
    dynamic_cast<Identifier const*>(&_expression),
2883
194
    ""
2884
194
  );
2885
194
  if (_expression.annotation().calledDirectly)
2886
176
    return;
2887
2888
18
  define(IRVariable(_expression).part("functionIdentifier")) <<
2889
18
    std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(&_referencedFunction)) <<
2890
18
    "\n";
2891
18
  m_context.addToInternalDispatch(_referencedFunction);
2892
18
}
2893
2894
IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to)
2895
6.03k
{
2896
6.03k
  if (_from.type() == _to)
2897
2.98k
    return _from;
2898
3.05k
  else
2899
3.05k
  {
2900
3.05k
    IRVariable converted(m_context.newYulVariable(), _to);
2901
3.05k
    define(converted, _from);
2902
3.05k
    return converted;
2903
3.05k
  }
2904
6.03k
}
2905
2906
IRVariable IRGeneratorForStatements::convertAndCleanup(IRVariable const& _from, Type const& _to)
2907
0
{
2908
0
  IRVariable converted(m_context.newYulVariable(), _to);
2909
0
  defineAndCleanup(converted, _from);
2910
0
  return converted;
2911
0
}
2912
2913
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
2914
17.5k
{
2915
17.5k
  IRVariable from(_expression);
2916
17.5k
  if (from.type() == _to)
2917
9.52k
    return from.commaSeparatedList();
2918
8.01k
  else
2919
8.01k
    return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
2920
17.5k
}
2921
2922
std::string IRGeneratorForStatements::expressionAsCleanedType(Expression const& _expression, Type const& _to)
2923
826
{
2924
826
  IRVariable from(_expression);
2925
826
  if (from.type() == _to)
2926
423
    return m_utils.cleanupFunction(_to) + "(" + expressionAsType(_expression, _to) + ")";
2927
403
  else
2928
403
    return expressionAsType(_expression, _to) ;
2929
826
}
2930
2931
std::ostream& IRGeneratorForStatements::define(IRVariable const& _var)
2932
35.3k
{
2933
35.3k
  if (_var.type().sizeOnStack() > 0)
2934
35.3k
    appendCode() << "let " << _var.commaSeparatedList() << " := ";
2935
35.3k
  return appendCode(false);
2936
35.3k
}
2937
2938
void IRGeneratorForStatements::declare(IRVariable const& _var)
2939
957
{
2940
957
  if (_var.type().sizeOnStack() > 0)
2941
957
    appendCode() << "let " << _var.commaSeparatedList() << "\n";
2942
957
}
2943
2944
void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare, bool _forceCleanup)
2945
58.1k
{
2946
58.1k
  std::string output;
2947
58.1k
  if (_lhs.type() == _rhs.type() && !_forceCleanup)
2948
52.1k
    for (auto const& [stackItemName, stackItemType]: _lhs.type().stackItems())
2949
52.2k
      if (stackItemType)
2950
16.5k
        declareAssign(_lhs.part(stackItemName), _rhs.part(stackItemName), _declare);
2951
35.6k
      else
2952
35.6k
        appendCode() << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n";
2953
5.96k
  else
2954
5.96k
  {
2955
5.96k
    if (_lhs.type().sizeOnStack() > 0)
2956
5.96k
      appendCode() <<
2957
5.96k
        (_declare ? "let ": "") <<
2958
5.96k
        _lhs.commaSeparatedList() <<
2959
5.96k
        " := ";
2960
5.96k
    appendCode() << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
2961
5.96k
      "(" <<
2962
5.96k
      _rhs.commaSeparatedList() <<
2963
5.96k
      ")\n";
2964
5.96k
  }
2965
58.1k
}
2966
2967
IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes)
2968
2.53k
{
2969
2.53k
  IRVariable irVar{IRNames::zeroValue(_type, m_context.newYulVariable()), _type};
2970
2.53k
  define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n";
2971
2.53k
  return irVar;
2972
2.53k
}
2973
2974
void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr)
2975
130
{
2976
130
  std::string func;
2977
2978
130
  if (_operation.getOperator() == Token::Not)
2979
1
    func = "iszero";
2980
129
  else if (_operation.getOperator() == Token::BitNot)
2981
129
    func = "not";
2982
0
  else
2983
129
    solAssert(false, "Invalid Token!");
2984
2985
130
  define(_operation) <<
2986
130
    m_utils.cleanupFunction(type(_expr)) <<
2987
130
    "(" <<
2988
130
      func <<
2989
130
      "(" <<
2990
130
      IRVariable(_expr).commaSeparatedList() <<
2991
130
      ")" <<
2992
130
    ")\n";
2993
130
}
2994
2995
std::string IRGeneratorForStatements::binaryOperation(
2996
  langutil::Token _operator,
2997
  Type const& _type,
2998
  std::string const& _left,
2999
  std::string const& _right
3000
)
3001
4.88k
{
3002
4.88k
  solAssert(
3003
4.88k
    !TokenTraits::isShiftOp(_operator),
3004
4.88k
    "Have to use specific shift operation function for shifts."
3005
4.88k
  );
3006
4.88k
  std::string fun;
3007
4.88k
  if (TokenTraits::isBitOp(_operator))
3008
184
  {
3009
184
    solAssert(
3010
184
      _type.category() == Type::Category::Integer ||
3011
184
      _type.category() == Type::Category::FixedBytes,
3012
184
      ""
3013
184
    );
3014
184
    switch (_operator)
3015
184
    {
3016
4
    case Token::BitOr: fun = "or"; break;
3017
48
    case Token::BitXor: fun = "xor"; break;
3018
132
    case Token::BitAnd: fun = "and"; break;
3019
0
    default: break;
3020
184
    }
3021
184
  }
3022
4.69k
  else if (TokenTraits::isArithmeticOp(_operator))
3023
4.69k
  {
3024
4.69k
    solUnimplementedAssert(
3025
4.69k
      _type.category() != Type::Category::FixedPoint,
3026
4.69k
      "Not yet implemented - FixedPointType."
3027
4.69k
    );
3028
4.69k
    IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
3029
4.69k
    solAssert(type);
3030
4.69k
    bool checked = m_context.arithmetic() == Arithmetic::Checked;
3031
4.69k
    switch (_operator)
3032
4.69k
    {
3033
694
    case Token::Add:
3034
694
      fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
3035
694
      break;
3036
2.35k
    case Token::Sub:
3037
2.35k
      fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
3038
2.35k
      break;
3039
1.21k
    case Token::Mul:
3040
1.21k
      fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
3041
1.21k
      break;
3042
266
    case Token::Div:
3043
266
      fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
3044
266
      break;
3045
172
    case Token::Mod:
3046
172
      fun = m_utils.intModFunction(*type);
3047
172
      break;
3048
0
    default:
3049
0
      break;
3050
4.69k
    }
3051
4.69k
  }
3052
3053
4.88k
  solUnimplementedAssert(!fun.empty(), "Type: " + _type.toString());
3054
4.88k
  return fun + "(" + _left + ", " + _right + ")\n";
3055
4.88k
}
3056
3057
std::string IRGeneratorForStatements::shiftOperation(
3058
  langutil::Token _operator,
3059
  IRVariable const& _value,
3060
  IRVariable const& _amountToShift
3061
)
3062
18
{
3063
18
  solUnimplementedAssert(
3064
18
    _amountToShift.type().category() != Type::Category::FixedPoint &&
3065
18
    _value.type().category() != Type::Category::FixedPoint,
3066
18
    "Not yet implemented - FixedPointType."
3067
18
  );
3068
18
  IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
3069
18
  solAssert(amountType);
3070
3071
18
  solAssert(_operator == Token::SHL || _operator == Token::SAR);
3072
3073
18
  return
3074
18
    Whiskers(R"(
3075
18
      <shift>(<value>, <amount>)
3076
18
    )")
3077
18
    ("shift",
3078
18
      _operator == Token::SHL ?
3079
2
      m_utils.typedShiftLeftFunction(_value.type(), *amountType) :
3080
18
      m_utils.typedShiftRightFunction(_value.type(), *amountType)
3081
18
    )
3082
18
    ("value", _value.name())
3083
18
    ("amount", _amountToShift.name())
3084
18
    .render();
3085
18
}
3086
3087
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
3088
11
{
3089
11
  langutil::Token const op = _binOp.getOperator();
3090
11
  solAssert(op == Token::Or || op == Token::And);
3091
3092
11
  _binOp.leftExpression().accept(*this);
3093
11
  setLocation(_binOp);
3094
3095
11
  IRVariable value(_binOp);
3096
11
  define(value, _binOp.leftExpression());
3097
11
  if (op == Token::Or)
3098
0
    appendCode() << "if iszero(" << value.name() << ") {\n";
3099
11
  else
3100
11
    appendCode() << "if " << value.name() << " {\n";
3101
11
  _binOp.rightExpression().accept(*this);
3102
11
  setLocation(_binOp);
3103
11
  assign(value, _binOp.rightExpression());
3104
11
  appendCode() << "}\n";
3105
11
}
3106
3107
void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable const& _value)
3108
2.92k
{
3109
2.92k
  std::visit(
3110
2.92k
    util::GenericVisitor{
3111
2.92k
      [&](IRLValue::Storage const& _storage) {
3112
1.60k
        std::string offsetArgument;
3113
1.60k
        std::optional<unsigned> offsetStatic;
3114
3115
1.60k
        std::visit(GenericVisitor{
3116
1.60k
          [&](unsigned _offset) { offsetStatic = _offset; },
3117
1.60k
          [&](std::string const& _offset) { offsetArgument = ", " + _offset; }
3118
1.60k
        }, _storage.offset);
3119
3120
1.60k
        appendCode() <<
3121
1.60k
          m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, VariableDeclaration::Location::Unspecified, offsetStatic) <<
3122
1.60k
          "(" <<
3123
1.60k
          _storage.slot <<
3124
1.60k
          offsetArgument <<
3125
1.60k
          _value.commaSeparatedListPrefixed() <<
3126
1.60k
          ")\n";
3127
1.60k
      },
3128
2.92k
      [&](IRLValue::TransientStorage const& _transientStorage) {
3129
0
        std::string offsetArgument;
3130
0
        std::optional<unsigned> offsetStatic;
3131
3132
0
        std::visit(GenericVisitor{
3133
0
          [&](unsigned _offset) { offsetStatic = _offset; },
3134
0
          [&](std::string const& _offset) { offsetArgument = ", " + _offset; }
3135
0
        }, _transientStorage.offset);
3136
3137
0
        appendCode() <<
3138
0
          m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, VariableDeclaration::Location::Transient, offsetStatic) <<
3139
0
          "(" <<
3140
0
          _transientStorage.slot <<
3141
0
          offsetArgument <<
3142
0
          _value.commaSeparatedListPrefixed() <<
3143
0
          ")\n";
3144
0
      },
3145
2.92k
      [&](IRLValue::Memory const& _memory) {
3146
753
        if (_lvalue.type.isValueType())
3147
647
        {
3148
647
          IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
3149
647
          define(prepared, _value);
3150
3151
647
          if (_memory.byteArrayElement)
3152
5
          {
3153
5
            solAssert(_lvalue.type == *TypeProvider::byte());
3154
5
            appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
3155
5
          }
3156
642
          else
3157
642
            appendCode() << m_utils.writeToMemoryFunction(_lvalue.type) <<
3158
642
              "(" <<
3159
642
              _memory.address <<
3160
642
              ", " <<
3161
642
              prepared.commaSeparatedList() <<
3162
642
              ")\n";
3163
647
        }
3164
106
        else if (auto const* literalType = dynamic_cast<StringLiteralType const*>(&_value.type()))
3165
0
        {
3166
0
          std::string writeUInt = m_utils.writeToMemoryFunction(*TypeProvider::uint256());
3167
0
          appendCode() <<
3168
0
            writeUInt <<
3169
0
            "(" <<
3170
0
            _memory.address <<
3171
0
            ", " <<
3172
0
            m_utils.copyLiteralToMemoryFunction(literalType->value()) + "()" <<
3173
0
            ")\n";
3174
0
        }
3175
106
        else
3176
106
        {
3177
106
          solAssert(_lvalue.type.sizeOnStack() == 1);
3178
106
          auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
3179
106
          solAssert(valueReferenceType);
3180
106
          if (valueReferenceType->dataStoredIn(DataLocation::Memory))
3181
106
            appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
3182
0
          else
3183
0
            appendCode() << "mstore(" + _memory.address + ", " + m_utils.conversionFunction(_value.type(), _lvalue.type) + "(" + _value.commaSeparatedList() + "))\n";
3184
106
        }
3185
753
      },
3186
2.92k
      [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
3187
2.92k
      [&](IRLValue::Immutable const& _immutable)
3188
2.92k
      {
3189
71
        solUnimplementedAssert(_lvalue.type.isValueType());
3190
71
        solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
3191
71
        solAssert(_lvalue.type == *_immutable.variable->type());
3192
71
        size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable);
3193
3194
71
        IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
3195
71
        define(prepared, _value);
3196
3197
71
        appendCode() << "mstore(" << std::to_string(memOffset) << ", " << prepared.commaSeparatedList() << ")\n";
3198
71
      },
3199
2.92k
      [&](IRLValue::Tuple const& _tuple) {
3200
5
        auto components = std::move(_tuple.components);
3201
15
        for (size_t i = 0; i < components.size(); i++)
3202
10
        {
3203
10
          size_t idx = components.size() - i - 1;
3204
10
          if (components[idx])
3205
5
            writeToLValue(*components[idx], _value.tupleComponent(idx));
3206
10
        }
3207
5
      }
3208
2.92k
    },
3209
2.92k
    _lvalue.kind
3210
2.92k
  );
3211
2.92k
}
3212
3213
IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
3214
18.0k
{
3215
18.0k
  IRVariable result{m_context.newYulVariable(), _lvalue.type};
3216
18.0k
  std::visit(GenericVisitor{
3217
18.0k
    [&](IRLValue::Storage const& _storage) {
3218
2.70k
      if (!_lvalue.type.isValueType())
3219
2.12k
        define(result) << _storage.slot << "\n";
3220
587
      else if (std::holds_alternative<std::string>(_storage.offset))
3221
149
        define(result) <<
3222
149
          m_utils.readFromStorageDynamic(_lvalue.type, true, VariableDeclaration::Location::Unspecified) <<
3223
149
          "(" <<
3224
149
          _storage.slot <<
3225
149
          ", " <<
3226
149
          std::get<std::string>(_storage.offset) <<
3227
149
          ")\n";
3228
438
      else
3229
438
        define(result) <<
3230
438
          m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), true, VariableDeclaration::Location::Unspecified) <<
3231
438
          "(" <<
3232
438
          _storage.slot <<
3233
438
          ")\n";
3234
2.70k
    },
3235
18.0k
    [&](IRLValue::TransientStorage const& _transientStorage) {
3236
0
      if (!_lvalue.type.isValueType())
3237
0
        define(result) << _transientStorage.slot << "\n";
3238
0
      else if (std::holds_alternative<std::string>(_transientStorage.offset))
3239
0
        define(result) <<
3240
0
          m_utils.readFromStorageDynamic(_lvalue.type, true, VariableDeclaration::Location::Transient) <<
3241
0
          "(" <<
3242
0
          _transientStorage.slot <<
3243
0
          ", " <<
3244
0
          std::get<std::string>(_transientStorage.offset) <<
3245
0
          ")\n";
3246
0
      else
3247
0
        define(result) <<
3248
0
          m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_transientStorage.offset), true, VariableDeclaration::Location::Transient) <<
3249
0
          "(" <<
3250
0
          _transientStorage.slot <<
3251
0
          ")\n";
3252
0
    },
3253
18.0k
    [&](IRLValue::Memory const& _memory) {
3254
6.22k
      if (_lvalue.type.isValueType())
3255
2.94k
        define(result) <<
3256
2.94k
          m_utils.readFromMemory(_lvalue.type) <<
3257
2.94k
          "(" <<
3258
2.94k
          _memory.address <<
3259
2.94k
          ")\n";
3260
3.28k
      else
3261
3.28k
        define(result) << "mload(" << _memory.address << ")\n";
3262
6.22k
    },
3263
18.0k
    [&](IRLValue::Stack const& _stack) {
3264
9.08k
      define(result, _stack.variable);
3265
9.08k
    },
3266
18.0k
    [&](IRLValue::Immutable const& _immutable) {
3267
26
      solUnimplementedAssert(_lvalue.type.isValueType());
3268
26
      solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
3269
26
      solAssert(_lvalue.type == *_immutable.variable->type());
3270
26
      if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation)
3271
0
      {
3272
0
        std::string readFunction = m_utils.readFromMemory(*_immutable.variable->type());
3273
0
        define(result) <<
3274
0
          readFunction <<
3275
0
          "(" <<
3276
0
          std::to_string(m_context.immutableMemoryOffset(*_immutable.variable)) <<
3277
0
          ")\n";
3278
0
      }
3279
26
      else
3280
26
        define(result) << "loadimmutable(\"" << std::to_string(_immutable.variable->id()) << "\")\n";
3281
26
    },
3282
18.0k
    [&](IRLValue::Tuple const&) {
3283
0
      solAssert(false, "Attempted to read from tuple lvalue.");
3284
0
    }
3285
18.0k
  }, _lvalue.kind);
3286
18.0k
  return result;
3287
18.0k
}
3288
3289
void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue)
3290
19.4k
{
3291
19.4k
  solAssert(!m_currentLValue);
3292
3293
19.4k
  if (_expression.annotation().willBeWrittenTo)
3294
2.45k
  {
3295
2.45k
    m_currentLValue.emplace(std::move(_lvalue));
3296
2.45k
    if (_lvalue.type.dataStoredIn(DataLocation::CallData))
3297
2.45k
      solAssert(std::holds_alternative<IRLValue::Stack>(_lvalue.kind));
3298
2.45k
  }
3299
17.0k
  else
3300
    // Only define the expression, if it will not be written to.
3301
17.0k
    define(_expression, readFromLValue(_lvalue));
3302
19.4k
}
3303
3304
void IRGeneratorForStatements::generateLoop(
3305
  Statement const& _body,
3306
  Expression const* _conditionExpression,
3307
  Statement const*  _initExpression,
3308
  ExpressionStatement const* _loopExpression,
3309
  bool _isDoWhile,
3310
  bool _isSimpleCounterLoop
3311
)
3312
106
{
3313
106
  std::string firstRun;
3314
3315
106
  if (_isDoWhile)
3316
2
  {
3317
2
    solAssert(_conditionExpression, "Expected condition for doWhile");
3318
2
    firstRun = m_context.newYulVariable();
3319
2
    appendCode() << "let " << firstRun << " := 1\n";
3320
2
  }
3321
3322
106
  appendCode() << "for {\n";
3323
106
  if (_initExpression)
3324
80
    _initExpression->accept(*this);
3325
106
  appendCode() << "} 1 {\n";
3326
106
  if (_loopExpression)
3327
80
  {
3328
80
    Arithmetic previousArithmetic = m_context.arithmetic();
3329
80
    if (m_optimiserSettings.simpleCounterForLoopUncheckedIncrement && _isSimpleCounterLoop)
3330
79
      m_context.setArithmetic(Arithmetic::Wrapping);
3331
80
    _loopExpression->accept(*this);
3332
80
    m_context.setArithmetic(previousArithmetic);
3333
80
  }
3334
106
  appendCode() << "}\n";
3335
106
  appendCode() << "{\n";
3336
3337
106
  if (_conditionExpression)
3338
106
  {
3339
106
    if (_isDoWhile)
3340
2
      appendCode() << "if iszero(" << firstRun << ") {\n";
3341
3342
106
    _conditionExpression->accept(*this);
3343
106
    appendCode() <<
3344
106
      "if iszero(" <<
3345
106
      expressionAsType(*_conditionExpression, *TypeProvider::boolean()) <<
3346
106
      ") { break }\n";
3347
3348
106
    if (_isDoWhile)
3349
2
      appendCode() << "}\n" << firstRun << " := 0\n";
3350
106
  }
3351
3352
106
  _body.accept(*this);
3353
3354
106
  appendCode() << "}\n";
3355
106
}
3356
3357
Type const& IRGeneratorForStatements::type(Expression const& _expression)
3358
22.9k
{
3359
22.9k
  solAssert(_expression.annotation().type, "Type of expression not set.");
3360
22.9k
  return *_expression.annotation().type;
3361
22.9k
}
3362
3363
bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
3364
53
{
3365
53
  Expression const& externalCall = _tryStatement.externalCall();
3366
53
  externalCall.accept(*this);
3367
53
  setLocation(_tryStatement);
3368
3369
53
  appendCode() << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n";
3370
3371
53
  appendCode() << "case 0 { // success case\n";
3372
53
  TryCatchClause const& successClause = *_tryStatement.clauses().front();
3373
53
  if (successClause.parameters())
3374
8
  {
3375
8
    size_t i = 0;
3376
8
    for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters())
3377
14
    {
3378
14
      solAssert(varDecl);
3379
14
      define(m_context.addLocalVariable(*varDecl),
3380
14
        successClause.parameters()->parameters().size() == 1 ?
3381
2
        IRVariable(externalCall) :
3382
14
        IRVariable(externalCall).tupleComponent(i++)
3383
14
      );
3384
14
    }
3385
8
  }
3386
3387
53
  successClause.block().accept(*this);
3388
53
  setLocation(_tryStatement);
3389
53
  appendCode() << "}\n";
3390
3391
53
  appendCode() << "default { // failure case\n";
3392
53
  handleCatch(_tryStatement);
3393
53
  appendCode() << "}\n";
3394
3395
53
  return false;
3396
53
}
3397
3398
void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
3399
53
{
3400
53
  setLocation(_tryStatement);
3401
53
  std::string const runFallback = m_context.newYulVariable();
3402
53
  appendCode() << "let " << runFallback << " := 1\n";
3403
3404
  // This function returns zero on "short returndata". We have to add a success flag
3405
  // once we implement custom error codes.
3406
53
  if (_tryStatement.errorClause() || _tryStatement.panicClause())
3407
0
    appendCode() << "switch " << m_utils.returnDataSelectorFunction() << "()\n";
3408
3409
53
  if (TryCatchClause const* errorClause = _tryStatement.errorClause())
3410
0
  {
3411
0
    appendCode() << "case " << selectorFromSignatureU32("Error(string)") << " {\n";
3412
0
    setLocation(*errorClause);
3413
0
    std::string const dataVariable = m_context.newYulVariable();
3414
0
    appendCode() << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
3415
0
    appendCode() << "if " << dataVariable << " {\n";
3416
0
    appendCode() << runFallback << " := 0\n";
3417
0
    if (errorClause->parameters())
3418
0
    {
3419
0
      solAssert(errorClause->parameters()->parameters().size() == 1);
3420
0
      IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front());
3421
0
      define(var) << dataVariable << "\n";
3422
0
    }
3423
0
    errorClause->accept(*this);
3424
0
    setLocation(*errorClause);
3425
0
    appendCode() << "}\n";
3426
0
    setLocation(_tryStatement);
3427
0
    appendCode() << "}\n";
3428
0
  }
3429
53
  if (TryCatchClause const* panicClause = _tryStatement.panicClause())
3430
0
  {
3431
0
    appendCode() << "case " << selectorFromSignatureU32("Panic(uint256)") << " {\n";
3432
0
    setLocation(*panicClause);
3433
0
    std::string const success = m_context.newYulVariable();
3434
0
    std::string const code = m_context.newYulVariable();
3435
0
    appendCode() << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n";
3436
0
    appendCode() << "if " << success << " {\n";
3437
0
    appendCode() << runFallback << " := 0\n";
3438
0
    if (panicClause->parameters())
3439
0
    {
3440
0
      solAssert(panicClause->parameters()->parameters().size() == 1);
3441
0
      IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front());
3442
0
      define(var) << code << "\n";
3443
0
    }
3444
0
    panicClause->accept(*this);
3445
0
    setLocation(*panicClause);
3446
0
    appendCode() << "}\n";
3447
0
    setLocation(_tryStatement);
3448
0
    appendCode() << "}\n";
3449
0
  }
3450
3451
53
  setLocation(_tryStatement);
3452
53
  appendCode() << "if " << runFallback << " {\n";
3453
53
  if (_tryStatement.fallbackClause())
3454
53
    handleCatchFallback(*_tryStatement.fallbackClause());
3455
0
  else
3456
0
    appendCode() << m_utils.forwardingRevertFunction() << "()\n";
3457
53
  setLocation(_tryStatement);
3458
53
  appendCode() << "}\n";
3459
53
}
3460
3461
void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallback)
3462
53
{
3463
53
  setLocation(_fallback);
3464
53
  if (_fallback.parameters())
3465
0
  {
3466
0
    solAssert(m_context.evmVersion().supportsReturndata());
3467
0
    solAssert(
3468
0
      _fallback.parameters()->parameters().size() == 1 &&
3469
0
      _fallback.parameters()->parameters().front() &&
3470
0
      *_fallback.parameters()->parameters().front()->annotation().type == *TypeProvider::bytesMemory(),
3471
0
      ""
3472
0
    );
3473
3474
0
    VariableDeclaration const& paramDecl = *_fallback.parameters()->parameters().front();
3475
0
    define(m_context.addLocalVariable(paramDecl)) << m_utils.extractReturndataFunction() << "()\n";
3476
0
  }
3477
53
  _fallback.accept(*this);
3478
53
}
3479
3480
void IRGeneratorForStatements::revertWithError(
3481
  std::string const& _signature,
3482
  std::vector<Type const*> const& _parameterTypes,
3483
  std::vector<ASTPointer<Expression const>> const& _errorArguments
3484
)
3485
13
{
3486
13
  appendCode() << m_utils.revertWithError(
3487
13
    _signature,
3488
13
    _parameterTypes,
3489
13
    _errorArguments,
3490
13
    m_context.newYulVariable(),
3491
13
    m_context.newYulVariable()
3492
13
  );
3493
13
}
3494
3495
bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
3496
53
{
3497
53
  _clause.block().accept(*this);
3498
53
  return false;
3499
53
}
3500
3501
std::string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
3502
47
{
3503
  solAssert(_library.isLibrary());
3504
47
  return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")";
3505
47
}