You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: scall-core/src/main/scala/io/chymyst/dhall/TypeCheck.scala
+21-5
Original file line number
Diff line number
Diff line change
@@ -604,7 +604,7 @@ object TypeCheck {
604
604
typeError(
605
605
s"Expression `assert` failed: Unequal sides, ${lop.alphaNormalized.betaNormalized.print} does not equal ${rop.alphaNormalized.betaNormalized.print}, in ${exprN.print}"
606
606
)
607
-
case other => typeError(s"An `assert` expression must have an equality type but has ${other.print}")
607
+
case other => typeError(s"An `assert` expression must have an equality type but has type ${other.print}")
608
608
}
609
609
case errors => errors
610
610
}
@@ -630,11 +630,27 @@ object TypeCheck {
630
630
}
631
631
632
632
case tt @Expression(Application(Expression(ExprBuiltin(Builtin.Optional)), t)) =>
633
-
if (pathComponents.head.isOptionalLabel) {
634
-
validate(gamma, body, t).map(_ => tt)
635
-
} else typeError(s"An Optional value must be updated with `?` but instead found ${pathComponents.head}")
633
+
pathComponents.head match {
634
+
casePathComponent.Label(fieldName) =>
635
+
typeError(s"An Optional value must be updated with `?` but instead found ${fieldName}")
636
+
casePathComponent.DescendOptional=>
637
+
pathComponents.tail match {
638
+
caseSeq() =>// The expression is (Some x) with ? = body. The type of this expression is Optional Y where body : Y.
639
+
// We use `validate` to impose the constraint that body's type is `t`. See https://github.com/dhall-lang/dhall-haskell/issues/2597
640
+
validate(gamma, body, t).map(_ => tt)
641
+
case tail =>// The expression is (Some x) with ?.a.b = body. The type of this expression is Optional Y where Y is the type of `x with a.b = body`. Here, `x` is a new variable that should not be free in `body`.
// newWith.inferTypeWith(newGamma).map(t1 => Expression(Application(Expression(ExprBuiltin(Builtin.Optional)), t1))) // This would allow changing types after `with`.
647
+
validate(newGamma, newWith, t).map(_ => tt)
636
648
637
-
case other => typeError(s"A `with` expression's arg must have record type, but instead found ${other.print}")
649
+
}
650
+
651
+
}
652
+
653
+
case other => typeError(s"A `with` expression's argument must have record type or Optional type, but instead found type ${other.print}")
Copy file name to clipboardExpand all lines: scall-core/src/test/scala/io/chymyst/dhall/unit/SimpleSemanticsTest.scala
+25-3
Original file line number
Diff line number
Diff line change
@@ -370,7 +370,6 @@ class SimpleSemanticsTest extends DhallTest {
370
370
371
371
test("failure in eta expansion with two curried arguments") {
372
372
valfailure="λ(f : Bool → Bool → Bool) → assert : f === (λ(x : Bool) → λ(y : Bool) → f y x)".dhall.inferTypeWith(KnownVars.empty)
373
-
println(failure)
374
373
expect(failure match {
375
374
caseTypecheckResult.Invalid(errors) => errors exists (_ contains "Unequal sides, f does not equal λ(_ : Bool) → λ(_ : Bool) → f _ _@1")
376
375
})
@@ -383,15 +382,13 @@ class SimpleSemanticsTest extends DhallTest {
383
382
384
383
test("failure 1 with f x in eta expansion with free occurrences of external bound variable") {
385
384
valfailure="λ(f : Bool → Bool → Bool) → λ(x : Bool) → assert : f x === (λ(x : Bool) → f x x)".dhall.inferTypeWith(KnownVars.empty)
386
-
println(failure)
387
385
expect(failure match {
388
386
caseTypecheckResult.Invalid(errors) => errors exists (_ contains "Unequal sides, f x does not equal λ(_ : Bool) → f _ _, in f x ≡ (λ(x : Bool) → f x x)")
389
387
})
390
388
}
391
389
392
390
test("failure 2 with f x in eta expansion with free occurrences of external bound variable") {
393
391
valfailure="λ(f : Bool → Bool → Bool) → λ(x : Bool) → assert : f === (λ(x : Bool) → f x x)".dhall.inferTypeWith(KnownVars.empty)
394
-
println(failure)
395
392
expect(failure match {
396
393
caseTypecheckResult.Invalid(errors) =>
397
394
errors exists (_ contains "Types of two sides of `===` are not equivalent: ∀(_ : Bool) → ∀(_ : Bool) → Bool and ∀(x : Bool) → Bool")
@@ -512,4 +509,29 @@ class SimpleSemanticsTest extends DhallTest {
512
509
expect(!Semantics.equivalent(x, y))
513
510
}
514
511
512
+
test("`with` for Optional works if it does not change type") { // https://github.com/dhall-lang/dhall-haskell/issues/2597
513
+
valresult="(Some 1) with ? = 2".dhall.typeCheckAndBetaNormalize()
514
+
expect(result.unsafeGet.print =="Some 2")
515
+
}
516
+
517
+
test("fail `with` for Optional if it changes type") {
518
+
valresult="""(Some 1) with ? = "hello"""".dhall.typeCheckAndBetaNormalize()
519
+
expect(
520
+
Try(
521
+
result.unsafeGet
522
+
).failed.get.getMessage contains "Inferred type Text differs from the expected type Natural, expression under type inference: \"hello\""
523
+
)
524
+
}
525
+
526
+
test("`with` for Optional works if it does not change type, with deep record access") { // https://github.com/dhall-lang/dhall-haskell/issues/2597
527
+
valresult="(Some { x.y = 1 }) with ?.x.y = 2".dhall.typeCheckAndBetaNormalize()
528
+
expect(result.unsafeGet.print =="Some { x = { y = 2 } }")
529
+
}
530
+
531
+
test("fail `with` for Optional if it changes type, with deep record access") {
532
+
valresult="""(Some { x.y = 1 }) with ?.x.y = "hello"""".dhall.typeCheckAndBetaNormalize()
533
+
// expect(result.unsafeGet.print == "Some { x = { y = \"hello\" } }")
534
+
expect(Try(result.unsafeGet).failed.get.getMessage contains "Inferred type { x : { y : Text } } differs from the expected type { x : { y : Natural } }")
0 commit comments