Open rrb3942 opened 6 months ago
@rrb3942 could you try to debug the problem?
@antonmedv
Even though the trace showed all the cpu in the checker it is actually a problem in the patching phase.
Looks like there is some special logic around the operator overload patching that causes it to re-run all the patchers.
https://github.com/expr-lang/expr/blob/e6dfb719a733a1751b0d4dc3ec21d93bacdff3e0/expr.go#L202-L222
So if the value
patcher is passed first it will continually get re-run and, for some reason the operator patching will keep returning true
for ShouldRepeat
and continue looping . This results in an ever expanding tree as the value
patcher gets applied over and over again.
&{_decimalMul(decimal($patcher_value_getter($patcher_value_getter($patcher_value_getter($patcher_value_getter($patcher_value_getter($patcher_value_getter($patcher_value_getter(ValueOne)))))))), decimal(2)) 0xc0000ab170}
Something I don't understand is why the operator patching continues to signal the ShouldRepeat(). My only guess is that OperatorOverloading.applied
might need to get reset for every run?
Reordering the options so that the value
patcher comes last fails instantly but the operator patching never seems to happen, most likely because of typing issues because the value isn't converted yet.
2024/05/06 12:49:16 &{decimal($patcher_value_getter(ValueOne)) * decimal(2) 0xc0000ab170}
panic: invalid operation: * (mismatched types decimal.Decimal and decimal.Decimal) (1:19)
| decimal(ValueOne) * decimal(2)
| ..................^
Removing the check for ShouldRepeat()
allows the statement to compile and run.
@antonmedv I can try to write up a patch for this, just not sure on the approach to take. How do you feel about splitting any patcher that satisfies the ShouldRepeat
interface into a separate patch phase that runs after the non-repeatable patchers?
I’m actually not understand this problem. Could you please explain what is happening?
@antonmedv Please see pull request #658 which fixes part of the problem.
The remaining issue is that an operator patcher applying causes all patchers to re-run, which makes the assumption that all patchers are idempotent. However in the case of this patcher it is not, as the node it patches is simply wrapped in a CallNode
with the current node as an argument, so when the tree is walked and patched again it will trigger on the argument. Patchers that fully replace the node they are patching are not impacted.
Please note that this is a change in behavior for patchers introduced with https://github.com/expr-lang/expr/commit/3da85278439a5e8ef5dc0d73f321f76742e2cecc
My thought is too run two passes over patchers. The first pass runs any non-repeatable patchers without looping (old patcher behavior). The second pass would run repeatable (aka operators) only, repeating as necessary.
I'm adding support for "github.com/shopspring/decimal" to my project that makes use of "expr/patcher/value" and ran into an issue where cpu usage spikes to 100% and expr gets stuck compiling the expression when I try to introduce operator support.
trace.txt
From the trace it seems like it is getting stuck in the checker for some reason.
Here is an example program that can recreate the problem.
If there is any additional information that would help just let me know.