From 5a76fb0f0eb3945754fdce04eda1c2d1c0ccdcdc Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 1 Oct 2024 08:31:46 +0530 Subject: [PATCH] Fix invalid accepted type for union and tables --- .../api/types/semtype/ShapeAnalyzer.java | 5 ++-- .../runtime/internal/TypeChecker.java | 5 +--- .../runtime/internal/types/BTableType.java | 14 ++++++++-- .../runtime/internal/types/BUnionType.java | 10 ++++++- .../internal/types/TypeWithAcceptedType.java | 15 ++++++++++ .../runtime/internal/types/TypeWithShape.java | 4 +-- .../internal/types/semtype/TableUtils.java | 28 +++++++++++++++++-- 7 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java index a7f26fd81c02..1eb31722231b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -3,6 +3,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BValue; +import io.ballerina.runtime.internal.types.TypeWithAcceptedType; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.values.DecimalValue; @@ -14,8 +15,8 @@ private ShapeAnalyzer() { } public static Optional acceptedTypeOf(Context cx, Type typeDesc) { - if (typeDesc instanceof TypeWithShape typeWithShape) { - return typeWithShape.acceptedTypeOf(cx); + if (typeDesc instanceof TypeWithAcceptedType typeWithAcceptedType) { + return typeWithAcceptedType.acceptedTypeOf(cx); } return Optional.of(SemType.tryInto(typeDesc)); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 22c765549c92..132d6e5fec8c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java @@ -68,7 +68,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; @@ -307,9 +306,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType) { */ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boolean allowNumericConversion) { Context cx = context(); - Optional readonlyShape = ShapeAnalyzer.shapeOf(cx, sourceValue); - assert readonlyShape.isPresent(); - SemType shape = readonlyShape.get(); + SemType shape = ShapeAnalyzer.shapeOf(cx, sourceValue).orElseThrow(); SemType targetSemType = ShapeAnalyzer.acceptedTypeOf(cx, targetType).orElseThrow(); if (Core.isSubType(cx, shape, NumericTypeHolder.NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java index 45226ee36d66..02948494ff14 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BTable; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.TableUtils; @@ -218,8 +219,17 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public Optional acceptedTypeOf(Context cx) { - // TODO: this is not correct but can we actually match tables? - return Optional.of(getSemType()); + SemType constraintType = ShapeAnalyzer.acceptedTypeOf(cx, this.constraint).orElseThrow(); + SemType semType; + if (fieldNames.length > 0) { + semType = TableUtils.acceptedTypeContainingKeySpecifier(cx, constraintType, fieldNames); + } else if (keyType != null) { + SemType keyAcceptedType = ShapeAnalyzer.acceptedTypeOf(cx, keyType).orElseThrow(); + semType = TableUtils.acceptedTypeContainingKeyConstraint(cx, constraintType, keyAcceptedType); + } else { + semType = TableUtils.acceptedType(cx.env, constraintType); + } + return Optional.of(semType); } private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java index e47094cbb4c3..27ba26ae860a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java @@ -27,8 +27,10 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -48,7 +50,7 @@ * * @since 0.995.0 */ -public class BUnionType extends BType implements UnionType, SelectivelyImmutableReferenceType { +public class BUnionType extends BType implements UnionType, SelectivelyImmutableReferenceType, TypeWithAcceptedType { public boolean isCyclic = false; public static final String PIPE = "|"; @@ -554,4 +556,10 @@ public void setIntersectionType(IntersectionType intersectionType) { public SemType createSemType() { return memberTypes.stream().map(SemType::tryInto).reduce(Builder.neverType(), Core::union); } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) + .reduce(Builder.neverType(), Core::union)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java new file mode 100644 index 000000000000..32e8d4243751 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java @@ -0,0 +1,15 @@ +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +/** + * Any {@code Type} that contains selectively immutable types must implement this interface. It represents the type + * against which {@code isLikeType} operation is performed. + */ +public interface TypeWithAcceptedType { + + Optional acceptedTypeOf(Context cx); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java index 650609221196..15b3623176b4 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java @@ -30,13 +30,11 @@ * different objects should be able to do their shape calculations in a non-blocking manner, even when they share the * same instance of {@code TypeWithShape}. */ -public interface TypeWithShape { +public interface TypeWithShape extends TypeWithAcceptedType { Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); - Optional acceptedTypeOf(Context cx); - boolean couldInherentTypeBeDifferent(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java index bc265037c571..9495616f9ec2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java @@ -31,6 +31,7 @@ import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; public final class TableUtils { @@ -39,7 +40,16 @@ public final class TableUtils { private TableUtils() { } + public static SemType acceptedTypeContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return tableContainingKeySpecifierInner(fieldNames, cx, tableConstraint, CELL_MUT_UNLIMITED); + } + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return tableContainingKeySpecifierInner(fieldNames, cx, tableConstraint, CELL_MUT_LIMITED); + } + + private static SemType tableContainingKeySpecifierInner(String[] fieldNames, Context cx, SemType tableConstraint, + CellAtomicType.CellMutability cellMutLimited) { SemType[] fieldNameSingletons = new SemType[fieldNames.length]; SemType[] fieldTypes = new SemType[fieldNames.length]; for (int i = 0; i < fieldNames.length; i++) { @@ -55,10 +65,20 @@ public static SemType tableContainingKeySpecifier(Context cx, SemType tableConst SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, fieldTypes.length, Builder.neverType(), CELL_MUT_NONE) : fieldTypes[0]; - return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, CELL_MUT_LIMITED); + return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, cellMutLimited); + } + + public static SemType acceptedTypeContainingKeyConstraint(Context cx, SemType tableConstraint, + SemType keyConstraint) { + return tableContainingKeyConstraintInner(cx, tableConstraint, keyConstraint, CELL_MUT_UNLIMITED); } public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + return tableContainingKeyConstraintInner(cx, tableConstraint, keyConstraint, CELL_MUT_LIMITED); + } + + private static SemType tableContainingKeyConstraintInner(Context cx, SemType tableConstraint, SemType keyConstraint, + CellAtomicType.CellMutability mut) { Optional lat = Core.listAtomicType(cx, keyConstraint); SemType normalizedKc = lat.map(atom -> { FixedLengthArray member = atom.members(); @@ -68,13 +88,17 @@ public static SemType tableContainingKeyConstraint(Context cx, SemType tableCons default -> keyConstraint; }; }).orElse(keyConstraint); - return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.getValType(), CELL_MUT_LIMITED); + return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.getValType(), mut); } public static SemType tableContaining(Env env, SemType tableConstraint) { return tableContaining(env, tableConstraint, CELL_MUT_LIMITED); } + public static SemType acceptedType(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_UNLIMITED); + } + private static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { return tableContaining(env, tableConstraint, Builder.getValType(), Builder.getValType(), mut); }