# CoalesceUndefinedToNull instruction test suite # # Used by the Azure Policy compiler to convert undefined (missing) field values # to null before passing them to comparison operators. This prevents undefined # propagation from short-circuiting subsequent builtins. # # Semantics: # - If register == Undefined → replaces it with Null (in-place). # - For any other value (null, bool, number, string, array, object) → no-op. cases: # ========================================================================= # Basic coalescing behavior # ========================================================================= - note: undefined_becomes_null description: Undefined register value is replaced with null literals: [] instructions: - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: null - note: null_unchanged description: Null value is not modified literals: [] instructions: - "LoadNull { dest: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: null - note: true_unchanged description: Boolean true is not modified literals: [] instructions: - "LoadTrue { dest: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: true - note: false_unchanged description: Boolean false is not modified literals: [] instructions: - "LoadFalse { dest: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: false - note: number_unchanged description: Number value is not modified literals: - 42 instructions: - "Load { dest: 0, literal_idx: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: 42 - note: string_unchanged description: String value is not modified literals: - "hello" instructions: - "Load { dest: 0, literal_idx: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: "hello" - note: empty_string_unchanged description: Empty string is not modified (not confused with undefined) literals: - "" instructions: - "Load { dest: 0, literal_idx: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: "" - note: array_unchanged description: Array value is not modified literals: [] instructions: - "ArrayNew { dest: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" want_result: [] # ========================================================================= # Multiple coalesces — only undefined registers are affected # ========================================================================= - note: selective_coalescing description: Only undefined registers are coalesced; others remain unchanged literals: - 99 instructions: - "Load { dest: 0, literal_idx: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "CoalesceUndefinedToNull { register: 1 }" - "ArrayNew { dest: 2 }" - "ArrayPush { arr: 2, value: 0 }" - "ArrayPush { arr: 2, value: 1 }" - "Return { value: 2 }" want_result: [99, null] # ========================================================================= # Interaction with ReturnUndefinedIfNotTrue # ========================================================================= - note: coalesce_then_guard_null_returns_undefined description: > Coalescing undefined to null, then passing to ReturnUndefinedIfNotTrue returns undefined (null is not true) literals: - "should not reach" instructions: - "CoalesceUndefinedToNull { register: 0 }" - "ReturnUndefinedIfNotTrue { condition: 0 }" - "Load { dest: 1, literal_idx: 0 }" - "Return { value: 1 }" want_result: "#undefined" - note: coalesce_preserves_true_for_guard description: > Coalescing a true value is a no-op, so ReturnUndefinedIfNotTrue continues execution literals: - "reached" instructions: - "LoadTrue { dest: 0 }" - "CoalesceUndefinedToNull { register: 0 }" - "ReturnUndefinedIfNotTrue { condition: 0 }" - "Load { dest: 1, literal_idx: 0 }" - "Return { value: 1 }" want_result: "reached" # ========================================================================= # Interaction with HostAwait (suspendable mode) # ========================================================================= - note: coalesce_after_host_await_undefined_response description: > HostAwait returns undefined, CoalesceUndefinedToNull converts it to null literals: - "await-id" - "arg" instructions: - "Load { dest: 1, literal_idx: 1 }" - "Load { dest: 2, literal_idx: 0 }" - "HostAwait { dest: 0, arg: 1, id: 2 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" host_await_responses: - id: "await-id" value: "#undefined" want_result: null - note: coalesce_after_host_await_value_response description: > HostAwait returns a real value, CoalesceUndefinedToNull is a no-op literals: - "await-id" - "arg" instructions: - "Load { dest: 1, literal_idx: 1 }" - "Load { dest: 2, literal_idx: 0 }" - "HostAwait { dest: 0, arg: 1, id: 2 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" host_await_responses: - id: "await-id" value: "real-value" want_result: "real-value" - note: suspendable_coalesce_after_host_await description: > In suspendable mode: HostAwait suspends and resumes with value, CoalesceUndefinedToNull is a no-op literals: - "await-id" - "arg" instructions: - "Load { dest: 1, literal_idx: 1 }" - "Load { dest: 2, literal_idx: 0 }" - "HostAwait { dest: 0, arg: 1, id: 2 }" - "CoalesceUndefinedToNull { register: 0 }" - "Return { value: 0 }" host_await_responses_suspendable: - id: "await-id" value: 42 ignore_run_to_completion_hostawait_failure: true want_result: 42