Lower typed nullable values to Kotlin

ober

d6122a9179c73e8cab5a75913466df6bc461e41d

diff --git a/lib/jerboa/typed/checker.ss b/lib/jerboa/typed/checker.ss
index 4e3f29d..372a799 100644
--- a/lib/jerboa/typed/checker.ss
+++ b/lib/jerboa/typed/checker.ss
@@ -1646,6 +1646,33 @@
                  (list (cons 'inner-type type-arg))))
           errors))))
 
+  (def (infer-nullable-some args env type-names expr)
+    (if (not (= (length args) 1))
+      (values #f (bad-constructor-arity expr 'nullable-some 1 args))
+      (let-values ([(value-ir errors)
+                    (infer-expression (car args) env type-names)])
+        (let ([value-type (ir-type value-ir)])
+          (values
+            (and value-type
+                 (make-typed-ir-call (list 'Nullable value-type)
+                   (expr-source expr) 'jvm-nullable-some 'nullable-some
+                   (list value-ir)
+                   (list (cons 'inner-type value-type))))
+            errors)))))
+
+  (def (infer-nullable-none args env type-names expr)
+    (if (not (= (length args) 1))
+      (values #f (bad-constructor-arity expr 'nullable-none 1 args))
+      (let* ([type-arg (strip-source-annotations (car args))]
+             [errors (check-type type-arg type-names)])
+        (values
+          (and (null? errors)
+               (make-typed-ir-call (list 'Nullable type-arg)
+                 (expr-source expr) 'jvm-nullable-none 'nullable-none
+                 '()
+                 (list (cons 'inner-type type-arg))))
+          errors))))
+
   (def (infer-result-ok args env type-names expr)
     (if (not (= (length args) 2))
       (values #f (bad-constructor-arity expr 'result-ok 2 args))
@@ -1968,6 +1995,10 @@
               (infer-option-some args env type-names expr)]
              [(option-none)
               (infer-option-none args env type-names expr)]
+             [(nullable-some)
+              (infer-nullable-some args env type-names expr)]
+             [(nullable-none)
+              (infer-nullable-none args env type-names expr)]
              [(result-ok)
               (infer-result-ok args env type-names expr)]
              [(result-err)
diff --git a/lib/jerboa/typed/kotlin/lower.ss b/lib/jerboa/typed/kotlin/lower.ss
index 57a23fe..5f0f5f6 100644
--- a/lib/jerboa/typed/kotlin/lower.ss
+++ b/lib/jerboa/typed/kotlin/lower.ss
@@ -328,6 +328,10 @@
          (make-kt-call (make-kt-name '(JbOption Some)) args)]
         [(option-none)
          (make-kt-call (make-kt-name '(JbOption None)) '())]
+        [(jvm-nullable-some)
+         (car args)]
+        [(jvm-nullable-none)
+         (make-kt-lit 'Null '())]
         [(result-ok)
          (make-kt-call (make-kt-name '(JbResult Ok)) args)]
         [(result-err)
diff --git a/tests/test-typed-kotlin.ss b/tests/test-typed-kotlin.ss
index 32ce571..fdd0d89 100644
--- a/tests/test-typed-kotlin.ss
+++ b/tests/test-typed-kotlin.ss
@@ -170,6 +170,28 @@
 (test-contains "Nullable lowers to Kotlin nullable type" jvm-kotlin
   "data class SsdSession(val sessionId: String, val ssdArea: FloatArray?)")
 
+(define nullable-form
+  '(typed-library (sample typed nullable)
+     (export nonBlank)
+     (def (nonBlank (value : String)) : (Nullable String)
+       (let ((trimmed (string-trim value)))
+         (if (string-blank? trimmed)
+           (nullable-none String)
+           (nullable-some trimmed))))))
+
+(define nullable-kotlin
+  (typed-library-form->kotlin-string nullable-form))
+
+(test-contains "nullable return lowers to Kotlin nullable return"
+  nullable-kotlin
+  "fun nonBlank(value: String): String?")
+(test-contains "nullable none lowers to Kotlin null"
+  nullable-kotlin
+  "null")
+(test-contains "nullable some lowers to wrapped value directly"
+  nullable-kotlin
+  "else trimmed")
+
 (define geometry-form
   '(typed-library (sample typed geometry)
      (export make-SsdCell SsdCell? SsdCell-x SsdCell-y SsdCell-w SsdCell-h