To prevent cyclic situations with arbitrary-substitution functions, we define a "substitution context", which tracks what we're currently substituting into, and maintain these in a stack as we bounce around substitutions. If you ever try to add a substitution context that's already in the stack, you're cycling.
(The Variables spec used to instead use the concept of a dependency graph that you built as you encountered var()s, and you searched it for cycles; this is the replacement that's more straightforwardly algorithmic instead of declarative.)
In custom functions, we want to avoid allowing recursion, so when we're processing one, we add a substitution context naming it; this prevents @function --foo() { result: --foo(); }.
But, as written, this also prevents color: --foo(--foo());, which isn't recursive at all, and should be allowed, especially for small utility functions like math helpers. (You can call hypot(1, hypot(2, 3)), for example.) I think this is pretty straightforwardly a spec bug; I just need to adjust exactly when we add the function's sub-context to wait until we're executing it, not evaluating its arguments.
@JaneOri ran into this in the wild and reported it as a Chrome bug (https://issues.chromium.org/issues/514021797)
To prevent cyclic situations with arbitrary-substitution functions, we define a "substitution context", which tracks what we're currently substituting into, and maintain these in a stack as we bounce around substitutions. If you ever try to add a substitution context that's already in the stack, you're cycling.
(The Variables spec used to instead use the concept of a dependency graph that you built as you encountered var()s, and you searched it for cycles; this is the replacement that's more straightforwardly algorithmic instead of declarative.)
In custom functions, we want to avoid allowing recursion, so when we're processing one, we add a substitution context naming it; this prevents
@function --foo() { result: --foo(); }.But, as written, this also prevents
color: --foo(--foo());, which isn't recursive at all, and should be allowed, especially for small utility functions like math helpers. (You can callhypot(1, hypot(2, 3)), for example.) I think this is pretty straightforwardly a spec bug; I just need to adjust exactly when we add the function's sub-context to wait until we're executing it, not evaluating its arguments.@JaneOri ran into this in the wild and reported it as a Chrome bug (https://issues.chromium.org/issues/514021797)