diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala b/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala index b5802e39ca..3795526d0f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala @@ -13,3 +13,6 @@ final class MethodNotFound(message: String, source: Option[SourceContext] = None final class NonApplicableMethod(message: String, source: Option[SourceContext] = None) extends TyperException(message, source) + +final class UnexpectedType(message: String, source: Option[SourceContext] = None) + extends TyperException(message, source) \ No newline at end of file diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala index ccc0784abe..1fd22dd746 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -42,19 +42,21 @@ class SigmaTyper(val builder: SigmaBuilder, } yield res node.getOrElse(mkMethodCall(global, method, args, EmptySubst).withPropagatedSrcCtx(srcCtx)) } + /** * Rewrite tree to typed tree. Checks constituent names and types. Uses * the env map to resolve bound variables and their types. */ def assignType(env: Map[String, SType], bound: SValue, - expected: Option[SType] = None): SValue = ( bound match { + expected: Option[SType] = None): SValue = { val typedValue = (bound match { case Block(bs, res) => var curEnv = env val bs1 = ArrayBuffer[Val]() - for (v @ Val(n, _, b) <- bs) { + for (v @ Val(n, givenTpe, b) <- bs) { if (curEnv.contains(n)) error(s"Variable $n already defined ($n = ${curEnv(n)}", v.sourceContext) - val b1 = assignType(curEnv, b) + val expectedTpe = if (givenTpe != NoType) Some(givenTpe) else None + val b1 = assignType(curEnv, b, expectedTpe) curEnv = curEnv + (n -> b1.tpe) builder.currentSrcCtx.withValue(v.sourceContext) { bs1 += mkVal(n, b1.tpe, b1) @@ -518,6 +520,16 @@ class SigmaTyper(val builder: SigmaBuilder, v => s"Errors found while assigning types to expression $bound: $v assigned NoType") .withEnsuredSrcCtx(bound.sourceContext) + expected match { + case Some(expectedTpe) => + if (expectedTpe != typedValue.tpe) + throw new UnexpectedType( + s"expression $bound was expected to have type: $expectedTpe but has type: ${typedValue.tpe}", bound.sourceContext.toOption) + case None => {} + } + + typedValue +} def assignConcreteCollection(cc: ConcreteCollection[SType], newItems: Seq[Value[SType]]) = { val types = newItems.map(_.tpe).distinct val tItem = if (cc.items.isEmpty) { diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 85476c3c6d..e4a97b370e 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -658,4 +658,14 @@ class SigmaTyperTest extends AnyPropSpec ) typecheck(customEnv, "substConstants(scriptBytes, positions, newVals)") shouldBe SByteArray } + + property("Inferred - Ascription mismatch") { + val code = + s"""{ + | val price: Long = 1.toBigInt + | price + |}""".stripMargin + + typefail(env, code, 2, 22) + } }