From 57b84dbe0745241aa87effcef7f4bf78224d5e3f Mon Sep 17 00:00:00 2001 From: Nadeeshan96 Date: Fri, 5 Jan 2024 09:42:31 +0530 Subject: [PATCH 1/5] Fix handling unresolved values with union types --- .../runtime/internal/JsonInternalUtils.java | 2 +- .../ballerina/runtime/internal/TypeConverter.java | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java index fc5a48f60dce..c1f9758c663b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java @@ -339,7 +339,7 @@ public static Object convertJSON(Object jsonValue, Type targetType) { return jsonValue; case TypeTags.UNION_TAG: matchingType = TypeConverter.getConvertibleTypeInTargetUnionType(jsonValue, - (BUnionType) targetType, null, new ArrayList<>(), new HashSet<>(), true); + (BUnionType) targetType, null, new ArrayList<>(), true); if (matchingType == null) { throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE, targetType, getTypeName(jsonValue)); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 6553c3628a52..160c2e8fd829 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -55,6 +55,7 @@ import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -275,7 +276,7 @@ public static Type getConvertibleType(Object inputValue, Type targetType, String switch (targetTypeTag) { case TypeTags.UNION_TAG: return getConvertibleTypeInTargetUnionType(inputValue, (BUnionType) targetType, varName, - errors, unresolvedValues, allowNumericConversion); + errors, allowNumericConversion); case TypeTags.ARRAY_TAG: if (isConvertibleToArrayType(inputValue, (BArrayType) targetType, unresolvedValues, varName, errors, allowNumericConversion)) { @@ -358,22 +359,21 @@ private static boolean isStringConvertibleToTargetXmlType(Object inputValue, Typ public static Type getConvertibleTypeInTargetUnionType(Object inputValue, BUnionType targetUnionType, String varName, List errors, - Set unresolvedValues, boolean allowNumericConversion) { List memberTypes = targetUnionType.getMemberTypes(); if (TypeChecker.isStructuredType(getType(inputValue))) { return getConvertibleStructuredTypeInUnion(inputValue, varName, errors, - unresolvedValues, allowNumericConversion, memberTypes); + allowNumericConversion, memberTypes); } for (Type memType : memberTypes) { if (TypeChecker.checkIsLikeType(inputValue, memType, false)) { - return getConvertibleType(inputValue, memType, varName, unresolvedValues, errors, false); + return getConvertibleType(inputValue, memType, varName, new HashSet<>(), errors, false); } } for (Type memType : memberTypes) { Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(), errors, allowNumericConversion); if (convertibleTypeInUnion != null) { return convertibleTypeInUnion; } @@ -382,7 +382,6 @@ public static Type getConvertibleTypeInTargetUnionType(Object inputValue, BUnion } private static Type getConvertibleStructuredTypeInUnion(Object inputValue, String varName, List errors, - Set unresolvedValues, boolean allowNumericConversion, List memberTypes) { int initialErrorCount; errors.add(ERROR_MESSAGE_UNION_START); @@ -391,7 +390,7 @@ private static Type getConvertibleStructuredTypeInUnion(Object inputValue, Strin for (Type memType : memberTypes) { initialErrorCount = errors.size(); Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(), errors, allowNumericConversion); currentErrorListSize = errors.size(); if (convertibleTypeInUnion != null) { errors.subList(initialErrorListSize - 1, currentErrorListSize).clear(); From 3c2a22129d737f1b6e21af39efa890b513e83d18 Mon Sep 17 00:00:00 2001 From: Nadeeshan96 Date: Fri, 5 Jan 2024 10:11:11 +0530 Subject: [PATCH 2/5] Test converting to union types with cyclic members --- .../test/resources/test-src/valuelib_test.bal | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index 5be34862ca6a..5922a16c9503 100644 --- a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal +++ b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal @@ -2432,6 +2432,60 @@ function testCloneWithTypeWithAmbiguousUnion() { checkpanic err.detail()["message"]); } +public type CodingExtension record {| + *Element; + + uri url; + Coding valueCoding; +|}; + +public type Coding record {| + *Element; + + string id?; + Extension[] extension?; + uri system?; + string 'version?; + code code?; + string display?; + boolean userSelected?; +|}; + +public type code string; + +public type uri string; + +public type Element record {| + string id?; + Extension[] extension?; + Element...; +|}; + +public type ExtensionExtension record {| + *Element; + + uri url; + Extension[] extension?; +|}; + +public type CodeableConceptExtension record {| + *Element; + + uri url; + CodeableConcept valueCodeableConcept; +|}; + +public type CodeableConcept record {| + *Element; + + string id?; + Extension[] extension?; + Coding[] coding?; + string text?; +|}; + +public type Extension CodeableConceptExtension|ExtensionExtension|CodingExtension; + function testCloneWithTypeToUnion() { int|float|[string, string] unionVar = 2; float|decimal|[string, int]|error tupleValue = unionVar.cloneWithType(UnionTypedesc); @@ -2440,6 +2494,23 @@ function testCloneWithTypeToUnion() { assertFalse(tupleValue is decimal); assertFalse(tupleValue is [string, int]); assertFalse(tupleValue is error); + + json extCoding = { + "valueCoding": { + "system": "http://loinc.org", + "code": "LA29518-0", + "display": "he/him/his/himself" + }, + "url": "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text" + }; + + Extension ext = checkpanic extCoding.cloneWithType(); + assertEquality(ext, { + "url": + "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text", + "valueCoding": {"system": "http://loinc.org", "code": "LA29518-0", "display": "he/him/his/himself"} + }); + assertEquality((typeof ext).toString(), "typedesc CodingExtension"); } type UnionTypedesc typedesc; From f1e4885b276d7069fbeae9fa79835759f4841d9e Mon Sep 17 00:00:00 2001 From: Nadeeshan96 Date: Fri, 5 Jan 2024 11:31:56 +0530 Subject: [PATCH 3/5] Revert "Fix handling unresolved values with union types" This reverts commit 2dc76a449311e382273be160d494c91fff51b60c. --- .../runtime/internal/JsonInternalUtils.java | 2 +- .../ballerina/runtime/internal/TypeConverter.java | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java index c1f9758c663b..fc5a48f60dce 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java @@ -339,7 +339,7 @@ public static Object convertJSON(Object jsonValue, Type targetType) { return jsonValue; case TypeTags.UNION_TAG: matchingType = TypeConverter.getConvertibleTypeInTargetUnionType(jsonValue, - (BUnionType) targetType, null, new ArrayList<>(), true); + (BUnionType) targetType, null, new ArrayList<>(), new HashSet<>(), true); if (matchingType == null) { throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE, targetType, getTypeName(jsonValue)); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 160c2e8fd829..6553c3628a52 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -55,7 +55,6 @@ import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -276,7 +275,7 @@ public static Type getConvertibleType(Object inputValue, Type targetType, String switch (targetTypeTag) { case TypeTags.UNION_TAG: return getConvertibleTypeInTargetUnionType(inputValue, (BUnionType) targetType, varName, - errors, allowNumericConversion); + errors, unresolvedValues, allowNumericConversion); case TypeTags.ARRAY_TAG: if (isConvertibleToArrayType(inputValue, (BArrayType) targetType, unresolvedValues, varName, errors, allowNumericConversion)) { @@ -359,21 +358,22 @@ private static boolean isStringConvertibleToTargetXmlType(Object inputValue, Typ public static Type getConvertibleTypeInTargetUnionType(Object inputValue, BUnionType targetUnionType, String varName, List errors, + Set unresolvedValues, boolean allowNumericConversion) { List memberTypes = targetUnionType.getMemberTypes(); if (TypeChecker.isStructuredType(getType(inputValue))) { return getConvertibleStructuredTypeInUnion(inputValue, varName, errors, - allowNumericConversion, memberTypes); + unresolvedValues, allowNumericConversion, memberTypes); } for (Type memType : memberTypes) { if (TypeChecker.checkIsLikeType(inputValue, memType, false)) { - return getConvertibleType(inputValue, memType, varName, new HashSet<>(), errors, false); + return getConvertibleType(inputValue, memType, varName, unresolvedValues, errors, false); } } for (Type memType : memberTypes) { Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - new HashSet<>(), errors, allowNumericConversion); + unresolvedValues, errors, allowNumericConversion); if (convertibleTypeInUnion != null) { return convertibleTypeInUnion; } @@ -382,6 +382,7 @@ public static Type getConvertibleTypeInTargetUnionType(Object inputValue, BUnion } private static Type getConvertibleStructuredTypeInUnion(Object inputValue, String varName, List errors, + Set unresolvedValues, boolean allowNumericConversion, List memberTypes) { int initialErrorCount; errors.add(ERROR_MESSAGE_UNION_START); @@ -390,7 +391,7 @@ private static Type getConvertibleStructuredTypeInUnion(Object inputValue, Strin for (Type memType : memberTypes) { initialErrorCount = errors.size(); Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - new HashSet<>(), errors, allowNumericConversion); + unresolvedValues, errors, allowNumericConversion); currentErrorListSize = errors.size(); if (convertibleTypeInUnion != null) { errors.subList(initialErrorListSize - 1, currentErrorListSize).clear(); From 48d2c46ae4e29d91bfb5d90246e2152c58b61d78 Mon Sep 17 00:00:00 2001 From: Nadeeshan96 Date: Fri, 5 Jan 2024 11:45:02 +0530 Subject: [PATCH 4/5] Fix converting to union types with cyclic members --- .../java/io/ballerina/runtime/internal/TypeConverter.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 6553c3628a52..eaa860e8e382 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -55,6 +55,7 @@ import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -368,12 +369,12 @@ public static Type getConvertibleTypeInTargetUnionType(Object inputValue, BUnion for (Type memType : memberTypes) { if (TypeChecker.checkIsLikeType(inputValue, memType, false)) { - return getConvertibleType(inputValue, memType, varName, unresolvedValues, errors, false); + return getConvertibleType(inputValue, memType, varName, new HashSet<>(unresolvedValues), errors, false); } } for (Type memType : memberTypes) { Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(unresolvedValues), errors, allowNumericConversion); if (convertibleTypeInUnion != null) { return convertibleTypeInUnion; } @@ -391,7 +392,7 @@ private static Type getConvertibleStructuredTypeInUnion(Object inputValue, Strin for (Type memType : memberTypes) { initialErrorCount = errors.size(); Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(unresolvedValues), errors, allowNumericConversion); currentErrorListSize = errors.size(); if (convertibleTypeInUnion != null) { errors.subList(initialErrorListSize - 1, currentErrorListSize).clear(); From 8dc11fe3e8e39af00fec3f6642c4e0202dc41509 Mon Sep 17 00:00:00 2001 From: Nadeeshan96 Date: Thu, 18 Jan 2024 11:59:54 +0530 Subject: [PATCH 5/5] Add tests with cyclic readonly and nilable types --- .../test/resources/test-src/valuelib_test.bal | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index 5922a16c9503..541815e4a6a2 100644 --- a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal +++ b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal @@ -2455,6 +2455,12 @@ public type code string; public type uri string; +public type ElementReadOnlyCyclic record {| + string id?; + Extension[] extension?; + ((ElementReadOnlyCyclic & readonly)|())...; +|}; + public type Element record {| string id?; Extension[] extension?; @@ -2484,8 +2490,31 @@ public type CodeableConcept record {| string text?; |}; +public type ExtensionExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + Extension[] extension?; +|}; + +public type CodeableConceptExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + CodeableConcept valueCodeableConcept; +|}; + +public type CodingExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + Coding valueCoding; +|}; + public type Extension CodeableConceptExtension|ExtensionExtension|CodingExtension; +public type Extension2 CodeableConceptExtension2|ExtensionExtension2|CodingExtension2; + function testCloneWithTypeToUnion() { int|float|[string, string] unionVar = 2; float|decimal|[string, int]|error tupleValue = unionVar.cloneWithType(UnionTypedesc); @@ -2511,6 +2540,14 @@ function testCloneWithTypeToUnion() { "valueCoding": {"system": "http://loinc.org", "code": "LA29518-0", "display": "he/him/his/himself"} }); assertEquality((typeof ext).toString(), "typedesc CodingExtension"); + + Extension2 ext2 = checkpanic extCoding.cloneWithType(); + assertEquality(ext2, { + "url": + "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text", + "valueCoding": {"system": "http://loinc.org", "code": "LA29518-0", "display": "he/him/his/himself"} + }); + assertEquality((typeof ext2).toString(), "typedesc CodingExtension2"); } type UnionTypedesc typedesc;