Treat unmatched JSON type during element deserialization of decoding … (#820)

* Treat unmatched JSON type during element deserialization of decoding exception instead of ISE to maintain decoding invariant

Fixes #816
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
index 639b3b9..635db46 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
@@ -52,7 +52,7 @@
         return deserializer.deserialize(this)
     }
 
-    val jsonTree = cast<JsonObject>(decodeJson())
+    val jsonTree = cast<JsonObject>(decodeJson(), deserializer.descriptor)
     val discriminator = json.configuration.classDiscriminator
     val type = jsonTree.getValue(discriminator).content
     val actualSerializer = deserializer.findPolymorphicSerializer(this, type).cast<T>()
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
index 6572eb0..eef6cf7 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
@@ -60,13 +60,13 @@
     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
         val currentObject = currentObject()
         return when (descriptor.kind) {
-            StructureKind.LIST, is PolymorphicKind -> JsonTreeListInput(json, cast(currentObject))
+            StructureKind.LIST, is PolymorphicKind -> JsonTreeListInput(json, cast(currentObject, descriptor))
             StructureKind.MAP -> json.selectMapMode(
                 descriptor,
-                { JsonTreeMapInput(json, cast(currentObject)) },
-                { JsonTreeListInput(json, cast(currentObject)) }
+                { JsonTreeMapInput(json, cast(currentObject, descriptor)) },
+                { JsonTreeListInput(json, cast(currentObject, descriptor)) }
             )
-            else -> JsonTreeInput(json, cast(currentObject))
+            else -> JsonTreeInput(json, cast(currentObject, descriptor))
         }
     }
 
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
index ef6fc66..c7163b2 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
@@ -183,7 +183,9 @@
     override fun getCurrent(): JsonElement = JsonArray(array)
 }
 
-internal inline fun <reified T : JsonElement> cast(value: JsonElement): T {
-    check(value is T) { "Expected ${T::class} but found ${value::class}" }
+internal inline fun <reified T : JsonElement> cast(value: JsonElement, descriptor: SerialDescriptor): T {
+    if (value !is T) {
+        throw JsonDecodingException(-1, "Expected ${T::class} as the serialized body of ${descriptor.serialName}, but had ${value::class}")
+    }
     return value
 }
diff --git a/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt b/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
new file mode 100644
index 0000000..578f39d
--- /dev/null
+++ b/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonPolymorphismExceptionTest : JsonTestBase() {
+
+    @Serializable
+    abstract class Base
+
+    @Serializable
+    @SerialName("derived")
+    class Derived(val nested: Nested = Nested()) : Base()
+
+    @Serializable
+    class Nested
+
+    @Test
+    fun testDecodingException() = parametrizedTest { useStreaming ->
+        val serialModule = SerializersModule {
+            polymorphic(Base::class) {
+                subclass<Derived>()
+            }
+        }
+
+        assertFailsWith<JsonDecodingException> {
+            Json(context = serialModule).parse(Base.serializer(), """{"type":"derived","nested":null}""", useStreaming)
+        }
+    }
+}
\ No newline at end of file