Skip to content
This repository was archived by the owner on Apr 29, 2026. It is now read-only.

More closure types allowed in capture and better error messages for t…#33

Merged
navicore merged 2 commits intomainfrom
more-closure-types
Nov 30, 2025
Merged

More closure types allowed in capture and better error messages for t…#33
navicore merged 2 commits intomainfrom
more-closure-types

Conversation

@navicore
Copy link
Copy Markdown
Owner

…he ones we do not support

Variant capture:
Error: Variant captures are not yet supported. Capturing Variants in closures requires additional implementation. Supported capture types: Int, Bool, Float, String, Quotation.

Closure capture (nested closures):
Error: Closure captures are not yet supported. Closures capturing other closures require additional implementation. Supported capture types: Int, Bool, Float, String, Quotation.

Summary of closure capture work completed:

Type Status Notes
Int ✅ Supported env_get_int returns i64
Bool ✅ Supported env_get_bool returns i64 (0/1)
Float ✅ Supported env_get_float returns f64
String ✅ Supported env_get_string returns SeqString
Quotation ✅ Supported env_get_quotation returns fn ptr
Variant ❌ Clear error Compound type, deferred
Closure ❌ Clear error Nested closures, deferred

…he ones we do not support

Variant capture:
Error: Variant captures are not yet supported. Capturing Variants in closures requires additional implementation. Supported capture types: Int, Bool,
Float, String, Quotation.

Closure capture (nested closures):
Error: Closure captures are not yet supported. Closures capturing other closures require additional implementation. Supported capture types: Int,
Bool, Float, String, Quotation.

Summary of closure capture work completed:

| Type      | Status        | Notes                            |
|-----------|---------------|----------------------------------|
| Int       | ✅ Supported   | env_get_int returns i64          |
| Bool      | ✅ Supported   | env_get_bool returns i64 (0/1)   |
| Float     | ✅ Supported   | env_get_float returns f64        |
| String    | ✅ Supported   | env_get_string returns SeqString |
| Quotation | ✅ Supported   | env_get_quotation returns fn ptr |
| Variant   | ❌ Clear error | Compound type, deferred          |
| Closure   | ❌ Clear error | Nested closures, deferred        |
@claude
Copy link
Copy Markdown

claude Bot commented Nov 30, 2025

Pull Request Review: Closure Capture Type Expansion

Summary

This PR extends closure capture support from just Int and String to include Bool, Float, and Quotation types. The implementation is well-structured with matching changes across the compiler's codegen and runtime layers, plus comprehensive test coverage.

✅ Strengths

Code Quality

  • Consistent pattern following: The new capture type handlers (Bool, Float, Quotation) follow the exact same pattern as the existing Int and String implementations
  • Clear documentation: Excellent safety documentation on all FFI functions with explicit preconditions
  • Good separation of concerns: Compiler codegen (LLVM IR generation) cleanly separated from runtime getter functions
  • Helpful error messages: Clear, actionable errors for unsupported types (Variant, Closure) explain what's supported and what's not

Architecture

  • Type-safe FFI boundary: Each getter function validates pointer nullness, index bounds, and value types with panic messages
  • Efficient representations:
    • Bool as i64 (0/1) matches LLVM IR conventions
    • Float as f64 direct return (no boxing)
    • Quotation as i64 (function pointer) is lightweight

Testing

  • Comprehensive unit tests: New tests in runtime/src/closures.rs:580-640 cover:
    • test_env_get_bool - both true/false cases
    • test_env_get_float - positive and negative floats
    • test_env_get_quotation - function pointer storage
  • Integration test: examples/test-closure-captures.seq provides end-to-end validation
  • Existing tests maintained: Updates don't break the existing test suite

Documentation

  • Accurate updates: ROADMAP.md properly reflects the new supported types with clear status indicators (✅/⏳)
  • Inline comments: Code comments explain design decisions (e.g., "Bool is stored as i64 in the generated code for simplicity")

🔍 Issues & Concerns

1. Potential Type Mismatch Issue (Minor)

