Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
hclsyntax: Short-circuit behavior for the && and || operators
Previously we just always evaluated both operands of any binary operator and then executed the operator. For the logical operators that was inconsistent with their treatment in several other languages with a similar grammar to ours, leading to users being surprised that HCL didn't short circuit the same way as their favorite other languages did. Our initial exclusion of short-circuiting here was, as with various other parts of HCL's design, motivated by the goals of producing consistent results even when unknown values are present and of proactively returning as many errors as possible in order to give better context when debugging. However, in acknowledgement of the fact that logical operator short-circuiting has considerable ergonomic benefits when writing out compound conditional expressions where later terms rely on the guarantees of earlier terms, this commit implements a compromise design where we can get the short-circuit benefits for dynamic-value-related errors without losing our ability to proactively detect type-related errors even when short-circuiting. Specifically, if a particular operator participates in short-circuiting then it gets an opportunity to decide for a particular known LHS value whether to short-circuit. If it decides to do so then HCL will evaluate the RHS in a type-checking-only mode, where we ignore any specific values in the variable scope but will still raise errors if the RHS expression tries to do anything to them that is inconsistent with their type. If the LHS of a short-circuit-capable operator turns out to be an unknown value of a suitable type, we'll pessimistically treat it as a short-circuit and defer full evaluation of the RHS until we have more information, so as to avoid raising errors that would be guarded away once the LHS becomes known. The effect of this compromise is that it's possible to use the short-circuit operators in common value-related guard situations, like checking whether a value is null or is a number that would be valid to index a particular list: foo != null && foo.something idx < 3 && foo[idx] On the other hand though, it is _not_ possible to use the behavior to guard against problems that are related to types rather than to values, which allows us to still proactively detect various kinds of errors that are likely to be mistakes: foo != null && fo.something # Typoed "foo" as "fo" foo != null && foo.smething # Typoed "something" as "smething" num < 3 && num.something # Numbers never have attributes Those coming from dynamic languages will probably still find these errors surprising, but HCL's other features follow a similar sort of hybrid model of statically checking types where possible and that tradeoff seems to have worked well to make it possible to express more complicated situations while still providing some of the help typically expected from a static type system for "obviously wrong" situations. It should typically be possible to adjust an expression to make it friendly to short-circuit guard style by being more precise about the types being used.
- Loading branch information