is/2: type_error(evaluable, _) culprit for an atom should be Atom/0, not the bare atom #36

Closed
opened 2026-06-26 04:29:53 +00:00 by navicore · 1 comment
Owner

Summary

is/2 raises a type_error(evaluable, Culprit) whose Culprit is a bare atom when an atom is used as an arithmetic expression, instead of the ISO-mandated predicate indicator Atom/0. The compound case is already correct (foo(1)foo/1), so this is an inconsistency in the atom path only.

ISO requirement

ISO 13211-1 §7.8.9 / §8.6 (is/2): when a sub-term of the expression is not evaluable, the error is

error(type_error(evaluable, Indicator), _)

where Indicator is a predicate indicator Name/Arity. For an atom foo (arity 0), the indicator is foo/0 — not the atom foo.

Reproduction

atom_culprit(C)     :- catch(_ is foo,    error(type_error(evaluable, C), _), true).
compound_culprit(C) :- catch(_ is foo(1), error(type_error(evaluable, C), _), true).
test :-
    atom_culprit(A),     write('X is foo    -> '), write(A), nl,
    compound_culprit(B), write('X is foo(1) -> '), write(B), nl.
$ plgc run repro.pl --query test --format text
X is foo    -> foo
X is foo(1) -> foo / 1

Expected vs actual

Expression Expected culprit (ISO) Actual (plgc 0.3.2)
X is foo foo/0 foo
X is foo(1) foo/1 foo/1

Likely locus

In crates/runtime/src/builtins/arith.rs, the non-evaluable atom path (≈ line 121) passes the bare atom term as the type_error(evaluable, _) culprit, whereas the unknown-arithmetic-operator path (≈ line 220) correctly constructs Name/Arity. The atom path should likewise build the indicator Atom/0 (functor / with the atom and integer 0) rather than passing the atom itself.

Impact

Low severity — catchers that match the culprit slot are nonstandard practice, and well-written code matches type_error(evaluable, _). But it's a genuine conformance gap: portable code that inspects the indicator (e.g. an error reporter that prints Name/Arity) gets a malformed term for the arity-0 case, and the same predicate produces two different shapes (foo vs foo/1) depending only on the culprit's arity.

Found while authoring the loglings exceptions chapter; all loglings exercises deliberately match type_error(evaluable, _) so the corpus is unaffected.

## Summary `is/2` raises a `type_error(evaluable, Culprit)` whose `Culprit` is a **bare atom** when an atom is used as an arithmetic expression, instead of the ISO-mandated **predicate indicator** `Atom/0`. The compound case is already correct (`foo(1)` → `foo/1`), so this is an inconsistency in the atom path only. ## ISO requirement ISO 13211-1 §7.8.9 / §8.6 (`is/2`): when a sub-term of the expression is not evaluable, the error is ``` error(type_error(evaluable, Indicator), _) ``` where `Indicator` is a **predicate indicator** `Name/Arity`. For an atom `foo` (arity 0), the indicator is `foo/0` — not the atom `foo`. ## Reproduction ```prolog atom_culprit(C) :- catch(_ is foo, error(type_error(evaluable, C), _), true). compound_culprit(C) :- catch(_ is foo(1), error(type_error(evaluable, C), _), true). test :- atom_culprit(A), write('X is foo -> '), write(A), nl, compound_culprit(B), write('X is foo(1) -> '), write(B), nl. ``` ``` $ plgc run repro.pl --query test --format text X is foo -> foo X is foo(1) -> foo / 1 ``` ## Expected vs actual | Expression | Expected culprit (ISO) | Actual (plgc 0.3.2) | |---|---|---| | `X is foo` | `foo/0` | `foo` ❌ | | `X is foo(1)` | `foo/1` | `foo/1` ✅ | ## Likely locus In `crates/runtime/src/builtins/arith.rs`, the non-evaluable **atom** path (≈ line 121) passes the bare atom term as the `type_error(evaluable, _)` culprit, whereas the unknown-arithmetic-**operator** path (≈ line 220) correctly constructs `Name/Arity`. The atom path should likewise build the indicator `Atom/0` (functor `/` with the atom and integer `0`) rather than passing the atom itself. ## Impact Low severity — catchers that match the culprit slot are nonstandard practice, and well-written code matches `type_error(evaluable, _)`. But it's a genuine conformance gap: portable code that inspects the indicator (e.g. an error reporter that prints `Name/Arity`) gets a malformed term for the arity-0 case, and the same predicate produces two different shapes (`foo` vs `foo/1`) depending only on the culprit's arity. Found while authoring the loglings exceptions chapter; all loglings exercises deliberately match `type_error(evaluable, _)` so the corpus is unaffected.
Author
Owner

#37

https://git.navicore.tech/navicore/patch-prolog/pulls/37
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog#36
No description provided.