Location: compiler/src/codegen.rs:461

The Bool capture handler retrieves a bool as i64 but then pushes it using patch_seq_push_int:

Type::Bool => {
    // Bool is stored as i64 (0 or 1)
    let bool_var = self.fresh_temp();
    writeln!(
        &mut self.output,
        "  %{} = call i64 @patch_seq_env_get_bool(ptr %env_data, i64 %env_len, i32 {})",
        bool_var, index
    )
    .unwrap();
    let new_stack_var = self.fresh_temp();
    writeln!(
        &mut self.output,
        "  %{} = call ptr @patch_seq_push_int(ptr %{}, i64 %{})",  // ⚠️ push_int for Bool
        new_stack_var, stack_var, bool_var
    )
    .unwrap();
    stack_var = new_stack_var;
}

Concern: This pushes the bool value as an Int on the stack rather than a Bool. While the comment says "Bool is represented as Int (0/1)", this could cause issues:

  • Type system may expect Value::Bool but gets Value::Int
  • Type checking might fail downstream
  • Breaks the type invariant that closures preserve captured value types

Recommendation: Either:

  1. Use patch_seq_push_bool if it exists, OR
  2. If Bool truly should be Int at runtime, add a comment explaining this design decision and ensure the type system expects this

2. Missing Quotation Push Function Declaration (Potential Bug)

Location: compiler/src/codegen.rs:248-256

The codegen declares the new getter functions:

writeln!(&mut ir, "declare i64 @patch_seq_env_get_bool(ptr, i64, i32)").unwrap();
writeln!(&mut ir, "declare double @patch_seq_env_get_float(ptr, i64, i32)").unwrap();
writeln!(&mut ir, "declare i64 @patch_seq_env_get_quotation(ptr, i64, i32)").unwrap();

But at line 496, the code calls:

writeln!(
    &mut self.output,
    "  %{} = call ptr @patch_seq_push_quotation(ptr %{}, i64 %{})",
    new_stack_var, stack_var, fn_ptr_var
)

Concern: I don't see declare ptr @patch_seq_push_quotation(ptr, i64) in the function declarations section. While patch_seq_push_quotation exists in runtime/src/quotations.rs:113, if it's not declared in the LLVM IR, linking will fail.

Recommendation: Verify that patch_seq_push_quotation is declared elsewhere in the codegen, or add:

writeln!(&mut ir, "declare ptr @patch_seq_push_quotation(ptr, i64)").unwrap();

3. Redundant unsafe in Function Signatures

Location: runtime/src/closures.rs:246, 289, 333

The code uses #[unsafe(no_mangle)] attribute:

#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_bool(...)

Issue: The #[unsafe(no_mangle)] syntax is not standard Rust. The correct attribute is #[no_mangle], and the function signature already has unsafe extern "C".

Current Rust version check: This syntax appears to be from an experimental or nightly feature. If this compiles on your Rust version, it may be using a different attribute syntax than standard.

Recommendation: Use standard Rust:

#[no_mangle]
pub unsafe extern "C" fn patch_seq_env_get_bool(...)

4. Test Coverage Gap: Integration Tests

Location: examples/test-closure-captures.seq

Observation: The test file is excellent but isn't referenced in any test automation.

Concern:

  • Is this test file compiled and run as part of cargo test?
  • Without automated execution, it could break silently in future changes

Recommendation:

  • Add integration tests that compile and run this .seq file
  • Or document how to manually run: seqc examples/test-closure-captures.seq --output /tmp/test && /tmp/test
  • Consider adding to CI if not already included

5. Float Comparison in Tests (Minor)

Location: runtime/src/closures.rs:615-616

assert!((env_get_float(env_data, env_len, 0) - 3.14159).abs() < 0.0001);
assert!((env_get_float(env_data, env_len, 1) - (-2.5)).abs() < 0.0001);

Observation: Good use of epsilon comparison for floats.

Minor suggestion: Consider using a const for the epsilon value:

