Skip to content

Add suport to is_integer/3 BIF (and to OTP-29)#2150

Merged
bettio merged 1 commit intoatomvm:mainfrom
bettio:is_integer_3
Mar 2, 2026
Merged

Add suport to is_integer/3 BIF (and to OTP-29)#2150
bettio merged 1 commit intoatomvm:mainfrom
bettio:is_integer_3

Conversation

@bettio
Copy link
Copy Markdown
Collaborator

@bettio bettio commented Feb 27, 2026

OTP-29 introduces is_integer/3 BIF, so in order to support it we have introduced it as well.

  • bif.c implements the new BIF. It checks first small integers in order to provide best performance with them.

  • opcodesswitch.h: has been updated to support the new opcode for calling BIFs with 3 arguments, such as is_integer/3.

  • intn has been changed in order to provide a helper for signed big integers comparison (now we have intn_cmpu and intn_cmp).

  • README and documentation has been updated with OTP-29 support.

Closes #2114

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@petermm
Copy link
Copy Markdown
Contributor

petermm commented Feb 27, 2026

The -export for id/1, classify/1, etc. was outside the -ifdef(OTP29_OR_LATER) block, so on OTP < 29 those functions were exported but never defined.

@petermm
Copy link
Copy Markdown
Contributor

petermm commented Feb 27, 2026

looks good:

Key Finding: Argument Validation Order Bug

bif_erlang_is_integer_3() validates arg1 (the term) before arg2/arg3 (the bounds). In OTP-29, if Min or Max is not an integer, it must raise badarg — even when Term is not an integer. The current code returns false early, diverging from OTP semantics outside of guards.

For example, erlang:is_integer(<<"x">>, foo, 100) should raise badarg, but currently returns false.

Recommended Fix

In bif.c, reorder validation:

Check arg2/arg3 are integers first → raise badarg if not
Then check arg1 → return false if not an integer
Then proceed to comparisons

Also:

Remove UNUSED(fail_label) since it's actually used by RAISE_ERROR_BIF
Add tests for is_integer(non_integer, bad_bound, good_bound) to pin OTP-29 failure semantics

@bettio bettio force-pushed the is_integer_3 branch 3 times, most recently from 79189c9 to 42a6fb1 Compare February 27, 2026 22:21
@bettio
Copy link
Copy Markdown
Collaborator Author

bettio commented Feb 27, 2026

erlang:is_integer(<<"x">>, foo, 100)

Thanks, everything fixed.

@petermm
Copy link
Copy Markdown
Contributor

petermm commented Feb 28, 2026

https://ampcode.com/threads/T-019ca09c-de95-774e-b3bb-bd023fcf800f

PR #2150 Review: is_integer/3 BIF (OTP-29)

# File Line Severity Issue
1 libs/jit/src/opcodes.hrl 171 Medium OP_BIF3 (185) added to opcodes.h but missing from opcodes.hrl. Comment says "keep this list in sync". JIT won't handle the new opcode.
2 src/libAtomVM/opcodesswitch.h 7733 Medium Copy-paste error: trace message says "bif2/2" but should be "bif3/3" in the OP_BIF3 handler.
3 src/libAtomVM/bif.c 259 Medium UNUSED(fail_label) declared but fail_label is used by RAISE_ERROR_BIF on line 274. Misleading and may cause warnings.
4 CHANGELOG.md 83 Low Typo: "OPT-29" should be "OTP-29".

and the formatting of course - otherwise good!

@bettio bettio force-pushed the is_integer_3 branch 2 times, most recently from 940180c to 1200210 Compare March 1, 2026 21:22
@bettio
Copy link
Copy Markdown
Collaborator Author

bettio commented Mar 1, 2026

https://ampcode.com/threads/T-019ca09c-de95-774e-b3bb-bd023fcf800f

PR #2150 Review: is_integer/3 BIF (OTP-29)

Fixed everything. Thanks for reporting this.

