diff --git a/Changelog.md b/Changelog.md index 8220e6d74bfe..63ed87ffbd72 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: * Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures. +* Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables. ### 0.8.30 (2025-05-07) diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 4aa7be1bb556..9f362215c889 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,19 @@ void ExpressionSimplifier::visit(Expression& _expression) while (auto const* match = SimplificationRules::findFirstMatch( _expression, m_dialect, - [this](YulName _var) { return variableValue(_var); } + [this](YulName const& _var) -> AssignedValue const* { + AssignedValue const* value = variableValue(_var); + if (!value || !value->value) + return nullptr; + + // check that all variables in the value expression are in current scope + MovableChecker const checker(m_dialect, *value->value); + for (YulName const& referencedVar: checker.referencedVariables()) + if (!inScope(referencedVar)) + return nullptr; // don't substitute if any referenced var is out of scope + + return value; + } )) { auto const* evmDialect = dynamic_cast(&m_dialect); diff --git a/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/args b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/args new file mode 100644 index 000000000000..0409bf821103 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/args @@ -0,0 +1 @@ +--strict-assembly --optimize --yul-optimizations T:ts --bin diff --git a/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/input.yul b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/input.yul new file mode 100644 index 000000000000..aeb78891470e --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/input.yul @@ -0,0 +1,23 @@ +{ + function identity(value) -> ret { + ret := value + } + function fun_fun_31() -> ret { + let x := 0x01 + let expr_9 := identity(x) + + let expr_10 := 0x01 + let expr_25 := 0x05 + switch expr_10 + case 0 { } + default { + let expr_14 := 0x06 + let expr_18 := identity(x) + expr_25 := or(expr_14, expr_18) + } + // expression simplifier spuriously gets a match on this: `expr_25 == or(0x06, expr_18)` where expr_18 is + // from the inner scope + ret := or(expr_9, expr_25) + leave + } +} diff --git a/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/output b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/output new file mode 100644 index 000000000000..c1abca29c0f6 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_expression_simplifier_with_invalid_match/output @@ -0,0 +1,5 @@ + +======= input.yul (EVM) ======= + +Binary representation: +00