Skip to content

Commit

Permalink
speedy: short circuit precondition checking (digital-asset#12984)
Browse files Browse the repository at this point in the history
* speedy: short circuit precondition checking

Instead of evaluating all preconditions of templates and interfaces we
evaluate preconditions one by one and throw an exception as soon as we
find a failing one.

This implemented via a lazy SBCheckPrecond builtin and a foldleft
expression over the list of preconditions.

CHANGELOG_BEGIN
CHANGELOG_END

* Update daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala

Co-authored-by: Remy <remy.haemmerle@daml.com>

* Update daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala

Co-authored-by: Remy <remy.haemmerle@daml.com>

* fix SUnit -> Unit

* tests

Co-authored-by: Remy <remy.haemmerle@daml.com>
  • Loading branch information
Robin Krom and remyhaemmerle-da authored Feb 22, 2022
1 parent 942fbe0 commit adaddde
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -722,17 +722,15 @@ private[lf] final class Compiler(
// `SBToInterface` is needed because interfaces do not have the same representation as the underlying template
t.InterfacePrecondDefRef(impl._1)(SBToInterface(tmplId)(env2.toSEVar(tmplArgPos)))
)
// TODO Clean this up as part of changing how we evaluate these
// https://github.com/digital-asset/daml/issues/11762

val precondsArray: ImmArray[s.SExpr] =
(Iterator(translateExp(env2, tmpl.precond)) ++ implementsPrecondsIterator ++ Iterator(
s.SEValue.EmptyList
)).to(ImmArray)
val preconds = s.SEApp(s.SEBuiltin(SBConsMany(precondsArray.length - 1)), precondsArray.toList)
// We check precondition in a separated builtin to prevent
// further evaluation of agreement, signatories, observers and key
// in case of failed precondition.
let(env2, SBCheckPrecond(tmplId)(env2.toSEVar(tmplArgPos), preconds)) { (_, env) =>
(Iterator(translateExp(env2, tmpl.precond)) ++ implementsPrecondsIterator).to(ImmArray)

val preconds = precondsArray.foldLeft[s.SExpr](s.SEValue.Unit)((acc, precond) =>
SBCheckPrecond(tmplId)(env2.toSEVar(tmplArgPos), acc, precond)
)

let(env2, preconds) { (_, env) =>
SBUCreate(tmplId, byInterface)(
env.toSEVar(tmplArgPos),
translateExp(env, tmpl.agreementText),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ private[speedy] sealed abstract class SBuiltin(val arity: Int) {
case otherwise => unexpectedType(i, "SBool", otherwise)
}

final protected def getSUnit(args: util.ArrayList[SValue], i: Int): Unit =
args.get(i) match {
case SUnit => ()
case otherwise => unexpectedType(i, "SUnit", otherwise)
}

final protected def getSInt64(args: util.ArrayList[SValue], i: Int): Long =
args.get(i) match {
case SInt64(value) => value
Expand Down Expand Up @@ -894,25 +900,23 @@ private[lf] object SBuiltin {

/** $checkPrecondition
* :: arg (template argument)
* -> Unit
* -> Bool (false if ensure failed)
* -> Unit
*/
final case class SBCheckPrecond(templateId: TypeConName) extends SBuiltinPure(2) {
final case class SBCheckPrecond(templateId: TypeConName) extends SBuiltinPure(3) {
override private[speedy] def executePure(args: util.ArrayList[SValue]): SUnit.type = {
if (
!(getSList(args, 1).iterator.forall(_ match {
case SBool(b) => b
case otherwise => crash(s"type mismatch SBCheckPrecond: expected SBool, got $otherwise")
}))
)
discard(getSUnit(args, 1))
val precond = getSBool(args, 2)
if (precond) SUnit
else
throw SErrorDamlException(
IE.TemplatePreconditionViolated(
templateId = templateId,
optLocation = None,
arg = args.get(0).toUnnormalizedValue,
)
)
SUnit
}
}

Expand Down
Loading

0 comments on commit adaddde

Please sign in to comment.