2424#include < liblangutil/SemVerHandler.h>
2525#include < libsolutil/Algorithms.h>
2626#include < libsolutil/FunctionSelector.h>
27+ #include < libyul/optimiser/ASTWalker.h>
28+ #include < libyul/AST.h>
29+
30+ #include < range/v3/algorithm/any_of.hpp>
2731
2832#include < memory>
2933
@@ -129,6 +133,17 @@ void PostTypeChecker::endVisit(ModifierInvocation const& _modifierInvocation)
129133 callEndVisit (_modifierInvocation);
130134}
131135
136+
137+ bool PostTypeChecker::visit (ForStatement const & _forStatement)
138+ {
139+ return callVisit (_forStatement);
140+ }
141+
142+ void PostTypeChecker::endVisit (ForStatement const & _forStatement)
143+ {
144+ callEndVisit (_forStatement);
145+ }
146+
132147namespace
133148{
134149struct ConstStateVarCircularReferenceChecker : public PostTypeChecker ::Checker
@@ -421,6 +436,116 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker
421436 }
422437};
423438
439+ class YulLValueChecker : public solidity ::yul::ASTWalker
440+ {
441+ public:
442+ YulLValueChecker (ASTString const & _identifierName): m_identifierName(_identifierName) {}
443+ bool willBeWrittenTo () const { return m_willBeWrittenTo; }
444+ using solidity::yul::ASTWalker::operator ();
445+ void operator ()(solidity::yul::Assignment const & _assignment) override
446+ {
447+ if (m_willBeWrittenTo)
448+ return ;
449+
450+ if (ranges::any_of (
451+ _assignment.variableNames ,
452+ [&](auto const & yulIdentifier) { return yulIdentifier.name .str () == m_identifierName; }
453+ ))
454+ m_willBeWrittenTo = true ;
455+ }
456+ private:
457+ ASTString const & m_identifierName;
458+ bool m_willBeWrittenTo = false ;
459+ };
460+
461+ class LValueChecker : public ASTConstVisitor
462+ {
463+ public:
464+ LValueChecker (Identifier const & _identifier):
465+ m_declaration (_identifier.annotation().referencedDeclaration)
466+ {}
467+ bool willBeWrittenTo () const { return m_willBeWrittenTo; }
468+ void endVisit (Identifier const & _identifier) override
469+ {
470+ if (m_willBeWrittenTo)
471+ return ;
472+
473+ solAssert (_identifier.annotation ().referencedDeclaration );
474+ if (
475+ *_identifier.annotation ().referencedDeclaration == *m_declaration &&
476+ _identifier.annotation ().willBeWrittenTo
477+ )
478+ m_willBeWrittenTo = true ;
479+ }
480+ void endVisit (InlineAssembly const & _inlineAssembly) override
481+ {
482+ if (m_willBeWrittenTo)
483+ return ;
484+
485+ YulLValueChecker yulChecker{m_declaration->name ()};
486+ yulChecker (_inlineAssembly.operations ());
487+ m_willBeWrittenTo = yulChecker.willBeWrittenTo ();
488+ }
489+ private:
490+ Declaration const * m_declaration{};
491+ bool m_willBeWrittenTo = false ;
492+ };
493+
494+ struct SimpleCounterForLoopChecker : public PostTypeChecker ::Checker
495+ {
496+ SimpleCounterForLoopChecker (ErrorReporter& _errorReporter): Checker(_errorReporter) {}
497+ bool visit (ForStatement const & _forStatement) override
498+ {
499+ _forStatement.annotation ().isSimpleCounterLoop = isSimpleCounterLoop (_forStatement);
500+ return true ;
501+ }
502+ bool isSimpleCounterLoop (ForStatement const & _forStatement) const
503+ {
504+ auto const * simpleCondition = dynamic_cast <BinaryOperation const *>(_forStatement.condition ());
505+ if (!simpleCondition || simpleCondition->getOperator () != Token::LessThan || simpleCondition->userDefinedFunctionType ())
506+ return false ;
507+ if (!_forStatement.loopExpression ())
508+ return false ;
509+
510+ auto const * simplePostExpression = dynamic_cast <UnaryOperation const *>(&_forStatement.loopExpression ()->expression ());
511+ // This matches both operators ++i and i++
512+ if (!simplePostExpression || simplePostExpression->getOperator () != Token::Inc || simplePostExpression->userDefinedFunctionType ())
513+ return false ;
514+
515+ auto const * lhsIdentifier = dynamic_cast <Identifier const *>(&simpleCondition->leftExpression ());
516+ auto const * lhsIntegerType = dynamic_cast <IntegerType const *>(simpleCondition->leftExpression ().annotation ().type );
517+ auto const * commonIntegerType = dynamic_cast <IntegerType const *>(simpleCondition->annotation ().commonType );
518+
519+ if (!lhsIdentifier || !lhsIntegerType || !commonIntegerType || *lhsIntegerType != *commonIntegerType)
520+ return false ;
521+
522+ auto const * incExpressionIdentifier = dynamic_cast <Identifier const *>(&simplePostExpression->subExpression ());
523+ if (
524+ !incExpressionIdentifier ||
525+ incExpressionIdentifier->annotation ().referencedDeclaration != lhsIdentifier->annotation ().referencedDeclaration
526+ )
527+ return false ;
528+
529+ solAssert (incExpressionIdentifier->annotation ().referencedDeclaration );
530+ if (
531+ auto const * incVariableDeclaration = dynamic_cast <VariableDeclaration const *>(
532+ incExpressionIdentifier->annotation ().referencedDeclaration
533+ );
534+ incVariableDeclaration &&
535+ !incVariableDeclaration->isLocalVariable ()
536+ )
537+ return false ;
538+
539+ solAssert (lhsIdentifier);
540+ LValueChecker lValueChecker{*lhsIdentifier};
541+ simpleCondition->rightExpression ().accept (lValueChecker);
542+ if (!lValueChecker.willBeWrittenTo ())
543+ _forStatement.body ().accept (lValueChecker);
544+
545+ return !lValueChecker.willBeWrittenTo ();
546+ }
547+ };
548+
424549}
425550
426551
@@ -432,4 +557,5 @@ PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_err
432557 m_checkers.push_back (std::make_shared<EventOutsideEmitErrorOutsideRevertChecker>(_errorReporter));
433558 m_checkers.push_back (std::make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
434559 m_checkers.push_back (std::make_shared<ReservedErrorSelector>(_errorReporter));
560+ m_checkers.push_back (std::make_shared<SimpleCounterForLoopChecker>(_errorReporter));
435561}
0 commit comments