const FLOAT_EPSILON: f64 = 1e-10; // or 1e-6 depending on precision needs

6. Variant Type Detection Pattern

Location: compiler/src/codegen.rs:510

Type::Var(name) if name.starts_with("Variant") => {
    return Err("Variant captures are not yet supported...");
}

Concern: Using string prefix matching (starts_with("Variant")) is fragile:

  • What if there's a type variable named VariantFoo that's not a Variant?
  • What if the type system changes how it names Variant types?

Recommendation: Check if there's a more robust way to detect Variant types, such as:

Type::Variant { .. } => { /* if this variant exists in Type enum */ }

🔒 Security Considerations

✅ No security issues found

The code properly:

  • Validates all pointer parameters (null checks)
  • Validates array bounds before indexing
  • Uses panic for error conditions (appropriate for FFI boundary)
  • Doesn't introduce any unsafe memory operations beyond what's necessary for FFI

⚡ Performance Considerations

✅ Efficient implementation

  • Direct return of primitives (i64, f64) avoids unnecessary allocations
  • No heap allocations in the getter functions
  • LLVM IR generation is straightforward (no extra indirection)

Minor optimization opportunity: The quotation capture converts usize -> i64. If quotation function pointers are never negative, this is fine, but document the assumption.


📋 Recommendations Summary

Critical (Should Fix)

  1. Verify Bool push function: Confirm whether patch_seq_push_int or patch_seq_push_bool should be used
  2. Check LLVM IR declarations: Ensure patch_seq_push_quotation is declared

Important (Should Address)

  1. Fix #[unsafe(no_mangle)] syntax: Use standard #[no_mangle]
  2. Add integration test automation: Ensure .seq test file runs in CI

Nice to Have

  1. Float epsilon constant: Extract magic number 0.0001
  2. Variant detection: Use stronger typing than string prefix matching

🎯 Overall Assessment