Comment thread src/libAtomVM/intn.h Outdated
* @brief Compare two signed multi-precision integers
*
* Compares two multi-precision integers with explicit sign information.
* Magnitudes are compared using intn_cmp(), which ignores sign and tolerates
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llm strongly insists intn_cmp() should be intn_cmpu() here - ?

Comment thread src/libAtomVM/bif.c Outdated
avm_int64_t b = term_maybe_unbox_int64(arg3);
return (a <= n) && (n <= b) ? TRUE_ATOM : FALSE_ATOM;
} else {
if ((!term_is_any_integer(arg2) || !term_is_any_integer(arg3))) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant outer parentheses..

Copy link
Copy Markdown
Contributor

@petermm petermm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolute nitpicks left - looking good:

PR #2150 Review (v3): is_integer/3 BIF (OTP-29)

Commit: 1200210e6

Summary

The maintainer addressed all previous medium/high-severity issues:

  • opcodes.hrl now includes OP_BIF3 and updated OPCODE_MAX
  • ✅ Trace message fixed to "bif3/6"
  • UNUSED(fail_label) removed (it's used by RAISE_ERROR_BIF)
  • UNUSED(ctx) correctly omitted (ctx is used inside RAISE_ERROR_BIF macro)
  • ✅ Test exports moved inside -ifdef(OTP29_OR_LATER)
  • ✅ JIT support added in jit.erl with OP_BIF3 handler
  • ✅ CI matrix extended with OTP-29 JIT build
  • test_errors/0 now covers non-integer Term + non-integer bounds → badarg
  • "OPT-29" typo in CHANGELOG fixed to "OTP-29"

Remaining Issues

# File Line Severity Category Issue
1 src/libAtomVM/intn.h 283 Low Doc Confirmed (oracle): Stale docstring: "Magnitudes are compared using intn_cmp()" should be "intn_cmpu()" — the unsigned function was renamed but the doc for intn_cmp (signed) still references the old name. Not a self-reference; the implementation (intn.c:468) calls intn_cmpu().
2 src/libAtomVM/bif.c 274 Low Style return FALSE_ATOM; uses 2-space indent (10 spaces) but surrounding code uses 4-space indent (12 spaces). See line 271 RAISE_ERROR_BIF for the expected indentation level.
3 src/libAtomVM/bif.c 270 Low Style Redundant outer parentheses: `if ((!term_is_any_integer(arg2)

Notes

  • The opcode gap (183–184 skipped, OP_BIF3 = 185) is correct — opcodes must match OTP's BEAM format numbering.
  • The fast paths (small-int, int64) correctly fall through to the else block when any argument is non-integer, where proper validation occurs.
  • RAISE_ERROR_BIF implicitly uses ctx via macro expansion (lines 55–56), so the ctx parameter is genuinely needed despite appearing unused at the source level.
  • intn_cmp signed comparison logic is correct: sign mismatch → immediate result; same sign → unsigned magnitude compare, negated if both negative.
  • Test coverage is comprehensive: small ints, int64, bigints, mixed types, guard context, direct call context, swapped intervals, single-point intervals, non-integer terms, non-integer bounds, and combined non-integer term + bounds.

OTP-29 introduces is_integer/3 BIF, so in order to support it we have
introduced it as well.

- bif.c implements the new BIF. It checks first small integers in order to
provide best performance with them.

- opcodesswitch.h: has been updated to support the new opcode for calling
BIFs with 3 arguments, such as `is_integer/3`.

- jit.erl likewise. Also now JIT is tested also with OTP-29.

- intn has been changed in order to provide a helper for signed big
integers comparison (now we have `intn_cmpu` and `intn_cmp`).

- README and documentation has been updated with OTP-29 support.

Signed-off-by: Davide Bettio <davide@uninstall.it>
@bettio bettio merged commit 1541f03 into atomvm:main Mar 2, 2026
214 of 215 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add is_integer/3 BIF

2 participants