Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ private[speedy] object PrettyLightweight { // lightweight pretty printer for CEK
case Control.Question(_) => "question"
case Control.Complete(_) => "complete"
case Control.Error(_) => "error"
case Control.Suspend(_) => "suspend"
case Control.WeAreUnset => "unset"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,9 @@ private[lf] object Speedy {
interruptionCountDown -= 1
metrics.incrCount[StepCount](StepCount.StepCtx)
thisControl match {
case Control.Suspend(thunk) =>
control = thunk()
loop()
case Control.Value(value) =>
popTempStackToBase()
control = popKont().execute(this, value)
Expand Down Expand Up @@ -1296,9 +1299,10 @@ private[lf] object Speedy {
final case class Complete(res: SValue) extends Control[Nothing]
final case class Error(err: interpretation.Error) extends Control[Nothing]
final case object WeAreUnset extends Control[Nothing]
final case class Suspend[Q](thunk: () => Control[Q]) extends Control[Q]

implicit object `Defer Control` extends cats.Defer[Control] {
override def defer[A](x: => Control[A]): Control[A] = x
override def defer[A](x: => Control[A]): Control[A] = Suspend(() => x)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.digitalasset.daml.lf
package speedy

import cats.data.ContT
import com.digitalasset.canton.logging.SuppressingLogging
import com.digitalasset.daml.lf.speedy.SResult.SResultFinal
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import Speedy.Control

class ContTTest
extends AnyWordSpec
with Matchers
with SuppressingLogging {

"ContT[Control, _, _]" should {

"drive a deep flatMap chain without overflowing stack" in {
val N = 100000
val chain: ContT[Control, Nothing, Int] =
(1 to N).foldLeft(ContT.pure[Control, Nothing, Int](0)) { (acc, _) =>
acc.flatMap(i => ContT.pure[Control, Nothing, Int](i + 1))
}

val control: Control[Nothing] = chain.run(n => Control.Value(SValue.SInt64(n.toLong)))

val machine = Speedy.Machine.fromPureSExpr(
PureCompiledPackages.Empty(Compiler.Config.Default),
SExpr.SEValue(SValue.SUnit),
MachineLogger(),
)
machine.setControl(control)

machine.run() shouldBe SResultFinal(SValue.SInt64(N.toLong))
}
}
}
Loading