Coverage Report

Created: 2026-08-14 07:35

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