Quality: ✅ Good
Readiness: ⚠️ Conditional Approval (pending verification of issues #1 and #2)

This is a solid, incremental improvement that expands closure capabilities in a clean, maintainable way. The main concerns are:

  1. Type correctness for Bool (Int vs Bool)
  2. LLVM IR declaration completeness

Once those are verified/fixed, this PR is ready to merge. Great work on following existing patterns and providing comprehensive documentation!


🔧 Testing Recommendations

Before merging, please verify:

# 1. Ensure all unit tests pass
cargo test --lib

# 2. Ensure integration tests pass  
cargo test --all

# 3. Try compiling the example
seqc examples/test-closure-captures.seq --output /tmp/test-closures

# 4. Run the compiled example
/tmp/test-closures

# 5. Check for clippy warnings
cargo clippy --all-targets -- -D warnings

Expected output from test-closures:

  • All "PASS:" messages
  • No "FAIL:" messages

Review completed with attention to code quality, correctness, performance, and security. Let me know if you need clarification on any points!

Issue 1: Bool push as Int ✅ Intentional
- Line 632-637 shows BoolLiteral also uses push_int with 0/1
- This is a consistent design decision - bools are represented as integers at runtime
- No change needed

Issue 2: push_quotation declaration ✅ Already declared
- Line 238: declare ptr @patch_seq_push_quotation(ptr, i64) exists
- No issue here

Issue 3: #[unsafe(no_mangle)] syntax ✅ Correct for Rust 2024
- This is the modern syntax requiring explicit unsafe marking on attributes
- Used consistently throughout the codebase
- Compiles fine - no change needed

Issue 4: Integration test automation ✅ Already covered
- just ci compiles all examples including test-closure-captures.seq
- Visible in CI output: Compiling examples/test-closure-captures.seq...

Issue 5: Float epsilon ✅ Already fixed
- Changed from pi/e approximations to 1.234/5.678

Issue 6: Variant detection via string prefix ⚠️ Valid but acceptable
- The reviewer is right that starts_with("Variant") is fragile
- However, Variant isn't a direct Type enum variant - it's represented as Type::Var("Variant$N")
- This is a known limitation that could be improved later

Verdict: The PR is ready to merge. All critical issues from the review are either already addressed or are intentional design decisions.
@claude
Copy link
Copy Markdown

claude Bot commented Nov 30, 2025

PR Review: Closure Capture Type Support

Summary

This PR adds support for capturing Bool, Float, and Quotation types in closures, significantly expanding closure functionality. The implementation is well-structured and follows existing patterns consistently.

✅ Strengths

Code Quality

  • Consistent patterns: New functions (env_get_bool, env_get_float, env_get_quotation) follow the exact same structure as existing env_get_int and env_get_string
  • Good documentation: Comprehensive safety documentation and FFI notes in runtime/src/closures.rs
  • Clear error messages: The new error messages explicitly list supported types, much better than before
  • Proper testing: Unit tests added for all three new getter functions (lines 591-644 in closures.rs)

Design Decisions

  • Bool as i64 representation: Intentional design choice that's consistent with how BoolLiteral is handled elsewhere in the codebase
  • Type-specific getters: Avoiding FFI issues by returning primitives (i64, f64) instead of large Value enums is the right approach
  • Quotation as function pointer: Clean design treating quotations as stateless function pointers

⚠️ Issues & Concerns

1. Fragile Variant Detection (Medium Priority)

Location: compiler/src/codegen.rs:522

Type::Var(name) if name.starts_with("Variant") => {

Issue: Using string prefix matching to detect Variants is brittle. If the type system changes or someone creates a type variable named "VariantFoo", this could break.

Recommendation: Consider adding a proper Type::Variant enum variant in the type system, or at minimum add a comment explaining why this approach is used and its limitations.

2. Missing Integration Test Automation

Location: examples/test-closure-captures.seq

Issue: The new test file test-closure-captures.seq is excellent, but I don't see it being run automatically in CI. The justfile shows build-examples compiles all .seq files, but there's no verification that they run successfully or that their output is correct.

Recommendation:

  • Add a test-examples recipe to the justfile that runs compiled examples and verifies output
  • Or add explicit test case in the CI to run this specific test file and check for "PASS" messages

3. Potential Bounds Check Redundancy

Location: All env_get_* functions in runtime/src/closures.rs

Observation: Each function performs identical bounds checking:

  • Null pointer check
  • Negative index check
  • Out of bounds check

Note: This is consistent with existing code, so not a blocker. Just noting that if the team wants to refactor later, extracting a shared validate_env_access() helper could reduce duplication.

🔒 Security

Good: Comprehensive bounds checking and validation prevents:

  • Null pointer dereferences
  • Buffer overflows
  • Negative index attacks

No security concerns found.

🚀 Performance

  • Good: Type-specific getters avoid expensive enum passing through FFI
  • Good: Direct primitive returns (i64, f64) are efficient
  • No performance concerns.

📊 Test Coverage

Runtime Tests

  • test_env_get_bool - Tests true/false cases
  • test_env_get_float - Tests positive/negative floats with epsilon comparison
  • test_env_get_quotation - Tests function pointer storage

Integration Tests

Missing Tests

  • No tests for the new error cases (Closure/Variant capture attempts)
  • No tests verifying the improved error messages

📝 Additional Notes

Documentation Updates

  • ✅ ROADMAP.md correctly updated with completion status
  • ✅ Runtime module documentation updated
  • ✅ Function-level documentation is thorough

Commit Messages

The second commit message is more of an analysis than a commit message. Consider keeping commit messages focused on "what changed" rather than analysis of previous reviews.

Verdict

Recommendation: Approve with minor suggestions

This is solid work that meaningfully extends closure functionality. The two main suggestions are:

  1. Add CI automation for the integration test (or document how to run it manually)
  2. Add a TODO comment about the fragile Variant detection approach

The code is production-ready, but addressing these would improve long-term maintainability.


🤖 Generated by Claude Code

@navicore navicore merged commit c0485a0 into main Nov 30, 2025
2 checks passed
@navicore navicore deleted the more-closure-types branch November 30, 2025 00:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant