Lower typed JVM list access to Kotlin

ober

609a5ad5bd9934bf11121218e2e15603ce0c77f8

diff --git a/lib/jerboa/typed/checker.ss b/lib/jerboa/typed/checker.ss
index 6a1bb6a..f4f5e78 100644
--- a/lib/jerboa/typed/checker.ss
+++ b/lib/jerboa/typed/checker.ss
@@ -1548,6 +1548,70 @@
                       (list (cons 'index idx-name))))
                (append size-errors body-errors type-errors)))))]))
 
+  (def (list-like-inner-type type)
+    (and (pair? type)
+         (memq (car type) '(List Vector MutableList))
+         (cadr type)))
+
+  (def (infer-list-size args env type-names expr)
+    (if (not (= (length args) 1))
+      (values #f (bad-constructor-arity expr 'list-size 1 args))
+      (let-values ([(list-ir errors)
+                    (infer-expression (car args) env type-names)])
+        (let* ([list-type (ir-type list-ir)]
+               [inner-type (list-like-inner-type list-type)]
+               [type-errors
+                (if inner-type
+                  '()
+                  (list (make-check-error 'argument-type-mismatch
+                          "list-size expects a List, Vector, or MutableList value"
+                          expr
+                          (expr-source (car args)))))]
+               [ok? (and list-ir (null? errors) (null? type-errors))])
+          (values
+            (and ok?
+                 (make-typed-ir-call 'Int32 (expr-source expr)
+                   'jvm-list-size 'list-size (list list-ir)
+                   (list (cons 'inner-type inner-type))))
+            (append errors type-errors))))))
+
+  (def (infer-list-ref args env type-names expr)
+    (if (not (= (length args) 2))
+      (values #f (bad-constructor-arity expr 'list-ref 2 args))
+      (let-values ([(arg-irs errors) (infer-args args env type-names)])
+        (let* ([types (ir-list-types arg-irs)]
+               [list-type (car types)]
+               [index-type (cadr types)]
+               [inner-type (list-like-inner-type list-type)]
+               [type-errors
+                (append
+                  (if inner-type
+                      '()
+                      (list
+                        (make-check-error
+                          'argument-type-mismatch
+                          "list-ref expects a List, Vector, or MutableList value"
+                          expr
+                          (and (pair? args) (expr-source (car args))))))
+                  (if (eq? index-type 'Int32)
+                      '()
+                      (list
+                        (make-check-error
+                          'argument-type-mismatch
+                          "list-ref index must be Int32"
+                          (list 'list-ref 'Int32 index-type)
+                          (and (pair? (cdr args))
+                               (expr-source (cadr args)))))))]
+               [ok? (and (all-irs-valid? arg-irs)
+                         (null? errors)
+                         (null? type-errors))])
+          (values
+            (and ok?
+                 (make-typed-ir-call inner-type (expr-source expr)
+                   'jvm-list-ref 'list-ref arg-irs
+                   (list (cons 'inner-type inner-type))))
+            (append errors type-errors))))))
+
   (def (infer-make-bytevector args env type-names expr)
     ;; (make-bytevector size) or (make-bytevector size fill). Size and fill are
     ;; numeric; the result is a fresh Bytes buffer. A missing fill defaults to a
@@ -2042,6 +2106,10 @@
               (infer-float-array args env type-names expr)]
              [(int-array-build)
               (infer-int-array-build args env type-names expr)]
+             [(list-size)
+              (infer-list-size args env type-names expr)]
+             [(list-ref)
+              (infer-list-ref args env type-names expr)]
              [(bytes-build)
               (infer-bytes-build args env type-names expr)]
              [(option-some)
diff --git a/lib/jerboa/typed/core.ss b/lib/jerboa/typed/core.ss
index 7295c31..6072930 100644
--- a/lib/jerboa/typed/core.ss
+++ b/lib/jerboa/typed/core.ss
@@ -131,6 +131,8 @@
       exact->inexact
       log2
       debug-string
+      jvm-list-size
+      jvm-list-ref
       record-ctor
       record-pred
       record-accessor
diff --git a/lib/jerboa/typed/kotlin/lower.ss b/lib/jerboa/typed/kotlin/lower.ss
index 7cc6395..979cfef 100644
--- a/lib/jerboa/typed/kotlin/lower.ss
+++ b/lib/jerboa/typed/kotlin/lower.ss
@@ -302,6 +302,10 @@
              (make-kt-lambda
                (list (info-ref info 'index 'index))
                (cadr args))))]
+        [(jvm-list-size)
+         (make-kt-member-get (car args) 'size)]
+        [(jvm-list-ref)
+         (make-kt-index-get (car args) (cadr args))]
         [(jvm-boolean-array-ref)
          (make-kt-index-get (car args) (cadr args))]
         [(jvm-boolean-array-length)
@@ -389,8 +393,8 @@
             (list
               (make-kt-assign
                 (kt-name1 acc-name)
-                (lower-expr (typed-ir-for-fold-body ir)))))))
-        (kt-name1 acc-name)))
+                (lower-expr (typed-ir-for-fold-body ir))))))
+        (kt-name1 acc-name))))
 
   (def (lower-expr ir)
     (cond
diff --git a/tests/test-typed-kotlin.ss b/tests/test-typed-kotlin.ss
index da8dc71..888a031 100644
--- a/tests/test-typed-kotlin.ss
+++ b/tests/test-typed-kotlin.ss
@@ -474,6 +474,57 @@
   int-array-build-kotlin
   "IntArray(items.size, { index -> (items[index] + 1) })")
 
+(define list-form
+  '(typed-library (sample typed lists)
+     (export make-Cell Cell? Cell-x listCount firstX secondName)
+     (type Int32)
+     (type Float32)
+     (record Cell
+       ((x : Float32)
+        (name : String)))
+     (def (listCount (cells : (List Cell))) : Int32
+       (list-size cells))
+     (def (firstX (cells : (MutableList Cell))) : Float32
+       (Cell-x (list-ref cells (int32 0))))
+     (def (secondName (cells : (List Cell))) : String
+       (Cell-name (list-ref cells (int32 1))))))
+
+(define list-kotlin (typed-library-form->kotlin-string list-form))
+
+(test-contains "List size lowers to Kotlin size"
+  list-kotlin
+  "fun listCount(cells: List<Cell>): Int")
+(test-contains "List size returns member property"
+  list-kotlin
+  "return cells.size")
+(test-contains "MutableList ref lowers to Kotlin indexing"
+  list-kotlin
+  "return cells[0].x")
+(test-contains "List ref preserves element type"
+  list-kotlin
+  "return cells[1].name")
+
+(define fold-form
+  '(typed-library (sample typed fold)
+     (export sumTo)
+     (type Int32)
+     (def (sumTo (end : Int32)) : Int32
+       (for/fold ((total (int32 0)))
+                 ((i (in-range (int32 0) end)))
+         (+ total i)))))
+
+(define fold-kotlin (typed-library-form->kotlin-string fold-form))
+
+(test-contains "for/fold lowers to Kotlin run block"
+  fold-kotlin
+  "return run {")
+(test-contains "for/fold lowers Kotlin range loop"
+  fold-kotlin
+  "for (i in 0 until end)")
+(test-contains "for/fold returns accumulator result"
+  fold-kotlin
+  "total\n}")
+
 (define note-form
   '(typed-library (sample typed notes)
      (export appendNote)