Skip to content

feat: add LiteralTimestamp lowering; improve vector ops (FMA, fast-ma…#1

Open
omsherikar wants to merge 10 commits intomainfrom
feature/literal-timestamp-v2
Open

feat: add LiteralTimestamp lowering; improve vector ops (FMA, fast-ma…#1
omsherikar wants to merge 10 commits intomainfrom
feature/literal-timestamp-v2

Conversation

@omsherikar
Copy link
Copy Markdown
Owner

@omsherikar omsherikar commented Oct 30, 2025

…th, type checks); lint/type fixes

Notes

  • This repository uses an AI bot for reviews. Keep your PR in Draft while
    you work. When you’re ready for a review, change the status to Ready for
    review
    to trigger a new review round. If you make additional changes and
    don’t want to trigger the bot, switch the PR back to Draft.
  • AI-bot comments may not always be accurate. Please review them critically and
    share your feedback; it helps us improve the tool.
  • Avoid changing code that is unrelated to your proposal. Keep your PR as short
    as possible to increase the chances of a timely review. Large PRs may not be
    reviewed and may be closed.
  • Don’t add unnecessary comments. Your code should be readable and
    self-documenting
    (guidance).
  • Don’t change core features without prior discussion with the community. Use
    our Discord to discuss ideas, blockers, or issues
    (https://discord.gg/Nu4MdGj9jB).
  • Do not include secrets (API keys, tokens, passwords), credentials, or
    sensitive data/PII in code, configs, logs, screenshots, or commit history. If
    something leaks, rotate the credentials immediately, invalidate the old key,
    and note it in the PR so maintainers can assist.
  • Do not commit large binaries or generated artifacts. If large datasets are
    needed for tests, prefer small fixtures or programmatic downloads declared in
    makim.yaml (e.g., a task that fetches data at test time). If a large binary is
    unavoidable, discuss first and consider Git LFS.

Pull Request description

How to test these changes

  • ...

Pull Request checklists

This PR is a:

  • bug-fix
  • new feature
  • maintenance

About this PR:

  • it includes tests.
  • the tests are executed on CI.
  • the tests generate log file(s) (path).
  • pre-commit hooks were executed locally.
  • this PR requires a project documentation update.

Author's checklist:

  • I have reviewed the changes and it contains no misspelling.
  • The code is well commented, especially in the parts that contain more
    complexity.
  • New and old tests passed locally.

Additional information

Reviewer's checklist

Copy and paste this template for your review's note:

## Reviewer's Checklist

- [ ] I managed to reproduce the problem locally from the `main` branch
- [ ] I managed to test the new changes locally
- [ ] I confirm that the issues mentioned were fixed/resolved .

Summary by CodeRabbit

  • New Features

    • Timestamp literals supported (ISO‑8601 date + time; T or space). Fractional seconds accepted up to 9 digits (truncated); timezone suffixes rejected.
    • Integer and float casts now produce formatted ASCII strings (e.g., "42", "42.000000") and allocate printable ASCII strings.
    • Print API widened to accept general expressions.
  • Tests

    • New tests for int→string and float→string casts and printed output, plus timestamp lowering/validation.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 30, 2025

Walkthrough

Adds TIMESTAMP_TYPE and SIZE_T_TYPE, timestamp-literal lowering (parsing/validation into a seven-field i32 struct), native size-type initialization, printf/snprintf/malloc/format helpers, cast-to-string formatting and inline string allocation changes, updates PrintExpr type, and adds tests for casts and timestamp literals.

Changes

Cohort / File(s) Summary
LLVM builder — timestamp + helpers
src/irx/builders/llvmliteir.py
Added TIMESTAMP_TYPE and SIZE_T_TYPE; added imports (ctypes, datetime); introduced _init_native_size_types. Implemented visit(astx.LiteralTimestamp) to parse/validate YYYY-MM-DD and HH:MM:SS(.fffffffff) (reject timezone suffixes), compute nanoseconds (pad/truncate to 9 digits), and emit a constant struct. Added helpers: _normalize_int_for_printf, _create_malloc_decl, _create_snprintf_decl, _snprintf_heap, _get_or_create_format_global.
LLVM builder — casts & inline vars
src/irx/builders/llvmliteir.py
Extended visit(system.Cast) to format casts to ASCII_STRING_TYPE/STRING_TYPE via snprintf/malloc helpers (handles ints, floats, others); adjusted visit(astx.InlineVariableDeclaration) to allocate inline string values as "stringascii".
System AST / API
src/irx/system.py
Changed PrintExpr annotation and constructor parameter from astx.LiteralUTF8String to astx.Expr; updated public export/usage.
Tests
tests/test_cast.py, tests/test_literal_timestamp.py
Added parameterized tests test_cast_int_to_string and test_cast_float_to_string (using LLVMLiteIR) and new timestamp-literal tests covering basic decomposition, fractional truncation, invalid date, and timezone rejection. Updated imports to include PrintExpr.

Sequence Diagram(s)

sequenceDiagram
  participant TS as LiteralTimestamp node
  participant V as LLVMLiteIRVisitor
  participant IR as LLVM IR builder

  TS->>V: visit(LiteralTimestamp)
  Note right of V #F0F4C3: split date/time, reject timezone suffixes
  V->>V: parse year,month,day,hour,minute,second\nvalidate via datetime
  V->>V: parse fractional seconds → nanos (pad/truncate to 9 digits)
  V->>IR: emit constant TIMESTAMP_TYPE {y,m,d,h,min,s,nanos}
  IR-->>V: constant pushed to result stack
Loading
sequenceDiagram
  participant C as Cast node (to STRING/ASCII)
  participant V as LLVMLiteIRVisitor
  participant F as Format globals / snprintf
  participant H as Heap allocator

  C->>V: visit(Cast)
  V->>V: choose format string by source type\n_normalize_int_for_printf for ints
  V->>F: get/create format global
  V->>F: call _snprintf_heap(fmt_gv, args)
  Note right of F #E6F5FF: snprintf decl + malloc allocate buffer on heap
  F->>H: allocate buffer and write formatted bytes
  F-->>V: pointer to heap string
  V->>C: push formatted string value onto result stack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review timestamp parsing/validation and exact error text for invalid dates/timezones.
  • Verify _init_native_size_types sets pointer/size_t widths correctly across platforms.
  • Inspect snprintf/malloc declarations, calling conventions, format-global lifetimes, and memory ownership.
  • Check integer normalization and float formatting choices and rounding/truncation behavior.
  • Validate tests’ expectations and the conditional skip for astx.LiteralTimestamp.

Poem

🐇 I hop through bytes and tuck in time,
I parse the date, I pad the chime.
Nanos trimmed and fields in line,
I snprintf numbers, heap-strings shine.
Hop—build succeeds; the IR smells fine.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title states "feat: add LiteralTimestamp lowering; improve vector ops (FMA, fast-ma…)". The first part about LiteralTimestamp lowering is clearly present in the raw_summary, which documents the addition of a visit method for astx.LiteralTimestamp, TIMESTAMP_TYPE definition, and comprehensive test coverage. However, the second part of the title claiming vector operations improvements (FMA support, fast-math, type checks) is not documented anywhere in the provided raw_summary. The raw_summary shows no changes related to vector ops, FMA, or fast-math functionality, making the title partially misleading or overstated in scope. The title should be revised to accurately reflect the actual changes in the pull request. Either remove the "improve vector ops (FMA, fast-math, type checks)" portion if those changes are not included in this PR, or ensure those changes are documented in the raw_summary. The title should focus on the primary change: LiteralTimestamp lowering and related improvements to string casting and helper methods.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/literal-timestamp-v2

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16864d6 and 583801e.

📒 Files selected for processing (1)
  • tests/test_literal_timestamp.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_literal_timestamp.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/irx/builders/llvmliteir.py (1)

1078-1097: Consider adding year validation.

The code doesn't validate the year value, which could accept unrealistic values (e.g., negative years or year 999999). Depending on your use case, you might want to add reasonable bounds.

Example:

if not (1 <= year <= 9999):
    raise Exception(
        f"LiteralTimestamp: year out of range in '{node.value}'."
    )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95bad11 and a348057.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1046-1046: Redefinition of unused visit from line 1040

(F811)


1065-1069: Create your own exception

(TRY002)


1065-1069: Avoid specifying long messages outside the exception class

(TRY003)


1073-1076: Create your own exception

(TRY002)


1073-1076: Avoid specifying long messages outside the exception class

(TRY003)


1085-1088: Create your own exception

(TRY002)


1085-1088: Avoid specifying long messages outside the exception class

(TRY003)


1091-1093: Create your own exception

(TRY002)


1091-1093: Avoid specifying long messages outside the exception class

(TRY003)


1095-1097: Create your own exception

(TRY002)


1095-1097: Avoid specifying long messages outside the exception class

(TRY003)


1105-1105: Abstract raise to an inner function

(TRY301)


1105-1105: Avoid specifying long messages outside the exception class

(TRY003)


1117-1121: Create your own exception

(TRY002)


1117-1121: Avoid specifying long messages outside the exception class

(TRY003)


1124-1126: Create your own exception

(TRY002)


1124-1126: Avoid specifying long messages outside the exception class

(TRY003)


1128-1130: Create your own exception

(TRY002)


1128-1130: Avoid specifying long messages outside the exception class

(TRY003)


1132-1134: Create your own exception

(TRY002)


1132-1134: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (5)
src/irx/builders/llvmliteir.py (5)

1045-1069: LGTM: Clear format parsing.

The docstring clearly documents the supported formats and the parsing logic correctly handles both 'T' and space separators.

Note: The F811 static analysis warning about function redefinition is a false positive—this is the expected pattern for plum-dispatch overloaded methods.


1071-1076: LGTM: Timezone rejection is clear.

The explicit rejection of timezone offsets with a descriptive error message is appropriate, and the detection logic correctly identifies timezone suffixes.


1099-1121: LGTM: Fractional seconds handling is correct.

The parsing correctly handles optional fractional seconds, truncates to 9 digits as documented, and pads appropriately to compute nanoseconds.


1123-1134: LGTM: Time component validation is correct.

The range checks for hour (0-23), minute (0-59), and second (0-59) are accurate, with clear error messages.


1136-1151: LGTM: LLVM struct construction is correct.

The constant struct is properly constructed with all seven i32 fields matching the documented layout, and the result is correctly pushed to the stack.

…Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk && /Users/omsherikar/miniforge3/envs/irx/bin/python -m pytest tests/test_timestamp.py -q
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a348057 and 7eb3caf.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1047-1047: Redefinition of unused visit from line 1041

(F811)


1066-1070: Create your own exception

(TRY002)


1066-1070: Avoid specifying long messages outside the exception class

(TRY003)


1074-1077: Create your own exception

(TRY002)


1074-1077: Avoid specifying long messages outside the exception class

(TRY003)


1080-1080: Redefinition of unused datetime from line 17

Remove definition: datetime

(F811)


1089-1092: Create your own exception

(TRY002)


1089-1092: Avoid specifying long messages outside the exception class

(TRY003)


1094-1097: Create your own exception

(TRY002)


1094-1097: Avoid specifying long messages outside the exception class

(TRY003)


1105-1105: Abstract raise to an inner function

(TRY301)


1105-1105: Avoid specifying long messages outside the exception class

(TRY003)


1117-1121: Create your own exception

(TRY002)


1117-1121: Avoid specifying long messages outside the exception class

(TRY003)


1124-1126: Create your own exception

(TRY002)


1124-1126: Avoid specifying long messages outside the exception class

(TRY003)


1128-1130: Create your own exception

(TRY002)


1128-1130: Avoid specifying long messages outside the exception class

(TRY003)


1132-1134: Create your own exception

(TRY002)


1132-1134: Avoid specifying long messages outside the exception class

(TRY003)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/irx/builders/llvmliteir.py (2)

1072-1077: Consider clarifying the timezone detection logic.

The condition "-" in time_part[2:] skips the first two characters to avoid false positives, but this might be unclear to future maintainers. Consider adding a comment explaining why character position 2 onwards is checked, or use a more explicit pattern match.

Example with comment:

-        if time_part.endswith("Z") or "+" in time_part or "-" in time_part[2:]:
+        # Check for timezone: 'Z', '+', or '-' (skip first 2 chars to avoid matching HH:MM minus)
+        if time_part.endswith("Z") or "+" in time_part or "-" in time_part[2:]:

1098-1120: Time parsing is correct, but consider the fractional seconds truncation behavior.

The code silently truncates fractional seconds beyond 9 digits (line 1106). This is reasonable for nanosecond precision, but you might want to add a comment explaining this behavior or consider whether to raise an error when precision would be lost.

Optional: Add a comment to document the truncation:

                if len(frac) > 9:
+                   # Truncate to nanosecond precision
                    frac = frac[:9]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb3caf and 48ea7dc.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1047-1047: Redefinition of unused visit from line 1041

(F811)


1066-1070: Create your own exception

(TRY002)


1066-1070: Avoid specifying long messages outside the exception class

(TRY003)


1074-1077: Create your own exception

(TRY002)


1074-1077: Avoid specifying long messages outside the exception class

(TRY003)


1088-1091: Create your own exception

(TRY002)


1088-1091: Avoid specifying long messages outside the exception class

(TRY003)


1093-1096: Create your own exception

(TRY002)


1093-1096: Avoid specifying long messages outside the exception class

(TRY003)


1104-1104: Abstract raise to an inner function

(TRY301)


1104-1104: Avoid specifying long messages outside the exception class

(TRY003)


1116-1120: Create your own exception

(TRY002)


1116-1120: Avoid specifying long messages outside the exception class

(TRY003)


1123-1125: Create your own exception

(TRY002)


1123-1125: Avoid specifying long messages outside the exception class

(TRY003)


1127-1129: Create your own exception

(TRY002)


1127-1129: Avoid specifying long messages outside the exception class

(TRY003)


1131-1133: Create your own exception

(TRY002)


1131-1133: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (6)
src/irx/builders/llvmliteir.py (6)

17-17: Good fix—module-level datetime import resolves the previous F811 issue.

The datetime import is now properly placed at module scope, addressing the redefinition warning from the previous review.


1046-1057: LGTM—clear documentation and method signature.

The docstring clearly specifies the struct layout and accepted formats.


1058-1070: LGTM—format detection handles both 'T' and space separators correctly.


1079-1096: LGTM—date validation now correctly handles month-specific limits and leap years.

Using datetime(year, month, day) ensures only valid calendar dates are accepted, properly addressing the previous review concern about accepting invalid dates like February 31.


1122-1133: LGTM—time component validation is correct for standard use cases.

The range checks properly validate hour (0-23), minute (0-59), and second (0-59). Note that leap seconds (second=60) are not supported, which is acceptable for most timestamp use cases.


1135-1149: LGTM—LLVM IR struct generation is correct and matches the documented layout.

The constant struct properly contains all seven i32 fields (year, month, day, hour, minute, second, nanos) as specified in the docstring.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/irx/builders/llvmliteir.py (1)

1046-1156: Excellent implementation! Timestamp lowering is correct and comprehensive.

The visitor method properly:

  • Parses both YYYY-MM-DDTHH:MM:SS and YYYY-MM-DD HH:MM:SS formats
  • Rejects timezone suffixes (Z, +, -)
  • Validates real calendar dates using datetime (correctly handles leap years and month-specific limits)
  • Handles optional fractional seconds (validates digits, truncates to 9, pads with zeros)
  • Validates time component ranges
  • Creates a well-documented 7-field i32 struct constant

Note: The Ruff F811 warning about visit redefinition is a false positive—the @dispatch decorator pattern intentionally creates multiple overloaded visit methods.

Optional refactor (low priority): Consider creating custom exception classes to replace the generic Exception usage throughout the method. This would improve error handling clarity and align with Python best practices (Ruff TRY002/TRY003). However, this is purely a code quality improvement and can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48ea7dc and 1001b38.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1047-1047: Redefinition of unused visit from line 1041

(F811)


1066-1070: Create your own exception

(TRY002)


1066-1070: Avoid specifying long messages outside the exception class

(TRY003)


1074-1077: Create your own exception

(TRY002)


1074-1077: Avoid specifying long messages outside the exception class

(TRY003)


1088-1091: Create your own exception

(TRY002)


1088-1091: Avoid specifying long messages outside the exception class

(TRY003)


1093-1096: Create your own exception

(TRY002)


1093-1096: Avoid specifying long messages outside the exception class

(TRY003)


1110-1110: Abstract raise to an inner function

(TRY301)


1110-1110: Avoid specifying long messages outside the exception class

(TRY003)


1122-1126: Create your own exception

(TRY002)


1122-1126: Avoid specifying long messages outside the exception class

(TRY003)


1129-1131: Create your own exception

(TRY002)


1129-1131: Avoid specifying long messages outside the exception class

(TRY003)


1133-1135: Create your own exception

(TRY002)


1133-1135: Avoid specifying long messages outside the exception class

(TRY003)


1137-1139: Create your own exception

(TRY002)


1137-1139: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (1)
src/irx/builders/llvmliteir.py (1)

8-8: LGTM! Required import for date validation.

The module-level datetime import is correctly positioned and used for validating calendar dates at line 1086, properly handling leap years and month-specific day limits.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/irx/builders/llvmliteir.py (2)

1111-1139: LGTM: Time parsing logic.

The implementation correctly handles:

  • Fractional seconds with proper truncation and padding to nanoseconds
  • HH:MM:SS parsing
  • Named constants improve code readability

Optional: Consider moving the named constants (NS_DIGITS, MAX_HOUR, etc.) to class or module level if they might be reused elsewhere, though current placement is acceptable for single-method use.


1059-1167: Optional: Ruff style warnings remain.

Ruff flags several exception-handling style issues (TRY002, TRY003 about custom exceptions; TRY301 about raise placement). These are style preferences and don't affect functionality. If the project aims to be Ruff-clean, consider addressing them; otherwise, they can be suppressed in the config.

Also note: The F811 warning about visit redefinition at line 1060 is expected and harmless—it's how plum-dispatch implements multiple dispatch.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1001b38 and be4e366.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1060-1060: Redefinition of unused visit from line 1054

(F811)


1079-1083: Create your own exception

(TRY002)


1079-1083: Avoid specifying long messages outside the exception class

(TRY003)


1087-1090: Create your own exception

(TRY002)


1087-1090: Avoid specifying long messages outside the exception class

(TRY003)


1101-1104: Create your own exception

(TRY002)


1101-1104: Avoid specifying long messages outside the exception class

(TRY003)


1106-1109: Create your own exception

(TRY002)


1106-1109: Avoid specifying long messages outside the exception class

(TRY003)


1123-1123: Abstract raise to an inner function

(TRY301)


1123-1123: Avoid specifying long messages outside the exception class

(TRY003)


1135-1139: Create your own exception

(TRY002)


1135-1139: Avoid specifying long messages outside the exception class

(TRY003)


1142-1144: Create your own exception

(TRY002)


1142-1144: Avoid specifying long messages outside the exception class

(TRY003)


1146-1148: Create your own exception

(TRY002)


1146-1148: Avoid specifying long messages outside the exception class

(TRY003)


1150-1152: Create your own exception

(TRY002)


1150-1152: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (8)
src/irx/builders/llvmliteir.py (8)

8-8: LGTM: datetime import correctly placed at module level.

The import is properly positioned and used for calendar-aware date validation in the LiteralTimestamp visitor.


49-49: LGTM: TIMESTAMP_TYPE field declaration.

Consistent with the existing type field pattern in VariablesLLVM.


165-176: LGTM: TIMESTAMP_TYPE initialization.

The 7-field i32 struct correctly represents the timestamp layout (year, month, day, hour, minute, second, nanos) as documented in the visitor method.


1059-1083: LGTM: Format parsing logic.

The visitor correctly handles both ISO-like formats (with 'T' or space separator) and provides clear error messages for invalid formats.


1085-1090: LGTM: Timezone rejection logic.

The checks correctly reject common timezone formats (Z, +offset, -offset) as documented. The logic is sufficient for the stated requirements.


1092-1109: LGTM: Date validation correctly implemented.

Using datetime(year, month, day) properly validates calendar dates including month-specific day limits and leap years. This addresses the past review feedback.


1141-1152: LGTM: Time component range validation.

Hour, minute, and second ranges are correctly validated with clear error messages.


1154-1167: LGTM: Constant struct creation.

The timestamp constant is correctly constructed with all 7 fields (year, month, day, hour, minute, second, nanos) and pushed to the result stack following the established pattern.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be4e366 and 2ee3b9b.

📒 Files selected for processing (3)
  • src/irx/builders/llvmliteir.py (7 hunks)
  • src/irx/system.py (1 hunks)
  • tests/test_cast.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_cast.py (4)
src/irx/system.py (2)
  • Cast (34-49)
  • PrintExpr (10-31)
src/irx/builders/llvmliteir.py (1)
  • LLVMLiteIR (1919-1953)
src/irx/builders/base.py (2)
  • Builder (52-93)
  • module (74-76)
tests/conftest.py (1)
  • check_result (22-65)
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1065-1065: Redefinition of unused visit from line 1059

(F811)


1084-1088: Create your own exception

(TRY002)


1084-1088: Avoid specifying long messages outside the exception class

(TRY003)


1092-1095: Create your own exception

(TRY002)


1092-1095: Avoid specifying long messages outside the exception class

(TRY003)


1106-1109: Create your own exception

(TRY002)


1106-1109: Avoid specifying long messages outside the exception class

(TRY003)


1111-1114: Create your own exception

(TRY002)


1111-1114: Avoid specifying long messages outside the exception class

(TRY003)


1128-1128: Abstract raise to an inner function

(TRY301)


1128-1128: Avoid specifying long messages outside the exception class

(TRY003)


1140-1144: Create your own exception

(TRY002)


1140-1144: Avoid specifying long messages outside the exception class

(TRY003)


1147-1149: Create your own exception

(TRY002)


1147-1149: Avoid specifying long messages outside the exception class

(TRY003)


1151-1153: Create your own exception

(TRY002)


1151-1153: Avoid specifying long messages outside the exception class

(TRY003)


1155-1157: Create your own exception

(TRY002)


1155-1157: Avoid specifying long messages outside the exception class

(TRY003)


1596-1596: Prefer TypeError exception for invalid type

(TRY004)


1596-1596: Create your own exception

(TRY002)


1596-1596: Avoid specifying long messages outside the exception class

(TRY003)


1603-1605: Create your own exception

(TRY002)


1603-1605: Avoid specifying long messages outside the exception class

(TRY003)

Comment on lines +1592 to +1605
def _normalize_int_for_printf(self, v: ir.Value) -> tuple[ir.Value, str]:
"""Promote/truncate integer to match printf format."""
INT64_WIDTH = 64
if not isinstance(v.type, ir.IntType):
raise Exception("Expected integer value")
w = v.type.width
if w < INT64_WIDTH:
arg = self._llvm.ir_builder.sext(v, self._llvm.INT64_TYPE)
return arg, "%lld"
if w == INT64_WIDTH:
return v, "%lld"
raise Exception(
"Casting integers wider than 64 bits to string is not supported"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix bool-to-string cast returning “-1”.
_normalize_int_for_printf() sign-extends every integer narrower than 64 bits. For an i1/boolean value that means true becomes -1, so casting a boolean to string yields "‑1" instead of "1". Please zero-extend i1 (or otherwise special-case booleans) before formatting so that true prints as 1.

🧰 Tools
🪛 Ruff (0.14.2)

1596-1596: Prefer TypeError exception for invalid type

(TRY004)


1596-1596: Create your own exception

(TRY002)


1596-1596: Avoid specifying long messages outside the exception class

(TRY003)


1603-1605: Create your own exception

(TRY002)


1603-1605: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In src/irx/builders/llvmliteir.py around lines 1592 to 1605, the function
currently sign-extends all integers narrower than 64 bits which makes i1 true
become -1; change the handling so that if v.type.width == 1 (boolean) you
zero-extend (zext) to INT64_TYPE instead of sign-extending, returning the
zextended value and the same printf format; for other widths < 64 keep the
existing sign-extension (sext) behavior and return as before; leave the equality
and >64 error branches unchanged.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/irx/builders/llvmliteir.py (1)

1595-1608: Boolean-to-string cast still yields "-1" for true.

The function sign-extends all integers narrower than 64 bits. For an i1 (boolean), this means true (bit value 1) sign-extends to -1, so casting a boolean to string prints "-1" instead of "1".

Zero-extend booleans instead of sign-extending:

     def _normalize_int_for_printf(self, v: ir.Value) -> tuple[ir.Value, str]:
         """Promote/truncate integer to match printf format."""
         INT64_WIDTH = 64
         if not isinstance(v.type, ir.IntType):
             raise Exception("Expected integer value")
         w = v.type.width
         if w < INT64_WIDTH:
-            arg = self._llvm.ir_builder.sext(v, self._llvm.INT64_TYPE)
+            # Zero-extend booleans, sign-extend other integers
+            if w == 1:
+                arg = self._llvm.ir_builder.zext(v, self._llvm.INT64_TYPE)
+            else:
+                arg = self._llvm.ir_builder.sext(v, self._llvm.INT64_TYPE)
             return arg, "%lld"
         if w == INT64_WIDTH:
             return v, "%lld"
🧹 Nitpick comments (1)
src/irx/builders/llvmliteir.py (1)

1087-1160: Consider custom exception classes for better error handling.

Multiple validation errors in the timestamp parsing use generic Exception with inline messages. While functional, Ruff suggests creating custom exception types for better categorization and testing.

Example refactor pattern:

class TimestampParseError(ValueError):
    """Raised when timestamp literal format is invalid."""
    pass

# Then use:
raise TimestampParseError(
    f"LiteralTimestamp: invalid format '{node.value}'. "
    "Expected 'YYYY-MM-DDTHH:MM:SS[.fffffffff]'."
)

This is optional—the current approach with descriptive messages works fine for this use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ee3b9b and adc2483.

📒 Files selected for processing (1)
  • src/irx/builders/llvmliteir.py (7 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1068-1068: Redefinition of unused visit from line 1062

(F811)


1087-1091: Create your own exception

(TRY002)


1087-1091: Avoid specifying long messages outside the exception class

(TRY003)


1095-1098: Create your own exception

(TRY002)


1095-1098: Avoid specifying long messages outside the exception class

(TRY003)


1109-1112: Create your own exception

(TRY002)


1109-1112: Avoid specifying long messages outside the exception class

(TRY003)


1114-1117: Create your own exception

(TRY002)


1114-1117: Avoid specifying long messages outside the exception class

(TRY003)


1131-1131: Abstract raise to an inner function

(TRY301)


1131-1131: Avoid specifying long messages outside the exception class

(TRY003)


1143-1147: Create your own exception

(TRY002)


1143-1147: Avoid specifying long messages outside the exception class

(TRY003)


1150-1152: Create your own exception

(TRY002)


1150-1152: Avoid specifying long messages outside the exception class

(TRY003)


1154-1156: Create your own exception

(TRY002)


1154-1156: Avoid specifying long messages outside the exception class

(TRY003)


1158-1160: Create your own exception

(TRY002)


1158-1160: Avoid specifying long messages outside the exception class

(TRY003)


1599-1599: Prefer TypeError exception for invalid type

(TRY004)


1599-1599: Create your own exception

(TRY002)


1599-1599: Avoid specifying long messages outside the exception class

(TRY003)


1606-1608: Create your own exception

(TRY002)


1606-1608: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (3)
src/irx/builders/llvmliteir.py (3)

1067-1175: LiteralTimestamp lowering looks solid.

The implementation correctly:

  • Validates dates using datetime constructor (addressing previous review feedback)
  • Handles fractional seconds with proper truncation/padding
  • Uses named constants for validation bounds
  • Rejects timezone suffixes appropriately

Note: Ruff's F811 warning at line 1068 is a false positive—plum-dispatch allows multiple visit methods with different type signatures.


1585-1588: String type mapping is correct.

Mapping "string" to "stringascii" for allocation is consistent with how VariableDeclaration handles strings elsewhere in the file.


1760-1784: String casting logic correctly covers all numeric types used in the codebase.

Verification confirms the implementation handles all integer widths (1, 8, 16, 32, 64 bits) via generic type checking and _normalize_int_for_printf, which sign-extends smaller integers to 64-bit and explicitly rejects wider integers. All floating types (Float, Double, Half) are covered with proper promotion to double for consistent formatting. No 128-bit integers or unsigned-specific types exist in the codebase, so no additional coverage is needed.

Comment on lines +135 to +138
def _init_native_size_types(self) -> None:
"""Initialize pointer/size_t types from host."""
self._llvm.POINTER_BITS = ctypes.sizeof(ctypes.c_void_p) * 8
self._llvm.SIZE_T_TYPE = ir.IntType(ctypes.sizeof(ctypes.c_size_t) * 8)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

_init_native_size_types() is defined but never called.

This method was added to initialize SIZE_T_TYPE based on the host platform, but it's not invoked from initialize() or anywhere else in the code. As a result, SIZE_T_TYPE remains hard-coded to 64-bit at line 184, defeating the purpose of this platform-detection method.

Call this method from initialize() before SIZE_T_TYPE is used:

     def initialize(self) -> None:
         """Initialize self."""
         self._llvm = VariablesLLVM()
         self._llvm.module = ir.module.Module("Arx")
+        
+        # Initialize platform-specific types
+        self._init_native_size_types()

Then remove the hard-coded line 184 initialization:

-        # Platform-sized unsigned integer (assume 64-bit for CI targets)
-        self._llvm.SIZE_T_TYPE = ir.IntType(64)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/irx/builders/llvmliteir.py around lines 135-138, the helper
_init_native_size_types() initializes POINTER_BITS and SIZE_T_TYPE for the host
but is never called; update initialize() to call self._init_native_size_types()
before any use of SIZE_T_TYPE (i.e., before the current hard-coded SIZE_T_TYPE
at line 184), and remove the hard-coded 64-bit SIZE_T_TYPE assignment at line
184 so the runtime-detected values are used instead.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/irx/builders/llvmliteir.py (1)

1598-1611: Bool-to-string cast still returns "-1" instead of "1".

Sign-extending i1 true (bit=1) to i64 yields -1 because the sign bit propagates. A past review flagged this, but the code still uses sext unconditionally.

Apply this diff to zero-extend booleans:

     def _normalize_int_for_printf(self, v: ir.Value) -> tuple[ir.Value, str]:
         """Promote/truncate integer to match printf format."""
         INT64_WIDTH = 64
         if not isinstance(v.type, ir.IntType):
             raise Exception("Expected integer value")
         w = v.type.width
         if w < INT64_WIDTH:
-            arg = self._llvm.ir_builder.sext(v, self._llvm.INT64_TYPE)
+            # Zero-extend i1 (boolean) to avoid true → -1
+            if w == 1:
+                arg = self._llvm.ir_builder.zext(v, self._llvm.INT64_TYPE)
+            else:
+                arg = self._llvm.ir_builder.sext(v, self._llvm.INT64_TYPE)
             return arg, "%lld"
         if w == INT64_WIDTH:
             return v, "%lld"
🧹 Nitpick comments (1)
tests/test_literal_timestamp.py (1)

16-18: Regex-based extraction is pragmatic but fragile.

The helper parses LLVM IR's string representation to extract field values. While this works for testing, it's brittle—changes to llvmlite's formatting could break it. For production code, consider accessing the constant's operands directly if llvmlite exposes that API.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adc2483 and 8228cd2.

📒 Files selected for processing (2)
  • src/irx/builders/llvmliteir.py (7 hunks)
  • tests/test_literal_timestamp.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.14.2)
src/irx/builders/llvmliteir.py

1071-1071: Redefinition of unused visit from line 1065

(F811)


1090-1094: Create your own exception

(TRY002)


1090-1094: Avoid specifying long messages outside the exception class

(TRY003)


1098-1101: Create your own exception

(TRY002)


1098-1101: Avoid specifying long messages outside the exception class

(TRY003)


1112-1115: Create your own exception

(TRY002)


1112-1115: Avoid specifying long messages outside the exception class

(TRY003)


1117-1120: Create your own exception

(TRY002)


1117-1120: Avoid specifying long messages outside the exception class

(TRY003)


1134-1134: Abstract raise to an inner function

(TRY301)


1134-1134: Avoid specifying long messages outside the exception class

(TRY003)


1146-1150: Create your own exception

(TRY002)


1146-1150: Avoid specifying long messages outside the exception class

(TRY003)


1153-1155: Create your own exception

(TRY002)


1153-1155: Avoid specifying long messages outside the exception class

(TRY003)


1157-1159: Create your own exception

(TRY002)


1157-1159: Avoid specifying long messages outside the exception class

(TRY003)


1161-1163: Create your own exception

(TRY002)


1161-1163: Avoid specifying long messages outside the exception class

(TRY003)


1602-1602: Prefer TypeError exception for invalid type

(TRY004)


1602-1602: Create your own exception

(TRY002)


1602-1602: Avoid specifying long messages outside the exception class

(TRY003)


1609-1611: Create your own exception

(TRY002)


1609-1611: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (11)
tests/test_literal_timestamp.py (3)

21-35: LGTM! Basic timestamp lowering test is thorough.

The test correctly validates all seven i32 fields, including proper conversion of fractional seconds (.123123_000_000 nanoseconds).


38-48: LGTM! Fractional truncation test is correct.

Properly validates that 10-digit fractional input (.1234567897) truncates to 9 nanosecond digits and that space separator is accepted.


51-66: LGTM! Error handling tests cover key rejection cases.

The tests validate that invalid dates (Feb 30) and timezone-aware timestamps are properly rejected with appropriate error messages.

src/irx/builders/llvmliteir.py (8)

5-5: LGTM! New imports support platform detection and date validation.

ctypes enables runtime pointer-size detection, and datetime validates calendar dates in timestamp literals.

Also applies to: 9-9


50-52: LGTM! New type fields properly declared.

The additions support timestamp lowering and platform-specific type handling.


136-139: LGTM! Platform-specific type detection is well-implemented.

The method correctly uses ctypes to detect pointer width and size_t size at runtime.


145-146: LGTM! Early initialization ensures SIZE_T_TYPE is available.

The call placement is correct—after instantiation but before any type usage.


1070-1178: LGTM! Timestamp lowering is thorough and correct.

The implementation properly handles:

  • Both T and space separators
  • Calendar validation via datetime() (catches invalid dates like Feb 30)
  • Fractional second truncation to 9 nanosecond digits
  • Time bounds checking with named constants
  • Timezone rejection

The Ruff F811 warning at line 1071 is a false positive—plum dispatch allows multiple visit methods.


1588-1591: LGTM! String type handling aligns with VariableDeclaration.

Using "stringascii" for inline string variables matches the pattern in VariableDeclaration and ensures consistent pointer-type handling.


1613-1695: LGTM! Printf/malloc helpers are well-implemented.

The functions correctly:

  • Declare external malloc and snprintf
  • Use SIZE_T_TYPE for size parameters
  • Implement safe heap formatting with two-pass snprintf (size check, then format)

However, these depend on the SIZE_T_TYPE fix at line 187 to work correctly on non-64-bit platforms.


1763-1787: LGTM! Cast-to-string implementation is sound.

The extension properly:

  • Handles integer-to-string via _normalize_int_for_printf
  • Promotes float/half to double for consistent formatting
  • Uses heap allocation for dynamic-length results

Note: Integer casting inherits the boolean sign-extension issue from _normalize_int_for_printf.

Comment on lines +186 to +187
# Platform-sized unsigned integer (assume 64-bit for CI targets)
self._llvm.SIZE_T_TYPE = ir.IntType(64)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove the hard-coded SIZE_T_TYPE initialization.

Line 187 overwrites the platform-detected SIZE_T_TYPE from _init_native_size_types() (line 139), defeating runtime detection. This leftover line should be removed.

Apply this diff:

-        # Platform-sized unsigned integer (assume 64-bit for CI targets)
-        self._llvm.SIZE_T_TYPE = ir.IntType(64)
🤖 Prompt for AI Agents
In src/irx/builders/llvmliteir.py around lines 186 to 187, a hard-coded override
sets self._llvm.SIZE_T_TYPE = ir.IntType(64) which clobbers the
platform-detected SIZE_T_TYPE set by _init_native_size_types() earlier; remove
that line so the runtime-detected SIZE_T_TYPE is preserved (i.e., delete the
assignment at line 187).

@omsherikar omsherikar force-pushed the feature/literal-timestamp-v2 branch from 8228cd2 to 16864d6 Compare October 31, 2025 11:26
@omsherikar omsherikar force-pushed the feature/literal-timestamp-v2 branch from 16864d6 to 583801e Compare October 31, 2025 15:12
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.

1 participant