Clause
← All documentation

Guards

Guard legality follows the pinned OTP 29 erl_internal/erl_lint rules; it is never derived from runtime registration. Every operand is checked, including unreachable ones. A guard succeeds only when the final value is atom true.

Control flow

OperatorSemantics
A andalso BA must be boolean; false → false; otherwise returns B as any term
A orelse BA must be boolean; true → true; otherwise returns B
and, or, xorBoth operands evaluated in order, both boolean
not ABoolean inverse

true andalso 7 returns 7 in a body and fails as a final guard test; (true andalso 7) =:= 7 succeeds. A reached error inside a nested expression rejects the whole alternative: hd([]) orelse true fails, hd([]); true succeeds. In bodies, bad strict operands raise badarg and a bad lazy left operand raises {badarg, Value}, as in OTP.

Catalog

guards.tsv lists all 81 guard signatures from the pinned guard_bif, new_type_test, old_type_test, arith_op, bool_op and comp_op tables, and a test enforces exact set equality with upstream. The audit manifest maps each to resolver, lowering and runtime owner. All 81 are implemented (the identity-dependent self/0, node/0,1 and native is_record/1 since plan step 52): self() is the calling process's pid, node() and node/1 of a pid, reference or port are nonode@nohost (there is one node; any other node/1 argument fails the guard, badarg in a body), and is_record/1 tests for a native record. is_record/2 with a local native record name tests module and name; is_record/3 with an atom third argument tests a native record's module and name; native field access fails the guard on any mismatch.

Resolution

Oracle limitations

Installed OTP 29.1.1 crashes in SSA conversion for some unrelated modern imports behind legacy scalar aliases; Clause rejects that unauthorized owner explicitly. OTP's loader also rejects a guard with a huge literal record arity despite lint accepting it; that case has semantic evidence only.